In previous blog we learned to integrate paddle in next js app. In this blog we will see how we can use paddle webhook in next js app.
Setting up webhook in paddle dashboard
Login to your paddle dashboard and go to developer tools > Notification. Click on New Destination button.
Give a description to the webhook and set the notification type to Webhook
. Now, you need to enter the url to the webhook. The url doesn’t support localhost so you need to create a tunnel to the localhost. You can use anything like ngrok or localtunnel or Cloudflare tunnel.
I use Cloudflare tunnel personally. You can follow this guide to setup cloudflare tunnel to the webhook endpoint. Once you have setup the tunnel, you can use the url in the form field.
Now, you need to set the events you want to receive in the webhook. You can select the events you want to receive. Once you have selected the events, click on save destination button. Now you are ready to receive the webhook events.
Creating webhook api route
Create a file route.ts
in app/api/paddle
directory. This file will be used to handle paddle webhook.
import { NextRequest, NextResponse } from "next/server";
import { validateSignature } from "@/utils/paddle";
export async function POST(req: NextRequest) {
const signature = req.headers.get("Paddle-Signature")!;
const body = await req.text();
// mentioned later in the blog
const isValid = await validateSignature(signature, body, process.env.PADDLE_WEBHOOK_SECRET!);
if (!isValid)
return NextResponse.json(
{
message: "Invalid webhook signature!",
},
{
status: 401,
}
);
const parsedBody = JSON.parse(body);
switch (parsedBody.event_type) {
case "subscription.created":
// handle subscription created event
break;
case "subscription.updated":
// handle subscription updated event
break;
case "subscription.cancelled":
// handle subscription cancelled event
break;
case "transaction.completed":
// handle transaction succeeded event
break;
default:
break;
}
return NextResponse.json(
{
message: "done",
},
{
status: 200,
}
);
}
Validating paddle webhook signature
Paddle signs webhook events it sends to your endpoints by including a signature in each event’s Paddle-Signature
header. This allows you to verify that the events were sent by Stripe, not by a third party. To verify the signature, you will need a utility function.
async function hashSignature(
ts: string,
requestBody: string,
h1: string,
secretKey: string
): Promise<boolean> {
const encoder = new TextEncoder();
const payload = ts + ":" + requestBody;
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secretKey),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
const signatureHex = Array.from(new Uint8Array(signature))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return signatureHex === h1;
}
function extractValues(input: string): { ts: string; h1: string } {
const matchTs = input.match(/ts=(\d+)/);
const matchH1 = input.match(/h1=([a-f0-9]+)/);
return {
ts: matchTs ? matchTs[1] : "",
h1: matchH1 ? matchH1[1] : "",
};
}
export async function validateSignature(signature: string, body: string, secret: string) {
const signatureComponents = extractValues(signature);
return await hashSignature(signatureComponents.ts, body, signatureComponents.h1, secret);
}
The validateSignature
function takes three arguments:
signature
: The value of thePaddle-Signature
header.body
: The request body as a string.secret
: The webhook signing secret.
It returns true
if the signature is valid and false
otherwise.
Conclusion
This is how you handle paddle webhook in next js 14 app. You can handle the events as per your need and perform the actions accordingly.
- niraj