Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Payment Services stored methods in My Account

Shoppers who save a card with Payment Services during checkout can see and remove those vaulted methods on a My Account page. Removal uses a confirmation step before the token is deleted—see Remove confirmation. The Commerce boilerplate does not include a payment-methods block out of the box. This tutorial shows how to add one yourself (for example a commerce-payment-methods block) that renders the User Account PaymentMethods container. That container loads tokens with the same GraphQL customerPaymentTokens query used for vault data elsewhere. What was vaulted through Payment Services appears here when the shopper is signed in.

This page is Payment Services–specific: it assumes vaulting is enabled for your Payment Services integration and focuses on wiring the account experience with a small custom block.

Create a block. The naming is up to you, but commerce-payment-methods is a clear convention. This block should:

  1. Redirect guests to login (same pattern as other account blocks).
  2. Initialize the User Account drop-in via account.js.
  3. Render PaymentMethods with optional with header and minified view from the block configuration.

Example decorate implementation:

import { PaymentMethods } from '@dropins/storefront-account/containers/PaymentMethods.js';
import { render as accountRenderer } from '@dropins/storefront-account/render.js';
import { readBlockConfig } from '../../scripts/aem.js';
import {
CUSTOMER_LOGIN_PATH,
checkIsAuthenticated,
rootLink,
} from '../../scripts/commerce.js';
import '../../scripts/initializers/account.js';
export default async function decorate(block) {
const {
'minified-view': minifiedViewConfig = 'false',
'with-header': withHeaderConfig = 'true',
} = readBlockConfig(block);
if (!checkIsAuthenticated()) {
window.location.href = rootLink(CUSTOMER_LOGIN_PATH);
} else {
await accountRenderer.render(PaymentMethods, {
withHeader: withHeaderConfig === 'true',
minifiedView: minifiedViewConfig === 'true',
})(block);
}
}

Place the block on the account page (or desired section) where the list should appear. Use the same section metadata, block registration, and readBlockConfig patterns as other commerce blocks (for example, customer info or orders).

Expose authoring keys in decorate that map to readBlockConfig. For example:

  • with-header — Controls whether a section title and divider are shown for the payments area (full account pages often omit this).
  • minified-view — Enables a compact layout with condensed rows and a simplified empty state, ideal for dashboards alongside other widgets.

Add a _your-block-name.json (and optional README) next to the block source so authors get defaults and labels consistent with the rest of your project.

Limit the list to Payment Services tokens (optional)

Section titled “Limit the list to Payment Services tokens (optional)”

Vault tokens include a payment_method_code. Payment Services methods typically use codes that start with payment_services (for example, hosted-field variants). The PaymentMethods container accepts filterPaymentMethodCodes: an array of strings. A token is shown if its code equals or starts with any entry.

The example decorate above doesn’t include this prop. To display only Payment Services–vaulted methods (and hide saved methods from other gateways), add it to the render call. For example:

await accountRenderer.render(PaymentMethods, {
withHeader: withHeaderConfig === 'true',
minifiedView: minifiedViewConfig === 'true',
filterPaymentMethodCodes: ['payment_services'],
})(block);

Adjust the prefix list if your Admin or integration uses a different payment method code pattern.

  • By default, the container calls getCustomerPaymentTokens, which executes the customerPaymentTokens query and transforms the returned vault details (JSON) into rows for the PaymentCard component. The details include Payment Services fields like brand and last four digits.
  • If another part of the page already has token rows on the client, you can emit account/customerPaymentTokens on the event bus so the container shows that list without an extra getCustomerPaymentTokens round trip (see Events and TypeScript declaration). That is optional injection only; Remove still calls deletePaymentToken on the GraphQL API, not the bus. Payment Services + GraphQL alone is the usual path for My Account.