Whop Checkout Setup Guide

For a Shopify store running TikTok ads · updated October 2026

Overview

This setup keeps your Shopify storefront and cart. A small server you control takes over when the shopper clicks Check out. Payment goes through Whop, and the paid order is then written back into Shopify, so your fulfilment app, emails and reports keep working as normal. Plan on about one working day for the first store and 1–2 hours for each store after that.

Every name, ID, domain and key in this guide is a placeholder written like <this>. Fill in your own.

Three facts shape the whole design:

What you need before starting

Need Notes
Shopify store with admin access You must be able to create an app (Dev Dashboard app or a legacy custom app).
Whop business account One Whop business per store. KYC approved, payout method added and set as default, 2FA on.
Cloudflare account on Workers Paid (about $5/month) Required. The free tier's KV limit of 1,000 writes/day stops checkout mid-day once real traffic arrives.
A hostname for the pay page Best is checkout.<your-store>.com, on the same site as the store.
Computer tools Node 20+ with wrangler, Python 3.11, git, Google Chrome.
Optional TikTok Events API token for each pixel, an attribution app, Resend (email sending), an AI key for support replies.

If you are given a copy of the checkout code, it must arrive without its .env file, KV exports or store-snapshots/ folder. Those hold someone else's keys and store data. You create your own in the steps below.

Steps 1–2: Whop business and API key

Do the dashboard settings first. The webhook and Apple Pay domain come later (step 4), because Whop checks that your server is reachable.

Step 1: Set up the Whop business (dashboard, once per store)

  1. Create a business. Type: physical product, then your category (for example clothing).
  2. Complete KYC for both the individual and the business. Wait until card payments show as active.
  3. Add a payout method and set it as default. Change the withdrawal schedule from manual to weekly.
  4. Keep a small balance in the account. Refunds and disputes are paid from it, and refunds fail when it is empty.
  5. Tax: new businesses start with Whop adding sales tax at checkout (a $1 test then charges about $1.08). Decide on purpose; ask an accountant if you are unsure.
  6. Turn customer emails off. Shopify sends the receipt, so Whop should send nothing.
  7. Set the statement descriptor suffix. Card statements show WHOP* <SUFFIX>. Write it down: it goes on your thanks page and in your FAQ so buyers recognise the charge.
  8. Turn on 2FA.
  9. Answer Whop's business review honestly. Describe exactly what you sell and show your real catalog.

Step 2: Create the Whop API key

In Developer → API keys, create a key with these permissions:

Keys belong to one business. If you own more than one, call GET https://api.whop.com/api/v1/accounts/me with the key and check it returns this store's biz_… id. Save the key in a password manager, not in a chat or email.

Steps 3–4: Connect Shopify and deploy the checkout server

Step 3: Shopify app, scopes and tokens

  1. Back up the store first. Export the theme (Online Store → Themes → Download) and keep a copy of pages, policies and menus.
  2. Create an app in the Shopify Dev Dashboard (or a legacy custom app under Settings → Apps → Develop apps). A Dev Dashboard app uses the client-credentials grant, so the server gets its own short-lived Admin token and no long-lived token can leak.
  3. Grant at least these scopes: read_products, read_orders, write_orders, read_customers, write_customers, read_themes, write_themes, read_shipping, write_content, read_publications, write_publications.
  4. Check the scopes you actually got by calling GET /admin/api/<version>/oauth/access_scopes.json. An app made in a hurry often has only two, and then everything after this step fails without a clear error.
  5. Create a Storefront API token with POST /admin/api/<version>/storefront_access_tokens.json. This one is public-tier and can be a plain config value.
  6. Set Settings → General → store contact email and Settings → Notifications → sender email to support@<your-domain>.

Step 4: Deploy the server (Cloudflare Worker)

  1. Run wrangler login, then wrangler kv namespace create HYDRA_KV and note the id.
  2. Copy the example config to wrangler.<store>.jsonc. Set the worker name, the route for checkout.<your-store>.com/*, the KV id and a cron of */5 * * * *.
  3. Fill in the plain config values (vars):
Var Value
SHOPIFY_STORE <store>.myshopify.com
SHOPIFY_API_VERSION a current version, e.g. 2025-10
SHOPIFY_APP_CLIENT_ID from step 3
SHOPIFY_STOREFRONT_TOKEN from step 3.5
WHOP_ACCOUNT_ID your biz_… id
WHOP_DISABLED_METHODS bank_wire,crypto,coinbase,us_bank_account
ALLOWED_ORIGINS your storefront address(es)
STORE_NAME, STORE_URL, STORE_LOGO_URL branding on the pay page
STATEMENT_DESCRIPTOR exactly WHOP* <SUFFIX> from step 1.7
SUPPORT_EMAIL, SUPPORT_FROM your inbox; the customer-facing sender
SHIP_TEXT_US, SHIP_TEXT_INTL delivery wording; keep it identical to your FAQ
  1. Add the secrets with wrangler secret put <NAME>: WHOP_API_KEY, SHOPIFY_APP_CLIENT_SECRET, OPS_PASSCODE, OPS_SECRET (random hex), and later WHOP_WEBHOOK_SECRET and the TikTok values from step 8.
  2. Deploy with wrangler deploy -c wrangler.<store>.jsonc. Watch live logs with wrangler tail <worker-name>.
  3. Hostname: if Shopify manages your domain's DNS, use Cloudflare for SaaS. Add a custom hostname checkout.<your-store>.com on a Cloudflare zone you own, then add the CNAME and verification records in Shopify admin → Domains. Wait until TLS is active.
  4. Whop webhook (the server must be live first): call POST /v1/webhooks with url https://checkout.<your-store>.com/webhooks/whop and events payment.succeeded, payment.failed, refund.*, dispute.*, dispute_alert.created. The signing secret is shown once. Store it straight away with wrangler secret put WHOP_WEBHOOK_SECRET.
  5. Apple Pay domain: register the hostname with POST /v1/payment_method_domains. Serve Whop's platform-integrator association file at /.well-known/apple-developer-merchantid-domain-association. It is at https://whop.com/.well-known/apple-platform-integrator/apple-developer-merchantid-domain-association, not whop.com's own file. Then call POST /v1/payment_method_domains/<id>/verify. A brand-new domain can take about 30 minutes, so retry.

Quick check: /help, /ops and the Apple Pay file should all return 200, and the cron should show in the Cloudflare dashboard.

Steps 5–6: Theme hook and pay page

Step 5: Send every Check out click to your pay page

  1. Duplicate your live theme and do all of this on the copy first. Preview it at https://<store>/?preview_theme_id=<id>.
  2. Add the checkout script as a theme asset. Put one tag in layout/theme.liquid before </head>: <script src="{{ 'hydra-checkout.js' | asset_url }}" defer data-hydra-endpoint="https://checkout.<your-store>.com/checkout"></script>
  3. The script must catch every route to checkout: button[name=checkout], links to /checkout, the cart drawer, and any bundle or upsell app that sends people to /cart/<items>?checkout. Open every app widget and check it.
  4. Turn off Shopify's dynamic checkout and wallet buttons (Shop Pay, Apple Pay on the product page or cart). They skip your script.
  5. The script sends the cart lines with their properties, the cart attributes and the tracking values (TikTok click id ttclid, _ttp cookie, landing page, referrer, and the ttpx pixel code from the ad URL) to the server.
  6. If the server is slow or fails, retry. Do not drop to Shopify's own checkout unless you know it can take payments. Our working setup swallows every tap, retries the request up to 3 times with a 15-second timeout, then shows "tap Checkout again". It also resets the button when Safari restores the page from Back. Before this fix, about 14% of orders on one store slipped through to the native checkout, and on stores with no Shopify payments those shoppers simply hit "can't accept payments".
  7. When it works on the preview theme, add the same tag to the live theme.

Step 6: The pay page

The server shows its own page at /pay/<session> with your logo, the order summary, the live shipping rate and the payment form. Use Whop Elements (Whop's newer embedded card fields and wallet button) as the main mode:

  1. The page appears straight away with a placeholder, then the card fields load. Target: card fields ready in under about 1.5 s on a phone.
  2. Your own form collects email, name and shipping address. It passes them to Whop when it creates the payment token, and the server then charges the payment through the Whop Payments API with the session id in its metadata.
  3. Show Apple Pay and Google Pay with Whop's payment-request button. Pass the real shipping line so the wallet sheet shows the right total.
  4. Require an email before the wallet buttons work. Wallet sheets keep the payer's email inside Whop, and it is not on the payment or the webhook. Without your own email field you get orders with no customer that you cannot fix.
  5. Keep a backup mode. If the Elements script is blocked or slow (TikTok's in-app browser sometimes blocks it), switch automatically to Whop's hosted checkout inside a frame on the same page. Put the switch in a KV flag so you can turn Elements off without a deploy.
  6. After a successful charge, go to /thanks/<session> immediately. Don't wait for the Shopify order there.
  7. Keep the page plain: your wordmark, one line of trust copy that matches your real policies (returns window, processing time), and real review numbers only. No fake badges, and never copy Shopify's or Shop Pay's branding.

Acceptance numbers on a phone: POST /checkout (cart pricing) ≤ 1.0 s, first paint of /pay ≤ 0.15 s, and the cart total must equal the charged total exactly.

Step 7: Write orders back to Shopify, then fulfil

When Whop sends payment.succeeded, the server creates a paid Shopify order. Your fulfilment app, Shopify's receipt email and your reports then treat it like any other order.

  1. Verify the webhook. Check the HMAC-SHA256 signature over <webhook-id>.<timestamp>.<body>. Reject anything older than 5 minutes, reply 200 within 5 seconds and do the work afterwards.
  2. Never create an order twice. Store pay:<payment_id> in KV before creating the order. If creation fails, delete that key and save the event as fail:<payment_id> so you can replay it.
  3. Create the order (POST /admin/api/<version>/orders.json) with:
    • financial_status: paid and one transaction (kind: sale, gateway: whop, amount = the total from the webhook)
    • line items, discount codes, the shipping line, shipping and billing address, customer email and phone
    • tags whop and whop-<payment_id>, plus a note with the Whop payment id (refunds, disputes and tracking find the order this way)
    • note_attributes carrying the attribution values (click id, _ttp, landing page, UTMs)
    • send_receipt: true when there is an email
  4. Two Shopify traps: don't set source_name to web (Shopify refuses it). And if last_name is empty, Shopify silently drops the address, so move a one-word name into last_name. Strip emoji from names too.
  5. Tracking back to Whop: register Shopify webhooks fulfillments/create and fulfillments/update. When one arrives, send the tracking number to Whop with POST /v1/shipments. Whop needs this to defend you in disputes.
  6. Refunds and disputes happen in Whop, not Shopify. Mirror them onto the Shopify order as a tag or note. Optionally also refund and cancel unshipped orders so your supplier stops shipping.
  7. Customer pages: a /thanks/<session> page with the order number, the statement descriptor and a link to /pages/track-order (a tracking widget). Then edit Shopify's three notification templates (order confirmation, shipping confirmation, shipping update) so their button goes to that tracking page.
  8. Check your fulfilment app sees the orders. Compare one of these orders field by field with a normal Shopify order: paid, unfulfilled, address, phone, variant, location.

Step 8: TikTok purchase tracking

TikTok never sees your checkout, because shoppers never load Shopify's checkout pages. So the server must send InitiateCheckout and CompletePayment to TikTok's Events API itself.

  1. Put the pixel code in every ad URL, e.g. https://<store>/products/<handle>?utm_source=tiktok&utm_campaign=__CAMPAIGN_NAME__&utm_id=__CAMPAIGN_ID__&ttpx=<pixel code>. The theme script saves ttpx in a cookie and in localStorage. The server uses it to choose the pixel.
  2. Get one Events API token per pixel in TikTok Events Manager. A token from one pixel is refused by another pixel, even in the same ad account. Store them as a JSON secret (TIKTOK_PIXEL_TOKENS = {"<pixel code>": "<token>"}), or in a KV overlay so you can add pixels without a deploy.
  3. No default pixel. If a session has no ttpx or known campaign, send nothing. Sending organic sales to some pixel ruins that campaign's optimisation.
  4. Test every pixel before an ad uses it: send one event with a test_event_code and confirm TikTok accepts it.
  5. Choose one owner per event. If you also run an attribution app (WeTracked or similar), let it handle PageView, ViewContent and AddToCart. Decide clearly who sends Purchase: either the app or your server, never both. If both send it, every sale counts twice. Give the browser and server InitiateCheckout the same event_id (<session>:InitiateCheckout) so TikTok removes the duplicate.
  6. Log every send (e.g. KV tt:<payment_id>:<event>:<pixel> with TikTok's response code). Then you can tell "sent", "refused" and "skipped on purpose" apart.
  7. In .env files, put JSON values in single quotes. With double quotes, scripts strip the quotes, the JSON breaks and tracking switches off without any error.

Step 9: Test end to end, then go live

Test with real money on the preview theme first. Tick each box:

Whop's fraud filter may block repeated self-tests from one card. That is not a bug: use a second card or wait.

Troubleshooting and security

Symptom Cause and fix
Checkout stops working mid-day Cloudflare free plan hit its KV write limit. Move to Workers Paid before launch.
Whop refuses the webhook URL The server or its TLS wasn't live yet. Deploy first, wait for TLS, then create the webhook.
Apple Pay domain won't verify Wrong association file, or DNS still propagating. Use Whop's platform-integrator file and retry after ~30 min.
Only one wallet button shows Whop shows one wallet per browser. Mount one button per wallet (Apple Pay, Google Pay).
Order has no customer email Wallet payment with no email field on your page. Keep the required email field, and ask for an email on /thanks if it's still missing.
Order created but address missing Empty last_name (one-word name) or emoji in the name. Apply the name fixes from step 7.
Payment area blank Elements script blocked or wrong API names. Check the browser console. The frame fallback should take over automatically.
Shoppers land on Shopify's "can't accept payments" page A checkout path skips your script (wallet buttons, a bundle app, or fallback code sending people to /checkout). Re-check step 5.
Ads Manager shows every purchase twice Both your server and an attribution app send Purchase. Keep one sender.
A pixel shows no purchases Its URL has no ttpx, or its token is missing or belongs to another pixel. Test it with a test_event_code.
Test buyers see a $0 shipping rate The cached shipping zones still hold the test rate. Delete the rate and the cache key.
Refund fails Whop balance is empty. Keep a minimum balance.

Security rules