{"record":{"id":"81f5e6a24b3846e1","repo":"payloadcms/payload","slug":"stripe-secret-key-is-required","errorCode":null,"errorMessage":"Stripe secret key is required","messagePattern":"Stripe secret key is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/plugin-ecommerce/src/payments/adapters/stripe/confirmOrder.ts","lineNumber":29,"sourceCode":"\nexport const confirmOrder: (props: Props) => NonNullable<PaymentAdapter>['confirmOrder'] =\n  (props) =>\n  async ({\n    cartsSlug = 'carts',\n    data,\n    ordersSlug = 'orders',\n    req,\n    transactionsSlug = 'transactions',\n  }) => {\n    const payload = req.payload\n    const { apiVersion, appInfo, secretKey } = props || {}\n\n    const customerEmail = data.customerEmail\n\n    const paymentIntentID = data.paymentIntentID as string\n\n    if (!secretKey) {\n      throw new Error('Stripe secret key is required')\n    }\n\n    if (!paymentIntentID) {\n      throw new Error('PaymentIntent ID is required')\n    }\n\n    const stripe = new Stripe(secretKey, {\n      // API version can only be the latest, stripe recommends ts ignoring it\n      // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n      // @ts-ignore - ignoring since possible versions are not type safe, only the latest version is recognised\n      apiVersion: apiVersion || '2025-03-31.basil',\n      appInfo: appInfo || {\n        name: 'Stripe Payload Plugin',\n        url: 'https://payloadcms.com',\n      },\n    })\n\n    try {","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/plugin-ecommerce/src/payments/adapters/stripe/confirmOrder.ts#L11-L47","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// payload.config.ts\nimport { stripePlugin } from '@payloadcms/plugin-ecommerce'\n\n// before: relying on an unset var\nstripePlugin({ stripe: { secretKey: process.env.STRIPE_KEY } })\n\n// after: assert at boot, read the canonical var\nif (!process.env.STRIPE_SECRET_KEY) {\n  throw new Error('STRIPE_SECRET_KEY is required')\n}\nstripePlugin({ stripe: { secretKey: process.env.STRIPE_SECRET_KEY } })","handlingStrategy":"validation","validationCode":"// At boot, before registering the stripe adapter\nfunction resolveStripeSecretKey(): string {\n  const key = process.env.STRIPE_SECRET_KEY\n  if (!key || typeof key !== 'string') {\n    throw new Error('STRIPE_SECRET_KEY is required to enable Stripe payments')\n  }\n  return key\n}\n\n// pass into the adapter\nstripePlugin({ stripe: { secretKey: resolveStripeSecretKey() } })","typeGuard":"export function hasSecretKey(props: unknown): props is { secretKey: string } {\n  return typeof (props as { secretKey?: unknown })?.secretKey === 'string' &&\n    (props as { secretKey: string }).secretKey.length > 0\n}","tryCatchPattern":"try {\n  await payments.confirmOrder({ data: { paymentIntentID } })\n} catch (err) {\n  if (err instanceof Error && err.message === 'Stripe secret key is required') {\n    // configuration error — do not retry; alert ops\n    console.error('STRIPE_SECRET_KEY is not configured')\n    return { ok: false, reason: 'misconfigured-stripe' }\n  }\n  throw err\n}","preventionTips":["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."],"tags":["stripe","config","secrets","env","ecommerce"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}