Sign In

We're the bridge between marketing ambition and financial wisdom. Born from the frustration of seeing e-commerce companies chase growth at any cost, we built a platform that turns complex customer data into simple profit signals.

Ready to turn your marketing budget into pure rocket fuel? 🚀

Drop us a line at hello@headw.ai and let's explore how we can boost your ROI. (We promise the demo will be worth your time!)

© Copyright 2026 Headwai. All Rights Reserved.

    Legal
    • Privacy Policy

    Webhook Integration

    Setting up and handling payment provider webhooks for subscription events.

    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

    1. Local Development (using Stripe CLI):
    stripe listen --forward-to localhost:3000/api/billing/webhook
    
    1. 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:
    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

    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.