Great to see we now have the capability to reject discount codes, but I’ve just noticed that enteredDiscountCodesReject is only supported with automatic discounts. So unless I’m not understanding here, this is pretty much of no use?
Lets say we want have a WELCOME10 or unique code generated by an app for first order etc, we can’t reject this because its a manual discount?
Can someone explain how this reject function is of any use when its only for automatic discounts?
To add here, I currently have a ruby script that basically does the following:
Blocks discount codes when cart contains products with promotion: 'true' property
Uses cart.discount_code.reject() to prevent discount application
Shows rejection message: “Discount codes cannot be used in conjunction with a promotion”
Obviously checking line item properties is an awful idea as this can be “hacked”, but as far as I’m aware I won’t be able to migrate this script still.
So its a bit disappointing that we finally have these rejection capabilities but they’re still not enough to fully migrate from Scripts to Functions.
The Discount API’s enteredDiscountCodesReject operation is designed to reject discount codes entered by customers. It cannot reject automatic discounts.
Automatic discounts run on every cart, regardless of whether a code is entered. Code discounts only run when their specific code is entered.
If you connect your rejection logic to a code discount, it only runs when that code is entered, which doesn’t help. You need something that’s always running to monitor and reject other codes.
As a result, the solution is to create an automatic discount that piggybacks the rejection logic.
For example, here I have an automatic discount set to apply 0% off products that is capable of rejecting discount codes:
I left a discount set to apply 0% to illustrate that we’re relying on automatic discount functionality to run the discount rejection functionality. It’s to do with when Discount Function API functions run. You don’t actually need a discount to apply at all.
Since the Discount Function API lets you access products tags, I recommend using product tags in your Function to determine whether it should apply or not.
Other examples for reference:
Here I’m selectively rejecting the PRODUCTDISCOUNT and ORDERDISCOUNT codes if a product with the product tag REJECT_CODES exists in the cart.
Here I’m rejecting all discount codes if a product with the product tag REJECT_CODES exists in the cart.
Apologies if I’m just a bit slow here, I’ll try explaining what I want to achieve in the simplest form possible.
My current Shopify Script essentially will block the use of ANY discount codes (I’m not sure about automatic) when the cart contains line items with a line item property of promotion: true.
Here’s the code from that Shopify Script (I didn’t originally write this but understand what its doing..I think ha)
DISABLED_DISCOUNT = {
enable: true,
rejection_message: 'Discount codes cannot be used in conjunction with a promotion',
campaigns: [
{
product_selector_match_type: :include,
product_selector_type: :property,
product_selectors: [{ :key => 'promotion', :value => 'true' }],
},
]
}
class DisableDiscountCodesCampaign
attr_reader :enable, :campaigns, :cart
def initialize(enable, campaigns)
@enable = enable
@campaigns = campaigns
end
def run(cart)
return if cart.discount_code.nil?
@campaigns.each do |campaign|
product_selector = ProductSelector.new(
campaign[:product_selector_match_type],
campaign[:product_selector_type],
campaign[:product_selectors]
)
applicable_items = cart.line_items.select { |line_item| product_selector.match?(line_item) }
next unless applicable_items.size > 0
cart.discount_code.reject(message: @rejection_message)
end
end
end
What your Shopify Script achieves is possible with the Discount Function API now
It’s essentially the same as my last example:
But I used product tags instead because they’re more secure than line item properties which can be manipulated client-side. You can use line item properties if that works best for how the promotions are currently handled on the shop.
This could probably be written a lot more nicely, but this is the kind of approach you can take:
export function cartLinesDiscountsGenerateRun(
input: CartInput,
): CartLinesDiscountsGenerateRunResult {
const operations = [];
// Check if cart contains any line item properties with on_promotion set to true
const cartOnPromotion = input.cart.lines.some((line) => line.onPromotionProperty?.value === 'true');
// Get all entered discount codes that are rejectable
const codesToReject = input.enteredDiscountCodes
.filter((code) => code.rejectable)
.map((code) => ({ code: code.code }));
// Reject entered discount codes if cart contains any line item properties with on_promotion set to true
if (cartOnPromotion && codesToReject.length > 0) {
operations.push({
enteredDiscountCodesReject: {
codes: codesToReject,
message: 'Discount codes cannot be used with this promotion',
},
});
}
return {
operations,
};
}