Discuss and resolve questions on Liquid, JavaScript, themes, sales channels, and site speed enhancements.
We've been asked to add an (Apply) Discount Code text input to the client's side cart. I've done some discovery and found a couple apps what say they support this. On the other hand, we naturally want to be thorough and see if we can do it ourselves.
I created a discount and used apply via link.
When I get to the actual checkout page, the discount shows up (as it should).
Then I added a get cart (via AJAX) to the side cart (prior to checkout):
https://shopify.dev/docs/themes/ajax-api/reference/cart#get-cart-js
And dump that via console.log() and the cart object returned doesn't show anything about a discount. No code. Nuttin'.
Yet, if I continue on to checkout, the discount shows up again / still.
Based on some additional research, evidently there's no way to apply a coupon code via AJAX. The last discussion I found was about a year old. Is there still no relatively easy way to apply a discount via Ajax?
All that being said, if there are no other options, we'll go the app route. Does anyone have any suggestions for such apps?
Bump. Anyone? Anything?
I tried out 6 apps that promised to add a discount code validation to the cart but turns out 5 out of 6 had various issues with stacked discounts or free shipping discounts, or didn't work at all. Decided to write down a detailed review of all of them here: https://appreviewhq.com/shopify-discount-app-review-2024. TLDR: the only app I found that supported both stacked discounts and free shipping discounts was https://apps.shopify.com/discount-before-checkout. Still crazy to me how this is not a built-in feature, and how many apps seem to get it wrong.
Usually I'd agree (I think Shopify has a lot of crazy stuff and I actively complain about it!) but this is actually a complex feature once you dive below the surface - some discounts are limited to specific customer emails/once per customer/address locations, and this information is not captured until checkout. Another missing but complex feature, due to similar constraints, is shipping costs on cart page.
Of course, it can be achieved in both scenarios via JavaScript/API, but needs consideration for certain use cases; what should the message be if I add a discount to the cart that requires information collected in checkout? Perhaps something like "Sorry, you can only add this discount during checkout", but then the user experience gets a bit messy because some discounts I can add in cart, and some I can't. The alternative I think would be to allow all discounts to be added in cart, and totals adjusted, but then kick off a discount with an error message if once entering checkout details the discount is no longer valid, but that could annoy the customer's expectations.
So I think I can see why they opted to leave discounts and shipping in checkout by default - much simpler, but still allowing developers/apps to achieve it via JavaScript/API if absolutely necessary. That said, there's no excuse for apps not delivering what they promise (too common, sadly). Glad you found one that can do it (thanks for sharing the link) - I'd be interested how it deals with the challenges I mentioned above.
An aside, Shopify uses a white-label version of Stripe checkout, so it may even be a decision at the Stripe level.
Dear friend, I'm struggled in the same situation. Could you find a solution for such problem? I need to apply discount in ajax cart so it's showed on cart like automatic discounts are.
Create a new code snippet with cart-apply-discount
{% comment %}
Replace the text below with the handle of your add-on product.
{% endcomment %}
{% assign product = all_products['put-your-product-handle-here'] %}
{% unless cart.item_count == 0 or product.empty? or product.variants.first.available == false %}
{% assign variant_id = product.variants.first.id %}
<script>
(function($) {
var cartItems = {{ cart.items | json }},
qtyInTheCart = 0,
cartUpdates = {};
for (var i=0; i<cartItems.length; i++) {
if ( cartItems[i].id === {{ variant_id }} ) {
qtyInTheCart = cartItems[i].quantity;
break;
}
}
if ( ( cartItems.length === 1 ) && ( qtyInTheCart > 0 ) ) {
cartUpdates = { {{ variant_id }}: 0 }
}
else if ( ( cartItems.length >= 1 ) && ( qtyInTheCart !== 1 ) ) {
cartUpdates = { {{ variant_id }}: 1 }
}
else {
return;
}
var params = {
type: 'POST',
url: '/cart/update.js',
data: { updates: cartUpdates },
dataType: 'json',
success: function(stuff) {
window.location.href = '/cart';
}
};
$.ajax(params);
})(jQuery);
</script>
{% endunless %}
Include the new snippet in your cart.liquid template
{% include 'cart-discount' %}
Thanks for replying @SantoshKori .
I can't see excatly in your code where are you applying a discount code. Can you explain it to me?
As I can see, you're only updating quantity of an item.
Thnaks again.
Shopify doesn't allow to process discounts in checkout directly. There is no API to apply a discount code. There is workaround with making ajax call to checkout page but it's quite complicated.
I was searching for liquid code to achieve that and stumbled upon an app. It works perfectly with cart drawer and cart page. Here is how my drawer now looks like:
https://apps.shopify.com/discount-in-cart
Late to the thread but I'm implementing this as well. Does the `checkoutdiscountcodeapplyv2` mutation of the StoreFront API not work for this? I believe the StoreFront API can be used on the frontend of a normal Shopify website without a Plus subscription.
https://shopify.dev/docs/storefront-api/reference/mutation/checkoutdiscountcodeapplyv2
As far as I understand the app should be a sales channel. You you are making a private app it's okay. But if you made a public app you may face with the limitation of the appstore. As the sales channel should not only create checkouts/add discounts there but also publish products at the different platforms.
This works, but the Storefront API does not with the the `cart.js` endpoint, so it's useless.
This wont work with the /cart.js endpoint, only the GraphQL endpoints, which aren't a cart, they're a checkout.
If you're only looking for a way to display a discount input, then you can add a regular input field with the name 'discount' to the cart's <form> element. This works on the cart page as well as a cart popup, as long as the form action posts into the checkout.
E.g. <input name="discount" type="text>
The tricky part is if you want to check that the coupon is applicable to the cart and display the discounted amount. There is no easy way to do that yourself at this time, which is why apps exist to handle this functionality. There are many apps that apply a discount on the cart page. They differ in appearance as well as supporting functionality and price. The app we've developed for this purpose is https://apps.shopify.com/discount-on-cart-pro. It has a 14-day free trial so you can give it a shot risk-free.
Hi, there is a simple way to add discount codes before checking out, this is what I do in my store:
In the cart-drawer.liquid file I add an input with the name of the previously created discount and at the time of checking out it should already appear applied.
<input type = "hidden" name = "discount" value = "DISCOUNT_NAME" />
this is what you need?
This JavaScript snippet should allow you to apply the discount to the current session.
// Setup the Apply Discount Code URL
let shopDomain = "somesite.myshopify.com";
let discountCode = "MYDISCOUNTCODE123";
let discountApplyUrl = "https://" + shopDomain + "/discount/" + discountCode;
// Applies discount using hidden iframe to the checkout session
let i = document.createElement('iframe');
i.style.display = 'none';
i.onload = function() { i.parentNode.removeChild(i); };
i.src=discountApplyUrl;
document.body.appendChild(i);
Hi Zanath,
We just found your solution, the main issue is that we are encountering a problem with the implementation:
Refused to frame 'https://some-site.myshopify.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'none'".
Have you encountered the same issue?? We keep reading the documentation and could not find a solution.
Thanks in advance!
It doesnt work any more.
One thing you could consider is to add the discount code to Shopify's discount cookie via JavaScript. This will then be auto-applied when you checkout (if your cart is eligible).
document.cookie = 'discount_code=YOURDISCOUNTCODE';
When
/cart?discount=ABC123
stopped working this method was a good replacement, thanks
Thanks @Zanath . But is there a way to apply multiple discounts with this link ?
The checkout permalink works with multiple discount codes separated by commas, e.g. `?discount=DISCOUNT1,DISCOUNT2`. If you append this to your cart form action (e.g. /cart?discount=DIS1,DIS2`), when submitting cart to proceed to checkout, it will apply the discounts.
For the `/discount/` solution, I tried `/discount/DISCOUNT1+DISCOUNT2` and `/discount/DISCOUNT1,DISCOUNT2` and `/discount/DISCOUNT1&DISCOUNT2` but none worked, so I suspect this solution is only for singular discounts.
Same goes for the `discount_code` cookie, singular only in my testing.
The above is unconfirmed from Shopify, just via experimenting.
I'm also curious about that. This seems undocumented so use with caution, but it seems this way we can apply a manual discount code to the cart page with a simple client-side fetch request:
fetch('/discount/discount-1').then(async () => {
// refresh cart here
});
But I too can't figure out a way to make this call with combined discounts (tried a few things as well). It seems only possible to combine in checkout page.
I used a modified version of this ajax solution:
https://gist.github.com/elghorfi/ce7e5b1080aae37d0a415643f33bc79e
It is complex but I can confirm that it works.
It's an interesting find! But I don't get why his solution is so... convoluted I'd say. I actually shared my own solution on the gist feed (I believe much simpler and more efficient), could be interesting to compare the pros & cons.
A few things I wanted to add:
fetch(`/checkout?discount=${code}`);
This alone seems to work, as does this:
fetch(`/discount/${code}`);
I'm wondering if anyone has seen a case when only doing one or other fails in some cases.
Also, with some of the built-in themes (Refresh, Craft) I've seen many cases where the cart views (both the cart drawer and the dedicated cart page) end up getting cached so they only ever show whatever discount was applied the first time you viewed that particular page. This has nothing to do with applying a code via either fetch call either, as the results held true even when applying the discount code manually in the checkout as a user would normally.
Anyway, in my tests, either fetch call above works to get the discount in the checkout but I'm betting not all themes would see the same result.
Also, with some of the built-in themes (Refresh, Craft) I've seen many cases where the cart views (both the cart drawer and the dedicated cart page) end up getting cached so they only ever show whatever discount was applied the first time you viewed that particular page. This has nothing to do with applying a code via either fetch call either, as the results held true even when applying the discount code manually in the checkout as a user would normally.
You might be talking about automatic discouts? Automatic discounts will apply in cart page "automatically" as their name imply (as long as the cart liquid template is using the discount_allocation and discount_application objects).
What seems to be new (in my experience) since the release of these objects, is that now the manual discounts that are applied also render within these objects, which is quite convenient and opens up some possibilities.
This isn't with automatic discounts, but it seems like some newer versions of these Shopify-provided themes will show you any cart-level discounts that you may have applied during checkout *in the cart view*. For example, in this view YUM10 isn't an automatic discount, but one I added during checkout and then clicked back to the store to continue shopping. In my experience, if I then go back to checkout, remove YUM10, then click from the checkout back to "Cart", the old discount is still there, even if reloading the page manually. I thought this was a bug with our plugin but it turns out this behavior exists even with no plugins installed.
Anyway, would love to hear if you've noticed any difference between the `/checkout?discount=CODE` and the `/discount/CODE` fetch techniques. Both seem to work equally well in my case.
Okay I see, so this is what I mentioned in the second part of my message. The caching issue, this might be a miss on Shopify side, or maybe due do the page caching by the browser, which happens when returning to a page with the back button (typical use case for navigation from checkout to cart).
I suppose this could be detected with javascript pageshow event, and then fetching a fresh version of the cart.
I've only used the '/discount/CODE' technique, which I guess is originally the API intended for shareable discount urls. I have not had any issue with it yet but I've only played around with it for a month, and since it's undocumented...
I suppose this method '/checkout?discount=CODE' might allow to apply multiple manual discounts at once (comma separated) whereas it's not possible with the first method. I have not checked for myself though.
Oh wow, I am really interested to see how this is achieved - do you have a link to the store? I wonder if it is checking the cookie, and showing it accordingly BUT the discount amount isn't stored in the cookie I don't think, so I'd be curious how it is doing that. Maybe the theme is using a discount API or something? My understanding was you couldn't show manual discounts in the cart because they rely on checkout data to check if eligible.
I've found the best solution for the discount input field on the cart page or in the cart drawer. It's a bit complex to explain in a comment, but my solution performs much better compared to using a marketplace app from the app store, as far as I can see. Check out the attached video https://screenrec.com/share/YkamSOF5sx . I didn’t use any app—everything is implemented directly in the theme, giving me full control. I have used GraphQL and Storefront API. Used cartDiscountCodesUpdate mutation. Feel free to contact me on LinkedIn: https://www.linkedin.com/in/bhshiam/
Here is the mutation Code.
mutation {
cartDiscountCodesUpdate(
cartId: "gid://shopify/Cart/Z2NwLWV1cm9wZS13ZXN0MzowMUo4OUtWM0RYQU44VjcyUFJSRkpNSDBaNg?key=d37071d35414d5f784bc04eaf4e2089f"
discountCodes: ["abo5"]
) {
cart {
id
discountCodes {
code
applicable
}
checkoutUrl
}
userErrors {
code
field
message
}
}
}
Starting a B2B store is a big undertaking that requires careful planning and execution. W...
By JasonH Sep 23, 2024By investing 30 minutes of your time, you can unlock the potential for increased sales,...
By Jacqui Sep 11, 2024We appreciate the diverse ways you participate in and engage with the Shopify Communi...
By JasonH Sep 9, 2024