Skip to content

"Save card" checkbox is decoupled from actual tokenisation (GDPR consent issue) #1509

Description

@aduponchel

Summary

In the SFRA cartridge, the display of the "Save my card for future payments" checkbox and the server-side decision to tokenise (store) the card are driven by two independent site preferences. As a result, the checkbox does not reliably reflect what actually happens to the shopper's card data. In particular, a card can be tokenised and stored even when the shopper did not tick the checkbox, which is a consent-validity problem under the GDPR.

We have confirmed this behaviour is still present in version 26.2.0 (and on the current develop branch).

Affected versions

  • Confirmed on tag 26.2.0
  • Confirmed on branch develop (as of July 2026)

Environment

  • Cartridge: adyen-salesforce-commerce-cloud (SFRA)
  • Payment method: credit/debit card (scheme)
  • Shopper: authenticated (logged-in) customer

Root cause

Two different preferences control two different things:

Concern Preference used Location
Whether the "Save card" checkbox is shown AdyenRecurringPaymentsEnabled app_adyen_SFRA/.../templates/default/checkout/billing/adyenComponentForm.isml
Whether the card is actually tokenised/stored AdyenTokenisationEnabled int_adyen_SFRA/.../adyen/scripts/payments/adyenCheckout.js

Checkbox visibility (client)

src/cartridges/app_adyen_SFRA/cartridge/templates/default/checkout/billing/adyenComponentForm.isml

<isset name="adyenRecurringPaymentsEnabled"
       value="${pdict.AdyenConfigs.getAdyenRecurringPaymentsEnabled()}" scope="page"/>
...
var showStoreDetails = ${customer.authenticated && adyenRecurringPaymentsEnabled};

showStoreDetails is passed to the Card component as enableStoreDetails, which is what makes the Adyen Web SDK render the "Save card" checkbox. Its value depends only on AdyenRecurringPaymentsEnabled.

Tokenisation decision (server)

src/cartridges/int_adyen_SFRA/cartridge/adyen/scripts/payments/adyenCheckout.js (createPaymentRequest)

// Set tokenisation
if (AdyenConfigs.getAdyenTokenisationEnabled()) {
  paymentRequest.storePaymentMethod = true;
  paymentRequest.recurringProcessingModel =
    constants.RECURRING_PROCESSING_MODEL.CARD_ON_FILE;
}

When AdyenTokenisationEnabled is on, storePaymentMethod is forced to true, overwriting the value that came from the shopper's checkbox (stateData.storePaymentMethod). The shopper's choice is discarded.

Steps to reproduce

  1. In Business Manager, enable both custom preferences:
    • AdyenRecurringPaymentsEnabled = true
    • AdyenTokenisationEnabled = true
  2. Log in as a shopper and go to checkout with a card payment.
  3. Observe that the "Save my card for future payments" checkbox is displayed.
  4. Leave the checkbox unchecked and complete the payment.
  5. Observe that the card is nevertheless tokenised and stored (a recurring.recurringDetailReference / stored payment method is created; the card appears as a saved/stored payment method for the shopper).

Expected vs actual behaviour

  • Expected: With the checkbox unticked, the card must not be tokenised/stored. The visible checkbox should be the single source of truth for the shopper's consent to store the card.
  • Actual: The card is tokenised and stored regardless of the checkbox state, because AdyenTokenisationEnabled forces storePaymentMethod = true.

Problematic configurations

AdyenTokenisationEnabled AdyenRecurringPaymentsEnabled Result
true true Checkbox is shown but ineffective — the card is stored even if the shopper leaves it unchecked.
true false Card is stored with no checkbox and no visible consent at all.
false true Correct behaviour — the checkbox is a genuine opt-in.

The first two rows are the concern.

Why this is a GDPR problem

Tokenising and storing a payment card for future payments is processing of the shopper's personal data. When a merchant presents a "Save my card" checkbox at checkout, the lawful basis being relied upon is the shopper's consent (GDPR Article 6(1)(a)). For that consent to be valid, the GDPR requires it to be a freely given, specific, informed and unambiguous indication of the shopper's wishes, expressed through a clear affirmative action:

In the current cartridge behaviour, when the checkbox is displayed but the card is stored regardless of whether it was ticked, the situation is functionally equivalent to a pre-ticked / ignored checkbox: the shopper's card is tokenised without a clear affirmative action. This is exactly the scenario that Recital 32 and the Planet49 ruling identify as invalid consent. In the second configuration (no checkbox shown at all while AdyenTokenisationEnabled is on), the card is stored with no consent mechanism surfaced to the shopper at that step.

Because the merchant cannot demonstrate valid, checkbox-based consent (Article 7(1)), relying on the visible checkbox as the consent mechanism becomes unsafe.

Suggested fix

Make the visible checkbox the single source of truth for consent, so that server-side tokenisation respects the shopper's choice. Two options:

  1. Gate tokenisation on the shopper's choice rather than forcing it. When AdyenTokenisationEnabled (or AdyenRecurringPaymentsEnabled) is used to enable the feature, still require the checkbox to have been ticked:

    // Only tokenise when the shopper actively opted in via the visible checkbox
    const shopperOptedIn = paymentRequest.storePaymentMethod === true;
    if (AdyenConfigs.getAdyenTokenisationEnabled() && shopperOptedIn) {
      paymentRequest.storePaymentMethod = true;
      paymentRequest.recurringProcessingModel =
        constants.RECURRING_PROCESSING_MODEL.CARD_ON_FILE;
    } else {
      paymentRequest.storePaymentMethod = false;
    }
  2. Align the two preferences and document them clearly: ensure the checkbox is always shown whenever server-side storing can occur, and that an unticked checkbox always prevents storing. If AdyenTokenisationEnabled is intended as an unconditional "always tokenise" mode (no consent step), that should be documented explicitly as a merchant responsibility, and it should not be combined with a shopper-facing checkbox that implies a choice.

At minimum, please document that AdyenTokenisationEnabled and AdyenRecurringPaymentsEnabled must not be enabled together, since combining them produces a checkbox that does not reflect the actual data processing.

References (source files)

  • src/cartridges/app_adyen_SFRA/cartridge/templates/default/checkout/billing/adyenComponentForm.isml (line: var showStoreDetails = ${customer.authenticated && adyenRecurringPaymentsEnabled};)
  • src/cartridges/int_adyen_SFRA/cartridge/adyen/scripts/payments/adyenCheckout.js (createPaymentRequest, if (AdyenConfigs.getAdyenTokenisationEnabled()) { paymentRequest.storePaymentMethod = true; ... })
  • src/cartridges/app_adyen_SFRA/cartridge/client/default/js/adyen/checkout/paymentMethodsConfiguration/card/cardConfig.js (this.enableStoreDetails = window.showStoreDetails;)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions