Hey Shopify Community,
I want to share a solution I just built for a Shopify Plus store (gant.ae) that I think will help others facing the same challenge.
The Challenge:
We have a mobile app built with Buildnatively and wanted app-exclusive discount codes (APP20, APP21) to only work inside the app completely blocked for web browser users.
Why it’s tricky:
- Shopify checkout runs on a separate domain, so theme JS won’t run there
- Shopify Functions API doesn’t give easy access to both discount codes and cart attributes together
- iOS WebView detection is notoriously difficult
The Solution — 3 layers:
Layer 1 — Cart Page (theme.liquid)
Detect the Buildnatively app using navigator.userAgent.toLowerCase().includes(‘natively’) — this works for both iOS and Android. Set a cart attribute checkout_context = mobile_app for app users. Block app-only codes on the cart page for web users.
Layer 2 — Checkout Page (Checkout UI Extension)
Built a UI Extension using @shopify/ui-extensions/checkout/preact with:
- useDiscountCodes() to detect applied codes
- useAttributes() to read the cart attribute
- useApplyDiscountCodeChange() to auto-remove the code
- useBuyerJourneyIntercept() to block checkout progression
- A 12-second error message timer so the user sees the message even after the code is removed
Layer 3 — iOS Fallback
Used both the cart attribute AND the user agent check as a fallback in the extension so iOS users are always correctly identified.
Key code snippets:
User agent detection:
const isApp = navigator.userAgent.toLowerCase().includes(‘natively’);
Checkout UI Extension core logic:
const isApp = checkoutContext === ‘mobile_app’ || ua.includes(‘natively’);
const shouldBlock = hasAppOnlyCode && !isApp;
Result:
Web users blocked on cart page
Web users blocked on checkout page
App-only codes auto-removed
iOS and Android both working perfectly
Hope this helps someone facing the same challenge! Happy to share full code if needed.
-– Noman