Recently updated the Shopify CLI and I can no longer get my discount functions to trigger on my development store. The exact same code that is running in my production app no longer triggers in my development store and Im at my wits end with how to fix it. Suddenly yesterday afternoon my functions now no longer trigger on cart updates.
Things Ive tried:
- Deleted the discount function and tried recreating it
- Reconfigure the discounts UI in the dev store
- Downgrade shopify CLI to 84.1
- Adding console logs and watching the function in the console, never triggers
- Uninstalling, reinstalling the app on different stores, recreating discounts, nothing
- Blew away my whole repo to get rid of any build artifacts, same
- Tried resetting the app config, nada
I know all the values are being set its just that when I post a value into the cart that my discount function should pickup, the cart never renders the discount. The function that hasnt been deployed since August continues to work fine in my store on shop.xpnetwork.com
My Development app renders fine as does the discount UI for the function. I can see the proper cart attributes being set in the cart for the function to pick them up. I have no build errors in watch or on deploy.
fetch(â/cart.jsâ).then(r => r.json()).then(cart => console.log(âCart attributes:â, cart.attributes))
Promise {}
VM519:1 Cart attributes:
{_xp_points_to_use: â280â, _xp_user_token: âeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4YâŚDAwfQ.Ih2_V8zrYyi2R1QEnhl8jpwquF8Cq8N1qgZs3tEsqO4â}
_xp_points_to_use
:
â280â
$ shopify --version
@shopify/cli/3.85.5 darwin-x64 node-v22.17.1
The only thing of note is that I converted the function to typescript at one point and after that is when it stopped working. I reverted it back to JS and it still doesnât work as it did before. These runs were all right before it stopped working with the exact same code. Dont know why this would matter as it is compiling into dist
folder just fine even with the change âŚ
// extensions/xpn-redeem-function/node_modules//shopify_function/run.tsfunction run_default(userfunction) {const input_obj = Javy.JSON.fromStdin();const output_obj = userfunction(input_obj);Javy.JSON.toStdout(output_obj);}
// extensions/xpn-redeem-function/src/cart_discounts_generate_run.jsfunction cartDiscountsGenerateRun(input) {const config = parseDiscountConfiguration(input.discount?.metafield?.value);const userXPPoints = getUserXPPointsFromCart(input.cart);console.log(âuserXPPointsâ, userXPPoints);if (userXPPoints === 0) {return { operations: };}const discountClasses = input.discount?.discountClasses || ;const hasOrderDiscountClass = discountClasses.includes(âORDERâ /* Order /);const hasProductDiscountClass = discountClasses.includes(âPRODUCTâ / Product /);const hasShippingDiscountClass = discountClasses.includes(âSHIPPINGâ / Shipping /);if (!hasOrderDiscountClass && !hasProductDiscountClass && !hasShippingDiscountClass) {return { operations: [] };}let activeDiscountType = null;if (hasOrderDiscountClass) {activeDiscountType = âorderâ;} else if (hasProductDiscountClass) {activeDiscountType = âproductâ;} else if (hasShippingDiscountClass) {activeDiscountType = âshippingâ;}const isPureDeliveryFunction = activeDiscountType === âshippingâ;if (isPureDeliveryFunction) {return handleDeliveryDiscounts(input, config, userXPPoints);} else {return handleCartLineDiscounts(input,config,userXPPoints,activeDiscountType === âorderâ,activeDiscountType === âproductâ);}}function handleDeliveryDiscounts(input, config, userXPPoints) {const operations = [];if (userXPPoints >= config.shippingXPRate && input.cart.deliveryGroups?.length > 0) {const firstDeliveryGroup = input.cart.deliveryGroups[0];const maxPossibleDiscount = Math.floor(userXPPoints / config.shippingXPRate);const cartSubtotal = parseFloat(input.cart.cost?.subtotalAmount?.amount || â0â);const discountAmount = cartSubtotal <= 0 ? 0 : Math.min(maxPossibleDiscount, Math.ceil(cartSubtotal));const xpUsed = discountAmount * config.shippingXPRate;if (discountAmount > 0) {operations.push({deliveryDiscountsAdd: {candidates: [{message: $${discountAmount} OFF SHIPPING (${xpUsed} XP),targets: [{deliveryGroup: {id: firstDeliveryGroup.id}}],value: {fixedAmount: {amount: discountAmount.toString()}}}],selectionStrategy: âALLâ / All /}});}}return { operations };}function handleCartLineDiscounts(input, config, userXPPoints, hasOrderDiscountClass, hasProductDiscountClass) {const operations = [];let remainingXP = userXPPoints;const cartSubtotal = parseFloat(input.cart.cost?.subtotalAmount?.amount || â0â);if (hasOrderDiscountClass && remainingXP >= config.orderXPRate) {const maxPossibleDiscount = Math.floor(remainingXP / config.orderXPRate);const discountAmount = cartSubtotal <= 0 ? 0 : Math.min(maxPossibleDiscount, Math.ceil(cartSubtotal));const xpUsed = discountAmount * config.orderXPRate;if (discountAmount > 0) {operations.push({orderDiscountsAdd: {candidates: [{message: $${discountAmount} OFF ORDER (${xpUsed} XP),targets: [{orderSubtotal: {excludedCartLineIds: []}}],value: {fixedAmount: {amount: discountAmount.toString()}}}],selectionStrategy: âFIRSTâ / First /}});remainingXP -= xpUsed;}}if (!hasOrderDiscountClass && hasProductDiscountClass && remainingXP >= config.cartLineXPRate && input.cart.lines?.length > 0) {const eligibleCartLines = getEligibleCartLines(input.cart.lines, config.collectionIds);if (eligibleCartLines.length > 0) {const maxPossibleDiscount = Math.floor(remainingXP / config.cartLineXPRate);const discountAmount = cartSubtotal <= 0 ? 0 : Math.min(maxPossibleDiscount, Math.ceil(cartSubtotal));const xpUsed = discountAmount * config.cartLineXPRate;const maxEligibleLine = eligibleCartLines.reduce((maxLine, line) => {if (line.cost.subtotalAmount.amount > maxLine.cost.subtotalAmount.amount) {return line;}return maxLine;}, eligibleCartLines[0]);if (discountAmount > 0) {operations.push({productDiscountsAdd: {candidates: [{message: $${discountAmount} OFF PRODUCT (${xpUsed} XP),targets: [{cartLine: {id: maxEligibleLine.id}}],value: {fixedAmount: {amount: discountAmount.toString()}}}],selectionStrategy: âFIRSTâ / First /}});remainingXP -= xpUsed;}}}return {operations};}function deliveryDiscountsGenerateRun(input) {const config = parseDiscountConfiguration(input.discount?.metafield?.value);const userXPPoints = getUserXPPointsFromCart(input.cart);if (userXPPoints === 0) {return { operations: [] };}const discountClasses = input.discount?.discountClasses || [];const hasShippingDiscountClass = discountClasses.includes(âSHIPPINGâ / Shipping /);const hasProductDiscountClass = discountClasses.includes(âPRODUCTâ / Product /);const hasOrderDiscountClass = discountClasses.includes(âORDERâ / Order */);if (!hasShippingDiscountClass || hasProductDiscountClass || hasOrderDiscountClass) {return { operations: };}return handleDeliveryDiscounts(input, config, userXPPoints);}function parseDiscountConfiguration(metafieldValue) {try {const parsed = JSON.parse(metafieldValue || â{}â);return {cartLineXPRate: Number(parsed.cartLineXPRate || 100),orderXPRate: Number(parsed.orderXPRate || 100),shippingXPRate: Number(parsed.shippingXPRate || 100),collectionIds: parsed.collectionIds || };} catch {return {cartLineXPRate: 100,orderXPRate: 100,shippingXPRate: 100,collectionIds: };}}function getEligibleCartLines(cartLines, collectionIds) {if (!cartLines || collectionIds.length === 0) {return cartLines || ;}return cartLines.filter((line) => {return line.merchandise?.product?.inAnyCollection && collectionIds.some((collectionId) => line.merchandise.product.inCollections?.some((collection) => collection.collectionId === collectionId));});}function getUserXPPointsFromCart(cart) {const xpPointsToUse = cart.attribute?.value;if (!xpPointsToUse) {return 0;}const xpAmount = parseInt(xpPointsToUse, 10);return Number.isFinite(xpAmount) ? Math.max(0, xpAmount) : 0;}
// function cartLinesDiscountsGenerateRun() {return run_default(cartDiscountsGenerateRun);}function cartDeliveryOptionsDiscountsGenerateRun() {return run_default(deliveryDiscountsGenerateRun);}export {cartDeliveryOptionsDiscountsGenerateRun,cartLinesDiscountsGenerateRun};
Theres the compiled JS. Please help im cooked here.