Documentation
Webhook Integration
Setting up and handling payment provider webhooks for subscription events.
Page type
Product documentation
Best for
Setup, workflow, and implementation details
Next action
Copy, export, or continue deeper into the doc tree
Note: This is mock/placeholder content for demonstration purposes.
Webhooks notify your application when billing events occur, ensuring your app stays synchronized with your payment provider.
Why Webhooks?
Webhooks are essential for:
- Real-time updates - Instant notification of payment events
- Reliability - Handles events even if users close their browser
- Security - Server-to-server communication
- Automation - Automatic subscription status updates
Webhook Endpoint
Your webhook endpoint receives events from the payment provider:
// app/api/billing/webhook/route.ts
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('stripe-signature');
// Verify webhook signature
const event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
// Handle the event
await handleBillingEvent(event);
return new Response('OK', { status: 200 });
}
Common Events
Subscription Created
case 'customer.subscription.created':
await prisma.subscription.create({
data: {
id: event.data.object.id,
accountId: event.data.object.metadata.accountId,
status: 'active',
planId: event.data.object.items.data[0].price.id,
currentPeriodEnd: new Date(event.data.object.current_period_end * 1000),
},
});
break;
Subscription Updated
case 'customer.subscription.updated':
await prisma.subscription.update({
where: { id: event.data.object.id },
data: {
status: event.data.object.status,
planId: event.data.object.items.data[0].price.id,
currentPeriodEnd: new Date(event.data.object.current_period_end * 1000),
},
});
break;
Subscription Deleted
case 'customer.subscription.deleted':
await prisma.subscription.update({
where: { id: event.data.object.id },
data: {
status: 'canceled',
canceledAt: new Date(),
},
});
break;
Payment Failed
case 'invoice.payment_failed':
const subscription = await prisma.subscription.findUnique({
where: { id: event.data.object.subscription },
});
// Send payment failure notification
await sendPaymentFailureEmail(subscription.accountId);
break;
Setting Up Webhooks
Stripe
- Local Development (using Stripe CLI):
stripe listen --forward-to localhost:3000/api/billing/webhook
- Production:
- Go to Stripe Dashboard → Developers → Webhooks
- Add endpoint:
https://yourdomain.com/api/billing/webhook - Select events to listen to
- Copy webhook signing secret to your
.env
Paddle
- Configure webhook URL in Paddle dashboard
- Add webhook secret to environment variables
- Verify webhook signature:
const signature = request.headers.get('paddle-signature');
const verified = paddle.webhooks.verify(body, signature);
if (!verified) {
return new Response('Invalid signature', { status: 401 });
}
Security Best Practices
- Always verify signatures - Prevents unauthorized requests
- Use HTTPS - Encrypts webhook data in transit
- Validate event data - Check for required fields
- Handle idempotently - Process duplicate events safely
- Return 200 quickly - Acknowledge receipt, process async
Error Handling
async function handleBillingEvent(event: Event) {
try {
await processEvent(event);
} catch (error) {
// Log error for debugging
console.error('Webhook error:', error);
// Store failed event for retry
await prisma.failedWebhook.create({
data: {
eventId: event.id,
type: event.type,
payload: event,
error: error.message,
},
});
// Throw to trigger provider retry
throw error;
}
}
Testing Webhooks
Using Provider's CLI Tools
# Stripe stripe trigger customer.subscription.created # Test specific scenarios stripe trigger payment_intent.payment_failed
Manual Testing
curl -X POST https://your-app.com/api/billing/webhook \ -H "Content-Type: application/json" \ -H "stripe-signature: test_signature" \ -d @test-event.json
Monitoring
Track webhook delivery:
- Response times
- Success/failure rates
- Event processing duration
- Failed events requiring manual intervention
Most providers offer webhook monitoring dashboards showing delivery attempts and failures.
