From 4f05681bcf1d490ae987b0692610a8c246234f2a Mon Sep 17 00:00:00 2001 From: Thejas775 Date: Mon, 13 Jul 2026 20:25:05 +0530 Subject: [PATCH] fix(stripe-webhook): grant lifetime on no_payment_required (100%-off checkout) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit (PR #115): a fully-discounted payment-mode checkout completes with payment_status='no_payment_required'. The prior guard skipped entitlement for anything != 'paid' and waited for async_payment_succeeded, which never fires for such sessions → the user never got lifetime access. Now grant on both 'paid' and 'no_payment_required'; only genuinely 'unpaid' async sessions await settlement. Co-Authored-By: Claude Opus 4.8 (1M context) --- supabase/functions/stripe-webhook/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/supabase/functions/stripe-webhook/index.ts b/supabase/functions/stripe-webhook/index.ts index c2eedac..27ac26b 100644 --- a/supabase/functions/stripe-webhook/index.ts +++ b/supabase/functions/stripe-webhook/index.ts @@ -225,8 +225,16 @@ serve(withWebhookRateLimit({ bucket: "webhook:stripe" }, async (req) => { // Guard against async payment methods (e.g. some bank debits) whose // session completes before funds settle. For those Stripe fires // `checkout.session.async_payment_succeeded` once paid; we only grant - // on a settled payment. Synchronous card checkouts arrive "paid". - if (session.payment_status && session.payment_status !== "paid") { + // on a settled payment. Synchronous card checkouts arrive "paid", and a + // fully-discounted (100%-off coupon) checkout settles immediately as + // "no_payment_required" — both are settled and must grant now, since + // `async_payment_succeeded` never fires for them. Only a genuinely + // "unpaid" async session should wait for settlement. + if ( + session.payment_status && + session.payment_status !== "paid" && + session.payment_status !== "no_payment_required" + ) { console.log( `[stripe-webhook] payment-mode session ${session.id} not yet paid (payment_status=${session.payment_status}); awaiting settlement`, );