fix: use svix library for Polar webhook signature verification

This commit is contained in:
kovakmedya
2026-05-04 18:47:28 +03:00
parent 89830aa28f
commit afbb029c67
4 changed files with 53 additions and 44 deletions
+7 -33
View File
@@ -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;
}
});
}