Created
June 26, 2023 09:04
-
-
Save feliche93/d3cbe6fff2468db7bc2ffae4c7c1a6be to your computer and use it in GitHub Desktop.
Route Handler for Lemon Squeezy Subscription Webhook
This file contains 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 characters
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