--- title: "Webhook Integration" description: "Setting up and handling payment provider webhooks for subscription events." publishedAt: 2024-04-11 order: 2 status: "published" --- > **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: ```typescript // 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 ```typescript 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 ```typescript 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 ```typescript case 'customer.subscription.deleted': await prisma.subscription.update({ where: { id: event.data.object.id }, data: { status: 'canceled', canceledAt: new Date(), }, }); break; ``` ### Payment Failed ```typescript 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 1. **Local Development** (using Stripe CLI): ```bash stripe listen --forward-to localhost:3000/api/billing/webhook ``` 2. **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 1. **Configure webhook URL** in Paddle dashboard 2. **Add webhook secret** to environment variables 3. **Verify webhook signature**: ```typescript 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 1. **Always verify signatures** - Prevents unauthorized requests 2. **Use HTTPS** - Encrypts webhook data in transit 3. **Validate event data** - Check for required fields 4. **Handle idempotently** - Process duplicate events safely 5. **Return 200 quickly** - Acknowledge receipt, process async ## Error Handling ```typescript 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 ```bash # Stripe stripe trigger customer.subscription.created # Test specific scenarios stripe trigger payment_intent.payment_failed ``` ### Manual Testing ```bash 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.