payloadcms/payload · critical · Error
Stripe secret key is required
Error message
Stripe secret key is required
What it means
Thrown at the top of the Stripe confirmOrder adapter when props.secretKey is falsy. The Stripe SDK cannot be constructed without a secret key, so the adapter refuses to proceed. This is a configuration error in the ecommerce plugin: the stripe adapter was instantiated without supplying a secretKey (typically read from STRIPE_SECRET_KEY env var).
Source
Thrown at packages/plugin-ecommerce/src/payments/adapters/stripe/confirmOrder.ts:29
export const confirmOrder: (props: Props) => NonNullable<PaymentAdapter>['confirmOrder'] =
(props) =>
async ({
cartsSlug = 'carts',
data,
ordersSlug = 'orders',
req,
transactionsSlug = 'transactions',
}) => {
const payload = req.payload
const { apiVersion, appInfo, secretKey } = props || {}
const customerEmail = data.customerEmail
const paymentIntentID = data.paymentIntentID as string
if (!secretKey) {
throw new Error('Stripe secret key is required')
}
if (!paymentIntentID) {
throw new Error('PaymentIntent ID is required')
}
const stripe = new Stripe(secretKey, {
// API version can only be the latest, stripe recommends ts ignoring it
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - ignoring since possible versions are not type safe, only the latest version is recognised
apiVersion: apiVersion || '2025-03-31.basil',
appInfo: appInfo || {
name: 'Stripe Payload Plugin',
url: 'https://payloadcms.com',
},
})
try {View on GitHub (pinned to 00c58b35c0)
Solutions
- Set STRIPE_SECRET_KEY (and ensure the plugin reads that exact variable) in every environment that runs payments.
- Verify the value is loaded: console.log(Boolean(process.env.STRIPE_SECRET_KEY)) before building the adapter.
- Pass secretKey explicitly when constructing the stripe adapter object so both initiatePayment and confirmOrder share it.
- Add a startup check in payload.config.ts that throws early if the key is missing rather than failing per-request.
Example fix
// payload.config.ts
import { stripePlugin } from '@payloadcms/plugin-ecommerce'
// before: relying on an unset var
stripePlugin({ stripe: { secretKey: process.env.STRIPE_KEY } })
// after: assert at boot, read the canonical var
if (!process.env.STRIPE_SECRET_KEY) {
throw new Error('STRIPE_SECRET_KEY is required')
}
stripePlugin({ stripe: { secretKey: process.env.STRIPE_SECRET_KEY } }) Defensive patterns
Strategy: validation
Validate before calling
// At boot, before registering the stripe adapter
function resolveStripeSecretKey(): string {
const key = process.env.STRIPE_SECRET_KEY
if (!key || typeof key !== 'string') {
throw new Error('STRIPE_SECRET_KEY is required to enable Stripe payments')
}
return key
}
// pass into the adapter
stripePlugin({ stripe: { secretKey: resolveStripeSecretKey() } }) Type guard
export function hasSecretKey(props: unknown): props is { secretKey: string } {
return typeof (props as { secretKey?: unknown })?.secretKey === 'string' &&
(props as { secretKey: string }).secretKey.length > 0
} Try / catch
try {
await payments.confirmOrder({ data: { paymentIntentID } })
} catch (err) {
if (err instanceof Error && err.message === 'Stripe secret key is required') {
// configuration error — do not retry; alert ops
console.error('STRIPE_SECRET_KEY is not configured')
return { ok: false, reason: 'misconfigured-stripe' }
}
throw err
} Prevention
- Fail fast at process boot if STRIPE_SECRET_KEY is unset rather than per-request.
- Load .env explicitly in dev (e.g. via dotenv) before the config is built.
- Use the same adapter instance for initiate and confirm so both share the key.
- In CI, inject a Stripe test key so the payment path is exercised end-to-end.
When it happens
Trigger: The plugin's stripe adapter is registered with a missing or empty secretKey (e.g. secretKey: process.env.STRIPE_SECRET_KEY where the env var is unset in the current environment). confirmOrder is then called on that adapter instance.
Common situations: Forgetting to set STRIPE_SECRET_KEY in a CI/staging/production environment; using a .env file that is not loaded; deploying with a different env var name than the config reads; passing secretKey only to initiatePayment but not confirmOrder; test runs that instantiate the plugin without mocking secrets.
Related errors
- Stripe secret key is required.
- PaymentIntent ID is required
- No transaction found for the provided PaymentIntent ID
- Payment not completed.
- Cart ID not found in the PaymentIntent metadata
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/81f5e6a24b3846e1.
Report an issue: GitHub.