Webhook Handler
The Stripe webhook handler processes subscription-related events from Stripe and keeps your database in sync with subscription changes. The webhook endpoint is located at/api/stripe/webhook and handles webhook signature verification and event routing.
Endpoint
Implementation
The webhook route handler is implemented inapp/api/stripe/webhook/route.ts and processes incoming Stripe events.
Webhook Handler Function
Request Processing
The Next.js request object containing:
- Body: Raw webhook payload from Stripe
- Headers: Must include
stripe-signatureheader for verification
Response
Returns
{ received: true } with status 200 when event is processed successfullyReturns
{ error: 'Webhook signature verification failed.' } with status 400 when signature verification failsWebhook Verification
The handler verifies webhook signatures to ensure events are from Stripe:Handled Events
The webhook handler processes the following Stripe events:Triggered when a subscription is modified:
- Plan changes (upgrades/downgrades)
- Status changes (active, trialing, past_due, etc.)
- Payment method updates
- Subscription renewals
Triggered when a subscription is canceled or deleted:
- Customer cancels subscription
- Subscription expires after failed payments
- Admin cancels subscription in Stripe Dashboard
Event Routing
handleSubscriptionChange
Processes subscription changes and updates the team’s subscription data in the database.Function Signature
Parameters
The Stripe subscription object from the webhook event. Contains:
id: The subscription IDcustomer: The Stripe customer IDstatus: Current subscription statusitems.data: Array of subscription line items with plan details
Behavior
- Extract Data: Gets customer ID, subscription ID, and status from the subscription object
- Find Team: Looks up the team by
stripeCustomerId - Validation: Logs error and returns if team is not found
- Status Handling:
- Active/Trialing: Updates team with subscription details
- Canceled/Unpaid: Clears subscription data from team
Subscription Status Handling
Subscription is active and paid:
- Updates
stripeSubscriptionId - Updates
stripeProductId - Updates
planNamefrom product name - Sets
subscriptionStatusto'active'
Subscription is in trial period:
- Updates
stripeSubscriptionId - Updates
stripeProductId - Updates
planNamefrom product name - Sets
subscriptionStatusto'trialing'
Subscription has been canceled:
- Clears
stripeSubscriptionId(set to null) - Clears
stripeProductId(set to null) - Clears
planName(set to null) - Sets
subscriptionStatusto'canceled'
Subscription payment failed:
- Clears
stripeSubscriptionId(set to null) - Clears
stripeProductId(set to null) - Clears
planName(set to null) - Sets
subscriptionStatusto'unpaid'
Implementation Details
Database Updates
The function callsupdateTeamSubscription() which updates the following fields in the teams table:
stripeSubscriptionId: The Stripe subscription ID or nullstripeProductId: The Stripe product ID or nullplanName: The human-readable plan name or nullsubscriptionStatus: The current subscription status
Usage Example
This function is typically only called by the webhook handler, but can be used manually if needed:Setting Up Webhooks
-
Create Webhook Endpoint in Stripe Dashboard:
- Go to Developers > Webhooks
- Click “Add endpoint”
- URL:
https://yourdomain.com/api/stripe/webhook - Events to send:
customer.subscription.updatedcustomer.subscription.deleted
-
Get Webhook Secret:
- Copy the webhook signing secret from the Stripe Dashboard
- Add to your environment variables as
STRIPE_WEBHOOK_SECRET
-
Test Webhook:
Environment Variables Required
Your Stripe secret API key for authentication
The webhook signing secret from your Stripe webhook endpoint configuration
Error Handling
The webhook handler includes several error handling mechanisms:-
Signature Verification Failure:
- Returns 400 status code
- Logs error message
- Does not process the event
-
Team Not Found:
- Logs error with customer ID
- Returns successfully to prevent Stripe retries
- Does not update database
-
Unhandled Event Types:
- Logs event type
- Returns success to prevent retries
- No action taken
Testing Webhooks Locally
Use the Stripe CLI to test webhooks during development:Webhook Security Best Practices
- Always Verify Signatures: Never process events without signature verification
- Use HTTPS: Webhook endpoints must use HTTPS in production
- Return 200 Quickly: Process events asynchronously if they take time
- Handle Duplicates: Events may be sent multiple times
- Log Everything: Keep detailed logs for debugging
Monitoring
Monitor webhook health in the Stripe Dashboard:- View webhook attempts and responses
- Check for failed deliveries
- Review error rates
- Resend failed events if needed
Subscription Statuses
Stripe subscriptions can have the following statuses:active: Subscription is active and paidtrialing: In trial period (14 days in this implementation)past_due: Payment failed but subscription still activeunpaid: Payment failed and subscription deactivatedcanceled: Subscription has been canceledincomplete: Initial payment pendingincomplete_expired: Initial payment failed
Related Functions
createCheckoutSession()- Create new subscriptions that trigger webhookscreateCustomerPortalSession()- Allow customers to manage subscriptionsgetTeamByStripeCustomerId()- Database query used by webhook handlerupdateTeamSubscription()- Database update used by webhook handler