Skip to content

feat(migrate): bundle money guards and identity check (S3 slice 1) - #405

Merged
ABB65 merged 2 commits into
mainfrom
feat/migrate-s3-hardening
Oct 3, 2026
Merged

ABB65 merged 2 commits into
mainfrom
feat/migrate-s3-hardening

Conversation

@ABB65

@ABB65 ABB65 commented Oct 3, 2026

Copy link
Copy Markdown
Member

Follow-up to #404 (t6's non-blocking findings) plus the types bump.

  • @contentrain/types 1.45.0 (renewal_cents); the local extra type is gone.
  • Supabase auth branch: ensureUserForProviderAccount refuses (IdentityConflictError) an email account already signed in with a different GitHub account, same guard as the managed pair.
  • Polar cannot expire or cancel a checkout (no such API), so a re-quote cannot close the old one. Instead: the webhook carries the subscription's checkoutId; a second subscription for a grant that already has one logs [migrate-bundle] ALARM duplicate payment … refund it and is not bound or moved; a payment from a non-current checkout is honoured but logs ALARM stale checkout paid. An unchanged price already reuses the open checkout (S2), so two payable checkouts exist only when the price changed.
  • Reconciler: a redeemed bundle with no subscription id raises an ALARM.
  • Orphan checkout cleanup: not needed, Polar expires them itself.

Refund of a flagged payment is the ops command in (e) (t5).

Local: lint 0 errors, typecheck 0, unit 1997 pass (run with --testTimeout=90000; the machine was loaded), integration 486, nuxt 274.

Pin @contentrain/types 1.45.0 (renewal_cents) and drop the local extra
type. Supabase auth refuses to hand an email account with another GitHub
identity to a stranger. The webhook carries the checkout id: a second
subscription for a grant, or a payment from a stale checkout, raises an
ALARM (Polar cannot expire a checkout); a redeemed bundle without a
subscription id alarms in the reconciler.
@ABB65

ABB65 commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

t6 review @ 206ec7f — CHANGES REQUESTED (1 blocker), rest OK

Local evidence at this head: targeted unit (3 files, 33 tests) pass, billing-webhook integration (40 tests) pass, eslint on changed files clean, vue-tsc --noEmit clean. CI ci job was still pending when I posted; postgres-lineage pass.

Blocker — duplicate-payment subscription is still stored as the workspace's active account.
In subscription.created, upsertPaymentAccount runs before redeemMigrateGrant. When the grant is already redeemed, redeemMigrateGrant returns early with the ALARM, but the duplicate subscription is already the workspace's active payment account. The refund the alarm asks for then fires subscription.canceled, which matches that account's subscription id, archives it and sets the workspace plan to free, so the customer's legitimate bundle gets downgraded. Fix: check the grant state first and skip (or park) the account upsert for a duplicate/stale checkout, or have the cancel path ignore subscriptions that never became the redeemed one. Add a webhook-ordering test (duplicate created, then canceled, workspace plan stays).

Non-blocking

  • mapSupabaseUser.providerAccountId = user_metadata.provider_id reflects the last sign-in provider, so IdentityConflictError can false-positive when that id belongs to another provider. Consider keying on (provider, id) from identities.
  • Current tests do not cover the webhook ordering above.

Re-ping me after the fix and I will re-review the delta quickly.

…ount

subscription.created/updated ask first (isDuplicateBundleSubscription) and
skip every account write for a second subscription on a bundle grant, so
refunding it cannot cancel the valid plan. Supabase identity check reads
the user identities instead of the last sign-in provider metadata.
@ABB65

ABB65 commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

t6 delta review @ 0464111 — ONAY (pending green CI)

Blocker fixed: isDuplicateBundleSubscription runs before the account upsert in subscription.created and .updated; a duplicate logs the ALARM and writes no account, so refunding it cannot cancel the valid plan. The Supabase branch now reads auth.identities for the provider instead of user_metadata.provider_id. The webhook-order integration test covers created-duplicate then canceled (no upsert, no redeem, no workspace update).

Local evidence at this head: unit (migrate-bundle-subscription + supabase-auth-provider) 22/22, billing-webhook integration 41/41, eslint on delta files clean, vue-tsc --noEmit clean. Not run: the full pnpm test:ci. The ci and postgres-lineage jobs were still pending at post time; merge only when they are green.

Known residual (non-blocking): a duplicate created event that lands before the first subscription is redeemed is not detectable (redeemed id still null); the reconciler/alarm path covers it.

@ABB65
ABB65 merged commit 71dd213 into main Oct 3, 2026
2 checks passed
@ABB65
ABB65 deleted the feat/migrate-s3-hardening branch October 3, 2026 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant