Skip to main content

Headless Checkout Changelog

v3.75.0

This release introduces Edenred Meal Voucher as a new payment method, offering buyers more flexibility at checkout. Key reliability improvements include preventing duplicate authorizations for Adyen Apple Pay, ensuring Google Pay remains active after double-clicks, and securing your payment forms by addressing potential data leaks. We've also enhanced how Lyra and Axepta 3DS challenges are presented and fortified the security of dynamically loaded external scripts.

New features
Edenred: Meal Voucher is now available as a payment method.

Your buyers can now pay with Edenred Meal Vouchers. This new payment method uses a secondary authentication flow where buyers log into their MyEdenred account. Integrators can initiate this flow using the getSecondaryToken method.

// Initiate the Edenred authentication flow
const tokenResult = await headlessCheckout.getSecondaryToken('edenred', 'mealvoucher');

if (tokenResult && tokenResult.authorizeUrl) {
  // Open the authorizeUrl in a new window or redirect the buyer
  const authWindow = window.open(tokenResult.authorizeUrl, '_blank');

  // After successful authentication, the buyer will be redirected back.
  // Extract the 'code' from the URL and complete the flow.
  const code = getCodeFromRedirectUrl(); // Implement your logic to get the code
  const finalToken = await headlessCheckout.getSecondaryToken('edenred', 'mealvoucher', { code });
  console.log('Edenred token:', finalToken);
}
Lyra, Axepta: 3DS challenges can now open in an SDK-owned popup or be delegated to a host-provided handler.

The 3DS challenge flow for Lyra and Axepta now offers two primary improvements:

  1. Challenges can be hosted in a dedicated SDK-owned popup, preventing the ACS from being cramped within an iframe. If the browser blocks the popup, the challenge gracefully falls back to an in-document dialog.
  2. Integrators can delegate the 3DS challenge handling to their own redirection handler using the new hooks.delegate3DSChallenge option. This allows for fine-grained control over the challenge presentation, especially in embedded contexts. A new RedirectionHandler.hostsChallenge property indicates this delegation.
// Example: Delegating 3DS challenge handling
const checkout = createHeadlessCheckout({
  // ... other configuration ...
  hooks: {
    redirectionHandler: {
      hostsChallenge: true,
      redirectToUrl: async (redirection) => {
        if (redirection.challengedInPopup) {
          // Handle the 3DS challenge form submission in your custom popup logic
          // For example, open a new window, display the form, and wait for its result.
          console.log('Handling 3DS challenge in custom popup:', redirection.jsonFormSubmit);
          return Promise.resolve(); // Or resolve with a status
        }
        // Handle other redirections as usual
        window.location.href = redirection.url;
      }
    }
  }
});
Dynamically loaded external scripts and iframes now validate their hosts for enhanced security.

To mitigate risks from potentially compromised API responses, the SDK now validates the hostname of dynamically loaded external resources (such as those for ACI, SDPX, Riverty, and Sequra). Before injecting scripts or iframes, the SDK checks that their src attribute points to a known and allowed partner domain. This prevents unexpected or malicious URLs from executing code within your payment origin. For Riverty, which uses merchant-owned subdomains, the hostname is validated for a valid shape. Sequra's identification iframe also now includes a sandbox attribute for additional security.

Bug fixes
Adyen Apple Pay: Duplicate authorizations are prevented by ensuring the component does not pay alongside API validation.

Previously, Adyen's Apple Pay component could trigger its own payment authorization while the SDK was simultaneously sending the token for validation via the /validate endpoint. This resulted in two authorizations for a single payment, leading to reconciliation issues. The component is now configured to delegate the payment submission entirely to the SDK's /validate call, ensuring only one authorization occurs per transaction and the Apple Pay sheet closes correctly based on the actual payment outcome.

Apple Pay: CB and Mastercard cards are now correctly displayed in the Apple Pay sheet when using Adyen.

Previously, when integrating Apple Pay via Adyen, card networks like CB and Mastercard might have been incorrectly greyed out in the Apple Pay sheet, even if supported by the merchant's Adyen account. This fix ensures that all accepted card networks are properly communicated to Apple Pay, allowing buyers to select their CB and Mastercard cards.

Axepta: 3DS messages are now accepted only from the designated 3DS iframe for enhanced security.

To prevent potential security vulnerabilities, the SDK now strictly verifies the source of messages received during the Axepta 3DS challenge. It will only process 3DS messages originating from the specific iframe it creates for the challenge, ignoring any messages from other potentially malicious frames on the page.

Google Pay: Buyers can now double-click the pay button without disabling the payment method.

Previously, rapidly clicking the Google Pay button could lead to a This method can only be called one at a time error, which would fatally disable the payment method for the remainder of the session. The SDK now safely guards against re-entrant calls, ignoring subsequent clicks while the payment sheet is opening and ensuring the method remains available for retry.

PayPal: A failed popup opening is now classified as a recoverable error, allowing buyers to retry.

If the PayPal SDK fails to open the authentication popup (e.g., due to a browser popup blocker), the method is no longer permanently disabled. The SDK now correctly classifies this error as recoverable, allowing buyers to retry the payment, for both PayPal Wallet and BNPL.

Saved card payments in hosted fields no longer fail on holderName and prevent excessive paymentTokens emissions.

When using token.getHostedFields().render() for saved cards, the card form no longer incorrectly requires the holderName field, which previously caused MissingTargetFromConfig errors and prevented the CVV iframe from booting. Additionally, the SDK now correctly subscribes to payment method state changes, preventing multiple identical paymentTokens or paymentMethods emissions when a method is deactivated.

API init and validate calls now provide more detailed error diagnostics, and validate() throws on 4xx responses.
The card form no longer leaks sensitive card data and provides accurate validation field keys.

This release includes several security and correctness fixes for the card form:

  • Sensitive card expiration date (expDate) values are now obfuscated in logs and monitoring events, similar to holderName, preventing accidental data exposure.
  • Raw holderName values are no longer included in CHECKOUT_PRE_VALIDATION_FAILED payloads to analytics systems.
  • The validateUi() method now correctly maps validation statuses to the card form's internal field keys (cardNumber, holderName), ensuring accurate feedback for card validation.

For integrators using Secure Fields, the event dictionary for Secure Fields (covering events like FIELD_BLUR, FIELD_FOCUS, and various log codes) has been updated in the documentation, providing clarity on event payloads and meanings.

Gift card numbers now automatically strip whitespace during input and processing, improving payment reliability.

To prevent payment rejections due to formatting issues, gift card numbers (for secondaries like Ogloba, Buybox, Illcado, Maxxing, ANCV) now automatically remove leading, trailing, and internal whitespace. This normalization occurs at both the input field level (for a cleaner buyer experience) and when constructing the payment payload, ensuring only clean, validated numbers reach the API.

Cross-origin errors are now unmasked and reported only once per page, improving observability.

Unhandled cross-origin errors (e.g., Script error.) are now properly reported with their actual message and stack trace instead of a generic message. Additionally, to prevent log flooding, these errors are capped to report only once per page, providing clearer and more actionable insights into unexpected client-side issues.

Minor: The card form's CVV placeholder now dynamically updates based on the selected card brand.
v3.74.2

An important fix ensures that Mastercard and Cartes Bancaires cards are now correctly displayed and selectable within the Apple Pay sheet when processing payments via Adyen.

Bug fixes
Adyen: Mastercard and Cartes Bancaires (CB) cards are now correctly available in the Apple Pay sheet.
v3.74.1

A critical fix for Adyen Apple Pay ensures that payments now correctly result in a single authorization, preventing previous issues with duplicate charges and improving payment reconciliation.

Bug fixes
Adyen: Apple Pay payments now correctly create a single authorization, preventing duplicate charges.
v3.74.0

This release introduces new payment integrations for SeQura and Lyra white-label credit cards, alongside crucial updates to improve the reliability and security of payment forms. Buyers will experience more accurate Apple Pay availability, and card forms will adapt better to different screen sizes.

New features
Lyra: The white-label credit card form is now available.
SeQura: The CB3X payment method is now integrated.
Bug fixes
ACI: A race condition that could cause payment display issues for non-vault credit card payments has been prevented.
Apple Pay: Availability is now correctly determined by the wallet SDK, ensuring it only appears when genuinely available to the buyer.
HiPay: The integration no longer contains unused code paths for token-based CVV fields, ensuring all CVV fields are managed by the SDK.
The card form now adapts responsively to narrow containers, ensuring correct display of expiration and CVV fields, particularly in CVV-only mode.
Merchant-supplied text in hosted forms is now sanitised, preventing injection of malicious content.
Minor: Adyen holderName input padding restored; American Express card forms use correct labels and placeholders.
v3.73.0

This release significantly enhances payment reliability and debugging capabilities for your checkout. We've introduced more detailed logging for payment failures and ensured that uncaught SDK errors are properly reported. Additionally, buyers will benefit from improved stability for Apple Pay and PayPal, and a more secure checkout element that no longer exposes sensitive data in the DOM.

New features
Payment failure error logs now include detailed information about the cause of the failure.

The HEADLESS_CHECKOUT_PAYMENT_ERROR event now includes a results array within its payload. This array provides per-attempt details for failed payments, including the payment method, partner, status, plugin_status, and a capped description of the failure. This helps you distinguish between different failure reasons.

interface HeadlessCheckoutPaymentErrorEventDetail {
  // ... other existing fields
  results: Array<{
    method?: string;
    partner?: string;
    status?: string;
    plugin_status?: string;
    description?: string;
  }>;
}

element.on('HEADLESS_CHECKOUT_PAYMENT_ERROR', (event) => {
  console.error('Payment failed:', event.detail.results);
});
Card payments using the Purse vault now perform fewer API calls by skipping an unnecessary /init request.
The card form now intelligently conditions the display of the CVV input based on network token detection.
Bug fixes
Adyen: The underlying Adyen SDK has been updated to version 6.41.0.
Apple Pay: The SDK now negotiates the compatible Apple Pay JS version, allowing more buyers on older devices to complete payments.
Google Pay: The countryCode is now correctly derived from the locale, resolving previous payment failures. Payment sheet failures are also now properly reported instead of being silent.
PayPal: The button is now dimmed and unclickable until the session is fully initialized, and non-fatal errors like popup closures correctly soft-disable the method.
Uncaught SDK errors are now properly reported to your monitoring tools.

Previously, the SDK was swallowing its own unhandled promise rejections and errors, making it difficult to detect issues. Now, uncaught errors originating from the SDK will correctly appear in your browser's console and any window.onerror or window.onunhandledrejection handlers you have implemented. This provides better visibility into potential client-side issues.

Concurrent payment submissions are now correctly prevented, avoiding duplicate /validate calls and incorrect redirections.

The SDK now guards against re-entrant calls to submitPayment. This prevents buyers from accidentally initiating multiple validations, which previously could lead to unnecessary API calls and being redirected to an error page even after a successful payment. A new HEADLESS_CHECKOUT_SUBMIT_BLOCKED event is dispatched when a submission is blocked.

Sensitive session data, payment method tokens, and environment variables are no longer exposed as DOM attributes on the <purse-partner-ui> element.
The payment element now correctly persists across session refreshes, preventing UI components from disappearing and avoiding console errors.

The PaymentElement.on() method now returns an unsubscribe function. Integrators should use this function to clean up event listeners when the element is unmounted to prevent memory leaks and ensure correct behavior across session updates.

const unsubscribe = element.on('ready', () => {
  // Handle ready event
});

// When the component unmounts or the element is no longer needed
unsubscribe();
The card form now correctly displays a numeric keyboard for expiration date inputs on mobile devices and disables autocorrect for holder name.

You can now pass extraProps to customize input attributes for hosted fields, allowing granular control over keyboard types and other HTML input properties.

interface HostedFieldOptions {
  extraProps?: {
    inputmode?: string;
    autocomplete?: string;
    autocorrect?: 'on' | 'off';
    spellcheck?: boolean;
    // ... other HTMLInputElement attributes
  };
}
v3.72.1

The PayPal payment flow is now more robust in webview environments.

Bug fixes
PayPal: The PayPal payment flow now uses 'auto' presentation mode when displayed as a popup, improving compatibility with webviews.
v3.72.0

We've added Stripe Google Pay as a new payment method and upgraded the PayPal SDK to V6 for improved performance and reliability. You now have more control over the display of the CVV field for tokenized payments, alongside several stability enhancements.

New features
PayPal: The PayPal SDK has been upgraded to V6, improving performance and reliability.
Stripe: Google Pay is now available as a payment method.
You can now configure the display of the CVV field for tokenized payments using the merchant_specifics.pay_token_no_cvv setting.

The SDK now correctly respects this setting, ensuring the CVV field is hidden when configured, and the payment element's hasUI() method accurately reflects this state.

Plugins can now react to runtime option changes using the new onOptionsUpdate lifecycle hook.

The new onOptionsUpdate hook allows plugins to dynamically update their UI or behavior when the Payment Element's options change at runtime. This is particularly useful for payment methods with imperatively built UIs, such as Google Pay.

interface Plugin {
  // ... other methods
  onOptionsUpdate?: (context: Context) => Promise<void>;
}
Bug fixes
Adyen: Apple Pay now performs a client compatibility check and degrades gracefully on incompatible browsers.
Alma: The in-page payment calendar now appears only for plans up to 4 instalments.
Maxxing: Missing loyalty accounts are now reported with a specific LOYALTY_ACCOUNT_NOT_FOUND error.

Previously, these were reported as a generic FAILED_TO_INIT_SECONDARY_TOKEN.

PayPal: The PayPal button's appearance and size are now consistent with previous versions when using SDK V6, and it no longer becomes clickable before it is ready to process payments.
Payzen Xpay: Button labels can now be successfully overridden for custom translations.
Tokens now correctly fulfill when their CVV is disabled by configuration.

Previously, tokens with a disabled CVV would not correctly fulfill, preventing the payment from being submitted. The SDK now correctly handles merchant_specifics.pay_token_no_cvv and hostedForm.noCVV to hide the CVV input and ensure the token form validates correctly.

Apple Pay no longer fails the checkout if the button is double-clicked.
Minor: Plugin incompatibility messages are now logged at a warning level instead of an error level.
v3.71.1
Bug fixes
Apple Pay: Double-clicking the Apple Pay button no longer kills the checkout — clicks are ignored while the payment sheet is open.
v3.71.0

Adds Adyen Apple Pay and a normalized partnerError payload. Apple Pay and PayPal initialization failures now surface instead of leaving a dead button.

New features
Adyen: Added Apple Pay.
partnerError payloads are normalized and no longer typed against partner SDK internals.
Card form input survives a session refresh.

Input in the Purse Vault card form is preserved during a session update unless the payment method configuration itself changes.

Minor: Custom apiPaths passed to a bundle that enforces its own API URLs now log a console warning instead of being silently ignored.
Bug fixes
Apple Pay: Initialization failures surface a fatal error instead of rendering a non-responsive button.
Apple Pay (Ingenico): Unsupported clients disable the payment method instead of failing the whole checkout.
PayPal: Initialization failures and unrecoverable errors disable the payment method button and surface a visible error.

SDK errors and init failures — NOT_AUTHORIZED, for example — previously left the button active or froze the checkout silently.

Rapid onAmountUpdate calls no longer exhaust the session step budget and reach SESSION_MAX_STEPS_REACHED.
v3.70.1
Bug fixes
The ESM build no longer fails to load on Safari and iOS 16.3 and below.

The build target is pinned explicitly again. A dependency bump had picked up a narrower default that stopped downleveling ES2022 syntax.

v3.70.0

Validation error keys are renamed. Favorite token management moves to a dedicated endpoint, and getPaymentElement() warns instead of crashing on secondary methods.

Breaking changes
Validation error keys are renamed from *CannotBeEmpty* to *Required*.low impact
Migration guide

The FieldValidationErrors enum and the matching configuration fields are renamed. Deprecated cannotBeEmpty keys still resolve, but will be removed in a future major.

// Before
const config = { cardHolderNameCannotBeEmptyError: 'Cardholder name is mandatory.' };

// After
const config = { cardHolderNameRequiredError: 'Cardholder name is mandatory.' };
New features
Favorite tokens are toggled and fetched through a dedicated endpoint, removing a race between local and server state.
getPaymentElement() on a secondary method warns and returns undefined instead of throwing an opaque TypeError.

Secondary methods — gift cards, vouchers — use getSecondaryToken() or take().

Bug fixes
Apple Pay: Validation no longer sends a request to Adyen when the token is missing, which returned paymentMethod not provided.

Tokens from a previous initialization are cleared. CHECKOUT_API_VALIDATION_SUCCEEDED now carries a per-partner-method statuses map.

interface CheckoutApiValidationSucceededEventPayload {
  statuses: Record<string, { state: string; code?: string; description?: string }>;
}
Lyra: Google Pay, Apple Pay, and XPay initialize when plugin_result.logs is absent.
PayPal: Initialization errors for Wallet and BNPL are re-thrown and dispatched as PluginInitFailed or partnerError instead of being swallowed.
No crash when a payment method is removed from the session while a lethal event listener is attached.
save_token: false overrides any conflicting save_token_mode.
Minor: formDataChanged is logged only once every field is valid, and holderName masking drops the last character; Corrected event categories for VALIDATION_SUCCESS, VALIDATION_FAILED, PAYMENT_ERROR, EXPIRED_SESSION, and CLIENT_HOOK_ERROR.
v3.69.0

Stripe Apple Pay is now available. The getPaymentElement() method is now more flexible, allowing optional method and partner parameters, and provides clearer error messages for incompatible hosted fields requests. Additionally, payment tokens now expose isFavorite, setAsFavorite(), and createdAt properties for enhanced management.

New features
Stripe: Apple Pay is now available as a payment method, leveraging Stripe's Express Checkout Element.

Integrators can now offer Apple Pay via Stripe. The button height is automatically clamped to Stripe's supported range of 40-55px as per Stripe's constraints.

The getPaymentElement() method now features significantly improved resolution logic and optional parameters.

The method and partner parameters are now optional for getPaymentElement():

  • When method is omitted, the SDK automatically selects the first hosted fields-compatible primary method if hostedFields is requested, or the first primary method in the session otherwise.
  • When partner is omitted, the SDK uses the first primary method matching the given method name across all partners. A warning is emitted if multiple partners expose the same method.
  • Requesting hostedFields for an incompatible payment method will now throw a METHOD_DOES_NOT_SUPPORT_HOSTED_FIELDS error instead of silently failing.
Payment tokens now expose isFavorite and a setAsFavorite() method.

The PurseHeadlessCheckoutPrimaryToken type now includes:

interface PurseHeadlessCheckoutPrimaryToken {
  isFavorite: Readable<boolean>;
  setAsFavorite(): Promise<void>;
  // ... other properties
}

Integrators can use these to display and manage a buyer's preferred tokens.

Payment methods and tokens now expose a supportsHostedFields boolean property.

The PurseHeadlessCheckoutPrimaryMethod and PurseHeadlessCheckoutPrimaryToken types now include:

interface PurseHeadlessCheckoutPrimaryMethod {
  supportsHostedFields: boolean;
  // ... other properties
}

interface PurseHeadlessCheckoutPrimaryToken {
  supportsHostedFields: boolean;
  // ... other properties
}

This property indicates whether the method or token is compatible with hosted fields.

Payment tokens now expose a createdAt timestamp.

The PaymentToken type now includes a createdAt: string property, allowing integrators to sort or display tokens by their creation date.

Bug fixes
Ingenico: Google Pay and Apple Pay no longer crash when the API returns incomplete initialization data.

When the Ingenico API returns incomplete data during initialization, the widgets now gracefully fall back to default payment networks (e.g., Mastercard/Visa) and continue to function, preventing a complete checkout blockage.

Hosted fields compatibility is now correctly determined for all payment methods.

This fix ensures that payment methods declaring hosted fields support in their plugin are correctly identified, even when the session vault doesn't explicitly state hosted-fields support.

Token wallet management now gracefully handles unexpected non-array API responses, preventing application crashes.
Apple Pay and Google Pay button customisation options (xPayButton) are now correctly forwarded by getPaymentElement().

Integrators can now use the xPayButton property in PurseHeadlessCheckoutGetPaymentElementOptions to customise the appearance of Apple Pay and Google Pay buttons.

checkout.getPaymentElement({
  partner: 'adyen',
  method: 'googlepay',
  xPayButton: {
    google: {
      buttonColor: 'black',
      buttonType: 'buy'
    }
  }
});
Payment element resolution now prioritises generic (template-driven) plugins over partner-specific plugins when a session template is defined.

This ensures that if your backend defines a custom template for a payment method, the generic plugin will be used even if a partner-specific plugin exists for that method.

v3.68.2
Bug fixes
The wallet now robustly handles API responses for stored payment methods, preventing crashes from malformed data.
v3.68.0
Breaking changes
methodSelected and methodUnselected removed from PaymentElementEventName. For payment methods that open a modal (Apple Pay, Google Pay, Lyra), replace with modalIn and modalOut.
// Before
element.on('methodSelected', handler)
// After
element.on('modalIn', handler)
New features
additionalAssets: PurseHeadlessCheckoutPaymentItemBase now exposes additionalAssets: CardSchemeAsset[] — one entry per supported card scheme (URL + label). iconUrl is still present.
// method.additionalAssets: Array<{ url: string; label: string }>
method.additionalAssets // [{ url: 'https://...', label: 'Visa' }, ...]
Hosted fields — hideHolderName: New option in HostedFieldsOptions to hide the cardholder name field.
method.setOptions({
  hostedForm: { hideHolderName: true },
});
Worldpay: Added direct credit card payment method.
PayPal — disableMaxWidth: New option to override the button's default max-width constraint.
method.setOptions({
  xPayButton: { paypal: { disableMaxWidth: true } },
});
Bug fixes
Google Pay (Ingenico/Worldline): Gateway is now correctly identified when payment product data is unavailable, preventing silent tokenization failures.
Lyra / X-Pay: Validation no longer triggers when the payment sheet failed to open.
Oney: Private card tokens requiring 3DS PIN authentication now complete correctly.
Worldpay: Card brand list is now correctly filtered.
Expiration date field no longer rejects years longer than 4 digits.
Malformed vault expiry dates no longer cause validation errors.
Hosted fields validation now uses getElementInstance() internally, fixing silent skips when rendering via getHostedFields().
Saved tokens now display item.name instead of the masked card number.
v3.67.1

This release enhances the reliability of Google Pay payments for our Ingenico/Worldline merchants by correcting configuration defaults, preventing silent tokenization failures. Additionally, we've fixed an issue ensuring that payment tokens saved in the wallet display their correct names.

Bug fixes
Google Pay: Ingenico/Worldline : Google Pay tokenization now uses the correct default gateway and narrower network list when specific payment product data is unavailable, preventing silent payment failures.
The payment token wallet now correctly uses the name field for saved payment tokens and ensures the editTokenName API path is correct.
v3.67.0

We've added new capabilities for a smoother payment experience and enhanced developer tooling. Buyers can now customize their PayPal button appearance, while developers benefit from a streamlined way to create PaymentElement instances and readily access payment method icons.

New features
PayPal: Developers can now disable the maximum width applied to the PayPal button.

The new disableMaxWidth option can be passed in XPayButtonUIOptions['paypal'] to prevent the PayPal button from expanding to its container's full width.

interface XPayButtonUIOptions {
  paypal?: {
    disableMaxWidth?: boolean;
    // ... other PayPal button options
  };
}

// Example usage:
checkout.getPaymentElement({
  partner: 'paypal',
  method: 'paypal',
  paypal: {
    disableMaxWidth: true,
  },
});
Developers can now create a PaymentElement directly from the checkout instance.

A new shortcut method, checkout.getPaymentElement(), simplifies the creation and rendering of a single payment method's UI.

// Before: More verbose for a single method
// const checkout = new PurseHeadlessCheckout(...);
// const paymentMethods = await checkout.getPaymentMethods();
// const specificMethod = paymentMethods.find(
//   (pm) => pm.partner === 'ingenico' && pm.method === 'creditcard'
// );
// if (specificMethod) {
//   const el = specificMethod.getPaymentElement({ theme: {} });
//   el.appendTo('#container');
// }

// After: Simplified approach
const el = checkout.getPaymentElement({
  partner: 'ingenico',
  method: 'creditcard',
  theme: { /* ... */ },
  hostedForm: { /* ... */ },
  hostedFields: { /* ... */ },
});

el.on('ready', () => console.log('PaymentElement is ready.'));
el.appendTo('#container');
Payment methods and secondary tokens now expose an iconUrl.

Integrators can now easily display official icons for payment methods and secondary tokens by accessing the new iconUrl property.

interface PaymentMethod {
  // ... existing properties
  iconUrl: string | null;
}

interface BuiltSecondaryToken {
  // ... existing properties
  iconUrl: string | null;
}

// Example usage:
const paymentMethods = await checkout.getPaymentMethods();
paymentMethods.forEach((method) => {
  if (method.iconUrl) {
    console.log(`Icon for ${method.name}: ${method.iconUrl}`);
  }
});
Bug fixes
Apple Pay: The payment sheet now handles cancellation gracefully without crashing.
Secondary payment tokens now correctly map payment data from API responses, and currency is consistently sourced from the payment session.

Dedicated mappers ensure accurate data extraction for secondary tokens, including those from partners like Illicado, Easy2play giftcard, Mybeezbox, and Ogloba. This includes migrating from deprecated payment_data.card to payment_data.cards[0] for card-based secondary tokens.

currencyCode has been removed from BuiltSecondaryToken, PurseHeadlessCheckoutSecondaryToken, and PartialSecondaryToken types. Integrators should now retrieve the currency directly from the payment session, which serves as the single source of truth.

The SDK now properly handles redirections, preventing crashes when a validation result lacks a redirect URL.
The cardholder name field no longer accepts semicolons.
Wallet sessions are now correctly parsed from widget_data when available.
Off-session payments no longer incorrectly filter amounts.
v3.66.0

This release expands payment options by introducing Apple Pay and Google Pay for Sogecommerce, PayPal BNPL, and Younited Installments. We've also enhanced Oney card support to correctly collect birthdates for private cards and added a noCVV mode for more flexible card form integrations.

New features
Alma: Alma payment sessions now validate before displaying the popup.

This prevents 'ghost sessions' with Alma when buyers close the popup prematurely, ensuring session consistency between Purse and Alma.

Oney: The card form now correctly collects a birthdate instead of a CVV for Oney private cards.

For Oney private cards, the card form dynamically adapts to collect the buyer's birthdate instead of the CVV. This ensures accurate data collection for Oney payments.

PayPal: Introduce a new BNPL plugin for PayPal payments.
Sogecommerce: Buyers can now pay using Apple Pay and Google Pay via xPay.
Younited: Buyers can now pay using Younited Installments.
Enable an optional noCVV mode for card forms.

Integrators can now configure the card form to optionally collect the CVV by setting the noCVV flag on PaymentElement initialization. This allows for use cases where CVV collection is not always required for specific card brands or payment flows.

const paymentElement = purse.initPaymentElement({
  // ... other config
  card: {
    noCVV: true, // CVV field becomes optional
  },
});
Bug fixes
Adyen: Redirection for 3DS challenges is now handled by the headless checkout manager, ensuring consistent behavior.
ANCV: Cancellation of ANCV Connect polling is now reliable.
ANCV: Correctly handle "Other Transaction Pending" errors for ANCV sessions.
Lyra: Prevent 725 errors when a payment button is not explicitly clicked.
Lyra: 3DS authentication is now correctly skipped when the validation response does not provide an authentication URL.
Maxxing: Correctly generate token IDs for Maxxing loyalty, resolving issues with loyalty program availability.
Support for information requests in the headless checkout.

Payment methods compatible with OFF_SESSION registrations are now correctly displayed and activated, allowing for payments even with zero-amount initial requests.

Ensure payment method splits are correctly processed when using the onBeforeValidate hook.
Centralize final redirection handling within the manager, preventing double redirection calls.
Hosted fields for CVV inputs now use a unique prefix to prevent conflicts between token and new card forms.

This resolves an issue where the CVV field could be duplicated or misbehave when quickly switching between a saved card token form and a new card entry form, especially on slow networks (SDK-11545).

The vault module now waits for the 'ready' event before resolving its boot promise, improving stability.
Card brand names in the vault are now displayed with natural language, instead of SCREAMING_SNAKE_CASE.
The system now emits a 'died' event when a payment plugin is not found, providing clearer error feedback.

This prevents unknown plugins from occupying slots in xPay displays when a template is not properly configured (SDK-11514).