saas-billing-patterns-for-stripe-and-paypal
Made by JeffAudit, harden, or implement SaaS subscription billing with Stripe/PayPal. Use when fixing webhooks, duplicate charges, billing security, dunning, MRR, migrations, or Next.js billing.
Works With
Quality and Utility come from canonical skill metadata. Community ratings appear separately as stars when subscriber reviews exist.
Subscribe to download
Public previews are available, but downloads, CLI sync, and premium content require an active subscription.
Sign In to Subscribe- Full skill content with deterministic versioning
- CLI sync + local search index
- Access to Jeffrey’s premium library
Save to your account
Saved skills live in your cloud list. Run jsm sync on each machine to pull them locally.
What's New with this Skill
4Updates, enhancements, and release notes for this skill.
Updated: saas-billing, Stripe code moved off the API shapes removed in 2025
Much of the Stripe code targeted API shapes removed in the 2025 basil and clover releases: invoices.retrieveUpcoming, invoice.payment_intent, invoice.subscription, subscription-level current_period fields and usage records. The refund chain built on them always fell back to revoking every subscription the customer had. Code now typechecks against stripe-node 22, and several PayPal and script details were fixed.
- Stripe code moved to current APIs: createPreview, invoicePayments, invoice.parent, item-level periods, meter events, total_taxes
- The refund chain no longer falls back to revoking every subscription for the customer
- The dunning retry pays the latest open invoice instead of trying to pay a preview
- Dispute evidence uses uploaded File IDs; Connect direct and destination charges described correctly
- Test-clock and staging drill scripts fixed and runnable without the Stripe CLI
Checked against stripe-node 22.1.1 (snippets typechecked with tsc), the Stripe changelog and testing docs, and PayPal's REST docs.
- Removed APIs.
invoices.retrieveUpcomingis nowinvoices.createPreview; PaymentIntents are found throughinvoicePayments.list; subscriptions throughinvoice.parent.subscription_details.subscription; billing periods onsubscription.items.data[0]; metered usage throughbilling.meterEvents.createand meter event summaries; taxes throughinvoice.total_taxes.
Enhanced: saas-billing-patterns, the audit swarm spawns into the repo it audits
The 12-pane billing-audit swarm used ntm new --session --layout and ntm send --pane all --message, none of which exist, and the session name would have resolved to an empty directory named after a timestamp. The recipes now spawn against the audited project with a per-run label, dispatch with real send --pane selectors, and tear down with kill --force.
- ntm spawn "$PROJECT_NAME" --label "billing-audit-${TIMESTAMP}" --cc=12 --no-user --verify-boot puts all twelve agents (eleven bundle owners, B10-B110, plus the harmonizer) inside the actual repo
- Kickoff dispatch and teardown target the real <project>--<label> session with send --pane=$i and kill --force
v4: payload integrity, refund-terminal absorbing state, SLO snapshots, and an ADR system, mined from a real Stripe + PayPal codebase
Adds production-hardened patterns the prior version lacked, taken from a live Stripe + PayPal stack: per-subscription advisory locks, the validatePaymentEventIntegrity check that refuses test-mode-in-prod / wrong-price / discount / trial sessions, the absorbing-state guard that stops post-refund webhook revival, the SLO snapshot cron with P0–P5 targets, ADR templates for billing decisions, and the 6-guard manual invoice retry that handles SCA, attempt-cap, and webhook race recovery.
- per-subscription advisory locks (pg_advisory_xact_lock + hashtext) for handler-level serialization beyond the row-level WHERE clause
- validatePaymentEventIntegrity rejects test-mode-in-prod, client-reference user mismatch, wrong price (allowlist), discount, and trial sessions; the owner-mismatch hijack guard is a separate check
- refund-terminal status none as the absorbing state that blocks post-refund webhook revival
- TOCTOU prevention pattern: access check INSIDE the FOR UPDATE row lock, not before
- cross-provider probe interface returning {existingSub, reusableCustomerId} from one scan
- PayPal eager sub-id persistence + single-shot rollback for orphaned subs; approval URL allowlisting
- detectStaleCheckoutRace with durable billing_critical_alert (re-throws on alert-queue failure)
- verify-endpoint-alerts + slo-snapshot + webhook-delivery-health crons with per-tag thresholds and ADR-pinned P0–P5 targets
- 6-guard retryLatestStripeInvoice with race recovery via reverifyInvoiceAfterPayError + SCA hosted_invoice_url email rewrite
- ADR system (docs/adr/billing/) with template + 10 load-bearing ADRs covering verify-as-write, payload integrity, SLO targets
Background: an audit ran the prior version of this skill against jeffreys-skills.md (a real Stripe + PayPal SaaS) and surfaced ~30 patterns the codebase had paid for in production but the skill had not yet codified. v4 folds them in.
B10 (schema): per-subscription advisory locks via pg_advisory_xact_lock(hashtext(...)) for serializing concurrent webhook handlers against the same sub even when the row doesn't exist yet; refund-terminal none as the absorbing state that blocks post-refund revival when Stripe sends a later customer.subscription.updated for a sub that wasn't auto-cancelled; the
saas-billing-patterns-for-stripe-and-paypal: audit your billing for duplicate-charge bugs, webhook hijacks, and dunning gaps; or build a hardened dual-provider Stripe + PayPal stack from scratch
Point this skill at any SaaS repo and either get a coverage matrix of what's broken or missing in your billing layer, or run the canonical step-ordered build that produces production-grade Stripe + PayPal subscriptions with hardened webhooks, idempotent reconciliation, dunning, MRR reporting, and a real-DB integration test suite. The kernel is one rule: the provider is the source of truth, the database is a fast cache, every other pattern is a corollary.
- Audit mode: point at an existing SaaS repo and get a coverage matrix, risk-scored gap list, beads-style implementation plan, real-DB integration tests, drift-guard tests, runbooks, and a secret-custody inventory.
- Build mode: from-scratch step-ordered build of production billing (schema, checkout, webhooks, state machine, security, observability, dunning, reconcile, MRR, tax, teams, usage, migration, evidence, disputes) in dependency order, schema before code.
- Kernel rule: provider is source of truth, DB is fast cache. When DB and provider disagree, provider wins. When they agree but the user experience is wrong, your projection is wrong. When they agree and the user is happy, you have not yet been attacked.
- Webhook hardening built in: signature verification, idempotency by event.id, replay-staleness guards, 200-on-error, dual-provider symmetric coverage, advisory-lock checks on cron. Seven security-audit findings (SA-01, 02, 03, 06, 13, 17, 22) shaped these patterns.
- Dunning ladder: D0 / D7 / D14 / D21 individual ladder, compressed team ladder, cycle-aware dedup, SCA / 3-D Secure routing, manual invoice retry with overcharge defenses, team coverage suppression.
- MRR reporting: canonical churn timestamp, replacement-coverage exclusion, payment fees blended rate, settlement ledger ingestion from Stripe Balance Transactions and PayPal Transaction Search, customer health scoring, Monte Carlo runway projection.
- Seven modes route from one intake: audit-only, audit-and-fix, harden-incident, add-feature, greenfield, migration, compliance-pass. Risk appetite (production-paying / pre-launch / internal) tunes which patterns are mandatory.
- 31 subagents have single charters: archaeologist, security-reviewer, dispute-defender, tax-implementer, observability-alerter, RLS-auditor, red-team-attacker, runbook-writer, fresh-eyes, harmonizer, and 21 others. Wire them through MCP Agent Mail for swarm runs.
- 12 audit scripts you can wire into pre-commit, CI, and pre-deploy: webhook 200-on-error, UPDATE staleness guards, cron advisory locks, RLS policy probes (anon, authenticated, service_role), Vercel env scope, CSP for Stripe Elements, Stripe event coverage versus live Dashboard.
- Phase 8 refuses to mark complete without a real-DB integration test: mock-only billing tests are rejected per source guide §69, because mocked tests pass while prod fails.
The kernel is one rule. The provider is the source of truth, the database is a fast cache of provider state, and every pattern in the skill is a corollary. When DB and provider disagree, the provider wins. When they agree but the user experience is wrong, the projection is wrong. When they agree and the user is happy, you have not yet been attacked. The skill is built around the asymmetric dual-provider case: Stripe and PayPal disagree on invoice semantics, idempotency surfaces, and webhook delivery guarantees, and pretending otherwise is how duplicate charges ship.