{"record":{"id":"fccd067bfacbee5e","repo":"payloadcms/payload","slug":"a-valid-customer-email-is-required-to-make-a-purch","errorCode":null,"errorMessage":"A valid customer email is required to make a purchase.","messagePattern":"A valid customer email is required to make a purchase\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugin-ecommerce/src/payments/adapters/stripe/initiatePayment.ts","lineNumber":38,"sourceCode":"    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\n      apiVersion: apiVersion || '2025-06-30.preview',\n      appInfo: appInfo || {\n        name: 'Stripe Payload Plugin',\n        url: 'https://payloadcms.com',\n      },\n    })\n\n    try {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/plugin-ecommerce/src/payments/adapters/stripe/initiatePayment.ts#L20-L56","documentation":"Thrown by Stripe initiatePayment when data.customerEmail is falsy or not a string. The adapter creates/lists a Stripe customer by email and records it on the transaction, so a valid email is mandatory. The check `!customerEmail || typeof customerEmail !== 'string'` rejects undefined, null, numbers, and objects.","triggerScenarios":"initiatePayment called without customerEmail, with an empty value, or with a non-string (e.g. an object {email}). Anonymous checkout where email was never collected.","commonSituations":"Guest checkout form missing email validation; email stored as user.email but the request sends the whole user object; SSR where the session user has no email; frontend reusing a logged-out state; email field named emailAddress vs customerEmail.","solutions":["Collect and validate a customer email (RFC-light check) before enabling payment.","Send data.customerEmail as a string (derive from req.user.email for logged-in users).","Map your field name to customerEmail before calling initiatePayment.","Require email server-side and return a clear validation error to the client if absent."],"exampleFix":"// before\nawait payments.initiatePayment({ data: { cart, currency, customerEmail: req.user } })\n// after\nconst customerEmail = req.user?.email ?? formData.email\nif (typeof customerEmail !== 'string') throw new Error('Email required')\nawait payments.initiatePayment({ data: { cart, currency, customerEmail } })","handlingStrategy":"validation","validationCode":"function resolveCustomerEmail(value: unknown, user?: { email?: string }): string {\n  const email = typeof value === 'string' ? value : user?.email\n  if (typeof email !== 'string' || !/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(email)) {\n    throw new Error('A valid customer email is required')\n  }\n  return email\n}\n\ndata.customerEmail = resolveCustomerEmail(data.customerEmail, req.user)","typeGuard":"export function isCustomerEmail(value: unknown): value is string {\n  return typeof value === 'string' && /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(value)\n}","tryCatchPattern":"try {\n  await payments.initiatePayment({ data })\n} catch (err) {\n  if (err instanceof Error && /valid customer email/.test(err.message)) {\n    return { ok: false, reason: 'email-required', redirect: '/checkout?step=contact' }\n  }\n  throw err\n}","preventionTips":["Validate email format client-side before enabling payment.","For logged-in users, default customerEmail to req.user.email.","Name the field customerEmail exactly when calling initiatePayment.","In guest checkout, require email collection before the pay step."],"tags":["stripe","validation","customer","ecommerce","input-contract"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}