From bc59656ebd76671686d6f1f8bc8a05228c165b3b Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 7 May 2026 21:54:41 +0900 Subject: [PATCH 1/2] fix(billing): backfill billing rows for pre-migration orgs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Production blocker: orgs created BEFORE 20260506132900 never got their `grida_billing.account` and free `grida_billing.subscription` rows. The provisioning trigger only fires on `AFTER INSERT`, so existing orgs were left empty. First checkout for those orgs crashed with: Error: resolveOrCreateStripeCustomer attach: billing account not provisioned for organization 255 Single-shot data fix: iterate `public.organization` and call `fn_provision_account` for each (idempotent — INSERT … ON CONFLICT DO NOTHING). The guard in `fn_attach_stripe_customer` stays loud — once the data is correct, that RAISE is the right behavior for any future anomaly (deleted row, bypassed trigger, partial restore). Self-heal in the guard's path would silence real signal. --- ...00000_grida_billing_backfill_provision.sql | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 supabase/migrations/20260507000000_grida_billing_backfill_provision.sql diff --git a/supabase/migrations/20260507000000_grida_billing_backfill_provision.sql b/supabase/migrations/20260507000000_grida_billing_backfill_provision.sql new file mode 100644 index 000000000..a379aa7af --- /dev/null +++ b/supabase/migrations/20260507000000_grida_billing_backfill_provision.sql @@ -0,0 +1,28 @@ +-- Backfill billing provisioning for orgs that pre-date 20260506132900. +-- +-- The base migration adds an `AFTER INSERT` trigger that calls +-- `fn_provision_account` to create the `grida_billing.account` and free +-- `grida_billing.subscription` rows. The trigger only fires on NEW orgs; +-- pre-existing orgs were left without their billing rows. This blocks +-- their first checkout because `fn_attach_stripe_customer` (correctly) +-- refuses to attach a Stripe customer to a non-provisioned account. +-- +-- Production hit this on org 255: "billing account not provisioned for +-- organization 255". +-- +-- Iterate every existing org and call `fn_provision_account` (idempotent: +-- INSERT … ON CONFLICT DO NOTHING). The guard in fn_attach_stripe_customer +-- stays loud — once the data is right, that RAISE is the correct behavior +-- for any future anomaly (deleted row, bypassed trigger, partial restore). + +DO $$ +DECLARE + rec RECORD; + n integer := 0; +BEGIN + FOR rec IN SELECT id FROM public.organization LOOP + PERFORM grida_billing.fn_provision_account(rec.id); + n := n + 1; + END LOOP; + RAISE NOTICE 'grida_billing backfill: provisioned % organizations', n; +END $$; From 7852e25ae5ee9021ad130f3d32fb3bfbdc01c66c Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 7 May 2026 22:00:07 +0900 Subject: [PATCH 2/2] chore(billing): drop dev test-card hint from upgrade page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The string "Test mode: use card 4242 4242 4242 4242…" was an unguarded footer on the plan cards — visible in production. The test card number belongs in docs/contributing/billing.md (where it already lives), not in the customer-facing UI. Other test-mode UI is properly gated behind state.is_test_mode (the sandbox disclaimer at the bottom of /billing); this string was the lone unguarded one. --- .../[organization_name]/settings/billing/upgrade/_view.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/editor/app/(site)/organizations/[organization_name]/settings/billing/upgrade/_view.tsx b/editor/app/(site)/organizations/[organization_name]/settings/billing/upgrade/_view.tsx index a7c003e09..d0d81377d 100644 --- a/editor/app/(site)/organizations/[organization_name]/settings/billing/upgrade/_view.tsx +++ b/editor/app/(site)/organizations/[organization_name]/settings/billing/upgrade/_view.tsx @@ -313,11 +313,6 @@ export default function UpgradeView({ ); })} - -

- Test mode: use card 4242 4242 4242 4242. After payment, - you'll be redirected back here. Provisioning takes a few seconds. -

);