diff --git a/apps/web/app/api/lemon-squeezy/webhook/route.ts b/apps/web/app/api/lemon-squeezy/webhook/route.ts index 35fe5c4df..c964ad554 100644 --- a/apps/web/app/api/lemon-squeezy/webhook/route.ts +++ b/apps/web/app/api/lemon-squeezy/webhook/route.ts @@ -91,6 +91,11 @@ export const POST = withError(async (request: Request) => { }); } + // payment success + if (payload.meta.event_name === "subscription_payment_success") { + return await subscriptionPaymentSuccess({ payload, premiumId }); + } + return NextResponse.json({ ok: true }); }); @@ -303,6 +308,37 @@ async function subscriptionCancelled({ return NextResponse.json({ ok: true }); } +async function subscriptionPaymentSuccess({ + payload, + premiumId, +}: { + payload: Payload; + premiumId: string; +}) { + if (payload.data.attributes.status !== "paid") { + throw new Error( + `Unexpected status for subscription payment success: ${payload.data.attributes.status}`, + ); + } + + const premium = await prisma.premium.findUnique({ + where: { id: premiumId }, + select: { + admins: { select: { email: true } }, + users: { select: { email: true } }, + }, + }); + + const email = premium?.admins?.[0]?.email || premium?.users?.[0]?.email; + if (!email) throw new Error("No email found"); + await posthogCaptureEvent(email, "Payment success", { + totalPaidUSD: payload.data.attributes.total_usd, + lemonSqueezyId: payload.data.id, + lemonSqueezyType: payload.data.type, + }); + return NextResponse.json({ ok: true }); +} + function getEmailFromPremium(premium: { users: Array<{ email: string | null }>; }) { diff --git a/apps/web/app/api/lemon-squeezy/webhook/types.ts b/apps/web/app/api/lemon-squeezy/webhook/types.ts index 2416c7003..d2498449f 100644 --- a/apps/web/app/api/lemon-squeezy/webhook/types.ts +++ b/apps/web/app/api/lemon-squeezy/webhook/types.ts @@ -42,7 +42,7 @@ export interface Attributes { variant_name: string; user_name: string; user_email: string; - status: string; // on_trial, active, cancelled, past_due, and optionally paused. + status: string; // on_trial, active, cancelled, past_due, paused, paid status_formatted: string; card_brand: string; card_last_four: string; @@ -58,6 +58,8 @@ export interface Attributes { test_mode: boolean; first_subscription_item?: FirstSubscriptionItem; first_order_item?: FirstOrderItem; + // in payment success + total_usd?: number; } export interface FirstSubscriptionItem {