Source: https://www.hype-stack.dev/docs/packs-templates/packs/pack-billing-stripe

# Billing (Stripe)

Stripe integration, subscriptions, and billing flows. This is the `pack-billing-stripe` pack, the default of the three
billing variants. The other two, [Lemon Squeezy](/docs/packs-templates/packs/pack-billing-lemonsqueezy) and
[Polar](/docs/packs-templates/packs/pack-billing-polar), are merchants of record and ship the same product shape.

## What you need

- A **Stripe** account. Sign up at [dashboard.stripe.com](https://dashboard.stripe.com).
- Postgres and Valkey (subscription status is cached in Valkey, keyed by organization).
- Optionally **Resend** for receipt emails. If `RESEND_API_KEY` is set, the pack sends receipts; otherwise it skips
  email.

## Stripe credentials

From the Stripe dashboard, grab:

- **Secret key** (`STRIPE_SECRET_KEY`) and **publishable key** (`STRIPE_PUBLISHABLE_KEY`) from
  [API Keys](https://dashboard.stripe.com/apikeys).
- **Webhook signing secret** (`STRIPE_WEBHOOK_SECRET`) from [Webhooks](https://dashboard.stripe.com/webhooks), or create
  one locally with the Stripe CLI (below).
- **Price IDs** for the plans you sell (`STRIPE_PRICE_PRO`, `STRIPE_PRICE_PREMIER` in the pack's env schema).

## Data model

There is no Prisma "subscriptions" table in this pack. Billing state is org-scoped and stored in Valkey:

| Key pattern                    | Meaning                                  |
| ------------------------------ | ---------------------------------------- |
| `stripe:org:{organizationId}`  | Stripe customer id for that organization |
| `stripe:customer:{customerId}` | Cached subscription / billing status     |

Checkout, portal, and webhook sync code live under `apps/backend/src/features/billing/` and
`apps/backend/src/libs/stripe/`. Organization admins get `MANAGE:BILLING` through the pack's permission grants.

## Checkout and portal flow

**Subscribe an organization**

1. **Create checkout** — The frontend calls the billing module. The backend creates a Stripe Checkout session for the org's customer (creating one if needed).
2. **Customer pays in Stripe** — Stripe hosts the checkout UI. Success returns to the pack's checkout-success route.
3. **Webhook sync** — POST /checkout/webhook verifies the signature, then syncs customer and subscription status into Valkey.
4. **Customer portal** — create-billing-portal opens Stripe's portal for plan changes and invoices against the same org customer.

## Webhook endpoint

The pack exposes a Stripe webhook receiver at:

```
POST /checkout/webhook
```

In production, create a webhook endpoint in Stripe pointing at `https://your-api-origin/checkout/webhook`, subscribe to
the events the pack handles (checkout, subscription, customer), and copy the signing secret into
`STRIPE_WEBHOOK_SECRET`.

## Local development with the Stripe CLI

Stripe cannot reach `localhost` over the internet, so use the Stripe CLI to forward events. Install it from
[stripe.com/docs/stripe-cli](https://stripe.com/docs/stripe-cli), then:

```bash
stripe listen --forward-to localhost:3000/checkout/webhook
```

The CLI prints a `whsec_...` signing secret. Put it in `STRIPE_WEBHOOK_SECRET`.

## Failure and retry notes

- Signature verification failures reject the webhook. Fix the secret before debugging business logic.
- Sync helpers update the Valkey cache from Stripe's customer / subscription objects. If the UI looks stale after a
  portal change, confirm the webhook events are arriving and the org → customer mapping exists.
- Receipt email is best-effort: without `RESEND_API_KEY` the send path returns early and checkout still succeeds.

## Environment variables

| Variable                 | Purpose                                 |
| ------------------------ | --------------------------------------- |
| `STRIPE_SECRET_KEY`      | Stripe API secret key                   |
| `STRIPE_PUBLISHABLE_KEY` | Publishable key the frontend uses       |
| `STRIPE_WEBHOOK_SECRET`  | Signing secret for the webhook endpoint |
| `STRIPE_PRICE_PRO`       | Stripe Price ID for Pro                 |
| `STRIPE_PRICE_PREMIER`   | Stripe Price ID for Premier             |
| `RESEND_API_KEY`         | Optional. Enables receipt emails        |

## One subscription per customer

In the Stripe dashboard, under Settings > Checkout and Payment Links, turn on **Limit customers to one subscription**.
The pack keys billing state by organization, and that setting is what stops a second checkout from creating a parallel
subscription. The CLI prints this reminder after install.

## In the admin

On top of a starter, this pack adds a revenue row to the [admin app](/docs/packs-templates/packs/admin) dashboard, a
billing card and a revenue card on the organization details page, and `/admin/billing` API routes. Nothing extra to
configure beyond Stripe env vars; the UI shows up once both packs are installed.

## Surfaces

Web, admin, and mobile. The Expo app gets the billing screen and the checkout handoff to Stripe's hosted page.

## Related

- [Organizations](/docs/backend/organizations) for the tenant boundary billing attaches to
- [Mailing](/docs/backend/mailing) for receipt templates
- [Going to production](/docs/getting-started/going-to-production) for webhook URL checklist
