General
Billing

Webhooks

Learn how to handle Stripe webhooks.

Webhooks are used to receive events from Stripe. They are important to get the latest data so your application is in sync with Stripe.

Setting up Webhooks

  1. Go to your Stripe Dashboard
  2. Click "Add endpoint"
  3. Enter your webhook URL: https://yourdomain.com/api/webhooks/stripe
  4. Select the required events (see list below)
  5. Copy the webhook signing secret

Required Webhook Events

The following Stripe webhook events are handled by the webhook handler:

Subscription Events

  • customer.subscription.created - When a new subscription is created
  • customer.subscription.updated - When a subscription is modified (plan changes, status updates)
  • customer.subscription.deleted - When a subscription is canceled or expires
  • customer.subscription.trial_will_end - When a trial is ending soon (3 days before)
  • customer.subscription.paused - When a subscription is paused
  • customer.subscription.resumed - When a paused subscription is resumed

Checkout Events

  • checkout.session.completed - When a checkout session completes (subscriptions, one-time payments, credit purchases)

Invoice Events

  • invoice.paid - When an invoice payment succeeds
  • invoice.payment_failed - When an invoice payment fails

Charge Events

  • charge.refunded - When a charge is refunded (handles both full and partial refunds)

Refund Events

  • refund.created - When a refund is initiated
  • refund.updated - When a refund's status updates
  • refund.failed - When a refund fails

Dispute Events

  • charge.dispute.created - When a customer disputes a charge
  • charge.dispute.updated - When a dispute status updates
  • charge.dispute.closed - When a dispute is resolved
  • charge.dispute.funds_withdrawn - Funds withdrawn from balance
  • charge.dispute.funds_reinstated - Funds reinstated to balance

Customer Events

  • customer.deleted - When a customer is deleted from Stripe

Payment Intent Events

  • payment_intent.succeeded - When a payment intent succeeds (for audit logging)

Webhook Handler

The starter kit includes a comprehensive webhook handler at app/api/webhooks/stripe/route.ts that handles all billing events. The handler includes:

  • Signature verification - Validates webhook authenticity using Stripe's signature
  • Idempotency - Prevents duplicate processing of the same event
  • Error handling - Distinguishes between transient and permanent errors
  • Event logging - Records all events in the database for audit trails

Supported Events

The handler processes the following events:

  • checkout.session.completed - Handles subscriptions, one-time payments and credit purchases
  • customer.subscription.created - Creates subscription records
  • customer.subscription.updated - Updates subscription status and plan changes
  • customer.subscription.deleted - Marks subscriptions as canceled
  • customer.subscription.trial_will_end - Sends trial ending notifications
  • customer.subscription.paused - Handles subscription pauses
  • customer.subscription.resumed - Handles subscription resumption
  • invoice.paid - Logs successful invoice payments
  • invoice.payment_failed - Sends payment failure notifications
  • charge.refunded - Handles refunds (full and partial)
  • refund.created - Tracks refund lifecycle
  • refund.updated - Updates refund status
  • refund.failed - Logs refund failure
  • charge.dispute.created - Alerts admins of new chargebacks
  • charge.dispute.updated - Updates dispute status
  • charge.dispute.closed - Logs dispute resolution
  • customer.deleted - Clears Stripe customer ID from organizations
  • payment_intent.succeeded - Logs payment intents for audit

Extending the Handler

To add custom logic for a specific event, you can modify the handler functions in app/api/webhooks/stripe/route.ts. For example, to add custom logic when a subscription is created:

app/api/webhooks/stripe/route.ts
async function handleSubscriptionCreated(
  eventId: string,
  subscription: Stripe.Subscription
): Promise<void> {
  // ... existing code ...

  // Add your custom logic here
  await sendWelcomeEmail(organizationId);
  await createInitialResources(organizationId);
}

Testing Webhooks

Use Stripe CLI to test webhooks locally:

Terminal
stripe listen --forward-to localhost:3000/api/webhooks/stripe