Goal: reject an entered discount code when the shipping address country isn’t allowed, using enteredDiscountCodesReject.
The problem
The two inputs I need never appear in the same function run:
cart.lines.discounts.generate.runreceivesenteredDiscountCodes, butcart.deliveryGroupsis always[], so there’s nodeliveryAddress.countryCodeto test.cart.delivery-options.discounts.generate.runreceives a populatedcart.deliveryGroupswith the country — but in my testing it only ever runs whileenteredDiscountCodesis empty. Once a code is entered, only the cart-lines target runs.
So the target that can see the code can’t see the country, and the target that can see the country doesn’t run when there’s a code to reject.
Observed function-run logs
Both targets are registered on one function. The discount is created with discountAutomaticAppCreate and discountClasses: [PRODUCT, ORDER, SHIPPING], so the delivery target is definitely active — it does run, just never at the same time as an entered code.
Session 1 — entering code SAVE10 at 11:33:42:
| Time | Target | enteredDiscountCodes |
cart.deliveryGroups |
|---|---|---|---|
| 11:33:36.180 | cart.lines | [] |
[] |
| 11:33:36.369 | cart.delivery-options | [] |
[{deliveryAddress:{countryCode:"GB"}}] |
| 11:33:38.364 | cart.lines | [] |
[] |
| 11:33:38.411 | cart.delivery-options | [] |
[{deliveryAddress:{countryCode:"GB"}}] |
| 11:33:42.785 | cart.lines | [{code:"SAVE10", rejectable:true}] |
[] |
Session 2 — code already applied throughout, 24 seconds, GB shipping address set:
11:49:27 cart.lines SAVE10(rejectable=true) deliveryGroups: []
11:49:27 cart.lines SAVE10(rejectable=true) deliveryGroups: []
11:49:29 cart.lines SAVE10(rejectable=true) deliveryGroups: []
11:49:29 cart.lines SAVE10(rejectable=true) deliveryGroups: []
11:49:36 cart.lines SAVE10(rejectable=true) deliveryGroups: []
11:49:49 cart.lines SAVE10(rejectable=true) deliveryGroups: []
11:49:51 cart.lines SAVE10(rejectable=true) deliveryGroups: []
Zero cart.delivery-options runs in that window.
Across both sessions: 12 cart-lines runs with the code entered, 0 delivery-options runs with the code entered.
rejectable stayed true throughout, so the documented delivery-target restriction (“in a delivery target it’s also false when the code already applied a product or order discount”) isn’t what’s blocking me — the delivery target simply doesn’t run.
My reading of the ordering: the delivery target runs when delivery options are recalculated, which happens when the buyer enters their address — before they enter a discount code. Entering the code afterwards recalculates cart-line discounts only. That’s the normal checkout sequence, so the two never coincide.
Verified locally that the logic itself is fine — feeding the delivery target a synthetic input with both the code and a GB address produces the correct rejection:
{"operations":[{"enteredDiscountCodesReject":{
"codes":[{"code":"SAVE10"}],
"message":"Sorry you cannot use SAVE10 with that address"}}]}
It just never receives that combination in a real checkout.
On the docs
The Cart.deliveryGroups schema description says deliveryGroups is always empty for the legacy Order Discount and Product Discount APIs, and points to the Discount Function API as the replacement. But I’m seeing it empty in cart.lines.discounts.generate.run on the new API too, and that isn’t stated anywhere I can find. The cart.lines.discounts.generate.run sample input object in the Discount Function API reference does show "deliveryGroups": [], which is consistent with what I observe — but it reads as incidental rather than documented behaviour.
Alternatives I’ve ruled out
- Cart and Checkout Validation function — it does get
cart.deliveryGroups[0].deliveryAddress.countryCode, but itsDiscountApplicationtype exposes no discount code, so I can only detect that a discount was applied, not which one. A rule scoped to one code would fire on any discount. localization.country.isoCode— available in the cart-lines target, but it’s the localized/market country, not the shipping address, so it’s wrong for a buyer on a different market’s storefront.cart.billingAddress— not populated at the point the code is entered.
Questions
- Is
cart.deliveryGroupsintentionally always empty incart.lines.discounts.generate.run? If so, can that be documented on the Discount Function API page rather than only in the legacy-API note? - Is there any supported way to read the shipping destination country from the cart-lines target?
- Is
cart.delivery-options.discounts.generate.runexpected to re-run when a discount code is entered? If it is, my logs suggest it isn’t happening; if it isn’t, thenenteredDiscountCodesRejectin that target seems only usable for codes entered before the address is set. - Is there a different recommended approach for “reject this specific code when shipping to country X” that I’ve missed?
Happy to provide full function-run log JSON if useful.