fix: use svix library for Polar webhook signature verification
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import "server-only";
|
||||
|
||||
import { createHmac, timingSafeEqual } from "crypto";
|
||||
import { Webhook } from "svix";
|
||||
|
||||
const POLAR_API_BASE = "https://api.polar.sh";
|
||||
const ACCESS_TOKEN = process.env.POLAR_ACCESS_TOKEN ?? "";
|
||||
@@ -52,43 +52,17 @@ export async function createPolarCheckout(params: {
|
||||
return res.json() as Promise<PolarCheckout>;
|
||||
}
|
||||
|
||||
// Standard Webhooks imza doğrulama
|
||||
// Header: webhook-id, webhook-timestamp, webhook-signature
|
||||
// Signed content: "{webhook-id}.{webhook-timestamp}.{rawBody}"
|
||||
// Signature: "v1," + base64(HMAC-SHA256(secret, signedContent))
|
||||
// Svix kullanarak Polar webhook imzasını doğrula
|
||||
export function verifyPolarWebhook(
|
||||
webhookId: string,
|
||||
webhookTimestamp: string,
|
||||
webhookSignature: string,
|
||||
headers: Record<string, string>,
|
||||
rawBody: string,
|
||||
): boolean {
|
||||
if (!WEBHOOK_SECRET) return false;
|
||||
|
||||
// Timestamp replay koruması (1 saat — Polar retry aralığı uzun olabilir)
|
||||
const ts = parseInt(webhookTimestamp, 10);
|
||||
if (isNaN(ts) || Math.abs(Date.now() / 1000 - ts) > 3600) return false;
|
||||
|
||||
const signedContent = `${webhookId}.${webhookTimestamp}.${rawBody}`;
|
||||
|
||||
// Polar secret: "polar_whs_<base64>" — prefix soyulup base64 decode edilir
|
||||
let secretBytes: Buffer;
|
||||
try {
|
||||
const raw = WEBHOOK_SECRET.replace(/^(whsec_|polar_whs_)/, "");
|
||||
secretBytes = Buffer.from(raw, "base64");
|
||||
const wh = new Webhook(WEBHOOK_SECRET);
|
||||
wh.verify(rawBody, headers);
|
||||
return true;
|
||||
} catch {
|
||||
secretBytes = Buffer.from(WEBHOOK_SECRET);
|
||||
return false;
|
||||
}
|
||||
|
||||
const expected = createHmac("sha256", secretBytes).update(signedContent).digest("base64");
|
||||
const expectedFull = `v1,${expected}`;
|
||||
|
||||
// Header birden fazla imza içerebilir (space ile ayrılmış)
|
||||
const signatures = webhookSignature.split(" ");
|
||||
return signatures.some((sig) => {
|
||||
try {
|
||||
return timingSafeEqual(Buffer.from(expectedFull), Buffer.from(sig));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user