fix(billing): correct Stripe API version + harden checkout; clarify wizard secrets - Stripe apiVersion was pinned to 2025-10-29.acacia, but stripe@22 is built for 2026-04-22.dahlia — where ui_mode embedded_page exists. The mismatch made the embedded checkout create call fail/hang, surfacing in the browser as an opaque CORS error (CF returns a 5xx without our ACAO header). Pin to dahlia + add a 20s client timeout so any failure returns a readable 502. - new-server wizard: step 1 now warns not to paste API keys into the prompt; the credentials section (which already collects each secret in its own encrypted field) is relabelled and its empty state invites adding one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
This commit is contained in:
parent
1349dc1dc0
commit
4687c8be52
@ -10,9 +10,20 @@ import { getRedis } from './redis.js';
|
|||||||
*/
|
*/
|
||||||
export const stripe: Stripe | null = config.STRIPE_SECRET_KEY
|
export const stripe: Stripe | null = config.STRIPE_SECRET_KEY
|
||||||
? new Stripe(config.STRIPE_SECRET_KEY, {
|
? new Stripe(config.STRIPE_SECRET_KEY, {
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: SDK type lags behind real API version strings
|
// Must match the version the installed SDK (stripe@22) is built against —
|
||||||
apiVersion: '2025-10-29.acacia' as any,
|
// its types expose ui_mode: 'embedded_page', which only exists from this
|
||||||
|
// version on. Pinning the older '2025-10-29.acacia' made Stripe reject the
|
||||||
|
// embedded checkout create call (acacia still used ui_mode: 'embedded').
|
||||||
|
apiVersion: '2026-04-22.dahlia',
|
||||||
typescript: true,
|
typescript: true,
|
||||||
|
// Fail fast + visibly. Without a tight timeout, a wedged Stripe call (bad
|
||||||
|
// version, egress hiccup) hangs past Cloudflare's ~100s edge limit, and
|
||||||
|
// CF returns its own 5xx WITHOUT our CORS headers — which surfaces in the
|
||||||
|
// browser as an opaque "No Access-Control-Allow-Origin" error instead of
|
||||||
|
// the real failure. 20s keeps us well inside the edge limit so the handler
|
||||||
|
// returns a proper 502 (with CORS) the client can actually read.
|
||||||
|
timeout: 20_000,
|
||||||
|
maxNetworkRetries: 2,
|
||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
|||||||
@ -480,7 +480,11 @@ function NewServerPageInner() {
|
|||||||
/>
|
/>
|
||||||
<p className="text-[12px] leading-relaxed text-[--color-fg-subtle]">
|
<p className="text-[12px] leading-relaxed text-[--color-fg-subtle]">
|
||||||
Next step we'll show you exactly which tools we'll expose and let you tweak
|
Next step we'll show you exactly which tools we'll expose and let you tweak
|
||||||
the spec before we build.
|
the spec before we build.{' '}
|
||||||
|
<span className="text-[--color-fg-muted]">
|
||||||
|
Don't paste API keys or access tokens here — you'll add each one in its own
|
||||||
|
encrypted field in the next step.
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-wrap gap-1.5 pt-1">
|
<div className="flex flex-wrap gap-1.5 pt-1">
|
||||||
{EXAMPLE_PROMPTS.map((p) => (
|
{EXAMPLE_PROMPTS.map((p) => (
|
||||||
@ -675,15 +679,18 @@ function NewServerPageInner() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-[13px] font-semibold tracking-tight">Credentials we need</h3>
|
<h3 className="text-[13px] font-semibold tracking-tight">API keys & credentials</h3>
|
||||||
<p className="mt-1 text-[12px] leading-relaxed text-[--color-fg-muted]">
|
<p className="mt-1 text-[12px] leading-relaxed text-[--color-fg-muted]">
|
||||||
AES-256-GCM encrypted at rest, injected as env vars at runtime. Remove if your
|
One field per key or access token — entered here, separately from your prompt.
|
||||||
implementation doesn't actually use one.
|
AES-256-GCM encrypted at rest, injected as env vars at runtime only. Remove any your
|
||||||
|
implementation doesn't use; add any we missed.
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-3 space-y-2">
|
<div className="mt-3 space-y-2">
|
||||||
{editable.requiredSecrets.length === 0 && (
|
{editable.requiredSecrets.length === 0 && (
|
||||||
<p className="text-[12.5px] text-[--color-fg-muted]">
|
<p className="text-[12.5px] text-[--color-fg-muted]">
|
||||||
No credentials. This server runs self-contained.
|
None detected. If your tool calls an API that needs a key or access token, add it
|
||||||
|
below with <span className="mono">+ Add credential</span> — never put secrets in
|
||||||
|
the prompt.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{editable.requiredSecrets.map((key, idx) => (
|
{editable.requiredSecrets.map((key, idx) => (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user