Skip to content

fix: prefill APM fields by canonical key and parse E.164 phone numbers - #277

Draft
roshan-gorasia-cko wants to merge 3 commits into
masterfrom
fix/apm-initial-data-prefill
Draft

fix: prefill APM fields by canonical key and parse E.164 phone numbers#277
roshan-gorasia-cko wants to merge 3 commits into
masterfrom
fix/apm-initial-data-prefill

Conversation

@roshan-gorasia-cko

@roshan-gorasia-cko roshan-gorasia-cko commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Problem

Three issues stopped initialData from prefilling an APM form.

1. Prefill only matched the payment method's own parameter key. NextSteps looked up initialData[param.key], so the documented initialData.phone_number silently no-op'd whenever a payment method named the field something else (for example customerPhone). In practice a merchant had to discover each payment method's internal parameter names to prefill anything.

2. An E.164 phone string wasn't parsed. A phone_number passed as "+48123123123" went into the national-number input whole, with the dialing code set to the first entry in the available list. The +-prefix splitting logic only existed in the phone field's paste/typing handler, so it never ran for seeded state.

3. The prefilled phone value never reached the field, and submitted the wrong key. Two separate breaks, both of which made the first two fixes invisible in the browser:

  • Phone reads a value prop, but its only call site passed the stored form value as number. Props<T> carries an [key: string]: any index signature, so the misnamed prop typechecked and was swallowed into the input's attributes. A prefilled phone rendered as an empty box on the default country — the same result as no prefill at all. This dates from the prop rename in fix(apm): rename phone "value" to "number" to conform with the BE API contracts #225.
  • validateField read the national number from value, but the field emits number. Once a phone held the field's own shape, actualValue was the whole object, so required passed for any phone including an empty one.

Changes

  • resolvePrefilledValue (src/apm/utils.ts) resolves a parameter's prefill value: an exact parameter-key match wins, otherwise the canonical key for that parameter's type (email → type email, phone_number → type phone). Canonical keys now work on every payment method, and a specific parameter can still be targeted directly or used to override a canonical value.
  • normalizePhoneValue (src/apm/utils.ts) coerces a prefilled phone into the shape the field renders. An E.164 string is split on a longest-prefix match against the available dialing codes (so +1242 beats +1), separators are ignored, and the browser-locale default is used when the country isn't offered.
  • The phone field's call site now passes value:, and PhoneProps.value / the field's initial state are keyed on number (src/apm/views/utils/form.ts, src/apm/elements/phone.ts).
  • validateField reads the national number under either key, so required catches an empty phone.
  • The field's initialisation callback seeds the form with { dialing_code, number } rather than its whole internal state, which also carries the flag picker's iso. That emit only started firing once the prop name was fixed, so iso would otherwise have been submitted as a gateway parameter.
  • InitialData (src/apm/types.ts) types phone_number as string | { dialing_code?, value? } and allows payment-method-specific keys.

Notes on the key name

The codebase disagreed with itself about whether a phone's national number lives under value or number. This settles on number: it matches the API contract per #225, the field's own oninput, and the PhoneState that FormState already declared. Both keys are still accepted as input, so the documented initialData object form keeps working unchanged.

Worth a reviewer's eye: the validateField change is shared by every field type. The pre-existing value lookup is kept as a fallback, but it's worth confirming no other field type stores an object with a meaningful value key.

The prefilling docs present email / phone_number as universal keys and document the phone value as a bare E.164 string. Both are now true of the SDK; the docs should be extended to cover the object form and to note that a specific parameter key can also be passed and takes precedence.

Tests

  • test/apm/prefill.test.ts — key/type resolution precedence and E.164 splitting.
  • test/apm/phone-prefill-wiring.test.ts — asserts which props the form hands the phone field, what the field renders from a prefill, what it seeds back to the form, and required validation. Two loaders in test/support/loadNamespace.ts evaluate form.ts and phone.ts against stubbed components to make prop-level assertions possible; the absence of that coverage is why the index-signature mismatch went unnoticed.

Reverting the three source fixes fails 4 of the 6 new wiring cases. yarn test, yarn build and yarn lint all pass.

@roshan-gorasia-cko
roshan-gorasia-cko force-pushed the fix/apm-initial-data-prefill branch 2 times, most recently from 282832c to 3cc9902 Compare August 6, 2026 10:47
Prefilling an APM form via `initialData` had two problems.

Values were matched only against the payment method's own parameter key, so
the documented `initialData.phone_number` silently did nothing whenever a
payment method named the field something else (for example `customerPhone`),
leaving merchants to discover each one's internal parameter names. `email`
and `phone_number` now also match on the parameter's type, so the canonical
keys prefill on every payment method. An exact parameter-key match still
wins, so a specific field can be targeted or a canonical value overridden.

A phone number passed as an E.164 string was dropped into the national
number input whole, with the dialing code set to the first entry in the
available list. That selected the wrong country, left `+CC` inside the
number box and submitted a malformed value. The string is now split on a
longest-prefix match against the available dialing codes, falling back to
the browser-locale default when the country isn't offered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@roshan-gorasia-cko
roshan-gorasia-cko force-pushed the fix/apm-initial-data-prefill branch from 3cc9902 to 89b6b96 Compare August 6, 2026 10:48
roshan-gorasia-cko and others added 2 commits September 8, 2026 08:51
The prefill computed in NextSteps never reached the phone field. The field
reads a `value` prop; the form's only call site passed it as `number`, and
`Props<T>`'s `[key: string]: any` index signature let the misnamed prop
typecheck and vanish into the input's attributes. A prefilled phone rendered
as an empty box on the locale default country.

`normalizePhoneValue` also returned the pre-#225 `value` key, so a prefilled
phone submitted without the user touching the field posted `value` where the
API expects `number`. It now emits `number`, matching the declared
`PhoneState` and the field's own `oninput`; both keys are still accepted as
input, so the documented `initialData` object form keeps working.

`validateField` read `value` too, which made `required` pass vacuously for
any phone once the field had emitted its own shape. It now reads the national
number under either key, so an empty phone is caught.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant