How to handle Stripe webhooks with Edge Functions

This guide demonstrates how to securely handle Stripe webhooks using Azion Edge Functions. You’ll learn to process payment events in real-time, verify webhook signatures, and build a robust payment processing system at the edge.

Requirements

Before you begin, ensure you have:

Code

This is a code example of how to use Stripe webhooks with Azion Edge Functions. The complete code example you can find in this GitHub repository.

import { Context, Hono } from "hono";
import { fire } from "hono/service-worker";
import { HTTPException } from "hono/http-exception";
import Stripe from "stripe";
type Bindings = {
STRIPE_SECRET_KEY: string;
};
type Variables = {
stripeEvent: Stripe.Event;
};
// Initialize Hono app with proper types
export const app = new Hono<{
Bindings: Bindings;
Variables: Variables;
}>();
// Initialize Stripe with your secret key
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", {
apiVersion: "2025-06-30.basil",
typescript: true,
});
// Middleware to verify the webhook signature
const verifyStripeWebhook = async (c: Context, next: () => Promise<void>) => {
try {
const signature = c.req.header("stripe-signature");
if (!signature) {
throw new HTTPException(400, {
message: "Missing stripe-signature header",
});
}
const payload = await c.req.raw.text();
// Verify the webhook signature
const event = stripe.webhooks.constructEvent(
payload,
signature,
process.env.STRIPE_WEBHOOK_SECRET || ""
);
// Attach the event to the context for use in the route handler
c.set("stripeEvent", event);
await next();
} catch (err) {
console.error("Webhook verification failed:", err);
return c.json({ error: "Webhook verification failed" }, 400);
}
};
// Webhook endpoint
app.post("/webhook", verifyStripeWebhook, async (c) => {
const event = c.get("stripeEvent");
try {
// Handle the event
switch (event.type) {
case "payment_intent.succeeded": {
const paymentIntent = event.data.object as Stripe.PaymentIntent;
console.log("PaymentIntent was successful!", paymentIntent.id);
// Handle successful payment
break;
}
case "payment_method.attached": {
const paymentMethod = event.data.object as Stripe.PaymentMethod;
console.log("PaymentMethod was attached!", paymentMethod.id);
break;
}
case "charge.succeeded": {
const charge = event.data.object as Stripe.Charge;
console.log("Charge was successful!", charge.id);
break;
}
// ... handle other event types
default:
console.log(`Unhandled event type ${event.type}`);
}
// Return a 200 response to acknowledge receipt of the event
return c.json({ received: true });
} catch (err) {
console.error("Error handling webhook:", err);
return c.json({ error: "Webhook handler failed" }, 400);
}
});
// Health check endpoint
app.get("/", (c) => {
return c.json({
status: "ok",
timestamp: new Date().toISOString(),
service: "stripe-webhooks",
});
});
// Error handling
app.onError((err: Error, c) => {
console.error("Error:", err);
return c.json({ error: "Internal Server Error" }, 500);
});
fire(app, { env: { STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY! } });

Deploying to Azion

Step 1: Authenticate with Azion

  1. Log in to your Azion account via CLI:
Terminal window
azion login
  1. Follow the authentication prompts to connect your CLI with your Azion account.

Step 2: Create a new Edge Application from a template

  1. Initialize a new Edge Application:
Terminal window
azion init
  1. Select the template Hono Boilerplate to use for your browserless application.

  2. Follow the prompts to configure your new Edge Application.

Step 3: Create secrets for Stripe credentials

For security, store your Stripe credentials as secrets:

Terminal window
azion create secret STRIPE_SECRET_KEY
azion create secret STRIPE_WEBHOOK_SECRET

When prompted, enter your respective Stripe credentials. This ensures your sensitive data is encrypted and secure.

Step 4: Deploy the Edge Function

Deploy your webhook handler to Azion’s edge network:

Terminal window
azion deploy

The deployment process will:

  • Upload your Edge Function code
  • Configure the edge application
  • Set up the necessary routing rules
  • Configure environment variables and secrets
  • Provide you with a unique domain

Step 5: Update Stripe webhook configuration

  1. After deployment, you’ll receive a domain like https://xxxxxxx.map.azionedge.net
  2. Go to your Stripe Dashboard > Developers > Webhooks
  3. Edit your webhook endpoint
  4. Update the URL to https://xxxxxxx.map.azionedge.net/webhook
  5. Save the changes

Step 6: Test webhook delivery

  1. Trigger test events in Stripe Dashboard
  2. Monitor webhook delivery and responses
  3. Check Edge Function logs for processing confirmation

Testing your webhook handler

Step 1: Use Stripe CLI for local testing

  1. Install Stripe CLI
  2. Forward webhooks to your local development server:
Terminal window
stripe listen --forward-to localhost:3000/webhook
  1. Trigger test events:
Terminal window
stripe trigger payment_intent.succeeded
stripe trigger charge.succeeded
stripe trigger invoice.payment_succeeded

Step 2: Test in production

  1. Use Stripe Dashboard to send test webhooks
  2. Monitor webhook delivery and retry attempts
  3. Check response codes and processing times
  4. Verify event handling accuracy

Step 3: Error handling testing

  1. Test with invalid signatures
  2. Test with malformed payloads
  3. Test timeout scenarios
  4. Verify retry mechanisms

Security best practices

Webhook verification

  • Always verify signatures: Never process unverified webhooks
  • Use HTTPS: Ensure all webhook endpoints use HTTPS
  • Validate payload: Check event structure and required fields
  • Implement replay protection: Track processed event IDs

Secret management

  • Use Azion secrets: Store credentials securely
  • Rotate secrets regularly: Update webhook secrets periodically
  • Limit access: Restrict secret access to necessary functions
  • Monitor usage: Track secret access and usage patterns

Monitoring and logging

Event logging

function logWebhookEvent(event, status, processingTime) {
console.log({
event_id: event.id,
event_type: event.type,
status,
processing_time: processingTime,
timestamp: new Date().toISOString()
});
}

Performance monitoring

  • Response times: Monitor webhook processing speed
  • Success rates: Track successful vs failed processing
  • Error patterns: Identify common failure scenarios
  • Retry frequency: Monitor webhook retry attempts

Troubleshooting

Common issues and solutions

  • Signature verification failures: Check webhook secret configuration
  • Timeout errors: Optimize processing logic for speed
  • Duplicate processing: Implement idempotency checks
  • Missing events: Verify webhook endpoint configuration

Debugging tips

  1. Enable detailed logging: Log all webhook events and processing steps
  2. Use Stripe Dashboard: Monitor webhook delivery attempts
  3. Test locally: Use Stripe CLI for local debugging
  4. Check signatures: Verify webhook signature calculation

Next steps

  • Implement comprehensive event logging and monitoring
  • Add webhook replay functionality for failed events
  • Integrate with your existing payment processing system
  • Implement advanced fraud detection mechanisms
  • Scale webhook processing for high-volume scenarios