Setup A β Pure SPA (React / Vue / Svelte / Astro Islands)
Best when your frontend is static or client-rendered and your only backend is your webhook receiver. Typical flow:- The browser reads public storefront data with
@shoppexio/storefront. - Checkout happens through hosted checkout or the Embed SDK.
- Shoppex sends signed webhooks to your worker.
- Your worker fulfills the order or updates your own systems.
| Piece | Responsibility |
|---|---|
| Browser SPA | UI, product grid, cart UI, buy button with @shoppexio/storefront or Embed SDK |
| Webhook worker | Verify X-Shoppex-Signature, run fulfillment, persist order state |
| Shoppex | Catalog, PSP orchestration, invoice, license delivery, webhook emission |
- Do not ship
shx_*API keys in the SPA bundle. Server-side reads need a tiny backend (worker or Route Handler). - If you need to transform data before it hits the browser, go to Setup B.
Setup B β Next.js SSR (or Nuxt / SvelteKit / Remix)
Best when you want SEO, per-request personalization, and a single deployable unit. Typical flow:- The browser requests HTML from your app.
- Server Components or Route Handlers call the Dev API with your server-held API key.
- A Server Action creates the payment and redirects the buyer to hosted checkout.
- Shoppex posts signed webhooks back to your app.
| Piece | Responsibility |
|---|---|
| Server Components | Catalog reads, personalization, pricing overrides, session lookup |
| Server Actions | Create payment session, return redirect URL |
| Route Handlers | Verify and handle signed webhooks |
| Shoppex | Everything payment, invoice, license, subscription |
- Server Actions and Route Handlers must be the only place you touch
SHOPPEX_API_KEY. Wrap your SDK client inimport 'server-only'. - Next.js 16 request APIs are async β
await cookies(),await headers(),await params,await searchParams. - Do not forget constant-time signature comparison (
timingSafeEqual) in your webhook route.
Setup C β Mobile + Backend-for-Frontend
Best for native iOS / Android / React Native apps, Electron apps, or any client that must not hold long-lived API keys. Typical flow:- The app sends its own session token to your BFF.
- The BFF creates the payment with the Dev API.
- The app opens the hosted checkout URL in the system browser.
- Shoppex redirects the buyer back to your app or fallback return page.
- Shoppex also sends the signed webhook to your BFF so your backend has the final source of truth.
| Piece | Responsibility |
|---|---|
| Native app | Your UI, your auth, deep-link handler for the return URL |
| BFF | Exchange user session for scoped Shoppex calls, create payment sessions, run fulfillment |
| Shoppex | Everything commerce |
- Use a universal link / app link as
return_urlso the OS hands control back to your app cleanly. - Never bundle the
shx_*key in the app binary. Always route through your BFF. - For Apple App Store apps selling digital goods consumed in-app, App Store review rules may require In-App Purchase instead of a web checkout β check before shipping.
Auth Surface by Setup
| Setup | Who holds the API key | Customer login |
|---|---|---|
| A β Pure SPA | Worker / tiny backend only | None, or your own magic-link/OAuth |
| B β Next.js SSR | Server Components + Route Handlers | Next-Auth / your own / Shoppex customer OTP |
| C β Mobile + BFF | BFF only | Your appβs existing auth |
/v1/customer/auth/otp/request, /verify) is available as a drop-in auth for Setup A. Setup B and C typically keep their own auth and use Shoppex for commerce only.
For headless frontends, pass an optional redirect_url on /v1/customer/auth/otp/request. The URL is validated against the shopβs allowed callback hosts (<slug>.myshoppex.io or any enabled custom domain) and is returned on the successful /verify response so your frontend can redirect after setting the session. Invalid or disallowed hosts are rejected with 400.
Idempotency and Retries
All three setups share the same rules:- Send
Idempotency-Keyon every mutating Dev API call (UUID v4 is fine). Shoppex stores the response for 24 hours and replays it on retry. - Webhook handlers must be idempotent. Shoppex retries on non-2xx. Use the eventβs
data.uniqid+eventtuple as your dedupe key. - Ack webhooks fast. Queue heavy fulfillment work and return 2xx within a few seconds.
Data Flow Per Checkout Stage
| Stage | Who acts | Event |
|---|---|---|
| Browse | Your frontend reads from Shoppex (Storefront SDK or Dev API proxy) | β |
| Add to cart | Your frontend (local state) | β |
| Checkout start | Your server calls POST /dev/v1/payments | β |
| Payment UI | Shoppex hosted checkout / Embed SDK | β |
| Payment success | Shoppex sends order:paid webhook | order:paid |
| Fulfillment | Your webhook handler grants access, sends license, provisions subscription | β |
| Return | Customer hits your return_url | β |
Server-side cart state is not required for any of these setups today β carts live in your frontend (local state, cookie, or your DB) until you create the payment session. If you need a hosted cart API, follow the changelog; it is on the roadmap for the
/dev/v1/carts surface.Next Steps
Next.js Quickstart
Hands-on version of Setup B.
Use Cases
Discord bots, SaaS paywalls, mobile apps, gaming-key resellers.
Webhook Security
HMAC-SHA512, constant-time comparison, retry behavior.
Authentication
API keys, scopes, OAuth2 for installable apps.