payloadcms/payload · error · Error
A valid customer email is required to make a purchase.
Error message
A valid customer email is required to make a purchase.
What it means
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.
Source
Thrown at packages/plugin-ecommerce/src/payments/adapters/stripe/initiatePayment.ts:38
const cart = data.cart
const amount = cart.subtotal
const billingAddressFromData = data.billingAddress
const shippingAddressFromData = data.shippingAddress
if (!secretKey) {
throw new Error('Stripe secret key is required.')
}
if (!currency) {
throw new Error('Currency is required.')
}
if (!cart || !cart.items || cart.items.length === 0) {
throw new Error('Cart is empty or not provided.')
}
if (!customerEmail || typeof customerEmail !== 'string') {
throw new Error('A valid customer email is required to make a purchase.')
}
if (!amount || typeof amount !== 'number' || amount <= 0) {
throw new Error('A valid amount is required to initiate a payment.')
}
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-06-30.preview',
appInfo: appInfo || {
name: 'Stripe Payload Plugin',
url: 'https://payloadcms.com',
},
})
try {View on GitHub (pinned to 00c58b35c0)
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.
Example fix
// before
await payments.initiatePayment({ data: { cart, currency, customerEmail: req.user } })
// after
const customerEmail = req.user?.email ?? formData.email
if (typeof customerEmail !== 'string') throw new Error('Email required')
await payments.initiatePayment({ data: { cart, currency, customerEmail } }) Defensive patterns
Strategy: validation
Validate before calling
function resolveCustomerEmail(value: unknown, user?: { email?: string }): string {
const email = typeof value === 'string' ? value : user?.email
if (typeof email !== 'string' || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
throw new Error('A valid customer email is required')
}
return email
}
data.customerEmail = resolveCustomerEmail(data.customerEmail, req.user) Type guard
export function isCustomerEmail(value: unknown): value is string {
return typeof value === 'string' && /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)
} Try / catch
try {
await payments.initiatePayment({ data })
} catch (err) {
if (err instanceof Error && /valid customer email/.test(err.message)) {
return { ok: false, reason: 'email-required', redirect: '/checkout?step=contact' }
}
throw err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- PaymentIntent ID is required
- Currency is required.
- Cart items snapshot not found or invalid in the PaymentInten
- Cart is empty or not provided.
- A valid amount is required to initiate a payment.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/fccd067bfacbee5e.
Report an issue: GitHub.