Skip to content
Tutorials

Enable Payment Services vaulted cards in checkout

time to complete
15 minutes

This tutorial shows how to let shoppers vault a card during checkout and pay with it during a later purchase. Reusing a saved card requires modifying your commerce-checkout block.

By the end of this tutorial, you’ll have updated the commerce-checkout block so that shoppers can check out using a card they’ve already saved.

Pay with vaulted card

Selecting a saved card during checkout

Pay with fresh card

Saving a new card during checkout

Before you begin, make sure you have:

The following steps describe how to update the commerce-checkout block so shoppers can pay with a vaulted card.

This step adds a StoredMethods slot to renderPaymentMethods, rendering each saved card as its own selectable option.

  1. Navigate to the blocks/commerce-checkout/containers.js file and import the VaultedCreditCard container from the Payment Services drop-in.

    import VaultedCreditCard from '/@dropins/storefront-payment-services/containers/VaultedCreditCard.js';
  2. In the same file, find the PaymentMethods container rendered in the renderPaymentMethods function.

    CheckoutProvider.render(PaymentMethods, {
    slots: {
    Methods: {
    // ...your existing entries
    },
    },
    })
  3. Add a StoredMethods slot as a sibling of Methods, which renders each saved card.

    CheckoutProvider.render(PaymentMethods, {
    slots: {
    Methods: {
    // ...your existing entries
    },
    StoredMethods: {
    [PaymentMethodCode.VAULT]: {
    tokenCode: PaymentMethodCode.CREDIT_CARD,
    render: (ctx) => {
    const $storedOption = document.createElement('div');
    PaymentServices.render(VaultedCreditCard, {
    tokenDetails: ctx.details,
    })($storedOption);
    ctx.replaceHTML($storedOption);
    },
    },
    },
    },
    })
  4. Remove the following entry from Methods, if you have one. StoredMethods already excludes registered codes from the live method list, making the explicit enabled: false now redundant.

    [PaymentMethodCode.VAULT]: {
    enabled: false,
    },

This step updates handlePlaceOrder so an order can actually be placed with a vaulted card.

  1. Navigate to the blocks/commerce-checkout/commerce-checkout.js file and find the handlePlaceOrder function.

    const handlePlaceOrder = async ({ cartId, code }) => {
    await displayOverlaySpinner(loaderRef, $loader, $loaderStatus);
    try {
    // Payment Services credit card
    if (code === paymentsApi.PaymentMethodCode.CREDIT_CARD) {
    const success = await trySubmitPaymentServicesCreditCard();
    if (!success) {
    return;
    }
    }
    await orderApi.placeOrder(cartId);
    } catch (error) {
    console.error(error);
    throw error;
    } finally {
    removeOverlaySpinner(loaderRef, $loader, $loaderStatus);
    }
    };
  2. Extend the if condition that guards the call to trySubmitPaymentServicesCreditCard() so it also matches vaulted cards.

    const handlePlaceOrder = async ({ cartId, code }) => {
    await displayOverlaySpinner(loaderRef, $loader, $loaderStatus);
    try {
    // Payment Services credit card
    if (code === paymentsApi.PaymentMethodCode.CREDIT_CARD
    || code === paymentsApi.PaymentMethodCode.VAULT) {
    const success = await trySubmitPaymentServicesCreditCard();
    if (!success) {
    return;
    }
    }
    await orderApi.placeOrder(cartId);
    } catch (error) {
    console.error(error);
    throw error;
    } finally {
    removeOverlaySpinner(loaderRef, $loader, $loaderStatus);
    }
    };

The boilerplate’s default checkout layout locates the “Bill to shipping” checkbox above the payment options and the billing address form below them. Since a stored card’s billing address is fixed to the card and already rendered inline with it, the checkbox and the form need to be hidden while one is selected. Also, the checkbox needs to move below the payment options, so shoppers are guaranteed to see it on their way to “Place order” if they switch to a method with customizable billing.

To make these changes, adapt your blocks/commerce-checkout block as follows:

  1. Navigate to the blocks/commerce-checkout/commerce-checkout.js file and locate the handleCheckoutValues function.

    function handleCheckoutValues(payload) {
    const { isBillToShipping } = payload;
    $billingForm.style.display = isBillToShipping ? 'none' : 'block';
    }
  2. Replace it with the following code to dynamically hide the billing checkbox and form while a stored card is selected.

    function handleCheckoutValues(payload) {
    const { isBillToShipping, selectedPaymentMethod } = payload;
    const isStoredPaymentMethodSelected = !!selectedPaymentMethod?.additionalData?.publicHash;
    $billToShipping.style.display = isStoredPaymentMethodSelected ? 'none' : 'block';
    $billingForm.style.display = (isBillToShipping || isStoredPaymentMethodSelected) ? 'none' : 'block';
    }
  3. In blocks/commerce-checkout/fragments.js, find the createCheckoutFragment function and ensure both checkout__bill-to-shipping and checkout__billing-form render after checkout__payment-methods, adjusting your layout if needed. In the default layout, only the checkbox needs to move.

    <div class="checkout__main">
    <!-- ...other sections -->
    <div class="checkout__payment-methods ${CHECKOUT_BLOCK}"></div>
    <div class="checkout__bill-to-shipping ${CHECKOUT_BLOCK}"></div>
    <div class="checkout__billing-form ${CHECKOUT_BLOCK}"></div>
    <div class="checkout__terms-and-conditions ${CHECKOUT_BLOCK}"></div>
    <div class="checkout__place-order ${CHECKOUT_BLOCK}"></div>
    </div>
  4. Finally, add the following style rule to blocks/commerce-checkout/commerce-checkout.css to hide the horizontal divisor when the shopper has stored cards.

    /* No billing section divisor needed when payment methods shown inside separate box */
    .checkout__payment-methods:has(.checkout-payment-methods__stored-methods) {
    padding-bottom: 0;
    border-bottom: none;
    }

See blocks/commerce-checkout in the payment-services branch of the boilerplate repository for a reference implementation of the vaulted cards integration. This branch also includes the PayPal, Apple Pay, and Google Pay integration covered in a separate tutorial.