Subscriptions & Usage Billing
Pathrule2 Rules • 2 Memories • 1 Skill
A pattern bundle for subscription and usage-based billing on Stripe Billing in 2026. It covers Billing Meters and meter events, idempotent webhook handling, entitlement sync, proration on plan changes, and dunning for failed payments. Keep your own database as the source of truth for usage and entitlements, and treat Stripe events as a queue you reconcile against.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
Rules
2Report usage through Billing Meters with idempotent meter events/src/usagehighstrictSend every usage event to a Billing Meter with a unique identifier and a valid timestamp.
| 1 | Meter all usage through Stripe Billing Meters and meter events; the legacy `usage_records` API is deprecated and must not be used for new code. |
| 2 | |
| 3 | - Set a unique `identifier` on every meter event so retries dedupe inside Stripe's rolling 24 hour+ window; reuse the same identifier when you retry a failed send. |
| 4 | - Keep the event `timestamp` within the past 35 days and no more than 5 minutes in the future, or Stripe rejects it. |
| 5 | - For high throughput (over a few hundred events/sec) use the v2 meter event stream with a session token instead of one synchronous call per event. |
| 6 | - Treat your own `usage_events` table as the source of truth; Stripe meter summaries are for billing, not for showing customers their live usage. |
Verify, dedupe, and acknowledge Stripe webhooks before doing work/src/api/webhookshighstrictEvery Stripe webhook handler verifies the signature, is idempotent, and returns 2xx fast.
| 1 | Drive all billing state transitions from verified webhooks, never from browser redirects, because the user can close the tab before the redirect fires. |
| 2 | |
| 3 | - Verify the signature with the raw request body and the endpoint secret; reject anything that fails before parsing. |
| 4 | - Make handlers idempotent by recording processed `event.id` values and skipping duplicates, since Stripe can deliver the same event more than once. |
| 5 | - Return a 2xx within seconds and offload slow work to a queue; long handlers cause Stripe retries and duplicate processing. |
| 6 | - Subscribe to `v1.billing.meter.error_report_triggered` and alert on `meter_event_customer_not_found` spikes, which signal a broken usage integration silently dropping events. |
Memories
2Entitlements are derived from subscription webhooks, not local guesses/src/billingSync feature access from Stripe Entitlements events and gate features on stored entitlements.
| 1 | Stripe Entitlements expose what each customer can access based on their active subscription, and we mirror them locally rather than hardcoding plan-to-feature maps. |
| 2 | |
| 3 | - On `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted`, and `entitlements.active_entitlement_summary.updated`, refetch the customer's active entitlements and upsert them into our `entitlements` table. |
| 4 | - Gate every paid feature on the stored entitlement lookup, not on the price ID or product name, so plan and packaging changes do not require code edits. |
| 5 | - Treat `customer.subscription.deleted` and past-due states as access removal, but keep a short grace window driven by `status` rather than deleting rows immediately. |
| 6 | - Reconcile nightly by listing live subscriptions and entitlements to heal any missed webhook. |
Proration, seats, and dunning are configured in Stripe, not hand-rolled/src/billingLet Stripe handle proration math, seat quantity changes, and failed-payment recovery.
| 1 | Plan changes, seat counts, and recovery from failed payments are owned by Stripe Billing; we configure behavior and react to the resulting events. |
| 2 | |
| 3 | - For upgrades and downgrades, update the subscription item and let Stripe compute proration (`proration_behavior`); never compute partial-period charges ourselves. |
| 4 | - Model seats as a licensed (per-seat) subscription item quantity; adjust the quantity on team membership changes so proration applies automatically. |
| 5 | - Enable Smart Retries for dunning (default schedule is 7 retries over 21 days) and react to `invoice.payment_failed`, `invoice.paid`, and `customer.subscription.updated` to flip account status. |
| 6 | - Never store prices locally; always reference Stripe price IDs so amounts stay authoritative. |
Skills
1subscriptions-usage-billing-review/rootPre-merge checklist for any change touching metering, webhooks, entitlements, proration, or dunning.
| 1 | --- |
| 2 | name: subscriptions-usage-billing-review |
| 3 | description: Review checklist for subscription and usage-based billing changes on Stripe Billing. Use before merging any code that records usage, handles billing webhooks, syncs entitlements, changes plans or seats, or touches dunning. |
| 4 | --- |
| 5 | |
| 6 | # Subscriptions & usage billing review |
| 7 | |
| 8 | - [ ] Usage is reported via Billing Meters and meter events, not the deprecated `usage_records` API. |
| 9 | - [ ] Every meter event sets a unique `identifier` and is safe to retry without double counting. |
| 10 | - [ ] Meter event timestamps fall within the past 35 days and under 5 minutes in the future. |
| 11 | - [ ] High-volume metering uses the v2 meter event stream rather than one synchronous call per event. |
| 12 | - [ ] A local `usage_events` ledger is written first and treated as the source of truth. |
| 13 | - [ ] Webhook signatures are verified against the raw body and endpoint secret before parsing. |
| 14 | - [ ] Webhook handlers are idempotent on `event.id` and tolerate duplicate delivery. |
| 15 | - [ ] Handlers return 2xx quickly and push slow work to a queue. |
| 16 | - [ ] `v1.billing.meter.error_report_triggered` is subscribed and alerts on `meter_event_customer_not_found`. |
| 17 | - [ ] Entitlements are synced from subscription and entitlement-summary webhooks and feature gates read the stored entitlement, not the price ID. |
| 18 | - [ ] Plan changes update the subscription item and let Stripe handle proration; no hand-rolled proration math. |
| 19 | - [ ] Seats are modeled as a per-seat item quantity that updates on membership changes. |
| 20 | - [ ] Dunning relies on Smart Retries plus reactions to `invoice.payment_failed` and `invoice.paid`. |
| 21 | - [ ] No prices are stored locally; code references Stripe price IDs. |
| 22 | - [ ] A nightly reconciliation job heals missed webhooks for subscriptions and entitlements. |
Why this pattern
Usage-based subscription billing silently loses meter events, grants the wrong entitlements, and mis-charges on plan changes when webhooks and metering are handled ad hoc.
Built for SaaS and AI product teams running subscription plus usage-based billing on Stripe..
Keeps your assistant from:
- Dropping usage because meter events are sent without idempotency or outside the accepted timestamp window
- Granting or revoking the wrong plan access because entitlements are derived from redirect callbacks instead of webhooks
- Double-applying webhook side effects or mis-charging customers on proration during plan upgrades and downgrades
- License
- Apache-2.0
- Version
- 1.0.0
- Updated
- 2026-06-09