Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.shoppex.io/llms.txt

Use this file to discover all available pages before exploring further.

Use Shoppex as the billing engine behind your SaaS. Your app handles signup, dashboard, and feature flags. Shoppex handles the checkout, card/PayPal/crypto, trials, renewals, failed-payment retries, and plan changes.

Why this works well

No Stripe integration to build

Shoppex already orchestrates PSPs. You get card, PayPal, and crypto without wiring each one.

Real subscription lifecycle events

Trial started, trial ended, renewed, cancelled, upcoming renewal — Shoppex emits them all as webhooks.

Flow

  1. The user signs up in your app.
  2. Your backend creates an order for the subscription product via POST /dev/v1/orders (not POST /dev/v1/payments).
  3. The user completes payment in hosted checkout.
  4. Shoppex emits subscription lifecycle webhooks.
  5. Your app updates entitlements from those events.
const { data: order } = await shoppex.raw.POST<{ data: { uniqid: string; checkout_url?: string } }>(
  '/dev/v1/orders',
  {
    body: {
      customer_email: user.email,
      custom_fields: { app_user_id: user.id },
      items: [{ product_id: subscriptionProductUniqid, quantity: 1 }],
    },
  },
);

redirect(order.checkout_url ?? `https://checkout.shoppex.io/invoice/${order.uniqid}`);

Shape

PieceResponsibility
Your SaaS appSignup, user record, feature flags, cancellation UI
Webhook handlerToggle feature flags based on subscription lifecycle events
ShoppexPlan pricing, trial logic, renewal billing, dunning, crypto support

Feature-flag pattern

Link your user record to the Shoppex customer on signup, then toggle entitlements on subscription events:
// On signup
const { data: customer } = await shoppex.customers.create({ email: user.email });
await db.users.update({ where: { id: user.id }, data: { shoppexCustomerId: customer.id } });

// Webhook handler (abbreviated)
switch (event.event) {
  case 'subscription:trial:started':
  case 'subscription:created':
  case 'subscription:renewed':
    await db.users.update({
      where: { shoppexCustomerId: event.data.customer_id },
      data: {
        billingStatus: 'active',
        planActiveUntil: event.data.current_period_end,
      },
    });
    break;

  case 'subscription:cancelled':
  case 'subscription:trial:ended':
    await db.users.update({
      where: { shoppexCustomerId: event.data.customer_id },
      data: {
        billingStatus: 'canceled',
        planActiveUntil: event.data.current_period_end,
      },
    });
    break;
}
Treat planActiveUntil as the source of truth in your feature-flag middleware. subscription:cancelled means “will not renew” — the user still has access until current_period_end.

Subscription management

Expose plan changes inside your own UI by calling the Dev API from your backend:
  • POST /dev/v1/subscriptions/{id}/change-plan — upgrade / downgrade
  • POST /dev/v1/subscriptions/{id}/pause / /resume
  • POST /dev/v1/subscriptions/{id}/cancel
  • GET /dev/v1/subscriptions/{id}/billing-history
You can build a full billing page without ever linking the user to a Shoppex-branded UI.

Pitfalls

  • Idempotency on signups — if your signup and checkout are two clicks apart, send the same Idempotency-Key on retry or you will create duplicate subscriptions.
  • subscription:upcoming is your churn signal — sent before the next renewal, good trigger for “upgrade your plan” emails or feature-usage reminders.
  • Dunning is automatic — Shoppex retries failed payments on its own schedule. Do not build your own retry loop on top.

Architecture Reference

Setup B (Next.js SSR) is the typical SaaS shape.

Dev API Subscriptions

Full subscription endpoint surface.