Skip to content

feat: migrate hosted checkout to embedded form - #7459

Merged
alx-xo merged 11 commits into
mainfrom
alexm/embedded-stripe-checkout-6e2a
Oct 29, 2026
Merged

alx-xo merged 11 commits into
mainfrom
alexm/embedded-stripe-checkout-6e2a

Conversation

@alx-xo

@alx-xo alx-xo commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

GRW-79

Summary

  • Migrate hosted Stripe Checkout to the Embedded form (ui_mode: "form") on a dedicated Billing setup page, with a themed payment form, order summary, and promotion-code entry.
  • Add subscription-aware Overview, Billing setup, and Payment method navigation. Checkout entry points share the setup route; Free/Base signup and partner discounts remain supported.
  • Add organization-scoped session-status lookup, redirect recovery, session replacement, and delayed-payment webhook handling. Preserve historical session parameters and idempotency during migration.
  • Update local Stripe setup to preserve configured key pairs, validate publishable-key readiness, and save configuration atomically. Cover checkout lifecycle behavior through real React Query flows and Stripe-boundary tests.

Deploy the server and dashboard together: createStripeCheckout now returns a client secret and publishable key. Configure STRIPE_PUBLISHABLE_KEY from the same Stripe account as STRIPE_API_KEY, and subscribe the webhook endpoint to checkout.session.async_payment_succeeded and checkout.session.async_payment_failed alongside its existing events.

Motivation

Collect payment details inside the dashboard and give billing setup a dedicated page. Keep existing promotion and subscription behavior while recovering canceled redirects and earlier hosted sessions through the same checkout lifecycle.

Gutternote review steps

  1. Enable gram-payg-self-serve-billing for a sandbox organization with an active trial or an unsubscribed Free/Base account, and sign in as an org admin. Configure the preview's matching Stripe secret/publishable keys and webhook delivery. The seeded demo does not enable this flag itself.
  2. Open Billing (/<org>/billing) and select Billing setup, or click Add payment method from the trial card. Expect the dedicated setup page (/<org>/billing/setup) with an order summary and Stripe form. Check light/dark themes, a narrow viewport, and promotion-code entry; an accepted partner promotion should be pre-applied.
  3. Complete checkout with Stripe sandbox payment details. Expect Confirming billing setup, followed by Payment method (/<org>/billing/payment-method) after backend subscription confirmation. A delayed payment remains in the processing state until confirmed.
  4. Try a redirect-based sandbox payment method and cancel its authorization. Returning to billing should resume an open checkout; an expired session should be replaced. Check retry behavior when checkout initialization or status lookup fails.
  5. For an already subscribed organization, select Payment method from Billing. Spot-check checkout entry points on the trial-ended gate and lockout panel, plus restricted access for a non-admin.

Linear Issue: GRW-79

Open in Web Open in Cursor 

Summary by cubic

Migrates hosted Stripe Checkout to the embedded form so pay-as-you-go setup starts inside the dashboard. The checkout endpoint now returns a client secret and publishable key that mount Stripe's form on a dedicated Billing setup page instead of redirecting to hosted Checkout.

  • Adds Billing setup and Payment method pages with Overview, Billing setup, and Payment method navigation, an order summary, promotion-code entry, and light/dark theming driven by the dashboard theme; a unified billing state picks between the setup and payment-method views.
  • Sets STRIPE_PUBLISHABLE_KEY paired with STRIPE_API_KEY; mise run stripe:setup saves the CLI publishable key only when it also uses the CLI secret, never next to an env-provided STRIPE_API_KEY.
  • Expires live hosted Checkout sessions before creating an embedded session, since Stripe rejects an idempotency key whose parameters change; new sessions accept a theme query parameter while existing sessions keep their original appearance.
  • Adds an organization-scoped session-status lookup and redirect recovery that replaces expired sessions; the dialog clears when the active organization changes, and the lockout and trial-ended gate open billing routes for the active organization.
  • Webhook handling now covers checkout.session.async_payment_succeeded and checkout.session.async_payment_failed alongside the existing events.
  • Pins @stripe/react-stripe-js 7.0.0 and @stripe/stripe-js 10.1.0 for the embedded form while preserving legacy versions for @polar-sh/checkout via packageExtensions.

Migration

  • Deploy the server and dashboard together: createStripeCheckout now returns a client secret and publishable key.
  • Configure STRIPE_PUBLISHABLE_KEY from the same Stripe account as STRIPE_API_KEY, and subscribe the webhook to the two async-payment checkout events.

Written for commit 775b80c. Summary will update on new commits.

View guided diff Turn on auto-fix

Screenshots

Billing setup

Billing setup

Payment method

Payment method

Platform MCP assessment

Decision: intentionally omit new or changed Platform MCP tools for this checkout migration. The outcome is an organization administrator completing billing setup or updating a payment method for the explicitly selected active organization, not a project-scoped management operation. The existing Platform MCP tool catalogue and registration have no billing/subscription checkout tool to update; this does not change existing tool contracts, server instructions, or shipped workflows.

  • Browser and sensitive-data boundary: embedded Stripe checkout is a browser-bound payment-entry and confirmation flow. The checkout client secret belongs only at the authenticated dashboard/Stripe boundary; payment details and checkout credentials must not be exposed in agent inputs, results, or transcripts. This assessment does not propose agents collecting payment details or driving checkout through a new endpoint-shaped tool.
  • Target and permission boundary: the dashboard requires organization read/admin access. Backend checkout status requires both permissions for the active organization and matches the returned session's organization and Stripe customer to that organization's billing metadata; a supplied session ID or return URL is not authority to access another tenant or evidence of completed payment. Organization switching must discard the prior checkout state.
  • External-client audience: OAuth organization membership alone does not substitute for per-operation organization-admin authorization. Keep this interactive payment workflow in the authenticated dashboard rather than exposing checkout client secrets through external MCP. A future bounded, non-sensitive billing inspection/navigation outcome would require its own design and authorization assessment; it is not being added here.
  • Managed-assistant audience: exact-project assistant identity is not an organization administrator's payment authority. Do not expand its scope into organization-wide billing or admit payment entry/checkout credentials to assistant tools.
  • Evidence and verification boundary: BillingSetup.tsx contains the scope gates and organization-keyed checkout lifecycle; stripe_checkout_status.go enforces organization/customer ownership, with admin/read and foreign-session cases in stripe_checkout_status_test.go. platformmcp/descriptor.go distinguishes external OAuth from project-assistant audiences. The browser review steps above cover payment entry, organization-admin access, redirect recovery, and confirmation. This records the intended design and inspected code/tests, not a claim that new browser checks were run or that the locally rebased changes have been pushed.

🚀 Preview Environment (PR #7459)

Preview URL: https://pr-7459.dev.getgram.ai

Component Status Details Updated (UTC)
✅ Database Ready Created and validated 2026-10-29 00:19:27.
✅ Images Available Container images ready 2026-10-29 00:20:08.

Gram Preview Bot

@changeset-bot

changeset-bot Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 775b80c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
server Minor
dashboard Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@linear-code

linear-code Bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

GRW-79

@alx-xo alx-xo added the preview Spawn a preview environment label Oct 13, 2026
@cursor
cursor Bot marked this pull request as ready for review October 13, 2026 00:59
@cursor
cursor Bot requested review from a team as code owners October 13, 2026 00:59

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 36 files

This PR changes payment, billing, or quota logic. Ultrareviews find 2.4x more serious bugs than standard reviews. Comment @cubic-dev-ai ultrareview to run one.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread client/dashboard/src/components/billing/start-payg-checkout-cta.tsx Outdated
Comment thread server/internal/usage/create_stripe_checkout.go
Comment thread client/dashboard/src/components/billing/use-start-payg-checkout.ts Outdated
Comment thread .mise-tasks/stripe/setup.mts Outdated
Comment thread client/dashboard/src/components/billing/start-payg-checkout-cta.test.tsx Outdated
Comment thread server/internal/usage/create_stripe_checkout_test.go
Comment thread client/dashboard/src/components/billing/payg-checkout-mount.tsx Outdated
Comment thread .mise-tasks/stripe/setup.mts Outdated
Comment thread client/dashboard/src/components/billing/payg-embedded-checkout-dialog.tsx Outdated
Comment thread client/dashboard/src/components/billing/payg-embedded-checkout-dialog.tsx Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 10 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread client/dashboard/src/components/billing/payg-checkout-mount.tsx Outdated
@alx-xo alx-xo removed the preview Spawn a preview environment label Oct 14, 2026
@alx-xo
alx-xo force-pushed the alexm/embedded-stripe-checkout-6e2a branch from 1ab8083 to c475c85 Compare October 19, 2026 22:50
@alx-xo alx-xo changed the title feat: embed Stripe Checkout for pay as you go feat: migrate hosted checkout to embedded form Oct 19, 2026
@blacksmith-sh

This comment has been minimized.

@alx-xo
alx-xo force-pushed the alexm/embedded-stripe-checkout-6e2a branch 5 times, most recently from 1e99604 to 229d717 Compare October 22, 2026 18:46

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 36 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread server/internal/usage/stripe_checkout_status.go
Comment thread client/dashboard/src/pages/billing/BillingSetup.test.tsx
Comment thread client/dashboard/src/components/billing/payg-checkout-summary.test.tsx Outdated
Comment thread client/dashboard/src/pages/billing/Billing.tsx
Comment thread client/dashboard/src/components/billing/payg-plan-section.tsx Outdated
Comment thread client/dashboard/src/components/trial-status-card.tsx Outdated
@alx-xo
alx-xo force-pushed the alexm/embedded-stripe-checkout-6e2a branch 2 times, most recently from 84390ef to 6c1344a Compare October 28, 2026 18:31
Add dedicated billing setup and payment-method pages with themed
Stripe forms, order summaries, and promotion-code support.

Add organization-scoped checkout status lookup, redirect recovery,
session replacement, and delayed-payment webhook handling.

Update Stripe credential setup and generated API clients.
Add lifecycle, authorization, promotion, and migration tests using
real React Query flows and Stripe boundary stubs.
@alx-xo
alx-xo force-pushed the alexm/embedded-stripe-checkout-6e2a branch from e90e56a to 775b80c Compare October 29, 2026 00:08
@alx-xo alx-xo added enhancement New feature or request preview Spawn a preview environment labels Oct 29, 2026
@alx-xo
alx-xo added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 9b80ab1 Oct 29, 2026
55 of 63 checks passed
@alx-xo
alx-xo deleted the alexm/embedded-stripe-checkout-6e2a branch October 29, 2026 00:36
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

enhancement New feature or request preview Spawn a preview environment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants