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,
};
}
Hi @Luke, did you manage to resolve this ? @Paige-Shopify I have a very similar issue where I have created an app with two extensions(1.an admin UI extension that surfaces a UI for merchants to create discounts, 2.a Function that validates the discount and either applies or rejects it based on certain properties).
Like Luke, I am trying to send custom rejection messages, but the rejectable property is always false. Any insight would be greatly appreciated! I have posted my own thread about this here.
2 separate ‘automatic discounts’ for rejecting discounts
An automatic discount that cannot be rejected
Multiple discount codes that can be rejected
Keep in mind:
You have can a single ‘discount’ that both rejects and applies discounts, but it must be automatic since the reject operation only works as an ‘automatic discount’.
You can have an ‘automatic discount’ that rejects discount codes and a discount code that applies a discount from the same Function like I do, but the UX isn’t great (APPDISCOUNT still looks like it’s applied until the page is refreshed - video)
Conditionally applying discounts is usually a better alternative to rejecting discount codes on application.
@Paige-Shopify Thanks for the response. The discount code is created via my custom app and is of type ‘Code’ (see attached screenshot). I take it this will not work given that you mentioned this feature is only valid for automatic discounts ?
If so, would it be possible to request this feature for code discounts ? I built an app that allows merchants to control how often customers can use a coupon code (e.g. 2 uses per month). It would be a great user experience for the customer if they knew exactly why the coupon code was rejected (“You can only use this coupon code 2 times a month”) vs the default message shown by Shopify, which is very ambiguous.
Yeah that’s right, it won’t work since it needs to be the type ‘automatic’.
Right now, the rejection logic needs to run continuously (when automatic discounts run), not just at the moment a discount code is applied (when discount code discounts run). That way, if the cart changes later, any previously applied discount codes can be re-evaluated and rejected automatically.
That said, there’s clear value in supporting a custom message when a discount doesn’t apply, since it can give customers useful information.
I’ll log this as a feature request on our side, but I can’t share an ETA or confirm if or when it’ll be implemented. If it does get shipped, we’ll post an update in this thread.
Thanks for explaining how your app’s discount flow works and where this would help
@Hamza_Hassan, I have uncovered a workaround for your question.
Simply have a separate automatic discount just for discount rejection purposes:
And ensure the discount code discount itself returns an empty operations array when it should be rejected by the automatic discount with rejection logic.
@Paige-Shopify That’s an interesting proposal but I am a software developer, not a merchant! From my understanding, your proposal involves having the merchant create an automatic discount code, which is not the best UX.
I would again urge you to flag this as a feature request: The ability to return a custom rejection message for code discounts. Thank you so much for your help!
Yeah, it would require you to leave instructions like this for the merchant:
Use this app to create a discount using a discount code discount type
Use this app again to create discount rejection using an automatic discount type
I can see how that UX could be confusing for the merchant. For their sake, it may make sense to run a discountAutomaticAppCreate mutation from your app.
It’s probably fair to say this was confusing for a lot of developers at first too, creating an automatic discount just to reject discount codes.
The feature request is already submitted, so I’ll keep tracking it from there. In the meantime, hope the workaround I shared helps.