From 65d8a28d54ed6189e6dd95492740dd50f7c3603c Mon Sep 17 00:00:00 2001 From: "rhea-security[bot]" <301977876+rhea-security[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:30:08 +0000 Subject: [PATCH] fix(authorization): paid plan entitlement assigned by email instead of stable user ID --- lib/billing.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/billing.ts b/lib/billing.ts index 07d1471..6fc8bac 100644 --- a/lib/billing.ts +++ b/lib/billing.ts @@ -32,8 +32,9 @@ export async function applyOrderPaidEntitlement( } const order = parsed.data; - const normalizedEmail = order.customer.email.toLowerCase(); - const planSlug = order.productId ? planByProductId.get(order.productId) : null; + const planSlug = order.productId + ? planByProductId.get(order.productId) + : null; if (!planSlug) { console.warn("[Polar] Unmapped product in order.paid", { @@ -43,17 +44,29 @@ export async function applyOrderPaidEntitlement( return; } - const user = await prisma.user.findUnique({ where: { email: normalizedEmail } }) + let user = order.customer.externalId + ? await prisma.user.findUnique({ where: { id: order.customer.externalId } }) + : null; + + if (!user) { + const normalizedEmail = order.customer.email.toLowerCase(); + user = await prisma.user.findUnique({ where: { email: normalizedEmail } }); + } if (!user) { console.error("[Polar] No matching user for paid order", { orderId: order.id, - email: normalizedEmail, externalId: order.customer.externalId, + email: order.customer.email.toLowerCase(), }); return; } + // Idempotent: skip if this order was already applied + if (user.polarLastOrderId === order.id) { + return; + } + await prisma.user.update({ where: { id: user.id }, data: {