Skip to content

Instantly share code, notes, and snippets.

@feliche93
Created June 26, 2023 09:04
Show Gist options
  • Save feliche93/d3cbe6fff2468db7bc2ffae4c7c1a6be to your computer and use it in GitHub Desktop.
Save feliche93/d3cbe6fff2468db7bc2ffae4c7c1a6be to your computer and use it in GitHub Desktop.
Route Handler for Lemon Squeezy Subscription Webhook
import crypto from 'crypto';
import { NextResponse } from 'next/server';
import { SLemonSqueezyWebhookRequest } from './models';
import { db } from '@lib/db';
import { subscriptions } from '@schema';
import { eq } from 'drizzle-orm';
import camelcaseKeys from 'camelcase-keys'
import { CamelCasedPropertiesDeep } from 'type-fest' // need CamelCasedPropertiesDeep because of https://github.com/sindresorhus/camelcase-keys/issues/77#issuecomment-1339844470
import { ZodEffects, z } from 'zod';
import PostHogClient from '@lib/posthog';
export const zodToCamelCase = <T extends z.ZodTypeAny>(zod: T): ZodEffects<z.ZodTypeAny, CamelCasedPropertiesDeep<T['_output']>> => zod.transform((val) => camelcaseKeys(val) as CamelCasedPropertiesDeep<T>)
export const runtime = 'nodejs'; // 'nodejs' is the default
export async function POST(request: Request) {
const posthog = PostHogClient()
const secret = process.env.LEMON_SQUEEZY_SIGNING_SECRET;
if (!secret) {
throw new Error('LEMON_SQUEEZY_SIGNING_SECRET is not set');
}
// Get the raw body text
const rawBody = await request.text();
if (!rawBody) {
throw new Error('No body');
}
const xSignature = request.headers.get('X-Signature');
const hmac = crypto.createHmac('sha256', secret);
hmac.update(rawBody);
const digest = hmac.digest('hex');
if (!xSignature || !crypto.timingSafeEqual(Buffer.from(digest, 'hex'), Buffer.from(xSignature, 'hex'))) {
throw new Error('Invalid signature.');
}
const body = JSON.parse(rawBody);
const type = body.data.type
if (type === 'subscriptions') {
const parsedBody = SLemonSqueezyWebhookRequest.parse(body);
posthog.identify({
distinctId: parsedBody.meta.customData.userId,
properties: {
subscription: {
id: parsedBody.data.id,
...parsedBody.data.attributes,
}
}
})
if ((parsedBody.meta.eventName === 'subscription_created')) {
const insertedData = await db.insert(subscriptions).values({
userId: parsedBody.meta.customData.userId,
id: parsedBody.data.id,
...parsedBody.data.attributes,
}).returning()
console.log(`Inserted subscription with id ${insertedData[0].id}`)
return NextResponse.json({
"status": "ok"
});
}
if (parsedBody.meta.eventName === 'subscription_updated') {
const updatedData = await db.update(subscriptions).set({
id: parsedBody.data.id,
...parsedBody.data.attributes,
}).where(eq(
subscriptions.id,
parsedBody.data.id,
)).returning()
console.log(`Updated subscription with id: ${updatedData[0].id}`)
return NextResponse.json({
"status": "ok"
});
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment