Webhooks
Receive real-time notifications when purchases, cancellations, and payment events occur
When to Use Webhooks
For most web app integrations, the Auth SDK is recommended — it handles authentication and access control without webhooks. Use webhooks when you need to:
- •Sync purchase data to your own database
- •Trigger emails or notifications on purchase events
- •Integrate with third-party services (CRMs, analytics)
- •Run custom business logic on subscription changes
Webhook Events
Sent when a customer completes a purchase (one-time or subscription).
{
"event": "purchase.completed",
"data": {
"purchaseId": "purch_abc123",
"buyerEmail": "customer@example.com",
"buyerName": "John Doe",
"productId": "prod_xyz789",
"productName": "Pro Plan",
"amountCents": 2999,
"currency": "usd",
"isSubscription": true,
"status": "active",
"currentPeriodEnd": "2024-02-15T00:00:00Z"
},
"timestamp": "2024-01-15T12:00:00Z"
}Sent when a subscription is cancelled.
{
"event": "purchase.cancelled",
"data": {
"purchaseId": "purch_abc123",
"buyerEmail": "customer@example.com",
"productId": "prod_xyz789",
"productName": "Pro Plan",
"status": "cancelled"
},
"timestamp": "2024-01-20T12:00:00Z"
}Sent when a recurring payment fails.
{
"event": "payment.failed",
"data": {
"purchaseId": "purch_abc123",
"buyerEmail": "customer@example.com",
"productId": "prod_xyz789",
"productName": "Pro Plan",
"status": "past_due"
},
"timestamp": "2024-01-15T12:00:00Z"
}Sent when a customer upgrades or downgrades their subscription.
{
"event": "plan.switched",
"data": {
"purchaseId": "purch_abc123",
"buyerEmail": "customer@example.com",
"previousProductId": "prod_basic",
"previousProductName": "Basic Plan",
"newProductId": "prod_pro",
"newProductName": "Pro Plan",
"amountCents": 4999,
"currency": "usd"
},
"timestamp": "2024-01-15T12:00:00Z"
}Security
All webhooks are signed with HMAC-SHA256. Verify using the x-rev-signature header and your webhook secret.
Signature Verification
import crypto from "crypto";
function verifySignature(
payload: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
const sigBuffer = Buffer.from(signature);
const expectedBuffer = Buffer.from(expected);
if (sigBuffer.length !== expectedBuffer.length) return false;
return crypto.timingSafeEqual(sigBuffer, expectedBuffer);
}Handler Example
// app/api/webhooks/revnu/route.ts
import crypto from "crypto";
import { NextResponse } from "next/server";
function verifySignature(payload: string, signature: string, secret: string): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
export async function POST(request: Request) {
const payload = await request.text();
const signature = request.headers.get("x-rev-signature") ?? "";
if (!verifySignature(payload, signature, process.env.REV_WEBHOOK_SECRET!)) {
return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
}
const event = JSON.parse(payload);
switch (event.event) {
case "purchase.completed":
// Sync to your database, send welcome email, etc.
break;
case "purchase.cancelled":
// Update your records, revoke access
break;
case "payment.failed":
// Notify the user, pause access
break;
case "plan.switched":
// Update user's plan in your system
break;
}
return NextResponse.json({ received: true });
}Retry Policy
- •Webhooks timeout after 10 seconds
- •Failed deliveries retry up to 4 times with exponential backoff
- •Retry schedule: 1 minute, 5 minutes, 30 minutes, 2 hours
- •Each webhook includes a unique event ID for idempotency
Checkout Redirect URLs
Configure where customers go after checkout:
- •Success URL — redirect after successful payment
- •Cancel URL — redirect if they cancel checkout
Use {CHECKOUT_SESSION_ID} as a placeholder to receive the Stripe session ID.
Dashboard Configuration
- 1. Go to Dashboard → Webhooks
- 2. Enter your webhook endpoint URL (must be HTTPS)
- 3. Click Test to verify your endpoint
- 4. Select which events to receive
Environment Variables
# Add to your .env.local
REV_WEBHOOK_SECRET=whsec_your_webhook_secretFind your webhook secret in Dashboard → Webhooks after configuring your endpoint.