Subscription Billing Recovery

New

Recover users who start upgrading their subscription but abandon at the billing page. Works with Stripe, Paddle, and Lemon Squeezy.

The Problem

A free user clicks "Upgrade to Pro", gets redirected to Stripe Checkout, then hesitates and closes the tab. Without Retake, this user is gone forever. With Retake, they get a friendly reminder email 1 hour later.

Step 1. Track Upgrade Intent

Fire an INTENT event when the user clicks "Upgrade" or views the upgrade modal.

typescript
// When user clicks "Upgrade" or views upgrade modal
async function trackUpgradeIntent(user: User, targetPlan: Plan) {
  await retake.track({
    event: "INTENT",
    type: "upgrade",
    userId: user.id,
    email: user.email,
    value: targetPlan.price,
    currency: "USD",
    items: [{
      id: targetPlan.id,
      name: targetPlan.name,
      price: targetPlan.price,
      quantity: 1
    }],
    metadata: {
      currentPlan: user.currentPlan,
      upgradeTo: targetPlan.id
    }
  });
}

Step 2. Track Conversion via Webhook

When Stripe/Paddle sends the payment success webhook, send a CONVERSION event to cancel recovery emails.

Stripe Webhook Handler
typescript
// app/api/webhooks/stripe/route.ts
import Stripe from 'stripe';

export async function POST(req: Request) {
  const event = await stripe.webhooks.constructEvent(
    await req.text(),
    req.headers.get('stripe-signature')!,
    process.env.STRIPE_WEBHOOK_SECRET!
  );

  switch (event.type) {
    case 'checkout.session.completed':
    case 'invoice.payment_succeeded':
      const session = event.data.object;
      
      // 🔥 Tell Retake: User converted!
      await retake.track({
        event: "CONVERSION",
        userId: session.metadata?.userId || session.customer_email,
        transactionId: session.id,
        value: session.amount_total! / 100
      });
      break;
  }

  return Response.json({ received: true });
}

Best Practices

Pass userId in metadata

When creating a Stripe Checkout session, include your userId in the metadata so you can match it in the webhook.

Track at modal open

Don't wait until Stripe Checkout opens. Track intent when the upgrade modal first appears to catch early dropoffs.

Handle multiple webhooks

Stripe may send multiple success events. Retake handles duplicates gracefully, but you may want to debounce on your end.

Include plan comparison

The metadata field is useful for including what plan they're upgrading from, enabling personalized recovery emails.