refactor(stripe): move upgrade-page auto-approval into API#2756
Merged
refactor(stripe): move upgrade-page auto-approval into API#2756
Conversation
The /upgrade page was instantiating its own Stripe client and writing
hasAccess directly to the DB from a Next.js server component. That meant
STRIPE_SECRET_KEY had to live on Vercel in addition to the API, the
hasAccess flip skipped the API's audit log + RBAC, and we had two
Stripe clients drifting apart over time.
Move both the Stripe lookup and the hasAccess write into a new API
endpoint:
POST /v1/organization-access/auto-approve
guarded by HybridAuthGuard + PermissionGuard with
@RequirePermission('organization', 'update'). The endpoint reuses the
existing global StripeService — no second Stripe client. Decision
matrix preserved exactly: self-hosted, @trycomp.ai email, or
domain-matched active Stripe customer.
App side: upgrade page now calls serverApi instead of importing
@/lib/stripe and writing to db; lib/stripe.ts and the
STRIPE_SECRET_KEY env declaration are removed from the Next.js app.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
NEXT_PUBLIC_SELF_HOSTED is a Next.js build-time env that the OSS Docker deployment sets on the app container only — there is no propagation to the API container (the root docker-compose.yml ships only app + portal services). Moving the entire auto-approval flow into the API would have broken self-hosted/OSS deployments, since neither SELF_HOSTED nor NEXT_PUBLIC_SELF_HOSTED is available there. Restore the inline self-hosted branch on the upgrade page (preserves original behavior bit-for-bit) and route only the Stripe-customer + @trycomp.ai paths through the API. The single remaining DB write on the page is gated on a build-time deploy flag, not user input — so the "all mutations through the API" rule is preserved in spirit for every user-facing decision. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/app/src/app/(app)/upgrade/[orgId]/page.tsx">
<violation number="1" location="apps/app/src/app/(app)/upgrade/[orgId]/page.tsx:80">
P2: Handle self-hosted auto-approve DB write failures so transient errors don’t break page rendering.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
/upgrade/{orgId}page was instantiating its own Stripe client and writinghasAccessdirectly to the DB from a Next.js server component. Three problems with that:STRIPE_SECRET_KEYhad to live on the Vercelapps/appproject in addition to the API. Two copies of a live Stripe key = 2× rotation, 2× leak risk, 2× misconfig opportunities. (Result:STRIPE_SECRET_KEY is not setwarnings in Vercel logs because we never set the duplicate.)hasAccessflip — the upgrade page bypassedAuditLogInterceptor+PermissionGuard, leaving zero audit trail of who got auto-approved.StripeService(apps/api/src/stripe/stripe.service.ts) wired up correctly; the Next.js app had its own.This PR moves both the Stripe lookup and the
hasAccesswrite into a new API endpoint and removes the duplicate Stripe client + env var from the Next.js app.What changed
API:
POST /v1/organization-access/auto-approve(apps/api/src/organization-access/)HybridAuthGuard+PermissionGuard+@RequirePermission('organization', 'update')StripeService— no second client{ hasAccess, autoApproved, reason }with reasons:already-has-access/self-hosted/trycomp-email/stripe-customer/not-eligiblelogger.log(...)on each grant (audit trail)StripeServicewithfindCustomerByDomain+isDomainActiveCustomer(logic ported verbatim fromapps/app/src/lib/stripe.ts)apps/api/src/stripe/domain.utils.tswith pureextractDomain+isPublicEmailDomainhelpers + testsApp:
apps/app/src/app/(app)/upgrade/[orgId]/page.tsxcalls the new API endpoint viaserverApi.post(...)instead of running Stripe + DB locallyapps/app/src/lib/stripe.tsSTRIPE_SECRET_KEYfromapps/app/src/env.mjs(declaration +runtimeEnv)Decision matrix (preserved bit-for-bit)
process.env.SELF_HOSTED === 'true'(orNEXT_PUBLIC_SELF_HOSTED)@trycomp.aiBehavior change to flag for reviewers
The endpoint requires
organization:update(option B in the design discussion). Today, any non-deactivated member of the org could trigger auto-approval implicitly via the page; from this PR forward only members withorganization:update(owner/admin and any custom role granting that permission) can. In practice owners are who land on/upgrade, so this is unlikely to matter — but it IS a behavior change. If you'd rather preserve the looser semantics, swap the controller toHybridAuthGuardonly with a manual membership check.Follow-up (NOT in this PR)
STRIPE_SECRET_KEYfrom the Vercelapps/appproject after this ships. Will silence the warnings in production logs.Test plan
domain.utils.spec.ts+organization-access.service.spec.tscovering all decision-matrix branches (cd apps/api && npx jest src/stripe src/organization-access)apps/api: 68 → 67;apps/app: 24 → 24)/upgrade/{orgId}as a@trycomp.aiuser → auto-approved, redirected to onboarding/org/upgrade/{orgId}as a non-trycomp user with no Stripe match → booking step renders/upgrade/{orgId}as a user whose email domain matches the org website AND is in Stripe → auto-approvedSTRIPE_SECRET_KEY is not setwarnings stop appearing after deployhasAccessflip🤖 Generated with Claude Code
Summary by cubic
Moves Stripe-domain auto-approval off the Next.js upgrade page into the API with RBAC and a single
StripeService, while keeping the self-hosted branch inline on the page to avoid OSS env mismatch. Removes the duplicate Stripe client and app-side secret; behavior stays the same.Refactors
POST /v1/organization-access/auto-approveguarded byHybridAuthGuard+PermissionGuardwith@RequirePermission('organization', 'update'); returns{ hasAccess, autoApproved, reason }and logs grants.StripeService; addedfindCustomerByDomain,isDomainActiveCustomer, and domain helpers with tests.@trycomp.aiand Stripe-customer paths; kept inline self-hosted auto-approve usingNEXT_PUBLIC_SELF_HOSTEDand a direct DB write to preserve OSS behavior. Removedapps/app/src/lib/stripe.tsandSTRIPE_SECRET_KEYfrom the app env.Migration
/upgrade/{orgId}now requiresorganization:updateto trigger auto-approval; previously any member could.STRIPE_SECRET_KEYfrom the Vercelapps/appproject after deploy.Written for commit 17a8d92. Summary will update on new commits.