TryGhost/Ghost · error · Error

Could not create Stripe checkout session

Error message

Could not create Stripe checkout session

What it means

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.

Source

Thrown at apps/portal/src/data-attributes.js:180

            return null;
        }
        return res.text();
    }).then(function (identity) {
        return fetch(`${siteUrl}/members/api/create-stripe-checkout-session/`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                ...requestData,
                identity: identity,
                successUrl: checkoutSuccessUrl,
                cancelUrl: checkoutCancelUrl,
                metadata
            })
        }).then(function (res) {
            if (!res.ok) {
                throw new Error(t('Could not create Stripe checkout session'));
            }
            return res.json();
        });
    }).then(function (responseBody) {
        if (responseBody.url) {
            return window.location.assign(responseBody.url);
        }
        const stripe = window.Stripe(responseBody.publicKey);
        return stripe.redirectToCheckout({
            sessionId: responseBody.sessionId
        }).then(function (redirectResult) {
            if (redirectResult.error) {
                throw new Error(redirectResult.error.message);
            }
        });
    }).catch(function (err) {
        console.error(err);
        el.addEventListener('click', clickHandler);

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. In Ghost admin go to Settings > Membership and confirm Stripe is connected and the tiers have valid prices.
  2. Check the response status and body in the Network tab for create-stripe-checkout — 402/403/429 each have distinct fixes.
  3. If 429, slow down — Portal enforces per-email and global checkout attempt limits.
  4. Ensure siteUrl in the Ghost config matches the actual origin so successUrl/cancelUrl validate server-side.

Example fix

// before
if (!res.ok) {
    throw new Error(t('Could not create Stripe checkout session'));
}

// after: include the server reason for debugging
if (!res.ok) {
    let reason = await res.text().catch(() => '');
    throw new Error(t('Could not create Stripe checkout session') + (reason ? ` (${res.status}: ${reason})` : ''));
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm tier is configured for checkout before opening Portal
function assertTierReady(tier) {
    if (!tier?.monthlyPrice && !tier?.yearlyPrice) {
        throw new Error('Tier has no price configured');
    }
}

Try / catch

try {
    await startCheckout(requestData);
} catch (err) {
    // err.message is i18n('Could not create Stripe checkout session')
    showError(el, err.message);
    el.classList.remove('loading');
    el.addEventListener('click', clickHandler);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/1ecc2c8ba65ec877. Report an issue: GitHub.