> ## 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.

# SaaS Paywall

> Gate a SaaS product with Shoppex subscriptions — your app owns the UX, Shoppex owns billing, trials, and dunning.

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

<CardGroup cols={2}>
  <Card title="No Stripe integration to build" icon="bolt">
    Shoppex already orchestrates PSPs. You get card, PayPal, and crypto without wiring each one.
  </Card>

  <Card title="Real subscription lifecycle events" icon="arrows-rotate">
    Trial started, trial ended, renewed, cancelled, upcoming renewal — Shoppex emits them all as webhooks.
  </Card>
</CardGroup>

## 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.

```typescript theme={"system"}
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

| Piece           | Responsibility                                                      |
| --------------- | ------------------------------------------------------------------- |
| Your SaaS app   | Signup, user record, feature flags, cancellation UI                 |
| Webhook handler | Toggle feature flags based on subscription lifecycle events         |
| Shoppex         | Plan 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:

```typescript theme={"system"}
// 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;
}
```

<Info>
  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`.
</Info>

## 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.

## Related

<CardGroup cols={2}>
  <Card title="Architecture Reference" icon="sitemap" href="/headless/architecture">
    Setup B (Next.js SSR) is the typical SaaS shape.
  </Card>

  <Card title="Dev API Subscriptions" icon="arrows-rotate" href="/api-reference/introduction">
    Full subscription endpoint surface.
  </Card>
</CardGroup>
