{"record":{"id":"5af86fff45bf75db","repo":"payloadcms/payload","slug":"currency-is-required","errorCode":null,"errorMessage":"Currency is required.","messagePattern":"Currency is required\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugin-ecommerce/src/payments/adapters/stripe/initiatePayment.ts","lineNumber":30,"sourceCode":"export const initiatePayment: (props: Props) => NonNullable<PaymentAdapter>['initiatePayment'] =\n  (props) =>\n  async ({ data, req, transactionsSlug }) => {\n    const payload = req.payload\n    const { apiVersion, appInfo, secretKey } = props || {}\n\n    const customerEmail = data.customerEmail\n    const currency = data.currency\n    const cart = data.cart\n    const amount = cart.subtotal\n    const billingAddressFromData = data.billingAddress\n    const shippingAddressFromData = data.shippingAddress\n\n    if (!secretKey) {\n      throw new Error('Stripe secret key is required.')\n    }\n\n    if (!currency) {\n      throw new Error('Currency is required.')\n    }\n\n    if (!cart || !cart.items || cart.items.length === 0) {\n      throw new Error('Cart is empty or not provided.')\n    }\n\n    if (!customerEmail || typeof customerEmail !== 'string') {\n      throw new Error('A valid customer email is required to make a purchase.')\n    }\n\n    if (!amount || typeof amount !== 'number' || amount <= 0) {\n      throw new Error('A valid amount is required to initiate a payment.')\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","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/plugin-ecommerce/src/payments/adapters/stripe/initiatePayment.ts#L12-L48","documentation":"Thrown by Stripe initiatePayment when data.currency is falsy. Stripe requires a currency code (e.g. 'usd') to create a PaymentIntent, and the adapter also uses it to look up the correct price field per currency config. Omitting currency is an input-contract violation: the initiate request body must include currency.","triggerScenarios":"initiatePayment is called without data.currency, or with currency set to null/empty string. The check runs before any Stripe call, so it fails fast.","commonSituations":"Frontend checkout form that doesn't submit the currency field; multi-currency store where the selected currency isn't propagated to the initiate call; default currency not set and client assumes server fills it in; currency stored under a different key (e.g. currencyCode).","solutions":["Send data.currency as a lowercase ISO 4217 code (e.g. 'usd') in the initiatePayment request body.","Default the currency server-side from the store config if the client omits it, before calling initiate.","Validate currency client-side and disable the pay button until selected.","Ensure the currency exists in currenciesConfig so downstream price lookups also succeed."],"exampleFix":"// before\nawait payments.initiatePayment({ data: { cart, customerEmail } })\n// after\nawait payments.initiatePayment({\n  data: { cart, customerEmail, currency: selectedCurrency },\n})","handlingStrategy":"validation","validationCode":"function resolveCurrency(value: unknown, fallback?: string): string {\n  if (typeof value === 'string' && /^[a-z]{3}$/i.test(value)) return value.toLowerCase()\n  if (fallback) return fallback.toLowerCase()\n  throw new Error('A valid 3-letter currency code is required')\n}\n\n// before initiatePayment\ndata.currency = resolveCurrency(data.currency, storeDefaultCurrency)","typeGuard":"export function isCurrencyCode(value: unknown): value is string {\n  return typeof value === 'string' && /^[a-z]{3}$/i.test(value)\n}","tryCatchPattern":"try {\n  await payments.initiatePayment({ data })\n} catch (err) {\n  if (err instanceof Error && err.message === 'Currency is required.') {\n    // prompt the customer to select a currency\n    return { ok: false, reason: 'currency-required' }\n  }\n  throw err\n}","preventionTips":["Resolve currency once at checkout entry and thread it through every call.","Default to the store's base currency when the customer hasn't selected one.","Validate the currency is configured in currenciesConfig so downstream price lookups succeed.","Disable the pay button until a valid currency is selected."],"tags":["stripe","validation","currency","ecommerce","input-contract"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}