I’m working on a Shopify store using the Horizon theme and need to create an automatic product pairing system. Here’s what I have:
Service Membership (main product)
- Basic Package (variant)
- Premium Package (variant)
- Trial Package (variant)
Deposit (secondary product)
- Refundable (Basic) (variant)
- Refundable (Premium) (variant)
- Refundable (Trial) (variant)
I want to automatically add the matching Deposit variant when someone adds a Service Membership variant to their cart. For example, when Basic Package is added, it should automatically include the Basic Deposit variant.
I also want to hide the remove button for the deposit item and make it so removing the main membership automatically removes the paired deposit.
Here’s my current code for the product page:
document.addEventListener("DOMContentLoaded", function () {
const depositMapping = {
46531128230137: 46586197672185, // Basic
46531128262905: 46586197704953, // Premium
46569399288057: 46586197737721, // Trial
};
const addForm = document.querySelector('form[action^="/cart/add"]');
if (!addForm) return;
const variantSelect = addForm.querySelector('input[name="id"]');
if (!variantSelect) return;
addForm.addEventListener("submit", function (event) {
const chosenVariant = parseInt(variantSelect.value);
const depositVariant = depositMapping[chosenVariant];
if (!depositVariant) return;
event.preventDefault();
fetch("/cart/add.js", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [{ id: chosenVariant, quantity: 1 }]
}),
})
.then(response => response.json())
.then(result => {
return fetch("/cart/add.js", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [{ id: depositVariant, quantity: 1 }]
}),
});
})
.then(response => response.json())
.then(result => {
window.location.href = '/cart';
})
.catch(error => {
console.error("Error adding products:", error);
addForm.submit();
});
});
});
And for the cart page:
{% if item.product.tags contains 'Membership' %}
<script>
document.addEventListener("DOMContentLoaded", function () {
const depositMapping = {
46531128230137: 46586197672185, // Basic
46531128262905: 46586197704953, // Premium
46569399288057: 46586197737721, // Trial
};
function findItemKey(cartData, variantId) {
const foundItem = cartData.find(item => item.variant_id === variantId);
return foundItem ? foundItem.key : null;
}
function cleanupOrphanedDeposits() {
fetch("/cart.js")
.then((response) => response.json())
.then((cartData) => {
const cartItems = cartData.items;
const presentVariants = cartItems.map((item) => item.variant_id);
Object.entries(depositMapping).forEach(([membershipVariant, depositVariant]) => {
const membershipExists = presentVariants.includes(parseInt(membershipVariant));
const depositExists = presentVariants.includes(depositVariant);
if (!membershipExists && depositExists) {
const depositKey = findItemKey(cartItems, depositVariant);
if (depositKey) {
fetch("/cart/change.js", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
id: depositKey,
quantity: 0,
}),
})
.then(() => {
window.location.reload();
})
.catch(error => console.error("Failed to remove deposit:", error));
}
}
});
})
.catch(error => console.error("Failed to fetch cart data:", error));
}
document.addEventListener('click', function(event) {
if (event.target.closest('[on\\:click^="/onLineItemRemove"]')) {
setTimeout(cleanupOrphanedDeposits, 1500);
}
});
});
</script>
{% endif %}
The issue is that sometimes only the deposit gets added to the cart, not both products. Any ideas on how to fix this?