Skip to main content

How to add free gift products to cart

Introduction

This guide will help you to add free gift products to cart.

Hooks

manage-cart-after-response

This hook helps to manage the cart after the response.It can be used to add free gift products to cart.

 appmakerFunctions.registerAppmakerFn({
trigger: 'manage-cart-after-response',
namespace: 'free-gift', // your namespace
function: AddFreeGiftToCart, // your function
});

Steps

  1. Use the manage-cart-after-response hook to add free gift products to cart. You can register the hook in the index.js file.
import appmakerFunctions from '@appmaker-xyz/core';
import addFreeGift from './lib/addFreeGift'; // your function file

export function activate() {
appmakerFunctions.registerAppmakerFn({
trigger: 'manage-cart-after-response',
namespace: 'free-gift', // your namespace
function: addFreeGift, // your function
});
}

// Register the plugin or theme. let's take example of plugin
const Plugin = {
id: 'custom-free-gift', // your plugin id
name: 'custom-free-gift', // your plugin name
activate,
};

export default Plugin;
  1. Create a function file addFreeGift.js in a folder lib and add the following code.
import { parse } from '@appmaker-xyz/core';
import { shopifyIdHelper, productsStore } from '@appmaker-xyz/shopify';
import getConditionalCartSum from './lib/getConditionalCartSum'; // your function to get cart total
import { isEmpty } from 'lodash';

export function addFreeGift(input, dependencies) {

const {
cartHelpers: { hasVariantInCart },
coreDispatch,
} = dependencies;

const freeGiftsToAdd = [];
const existingFreeGifts = [];
const freeGiftToRemove = [];

let nextFreeGiftIndex = false;
let cartTotals = [];

// Logger for debugging
function log(...args) {
if (debugLog) {
console.log('free-gift', ...args);
}
}

const freeGiftDependencies = {
log,
...dependencies,
};

// set cart message if any
function setCartMessage(message, product) {
coreDispatch({
type: 'SET_APP_STORAGE_STATE',
value: {
cart_custom_message: message,
next_free_gift_product: product,
},
});
}


// get freeGifts
const freeGifts = getFreeGifts(); // your function to get free gifts

if (Array.isArray(freeGifts) && freeGifts.length > 0) {
// Sort freeGift by cart_value_amount
freeGifts.sort((a, b) => a.cart_value_amount - b.cart_value_amount);
nextFreeGiftIndex = 0;

for (const [freeGiftIndex, gift] of freeGifts.entries()) {

const cartTotal = getConditionalCartSum({
input,
cartSumCondition: gift?.cart_value_condition,
collections: gift?.collections,
dependencies: freeGiftDependencies,
}); // your function to get cart total

cartTotals[freeGiftIndex] = cartTotal;

const { products, cart_value_amount } = gift;
const cart_value_required = parseFloat(cart_value_amount);
if (cartTotal >= cart_value_required) {
log('free gift condition met', {
cartTotal,
cart_value_required,
});

for (const product of products) {


let availableVariantId = await getAvailableVariant();// your function to get available variant

// Check if product is available
if (
availableVariantId &&
!hasVariantInCart(input, availableVariantId)
) {
log('Adding free gift', availableVariantId);

freeGiftsToAdd.push({
variantId: availableVariantId,
quantity: 1,
customAttributes: [
{
key: 'appmaker_free_gift',
value: 'Free Gift',
},
...(gift?.attributes || []),
],
});
} else if (availableVariantId) {
log('Free gift already in cart', availableVariantId);
existingFreeGifts.push(availableVariantId);
} else {
log('Not adding as all gift unavailable', availableVariantId);
}
}
nextFreeGiftIndex = freeGifts[freeGiftIndex + 1]
? freeGiftIndex + 1
: false;
} else if (cartTotal < cart_value_required) {
log('free gift condition not met', {
cartTotal,
cart_value_required,
});
}
}

// Remove all non-elgible free gifts
if (input?.currentCart?.lineItems?.edges?.length > 0) {
input.currentCart.lineItems.edges.forEach((item) => {
const currentLineItem = item.node;
const { customAttributes } = currentLineItem;
const isFreeGift = customAttributes.find(
(attribute) => attribute.key === 'appmaker_free_gift',
);
if (isFreeGift) {
const variantId = currentLineItem.variant.id;
// check if variant is in existingFreeGifts
const isExistingFreeGift = existingFreeGifts.find(
(existingFreeGiftItem) => existingFreeGiftItem === variantId,
);
if (!isExistingFreeGift) {
log('Removing free gift', variantId);
freeGiftToRemove.push({
id: currentLineItem.id,
variantId: variantId,
quantity: 0,
});
}
} else {
// log('not a free gift', currentLineItem);
}
});
}

log('nextFreeGiftIndex', nextFreeGiftIndex);

/**
* Add free gift message if any free gift is pending to add and message is set
*/
if (
nextFreeGiftIndex !== false &&
!isEmpty(freeGifts[nextFreeGiftIndex].pre_offer_apply_cart_message)
) {
const nextFreeGift = freeGifts[nextFreeGiftIndex];
const difference =
nextFreeGift.cart_value_amount - cartTotals[nextFreeGiftIndex];

const cartMessage = parse({
template: {
message: nextFreeGift.pre_offer_apply_cart_message,
},
data: {
difference: difference.toFixed(2),
currentCart: input.currentCart,
freeGift: nextFreeGift,
},
});

log('cart message added', cartMessage?.message);
const nextFreeGiftProduct = nextFreeGift?.products[0]?.product_gid;
const productData = await productsStore.getProduct(
shopifyIdHelper(nextFreeGiftProduct),
);
log('productData', nextFreeGift.products[0].product_gid, productData);
setCartMessage(cartMessage?.message, { productData, nextFreeGift });
} else {
log('cart message removed');
setCartMessage('', {});
}
}

// Merge freeGiftsToAdd and lineItemsToUpdate to input. This will be used to update cart

input.lineItemsToAdd = input.lineItemsToAdd
? [...input.lineItemsToAdd, ...freeGiftsToAdd]
: freeGiftsToAdd;

input.lineItemsToUpdate = input.lineItemsToUpdate
? [...input.lineItemsToUpdate, ...freeGiftToRemove]
: freeGiftToRemove;

return input;
};

Above is a sample code for free gift. You can use this as a reference to create your own custom logic.