Last active
August 6, 2024 20:08
Revisions
-
amosbastian revised this gist
May 22, 2023 . 1 changed file with 11 additions and 11 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,6 +5,10 @@ import { NextRequest } from "next/server"; // Put this in your billing lib and just import the type instead type LemonsqueezySubscription = Awaited<ReturnType<typeof listAllSubscriptions>>["data"][number]; const isError = (error: unknown): error is Error => { return error instanceof Error; }; // Add more events here if you want // https://docs.lemonsqueezy.com/api/webhooks#event-types type EventName = @@ -29,12 +33,12 @@ type Payload = { data: LemonsqueezySubscription; }; export const POST = async (request: NextRequest) => { try { const text = await request.text(); const hmac = crypto.createHmac("sha256", process.env["LEMON_SQUEEZY_WEBHOOK_SECRET"] || ""); const digest = Buffer.from(hmac.update(text).digest("hex"), "utf8"); const signature = Buffer.from(request.headers.get("x-signature") as string, "utf8"); if (!crypto.timingSafeEqual(digest, signature)) { return new Response("Invalid signature.", { @@ -72,19 +76,15 @@ export const POST = async (req: NextRequest) => { throw new Error(`🤷♀️ Unhandled event: ${eventName}`); } } catch (error: unknown) { if (isError(error)) { return new Response(`Webhook error: ${error.message}`, { status: 400, }); } return new Response("Webhook error", { status: 400, }); } return new Response(null, { -
amosbastian revised this gist
May 21, 2023 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -31,9 +31,9 @@ type Payload = { export const POST = async (req: NextRequest) => { try { const text = await req.text(); const hmac = crypto.createHmac("sha256", process.env["LEMON_SQUEEZY_WEBHOOK_SECRET"] || ""); const digest = Buffer.from(hmac.update(text).digest("hex"), "utf8"); const signature = Buffer.from(req.headers.get("x-signature") as string, "utf8"); if (!crypto.timingSafeEqual(digest, signature)) { @@ -42,7 +42,7 @@ export const POST = async (req: NextRequest) => { }); } const payload = JSON.parse(text); const { meta: { event_name: eventName }, -
amosbastian revised this gist
May 21, 2023 . No changes.There are no files selected for viewing
-
amosbastian revised this gist
May 21, 2023 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -25,13 +25,14 @@ type Payload = { test_mode: boolean; event_name: EventName; }; // Possibly not accurate: it's missing the relationships field and any custom data you add data: LemonsqueezySubscription; }; export const POST = async (req: NextRequest) => { try { const rawBody = await req.text(); const hmac = crypto.createHmac("sha256", process.env["LEMON_SQUEEZY_WEBHOOK_SECRET"] || ""); const digest = Buffer.from(hmac.update(rawBody).digest("hex"), "utf8"); const signature = Buffer.from(req.headers.get("x-signature") as string, "utf8"); -
amosbastian created this gist
May 21, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,92 @@ import crypto from "crypto"; import { listAllSubscriptions } from "lemonsqueezy.ts"; import { NextRequest } from "next/server"; // Put this in your billing lib and just import the type instead type LemonsqueezySubscription = Awaited<ReturnType<typeof listAllSubscriptions>>["data"][number]; // Add more events here if you want // https://docs.lemonsqueezy.com/api/webhooks#event-types type EventName = | "order_created" | "order_refunded" | "subscription_created" | "subscription_cancelled" | "subscription_resumed" | "subscription_expired" | "subscription_paused" | "subscription_unpaused" | "subscription_payment_failed" | "subscription_payment_success" | "subscription_payment_recovered"; type Payload = { meta: { test_mode: boolean; event_name: EventName; }; data: LemonsqueezySubscription; }; export const POST = async (req: NextRequest) => { try { const rawBody = await req.text(); const hmac = crypto.createHmac("sha256", process.env.LEMON_SQUEEZY_WEBHOOK_SECRET || ""); const digest = Buffer.from(hmac.update(rawBody).digest("hex"), "utf8"); const signature = Buffer.from(req.headers.get("x-signature") as string, "utf8"); if (!crypto.timingSafeEqual(digest, signature)) { return new Response("Invalid signature.", { status: 400, }); } const payload = JSON.parse(rawBody); const { meta: { event_name: eventName }, data: subscription, } = payload as Payload; switch (eventName) { case "order_created": // Do stuff here if you are using orders break; case "order_refunded": // Do stuff here if you are using orders break; case "subscription_created": case "subscription_cancelled": case "subscription_resumed": case "subscription_expired": case "subscription_paused": case "subscription_unpaused": case "subscription_payment_failed": case "subscription_payment_success": case "subscription_payment_recovered": // Do something with the subscription here, like syncing to your database console.log(subscription); break; default: throw new Error(`🤷♀️ Unhandled event: ${eventName}`); } } catch (error: unknown) { if (typeof error === "string") { return new Response("Webhook error", { status: 400, }); } if (error instanceof Error) { return new Response(`Webhook error: ${error.message}`, { status: 400, }); } throw error; } return new Response(null, { status: 200, }); };