Stripe Churn Deflection — App Description

A niche, focused micro‑SaaS to reduce involuntary churn by automating dunning, providing recovery workflows, and offering an admin recovery surface.

Why Stripe Churn Deflection?

1. High-level purpose

Stripe Churn Deflection is a focused micro-SaaS built to reduce churn for subscription-based products by automating dunning (failed payment retries and reminders), offering recovery workflows, and providing an admin interface for management and recovery operations.

2. What it does (Features)

3. Supported workflows

Normal billing, retry/reminder flows, manual admin recovery, and export/audit workflows are supported.

4. Core data models (Prisma)

// User, Subscription, DunningCase, RetryAttempt, DunningReminder, RecoveryAttribution, Settings, StripeEventLog, AuditLog, CspReport

5. Technology stack

Next.js 14 + TypeScript, Prisma, SQLite (dev) / Postgres (prod), Stripe SDK, optional Redis, Postmark/SMTP, Sentry, Jest + Playwright.

6. Notable locations

pages/api/stripe/webhook.ts, pages/api/cron/*, pages/admin/*, lib/prisma.ts, scripts/seed-demo.js.

7. Simple yet effective dev workflow

npm install
npx prisma generate
npx prisma db push
node scripts/seed-demo.js
npm run dev

8. CI & E2E

CI runs unit tests (Jest) and Playwright E2E tests. Use the `DATABASE_URL`condition to switch migrations vs db push behavior.

9. Security

Validate webhooks, use secret managers, protect admin endpoints, and enable Sentry in production.

10. Observability

Sentry, StripeEventLog, and AuditLog are primary telemetry sources. Add Prometheus metrics for queue backlog and webhook failure rates.

11. Limitations

SQLite vs Postgres differences and considerations around enums and FK behavior noted.

. Next steps

  1. Enable Postgres in staging, run migrations.
  2. Enable Sentry and secrets manager in prod.
  3. Separate cron runner for scale.

Architecture diagram

Your browser does not support SVG.

14. Quick start

Run the dev workflow above and open http://localhost:3000.

Quick code snippets

Stripe webhook (Next.js API route, TypeScript)

import type { NextApiRequest, NextApiResponse } from 'next'
import Stripe from 'stripe'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2024-11-15' })

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const sig = req.headers['stripe-signature'] as string | undefined
  const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!
  let event: Stripe.Event
  try {
    event = stripe.webhooks.constructEvent(req.body, sig!, webhookSecret)
  } catch (err) {
    console.error('Webhook signature verification failed', err)
    return res.status(400).send('Invalid signature')
  }

  switch (event.type) {
    case 'invoice.payment_failed':
      // create or update DunningCase, schedule RetryAttempt
      break
    case 'invoice.payment_succeeded':
      // mark recovered, create RecoveryAttribution
      break
    default:
      break
  }
  res.status(200).json({ received: true })
}

Cron-like API stub (secure endpoint to run retries)

// pages/api/cron/run-dunning.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(req: NextApiRequest, res: NextApiResponse){
  const secret = req.query.secret || req.headers['x-cron-secret']
  if(secret !== process.env.CRON_SECRET) return res.status(401).send('Unauthorized')

  // find due RetryAttempt records, attempt charges using Stripe SDK, update statuses
  // keep idempotency and backoff safety

  return res.status(200).json({ ok: true })
}