Skip to content

Stripe

Billing uses Stripe Checkout for subscriptions and signing-request packs, the Stripe customer portal for payment methods and invoices, and webhooks to keep each organization’s plan in sync.

  1. Create the products and prices. With your secret key:

    Terminal window
    STRIPE_SECRET_KEY=sk_live_... npm run stripe:setup -w @pdfeditor/api

    This creates a monthly USD price for each plan and a one-time price for the signing-request pack, with the prices from @pdfeditor/shared. It’s safe to run again: prices that already match are left alone, and changed amounts get a new price that takes over the lookup key. The app finds prices by lookup key (pdfeditor_small_monthly, pdfeditor_medium_monthly, pdfeditor_large_monthly, pdfeditor_xlarge_monthly, pdfeditor_signing_pack_100), so there are no price IDs to configure.

  2. Add a webhook endpoint in the Stripe dashboard (Developers → Webhooks) for https://app.example.com/api/billing/webhook, sending these events:

    • checkout.session.completed
    • checkout.session.async_payment_succeeded
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • invoice.payment_failed
  3. Copy the endpoint’s signing secret (whsec_…) into STRIPE_WEBHOOK_SECRET, set STRIPE_SECRET_KEY, and restart the API. With a secret key set, BILLING_MODE defaults to stripe.

  4. Enable the customer portal in the Stripe dashboard (Settings → Billing → Customer portal). The Payment method & invoices button opens it. Choose there what customers may do, for example update payment methods, view invoices and cancel subscriptions.

Event Effect
Checkout completed (subscription) The organization’s plan, status and billing period are copied from the subscription.
Checkout completed or async payment succeeded (pack) Once paid, 100 signing requests per pack are added to the organization’s extra requests.
Subscription created, updated or deleted Plan, status, period and cancel at period end are updated.
Invoice payment failed Recorded in the activity log, and every active Tenant Owner is emailed.
  • Webhooks are verified with the signing secret and processed once each (Stripe event IDs are stored, so retries are ignored).
  • Plan changes while a subscription is active are made directly on the subscription with prorations, without Checkout.
  • Checkout allows promotion codes and collects the billing address when needed. Automatic tax is off; configure tax in Stripe if you need it.
  • The organization’s status in the app follows Stripe: active, trialing and past_due allow use; other statuses block it.

Use a test-mode key (sk_test_…) and the Stripe CLI to forward events to a local API: stripe listen --forward-to localhost:3001/api/billing/webhook, then set STRIPE_WEBHOOK_SECRET to the secret it prints. Or leave Stripe unset and use simulated billing.