{"record":{"id":"1ecc2c8ba65ec877","repo":"TryGhost/Ghost","slug":"could-not-create-stripe-checkout-session","errorCode":null,"errorMessage":"Could not create Stripe checkout session","messagePattern":"Could not create Stripe checkout session","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/portal/src/data-attributes.js","lineNumber":180,"sourceCode":"            return null;\n        }\n        return res.text();\n    }).then(function (identity) {\n        return fetch(`${siteUrl}/members/api/create-stripe-checkout-session/`, {\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/json'\n            },\n            body: JSON.stringify({\n                ...requestData,\n                identity: identity,\n                successUrl: checkoutSuccessUrl,\n                cancelUrl: checkoutCancelUrl,\n                metadata\n            })\n        }).then(function (res) {\n            if (!res.ok) {\n                throw new Error(t('Could not create Stripe checkout session'));\n            }\n            return res.json();\n        });\n    }).then(function (responseBody) {\n        if (responseBody.url) {\n            return window.location.assign(responseBody.url);\n        }\n        const stripe = window.Stripe(responseBody.publicKey);\n        return stripe.redirectToCheckout({\n            sessionId: responseBody.sessionId\n        }).then(function (redirectResult) {\n            if (redirectResult.error) {\n                throw new Error(redirectResult.error.message);\n            }\n        });\n    }).catch(function (err) {\n        console.error(err);\n        el.addEventListener('click', clickHandler);","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/portal/src/data-attributes.js#L162-L198","documentation":"Thrown in apps/portal/src/data-attributes.js when the POST to {siteUrl}/members/api/create-stripe-checkout/ returns a non-ok HTTP status. The error message is the i18n string 'Could not create Stripe checkout session'. The promise then chain rejects, falling into the .catch that re-enables the button and shows the error.","triggerScenarios":"Visitor clicks a Portal checkout button for a tier; the create-stripe-checkout endpoint responds with 4xx/5xx (e.g. 402 TierNotConfigured, 429 rate limited, 500 Stripe key missing). res.ok is false so the throw fires.","commonSituations":"Stripe connect account not configured or disconnected in Ghost admin; tier has no price configured; checkout rate limit triggered by repeated attempts; member already has an active subscription; site URL mismatch breaking the successUrl/cancelUrl; Ghost server version older than Portal bundle.","solutions":["In Ghost admin go to Settings > Membership and confirm Stripe is connected and the tiers have valid prices.","Check the response status and body in the Network tab for create-stripe-checkout — 402/403/429 each have distinct fixes.","If 429, slow down — Portal enforces per-email and global checkout attempt limits.","Ensure siteUrl in the Ghost config matches the actual origin so successUrl/cancelUrl validate server-side."],"exampleFix":"// before\nif (!res.ok) {\n    throw new Error(t('Could not create Stripe checkout session'));\n}\n\n// after: include the server reason for debugging\nif (!res.ok) {\n    let reason = await res.text().catch(() => '');\n    throw new Error(t('Could not create Stripe checkout session') + (reason ? ` (${res.status}: ${reason})` : ''));\n}","handlingStrategy":"try-catch","validationCode":"// confirm tier is configured for checkout before opening Portal\nfunction assertTierReady(tier) {\n    if (!tier?.monthlyPrice && !tier?.yearlyPrice) {\n        throw new Error('Tier has no price configured');\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    await startCheckout(requestData);\n} catch (err) {\n    // err.message is i18n('Could not create Stripe checkout session')\n    showError(el, err.message);\n    el.classList.remove('loading');\n    el.addEventListener('click', clickHandler);\n}","preventionTips":["Verify Stripe connection and tier prices in Settings > Membership before launching checkout.","Rate-limit checkout clicks client-side to avoid tripping server-side 429s.","Ensure siteUrl config matches the page origin so successUrl/cancelUrl validate.","Keep Portal bundle version aligned with the Ghost server version."],"tags":["portal","stripe","checkout","membership","network"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}