This is old but the same issue still occurs as we’ve been dealing with it recently. So I’m chiming in on our ‘solution’.
Honestly, we’ve been really disappointed with how “headless” Shopify actually is when it comes to checkout — it’s headless everywhere except the one place it matters most for continuity. There’s no supported way to keep checkout on your own application’s domain, and the Checkout API route others mentioned above is dead.
That said, after a lot of back and forth we’ve landed on a workaround that gets us ~90% of the way there in terms of branding and flow continuity. Sharing in case it helps anyone:
1. Subdomain for checkout branding
Add a subdomain like checkout.yourapp.com as a domain in Shopify, and set it as the primary domain. This means all checkout pages sit on checkout.yourapp.com/... instead of your-store.myshopify.com/``.... Your actual app stays on yourapp.com as normal — so to the customer it all reads as one brand.
2. Store the return location before handoff
On the checkout trigger button in your app (just before redirecting the user to Shopify checkout), write the URL they should return to into localStorage — based on wherever they are in your app at that moment. This is what lets you drop them back into the correct flow later.
3. Let them check out on Shopify as normal
They complete checkout on the checkout.yourapp.com subdomain.
4. Hijack the post-checkout return
On the order confirmation page, we changed the button copy from “Continue shopping” to just “Continue”. That button naturally points at your Shopify online storefront — which nobody should ever see in a headless setup — so we turned the entire theme into a redirect. In theme.liquid:
liquid
<script>
window.location.replace('https://yourapp.com/redirect');
</script>
Any hit on the templated store instantly bounces to your app. (This also fixes the problem mentioned above where the checkout logo links back to the templated store as this just hits the redirect too.)
5. Redirect page restores their position
/redirect in your app is just a page with a quick loader. On mount, it reads the stored URL out of localStorage and pushes the user straight there — so they land back exactly where they were in the app, with the flow intact.