Cloud: Stripe card trial after sign-up, ended-trial wall, live connection check - #806
Merged
Merged
Conversation
Backend: POST /api/license/checkout-link (Cloud only, ADMIN, throttled) asks the licence site for a Stripe Checkout intent on behalf of the signed-in admin. Email and workspace come from the session; a card trial ends when the workspace's free trial would have, and after the trial the link is a plain purchase. A consented Google Ads click id is passed as ad metadata. Paying workspaces get 409 (plan changes go through the billing portal); licence-site failures map to a generic 502. Frontend: new /start-trial page with a plan picker (Starter, Team, Business; monthly or yearly), shown once to cloud admins on a running trial, before the welcome wizard, with a small "Continue without payment details" link that keeps the no-card trial. The plan picked on the pricing page is kept through email verification. The trial banner, licence wall (after the trial) and licence settings open the checkout. Self-hosted behaviour is unchanged.
/api/license/status answered plan null for a Cloud workspace whose trial had run out, the same as for one that never had a licence, so the licence wall offered 'Start 7-Day Free Trial' (which the licence site does not grant twice) instead of 'your trial has ended' and the plans. It now reports the latest inactive licence's plan and status, without features; gating is unchanged. The trial banner hides for an ended trial, the activation page reloads fully so the shell drops the trial banner, and LICENSE_API_URL (set by the cloud compose file) overrides the licence site URL.
…nnect a client Most workspaces that build a connector never send an MCP request, and the connect page never told them whether what they did in Claude or ChatGPT worked. It now polls the server's activity (new GET /api/mcp-servers/:id/ activity, org-scoped) and turns green the moment the first request lands (product event first_call_seen). The dashboard sends workspaces with connectors but no calls to that page.
| const offerCard = cardTrialEligible({ | ||
| isCloud: deploymentMode === 'cloud', | ||
| role: user?.role, | ||
| license: { plan, status: status ?? '', expiresAt, trialDaysLeft: daysLeft }, |
…lready registered' notice, trial without card made explicit - Cloud login flagged every admin as needing licence setup (it read an instance-wide key cloud never writes), so the app showed 'Trial Activated!' at each sign-in, '0 days' to paying workspaces. Only a workspace with no licence at all needs setup; the client shows the card only for a trial started just now. - Signing up with an address that already has an account (and its password) signs in as before, now with a notice saying so. Only the account's owner can see it, so it reveals nothing. - The trial offer says plainly that the trial also works without a card. - LICENSE_API_URL applies outside production only: in production a URL from the environment would let a self-hosted operator point licence checks at a server of their own.
…nge in cloud, card trial never lengthened
- EmailVerifiedGuard ran before AuthGuard('jwt'), found no req.user and let
every request through: an unverified cloud account could call the whole
API (and open a checkout). It now reads the session token itself. Audited
on cloud first: none of the 427 unverified accounts has a connector, a
call or a subscription.
- Cloud: PUT /api/users/me no longer changes the email address (it neither
re-verified nor asked for the password, so a stolen session could move an
account to another address). The app never did it; self-hosted unchanged.
- A card trial needs 48 hours of the free trial left; with less the plan is
sold without a trial instead of stretching it to Stripe's minimum.
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Three changes to the Cloud funnel (trial to paid is ~2%; of the users who get a first MCP call working, 37% pay).
Card trial
/start-trial: 7-day free trial with a card through Stripe (€0 today, converts unless cancelled). "Continue without payment details" is a small link in the corner and keeps today's no-card trial.POST /api/license/checkout-link(cloud, ADMIN): asks the licence site for a one-time checkout intent with the user's own email and workspace; the card trial ends when the free trial would have.?plan=&period=) survives sign-up and verification.Ended trials were shown "Start 7-Day Free Trial"
/api/license/statusansweredplan: nullfor an expired trial, so ~1,350 workspaces got a trial button the licence site never grants twice instead of "your trial has ended". It now reports the latest inactive licence (plan + status, no features). Gating unchanged.Activation
GET /api/mcp-servers/:id/activity, org-scoped).Also: returning customers with a revoked/expired paid licence can buy again;
LICENSE_API_URLoverrides the licence site URL (already set by the cloud compose file).Tested end to end locally in cloud mode against the website branch and Stripe test mode: sign-up → verify → /start-trial → Checkout (trial) → licence active in the app → trial end → first payment; skip path; expired-trial wall → pay-now checkout; live connection check.
Pairs with website PR (card trial on the licence site). Deploy the website first.