change event — there’s no separate “submit and see what’s wrong” step.
TL;DR:
change fires with event.valid: false and an event.error → gate the Pay button on valid and show error.message inline.When this happens
- The shopper is still typing (incomplete number, expiry, or CVV).
- The card number fails a brand/Luhn check, or the detected brand isn’t one you support.
- The expiry date is invalid or already in the past.
- The CVV doesn’t match the expected length for the detected brand.
- On pre-15.4 Safari without
BroadcastChannel, a split expiry/CVV field can’t relay its value to the primary field.
What you see
What to do
1
Gate the pay button on valid
Disable submission until every mounted field reports
valid: true. event.error can still be null here — it’s only set once blur-time validation runs — so don’t gate on error being absent. Don’t rely on a separate manual validation pass.2
Show the error only while it's present
event.error is null once the field becomes valid again — clear your inline message in that case rather than leaving a stale error visible.3
Handle the Safari fallback
If you support split fields and see repeated
expiry_missing/cvv_missing on older Safari, either advise the shopper to update their browser or switch to the combined card element, which doesn’t need cross-iframe relay.Prevent it
- Prefer the combined
cardelement unless you specifically need a custom layout — it has noBroadcastChanneldependency and one fewer iframe to coordinate. - Advertise supported card brands with
supportedBrandsso shoppers see upfront which cards you accept, before they type a full number.
Related
Card fields and events
Element types, create options, events, and Safari notes.
Accept a card payment
Where the
change event fits in the full charge flow.