Spec Template
Payment Integration
Payment integrations are the highest-stakes features a product team ships. Errors here affect revenue directly, and the failure modes — failed webhooks, duplicate charges, missed subscription cancellations — are often silent until they aren't. This template builds in PCI constraints and the edge cases that matter most before any code is written.
Use it for: subscription billing setup, one-time payment flows, payment method management, plan upgrades and downgrades, and refund flows.
Feature: [one-line name — e.g., "Stripe subscription checkout with plan selection"]
## What and why
Problem
[What can't users do today, or what's broken in the current billing flow? What is the business impact?]
Who this is for
[Specific user and context — e.g., "Free-tier users choosing to upgrade to a paid plan" or "Admin users managing team billing"]
In scope
- [Payment methods supported — e.g., card, SEPA, BACS]
- [Currencies supported]
- [Billing model — one-time, subscription, usage-based]
- [Plans or products being included]
- [Webhook events being handled]
Out of scope
- [Payment methods not in this phase]
- [Currencies or regions not in scope]
- [Billing features deferred — e.g., "Invoicing and VAT handling not in scope"]
Key behaviors
- When [user completes checkout], [what happens in the product — access granted, email sent, etc.]
- When [payment succeeds via webhook], [how the system confirms and updates the user's state]
- When [payment fails], [what the user sees and what recovery options they have]
- When [subscription renews], [what the system does]
- When [user cancels], [when access ends and what the user sees]
- When [refund is issued], [what the system updates and whether access is revoked]
Edge cases
- What happens if the payment provider webhook fires before the checkout redirect completes?
- What happens if the same webhook event is delivered more than once?
- What happens if the user closes the browser immediately after payment confirmation?
- What happens when a card is declined — soft decline vs. hard decline?
- What happens when a subscription renewal fails — first failure, second failure, and final failure?
- What happens if a user attempts to subscribe while already subscribed?
- What happens during payment provider downtime?
Open questions
- [Payment provider confirmed? Stripe unless otherwise stated.]
- [Stripe test mode credentials available?]
- [Webhook endpoint configured in Stripe dashboard for this environment?]
- [Which Stripe webhook events need handlers? — invoice.paid, invoice.payment_failed, customer.subscription.deleted, etc.]
- [Is VAT or tax calculation in scope?]
- [What's the dunning policy — how many retries before subscription is cancelled?]
## Coding agent context
Tech stack
[e.g., "Next.js 14, TypeScript, Stripe Node SDK v14, Prisma, PostgreSQL"]
Reference files
[e.g., "See /lib/stripe.ts for the Stripe client initialisation"]
[e.g., "See /pages/api/webhooks/stripe.ts for the existing webhook handler pattern"]
[e.g., "See /lib/billing/ for subscription creation and cancellation helpers"]
Patterns to follow
- [e.g., "Webhook handlers must verify the Stripe signature using the pattern in the existing handler — do not skip this step"]
- [e.g., "All Stripe API calls are wrapped in try/catch with structured error logging — follow the pattern in /lib/stripe.ts"]
- [e.g., "Subscription state is stored in the subscriptions table — see schema.prisma for the model"]
Do not
- [ ] Do not store card numbers, CVVs, or full PANs anywhere — use Stripe's tokenisation exclusively
- [ ] Do not log payment method details, card numbers, or raw Stripe payloads that may contain sensitive fields
- [ ] Do not grant product access based on the checkout redirect alone — always wait for webhook confirmation
- [ ] Do not process webhook events without verifying the Stripe signature first
- [ ] Do not skip idempotency — use Stripe idempotency keys on all write operations that may be retried
- [ ] Do not handle subscription state in the frontend — all billing logic runs server-side
- [ ] Do not introduce new Stripe API calls without checking the version pinned in /lib/stripe.ts
Acceptance criteria
- [ ] Checkout flow works end-to-end in Stripe test mode before any production credentials are used
- [ ] Payment success is confirmed via webhook, not redirect — access is granted only after webhook fires
- [ ] Duplicate webhook events do not create duplicate records or grant duplicate access
- [ ] Card decline handling shows an appropriate user-facing message and offers retry or alternative payment method
- [ ] Subscription cancellation correctly sets an access end date — does not revoke access immediately unless specified
- [ ] Refund flow updates the user's subscription state and access correctly
- [ ] No card numbers, CVVs, or sensitive Stripe fields appear in application logs
- [ ] Webhook signature verification fails closed — unverified events are rejected, not processed
- [ ] Existing tests pass without modificationRezonant aggregates context from your codebase, Figma, and meeting notes into specs your coding agents can act on — without the PM having to look everything up manually.
Join the waitlist →