Refund flow is implemented
Why it matters
When charge.refunded webhooks go unhandled, the application keeps granting paid features to users who have already been refunded — a direct revenue leak and a fraud vector where attackers chargeback a subscription then continue using the product indefinitely. Missing refund workflows also violate consumer-protection requirements in jurisdictions with mandatory refund windows (EU distance selling, California auto-renewal laws), exposing the business to regulatory complaints and chargeback disputes.
Severity rationale
Medium because refunded users retain paid access, creating ongoing revenue leakage and chargeback-fraud exposure across the subscriber base.
Remediation
Handle charge.refunded in the Stripe webhook route and revoke access when the full amount is refunded. In app/api/webhooks/stripe/route.ts, add a case that compares charge.amount_refunded === charge.amount and updates the user record to downgrade the plan:
case 'charge.refunded':
if (charge.amount_refunded === charge.amount) {
await db.user.update({ where: { stripe_customer_id: charge.customer }, data: { plan: 'free' } })
}
break
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Billing & Payments Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
Taxons
History
- 2026-04-18·v1.0.0·Initial import from saas-billing·automated