fix(adyen): send the shopper locale so redirect pages match the Drop-in language - #824
Open
acasazza wants to merge 3 commits into
Open
fix(adyen): send the shopper locale so redirect pages match the Drop-in language#824acasazza wants to merge 3 commits into
acasazza wants to merge 3 commits into
Conversation
…anguage Port of ddfb9a7, which fixed this on the v4 line and never reached v5. Adyen uses two locales and only one of them was travelling: - `locale` in the Core configuration is client-side only — it selects the Drop-in's translation bundle. - the shopper locale in the payment request is what Adyen uses for the pages it renders itself, in particular the hosted page a redirect method sends the shopper to. `payment_request_data` carried `payment_method`, `return_url`, `origin`, `redirect_from_issuer_method`, `shopper_ip` and `browser_info`, but no shopper locale, so Adyen fell back to the merchant account default or the country code. A Drop-in correctly rendered in English then handed over to a Klarna page in Italian for an IT market. `shopper_locale` is now sent, derived from the same value that drives the Drop-in. Commerce Layer's `language_code` is a bare ISO 639-1 code, so `getAdyenShopperLocale` expands it to the `language-REGION` form Adyen expects (`en` -> `en-US`, `it` -> `it-IT`). When a language cannot be expanded confidently it returns undefined and the field is omitted, preserving Adyen's current fallback rather than sending a locale it may reject. `ca` is expanded too, which ddfb9a7 missed. The attribute is snake_case, like the other Commerce Layer attributes in that payload — it is the API that maps them onto Adyen's camelCase names. ddfb9a7 sent `shopperLocale` instead, which is the name Adyen uses but not the one this payload takes. `AdyenPaymentConfig.shopperLocale` overrides the derived value, camelCase as a component option. Unlike in ddfb9a7 it also moves the Drop-in's own `locale`: an integration that sets it would otherwise get a Klarna page in Italian behind a Drop-in in English, which is the original mismatch chosen on purpose. Nothing changes for an integration that does not set it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two things the shopperLocale work walked past. The `locale` prop defaulted to `en_US`. Adyen's client normalizes that — it replaces the underscore and then matches against its supported list — so it worked, but the payment request has no such normalizer, and the same string in both places would not have. It is now a named `DEFAULT_LOCALE` in the `language-REGION` form, which is also Adyen's own fallback value. `AdyenGateway` cast `order.language_code` to `StripeElementLocale`, importing a Stripe type to feed an Adyen prop. It compiled only because that prop is `string`. `language_code` is `string | null` and the prop is `string | undefined`, so the cast was laundering the null: `?? undefined` does that without claiming the value is something it is not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`payment_request_data` is a Commerce Layer payload and the API maps its snake_case keys onto Adyen's camelCase names. `shopperInteraction` was written with Adyen's own spelling, so it went into the payload under a name that side does not take — the same mistake the shopper locale nearly shipped with. `Ecommerce` is Adyen's own default for this field, so a payment that has been working was very likely not relying on it arriving. It arrives now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Targets
fix/adyen-dropin-session-key(#818) rather thanmain, so it lands inside the work already open there.The bug
Adyen uses two locales and only one of them was travelling:
localein the Core configuration is client-side only — it selects the Drop-in's translation bundle.payment_request_datacarriedpayment_method,return_url,origin,redirect_from_issuer_method,shopper_ipandbrowser_info, but no shopper locale, so Adyen fell back to the merchant account default or the country code. A Drop-in correctly rendered in English then handed over to a Klarna page in Italian for an IT market.This is a port of
ddfb9a7a, which fixed it on the v4 line and never reached v5.What changed
shopper_localeis sent, derived from the same value that drives the Drop-in. Commerce Layer'slanguage_codeis a bare ISO 639-1 code, sogetAdyenShopperLocaleexpands it to thelanguage-REGIONform Adyen expects (en→en-US,it→it-IT). A language it cannot expand confidently yieldsundefinedand the key is omitted, preserving Adyen's own fallback rather than sending a locale it may reject.cais expanded too, whichddfb9a7amissed.AdyenPaymentConfig.shopperLocaleoverrides the derived value, camelCase as a component option. Unlike inddfb9a7ait also moves the Drop-in's ownlocale: an integration that sets it would otherwise get a Klarna page in Italian behind a Drop-in in English, which is the original mismatch chosen on purpose. Nothing changes for an integration that does not set it.Attributes are snake_case.
ddfb9a7asentshopperLocale, which is Adyen's name but not the one this payload takes — the API maps snake_case attributes onto Adyen's camelCase.shopperInteractionhad the same problem and is nowshopper_interaction;Ecommerceis Adyen's default for a request carryingbrowser_info, which is why nothing visibly broke while it was not arriving.Two cleanups in their own commit: the
localeprop defaulted toen_US, now a namedDEFAULT_LOCALEin thelanguage-REGIONform Adyen documents (their client normalizes the underscore, the payment request does not); andAdyenGatewayno longer castsorder.language_codeto Stripe'sStripeElementLocaleto feed an Adyen prop —?? undefinedlaunders the null without the lie.Commits
ff111221fix(adyen): send shopper_locale so redirect pages match the Drop-in languagec7ec0082chore(adyen): spell the default locale the way Adyen documents it58844107fix(adyen): send shopper_interaction in snake_case like its neighboursThe port is revertible on its own.
Tests
specs/payment_source/AdyenPayment.shopperLocale.spec.tsx— 6 cases: derived from the order language, not from the country code, config override, the override moving the Drop-in with it, omission when the language cannot be expanded, fallback to thelocaleprop.specs/utils/adyenShopperLocale.spec.ts— 4 cases on the expansion, includingen_USandPT_brnormalization.Full suite green: 1032 tests in react-components, 129 in core-components, 99 in react-hooks-components.
pnpm typecheckunchanged from the baseline on this branch (22 pre-existing errors, none in the touched files).Still unverified
The snake_case attribute name is not documented — the API reference lists only
payment_method,origin,return_urlandbrowser_infoinsidepayment_request_data. If a redirect method still comes up in the wrong language, the attribute name is the first thing to check against a real order.🤖 Generated with Claude Code