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:
- An Azion account
- A Stripe account with API access
- Azion CLI installed and configured
- Node.js version 18 or higher
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 typesexport const app = new Hono<{ Bindings: Bindings; Variables: Variables;}>();
// Initialize Stripe with your secret keyconst stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { apiVersion: "2025-06-30.basil", typescript: true,});
// Middleware to verify the webhook signatureconst 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 endpointapp.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 endpointapp.get("/", (c) => { return c.json({ status: "ok", timestamp: new Date().toISOString(), service: "stripe-webhooks", });});
// Error handlingapp.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
- Log in to your Azion account via CLI:
azion login
- Follow the authentication prompts to connect your CLI with your Azion account.
Step 2: Create a new Edge Application from a template
- Initialize a new Edge Application:
azion init
-
Select the template Hono Boilerplate to use for your browserless application.
-
Follow the prompts to configure your new Edge Application.
Step 3: Create secrets for Stripe credentials
For security, store your Stripe credentials as secrets:
azion create secret STRIPE_SECRET_KEYazion 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:
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
- After deployment, you’ll receive a domain like
https://xxxxxxx.map.azionedge.net
- Go to your Stripe Dashboard > Developers > Webhooks
- Edit your webhook endpoint
- Update the URL to
https://xxxxxxx.map.azionedge.net/webhook
- Save the changes
Step 6: Test webhook delivery
- Trigger test events in Stripe Dashboard
- Monitor webhook delivery and responses
- Check Edge Function logs for processing confirmation
Testing your webhook handler
Step 1: Use Stripe CLI for local testing
- Install Stripe CLI
- Forward webhooks to your local development server:
stripe listen --forward-to localhost:3000/webhook
- Trigger test events:
stripe trigger payment_intent.succeededstripe trigger charge.succeededstripe trigger invoice.payment_succeeded
Step 2: Test in production
- Use Stripe Dashboard to send test webhooks
- Monitor webhook delivery and retry attempts
- Check response codes and processing times
- Verify event handling accuracy
Step 3: Error handling testing
- Test with invalid signatures
- Test with malformed payloads
- Test timeout scenarios
- 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
- Enable detailed logging: Log all webhook events and processing steps
- Use Stripe Dashboard: Monitor webhook delivery attempts
- Test locally: Use Stripe CLI for local debugging
- 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