Skip to main content
@pylonsync/stripe replaces the ~400 lines every Pylon app rewrites when adding Stripe: customer creation, checkout session minting, billing portal, webhook signature verification, plan derivation, subscription state. One stripe({plans, hooks}) block returns a manifest fragment + handler factories.

Install

Config

Wrapper files

Pylon loads function handlers by file. Create one-line wrappers under functions/:
…and one per handler key exposed by billing.handlers (cancelSubscription, restoreSubscription, stripeWebhook, plus the _pylonStripe* internals).

Required env

Webhook endpoint

Configure in Stripe Dashboard → Developers → Webhooks → Add endpoint:
Copy the signing secret (whsec_...) → set as STRIPE_WEBHOOK_SECRET on your Pylon machine.

Lifecycle hooks

RBAC

The authorizeReference hook gates subscription mutations. Defaults:
  • referenceType: "org" — caller must be the org’s owner or admin.
  • referenceType: "user" — caller must be the user themselves.
  • referenceType: "custom" — you must supply your own resolveCustomer hook (maps referenceId → Stripe customer id, creating one if needed). There’s no default authorization for custom, so supply authorizeReference too.

Security

  • Constant-time signature verification with a 5-minute replay window. The Stripe-Signature header can carry multiple v1= signatures (Stripe emits several during a signing-secret rotation); the verifier accepts a match on any of them against your STRIPE_WEBHOOK_SECRET.
  • URL allowlist auto-derived from PYLON_PUBLIC_URL + PYLON_CORS_ORIGIN — no hardcoded host strings.
  • Three-signal plan resolver (lookup_key → nickname → priceId match) — Stripe occasionally elides the first two on webhook payloads.
  • Double-trial guard — prior Subscription rows for the same reference disable the trial period regardless of plan config.
  • Idempotent webhook upsert via stripeSubscriptionId lookup, so Stripe retries produce the same state.