ZionClad
Platform
Solutions
Deal AnalyzerCRM & PipelineContactsMail CenterReports & Exports
About UsResourcesPricingContact Us
Sign InGet Started →
Menu
Platform
SolutionsDeal AnalyzerCRM & PipelineContactsMail CenterReports & Exports
About UsResourcesPricingContact UsSign InGet Started
ZionClad

Commercial real estate intelligence for research, acquisition, and disposition.

PlatformDeal AnalyzerCRM & PipelineContactsMail CenterReports & Exports
AboutHelp CenterFAQContact Us
LegalTerms of ServicePrivacy PolicyCookie PolicyContacts Use Notice
© 2026 ZionClad. All rights reserved.
HomePlatformSign In

ZionClad provides software tools and information for general business use. Nothing on this site is legal, financial, tax or investment advice. You are responsible for checking the accuracy of information you enter, import or scan, and for how you use it.

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.