keepcalculator

06 Numbers and Powers

Stripe Webhooks: Null Customer Email Fix

Card No. 06·41056 · Updated · Standard formula, shown below

When a Stripe webhook delivers a customer_email that is null, the cause is usually that the customer object was not created with an email address at the time the event fired. To fix it, check the event payload for the customer ID, retrieve the customer object via the API, and fall back to the payment method’s billing details if the customer still has no email.

Why Stripe Sends a Null Customer Email

Stripe’s webhook payloads include a customer_email field on many event types (e.g., invoice.payment_succeeded, checkout.session.completed, payment_intent.succeeded). This field is populated from the customer object’s email attribute at the moment the event is created. If the customer was created without an email—for example, during a guest checkout where only a card is collected—the field will be null. Additionally, if the customer object is later updated with an email, older webhook events that were already sent will still contain the original null value.

What to Check in the Webhook Payload

Start by inspecting the raw JSON of the webhook event. Look for the following fields:

  • data.object.customer_email – often the direct source of the null.
  • data.object.customer – the customer ID (e.g., cus_...). This is always present if the event is customer-related.
  • data.object.receipt_email – sometimes set on the payment intent or charge, separate from the customer email.
  • data.object.billing_details.email – on payment methods or charges, this may hold the email used during checkout.

If customer_email is null but billing_details.email exists, you can use that as a fallback. If both are null, you must retrieve the customer object.

How to Retrieve the Customer Object Correctly

In your webhook handler, after receiving the event, extract the customer ID and call the Stripe API to fetch the customer:

  1. Parse the event JSON and get event.data.object.customer.
  2. Call stripe.customers.retrieve(customerId) (or the equivalent in your SDK).
  3. Check the returned customer’s email field. If it is set, use it.
  4. If the customer email is still null, look at the customer’s default payment method or the most recent payment method’s billing_details.email.

Note: The customer object may not have an email if the payment was made without creating a customer (e.g., using a PaymentIntent directly with receipt_email). In that case, the receipt_email on the payment intent is your best bet.

Common Scenarios and Their Fixes

  • Guest checkout with Checkout Sessions: If you use Stripe Checkout without requiring email, the customer may be created with no email. Fix: In your Checkout Session creation, set customer_creation: 'always' and pass customer_email if you have it, or collect it via a custom field.
  • PaymentIntent without a customer: The customer field is null. In this case, use receipt_email from the PaymentIntent or Charge object.
  • Customer created via the API without email: If your backend creates a customer with only a payment method, the email is null. Fix: update the customer with the email as soon as you have it (e.g., after the user submits a form).
  • Webhook retries: Stripe retries failed webhooks. If you fixed the email after the first attempt, the retry will still have the old null value in the payload, so always fetch fresh data.

Best Practices for Handling Missing Emails in Webhooks

Never rely solely on customer_email in webhook payloads. Instead, implement a fallback chain:

  1. Use customer_email if present.
  2. Else, use data.object.billing_details.email (or receipt_email).
  3. Else, fetch the customer via API and use customer.email.
  4. Else, fetch the default payment method and use its billing_details.email.
  5. If all else fails, log the event and mark it for manual review.

Also, consider setting receipt_email on PaymentIntents at creation time if you have the email. This is separate from the customer email and often populated even in guest checkouts.

Testing Your Webhook Handler

To ensure your handler works with null emails, use Stripe’s test mode and create events that mimic the scenario:

  • Create a customer without an email, then trigger an invoice or payment event.
  • Use the Stripe CLI stripe trigger customer.created or payment_intent.succeeded to send test webhooks.
  • Inspect the payload in your logs to confirm the fallback logic works.

Remember to test with both customer_email present and absent, and verify that your code handles the null gracefully without throwing an error.

Frequently Asked Questions

Why is customer_email null even though the customer has an email in the dashboard?

The webhook event is a snapshot of the data at the time the event occurred. If the email was added to the customer after the event was created, the event will still show null. Always fetch the current customer object for the latest data.

Can I use the payment method’s email instead?

Yes, if the payment method has billing_details.email, it is often the email used during checkout. It’s a reliable fallback, especially for guest checkouts.

What if the customer ID is also null?

If customer is null, the event is not tied to a customer. In that case, look for receipt_email on the PaymentIntent or Charge. If that’s also null, you may need to collect the email through other means (e.g., a custom metadata field).

Does Stripe require an email for webhook events?

No, Stripe does not require an email on customers or payments. It’s optional, so webhooks will happily deliver null emails. Your code must handle that case.

How can I prevent null emails in the future?

Ensure that when you create customers or PaymentIntents, you pass receipt_email or customer_email if you have the email. For Checkout Sessions, set customer_creation: 'always' and use customer_email if available, or collect the email via a custom field.

Is there a way to update the email on an existing customer from a webhook?

Yes, if you receive an email from another source (e.g., a form submission), you can call stripe.customers.update(customerId, {email: '...'}). This will update the customer for future events.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →