Skip to content

feat(billing): make finishing the trial discoverable (billing button + API/MCP error hint) - #2113

Open
giladresisi wants to merge 2 commits into
mainfrom
feat/finish-trial-visibility
Open

giladresisi wants to merge 2 commits into
mainfrom
feat/finish-trial-visibility

Conversation

@giladresisi

Copy link
Copy Markdown
Collaborator

What kind of change does this PR introduce?

Feature (frontend billing page, backend trial gate messages). Adds a "Finish trial" button to main.billing.component.tsx, shown only while user.isTrailing is true. Clicking it opens a deleteDialog confirmation, and only on confirm mounts the existing FinishTrial modal. Closing that modal now also revalidates /user/self so the button disappears once the trial has ended. On the backend, the 406 trial gate messages in MediaService (generateVideoAllowed, validateVideoRequest) and ClippingService.startClipping keep their original first sentence and append where to finish the trial: the plain /billing page and the button name.

What deliberately stayed the same: who is gated (!video.trial && org.isTrailing for videos, org.isTrailing for clipping), the 406 status, the plain string body, POST /billing/finish-trial, StripeService.finishTrial, /billing/is-trial-finished, and the existing /billing?finishTrial=true entry point used by the web 406 dialog. No workflow, activity, DTO or schema changes.

Why was this change needed?

A Cloud customer on a trial tried to generate a Seedance video through the API and got "This video is not available in trial mode". The gate is intentional (paid-only generators have trial: false), but the customer had no way to unblock themselves: the error gave no next step, and the billing page had no visible control for ending the trial early. The only entry points to finishing a trial were the web app's 406 dialog and the precondition modal, which API and MCP callers never see.

The error text points at the plain /billing page on purpose and never at /billing?finishTrial=true. That URL mounts FinishTrial, which charges immediately with no confirmation of its own, so it must not be handed out in API or MCP error text where an agent, a link previewer or a careless click could trigger a charge. The user opens /billing and clicks the new button, which confirms first. That makes the button a prerequisite of the message, so both ship together here.

Clipping gets the same hint because its trial gate is reachable only from the public API and the MCP clippingTool. It has no web screen, so the web 406 dialog never covers it.

Other information:

Independent of #2062, which only adds frontend copy in the video generator UI and does not touch the billing page or the backend messages.

The four new translation keys (finish_trial, finish_trial_confirmation, finish_trial_charge_me_now, trial) are added to the English source and translated for the other locales with lingo.dev in a second commit.

Testing done on this branch:

  • Billing page, trialing org with a subscription: the "Finish trial" button renders between "Update Payment Method / Invoices History" and "Cancel subscription". Clicking it opens the "Trial" confirmation. Cancelling closes it and sends no request to /billing/finish-trial (checked in the browser network log and the backend log).
  • Billing page, non-trialing paid orgs (STANDARD and ULTIMATE): no button.
  • Public API, trialing org: POST /public/v1/generate-video with type: "seedance" returns 406 with the original sentence, the plain /billing URL and the button name, and no finishTrial=true.
  • Public API, non-trialing org: the same request gets past the trial gate and stops at DTO validation (400).
  • MCP, trialing org: generateVideoTool with seedance returns the new message inside the existing wrapper, ending with the credit-not-used sentence and reading cleanly.
  • MCP, non-trialing org: the same call gets past the trial gate.

Not tested:

  • Confirming the dialog end to end (the charge, the button disappearing afterwards, the /user/self revalidation). It needs a real Stripe test-mode trial subscription.
  • The clipping message at runtime. It needs an environment with clipping storage configured; without it the endpoint returns 503 before the trial gate and the MCP tool is not registered.
  • Lint and production builds were not run. ESLint currently fails on config load in the repo before checking any file. The backend and frontend dev servers compiled and ran with the change.

QA

  1. Log in to an org that is on a trial with an active subscription and open /billing. Expected: a "Finish trial" button next to "Update Payment Method / Invoices History".
  2. Click "Finish trial". Expected: a "Trial" confirmation saying the plan will be charged immediately, with "Finish the trial, charge me now" and "No, cancel!".
  3. Click "No, cancel!" with the network tab open. Expected: the dialog closes and no request to /billing/finish-trial is sent.
  4. Click "Finish trial" again and confirm (Stripe test mode). Expected: the "Finishing Trial" modal appears, reports the trial finished, and after "Close dialog" the "Finish trial" button is gone.
  5. Open /billing on a paid org that is not on a trial, on a FREE org, and on a lifetime org. Expected: no "Finish trial" button.
  6. Open /billing?finishTrial=true on a trialing org in Stripe test mode. Expected: same behavior as before this PR, the "Finishing Trial" modal mounts right away.
  7. With the API key of a trialing org, call POST /public/v1/generate-video with {"type":"seedance","output":"vertical","customParams":{"prompt":"a cat"}} (requires EVOLINK_API_KEY so Seedance is registered). Expected: 406, and message starts with "This video is not available in trial mode." followed by the /billing URL and the "Finish trial" button name, without finishTrial=true.
  8. Repeat step 7 with "type":"image-text-slides" and valid params. Expected: no 406, generation starts as before.
  9. Through MCP on the same trialing org, call generateVideoTool with identifier seedance. Expected: the error string contains the same message and ends with "The user's video credit was not used."
  10. In the web app on a trialing org, try to generate a Seedance video. Expected: the existing "You are currently on trial" 406 dialog still appears.
  11. On an environment with clipping enabled and a trialing org, call POST /public/v1/clipping with a YouTube URL. Expected: 406 with "Clipping is not available in trial mode." followed by the same /billing hint.
  12. Repeat steps 7 and 9 with a non-trialing paid org. Expected: no trial message.

Checklist:

Put a "X" in the boxes below to indicate you have followed the checklist;

  • I have read the CONTRIBUTING guide.
  • I have signed the Contributor License Agreement (CLA) (ICLA for individuals, CCLA for entities).
  • I confirm I have not used AI to submit this PR or generate code for it.
  • I checked that there were no similar issues or PRs already open for this.
  • This PR fixes just ONE issue
  • I have filled in the QA section above with real steps to verify this change.

🤖 Generated with Claude Code

giladresisi and others added 2 commits September 21, 2026 20:13
Add a "Finish trial" button to the billing page for trialing orgs, behind a confirmation, and point the video and clipping trial gate errors at the billing page so API and MCP callers know how to unblock themselves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@strix-security

strix-security Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Strix Security Review

No security issues found.

Updated for 8978fe1.


Reviewed by Strix
Re-run review · Configure security review settings

@postiz-agent

postiz-agent Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant