# Showing Saved Cards

## Overview

The embedded SDK allows you to **show previously saved cards to your customers**, enabling them to select a saved card for payment during the checkout process. This feature enhances the user experience by providing a quick and convenient way for returning customers to complete their purchases.

## Storing Customer Payment Methods

Before you can show saved cards to your customers, you need to ensure that the customer's payment methods are being stored during the checkout process. This is controlled by the `allowStorage` configuration flag in the `customerPaymentMethods` object when initializing the cardForm component. Here's an example of how to configure the embedded SDK:


```javascript Enable Customer Payment Method Storage Example
const cardForm = createCardForm(controller, {
    customerPaymentMethods: {
        allowStorage: {
            enabled: true
        }
    }
});
```

By setting `allowStorage.enabled` to `true`, you enable the functionality for customers to save their card details for future use. When a customer completes a payment and opts to save their card, the card details will be securely stored and associated with the customer's profile in the Ryft system.

## Showing Customer Payment Methods

### Preliminary Steps

In order to show the saved cards to your customers during checkout, there are some preliminary steps that are needed to ensure that the saved cards are properly associated with the customer and available for display in the embedded SDK. These steps include:

1. Creating a Payment Session with the `customerDetails.id` field specified to associate the payment with a specific customer;
2. Retrieving the list of saved cards for the customer using the [customerGetPaymentMethods](/documentation/api/reference/openapi#operation/customerGetPaymentMethods) and ensuring that the full response is available to be passed to the embedded SDK.


### SDK Configuration

Once the preliminary steps are completed, you can configure the embedded SDK to show the saved cards to your customers. This can be done by passing the full response from the `customerGetPaymentMethods` endpoint to the `customerPaymentMethods.details` field in the card form component.

It is also recommended to set the `validationMode` to `"onBlur"` in order to optimize the user experience when selecting saved cards, as this will only trigger validation when the customer has finished interacting with the form fields, rather than on every change.

Additionally, you can listen for the `paymentMethodChange` event emitted by the card form component to enable the PAY button when a saved card is selected by the customer.

Here's an example of how to configure the embedded SDK to show saved cards:


```javascript Show Saved Cards SDK Configuration Example
// Initialise the cardForm component
const cardForm = createCardForm(controller, {
    customerPaymentMethods: {
        // `customerCardDetails` should be the full response from
        // the customerGetPaymentMethods endpoint
        details: customerCardDetails,
    },
    // Optimize validation to only trigger when the customer
    // has finished interacting with the form fields
    validationMode: "onBlur"
});

// Listen to validation changes to enable/disable the PAY button
// when the customer is interacting with the form fields
cardForm.on("validationChange", (event) => {
    payButton.disabled = !event.isValid;
});

// Listen to payment method changes to enable the PAY button
// when a saved card is selected
cardForm.on("paymentMethodChange", (event) => {
    payButton.disabled = false;
});
```

### Complete Example

Below is a complete example of how to show saved cards to your customers using the embedded SDK:


```html Show Saved Cards Example
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ryft Card Form</title>

    <!-- Only if using the script tag method to install the SDK -->
    <script src="https://web-sdk.ryftpay.com/embedded/latest/ryft.min.js"></script>

    <style>
        #pay-btn {
            background-color: #0070f3;
            color: white;
            border: none;
            padding: 14px 22px;
            /* slightly bigger */
            font-size: 16px;
            /* slightly larger text */
            border-radius: 4px;
            cursor: pointer;
            margin-top: 12px;
        }

        #pay-btn:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }

        .spinner {
            border: 4px solid rgba(0, 0, 0, 0.1);
            width: 36px;
            height: 36px;
            border-radius: 50%;
            border-left-color: #09f;
            animation: spin 1s ease infinite;
            display: none;
            /* Initially hidden */
            margin: 10px auto;
        }

        @keyframes spin {
            0% {
                transform: rotate(0deg);
            }

            100% {
                transform: rotate(360deg);
            }
        }
    </style>
</head>

<body>

    <div class="Ryft--paysection">
        <form id="ryft-pay-form" class="Ryft--payform">
            <div id="card-form-container"></div>

            <button id="pay-btn" disabled>PAY NOW</button>

            <div id="ryft-pay-error"></div>
            <div id="spinner-container" class="spinner"></div>
        </form>
    </div>

    <script type="module">
        import { createController, createCardForm } from '@ryftpay/web';  // If using npm to install the SDK
        // const { createController, createCardForm } = window.Ryft;  // If using the script tag method to install the SDK

        // Returned when creating a Payment Session
        const clientSecret = "";
        // Available in the Ryft Portal
        const publicKey = "";
        // The unique identifier for the Sub-Account, if using the Platform Fee model
        const accountId = "";
        // Example response from the customerGetPaymentMethods endpoint containing the customer's saved cards
        const customerCardDetails = {
            "items": [
                {
                    "id": "pmt_xxxxxxxxx",
                    "type": "Card",
                    "card": {
                        "scheme": "Visa",
                        "last4": "1000",
                        "expiryMonth": "10",
                        "expiryYear": "2026",
                    },
                    "customerId": "cus_xxxxxxxx",
                    "createdTimestamp": 1773069005,
                },
                {
                    "id": "pmt_xxxxxxxxx",
                    "type": "Card",
                    "card": {
                        "scheme": "Visa",
                        "last4": "1111",
                        "expiryMonth": "12",
                        "expiryYear": "2028",
                    },
                    "billingAddress": {
                        "firstName": "Card",
                        "lastName": "Name",
                        "lineOne": "1600 Amphitheatre Parkway",
                        "city": "Mountain View",
                        "country": "US",
                        "postalCode": "94043",
                        "region": "CA",
                    },
                    "customerId": "cus_xxxxxxxx",
                    "createdTimestamp": 1771322256,
                }
            ]
        }

        // Get a reference to the pay button
        const payButton = document.getElementById("pay-btn");
        // Get a reference to the error div
        const errorDiv = document.getElementById("ryft-pay-error");
        // Get a reference to the spinner container
        const spinner = document.getElementById("spinner-container");

        // Initialise Controller
        const controller = createController({
            publicKey,
            clientSecret,
            // accountId  // Uncomment if using the Platform Fee model
        });

        // Initialise the cardForm component
        const cardForm = createCardForm(controller, {
            customerPaymentMethods: {
                // `customerCardDetails` should be the full response from
                // the customerGetPaymentMethods endpoint
                details: customerCardDetails,
            },
            // Optimize validation to only trigger when the customer
            // has finished interacting with the form fields
            validationMode: "onBlur"
        });

        // Listen to validation changes to enable/disable the PAY button
        // when the customer is interacting with the form fields
        cardForm.on("validationChange", (event) => {
            payButton.disabled = !event.isValid;
        });

        // Listen to payment method changes to enable the PAY button
        // when a saved card is selected
        cardForm.on("paymentMethodChange", (event) => {
            payButton.disabled = false;
        });

        // Disable the PAY button when the payment is being processed to prevent multiple submissions
        payButton.disabled = true;

        // Handle the payment result after attempting payment
        function handlePaymentResult(result) {
            spinner.style.display = "none";
            const session = result.paymentSession;

            // Check the payment session status to determine the outcome of the payment
            // If the status is Approved or Captured, the payment was successful
            if (session.status === "Approved" || session.status === "Captured") {
                console.log("Payment Successful", session);
                errorDiv.innerHTML = "<p style='color: green;'>Payment Successful!</p>";
                return;
            }

            // If there is a lastError, display the error message to the user and log the error for debugging
            if (session.lastError) {
                payButton.disabled = false;
                const lastError = session.lastError;
                const message = result.userFacingErrorMessage;
                console.error("Payment Error", lastError);
                errorDiv.innerHTML = `<p style='color: red;'>${message}</p>`;
            }
        }

        // Add event listener to the PAY button to handle form submission and attempt payment
        payButton.addEventListener("click", async (e) => {
            e.preventDefault();

            errorDiv.innerHTML = "";
            payButton.disabled = true;
            spinner.style.display = "block";

            // Attempt the payment using the card form instance and handle the result
            try {
                const result = await cardForm.attemptPayment();
                handlePaymentResult(result);
            } catch (error) {
                spinner.style.display = "none";
                payButton.disabled = false;

                console.error("System Error:", error);
                errorDiv.innerHTML = "<p style='color: red;'>An unexpected error occurred.</p>";
            }
        });

        // Mount the card form to the specified container in the HTML
        cardForm.mount("#card-form-container");
    </script>

</body>

</html>
```

Provided that the embedded SDK is correctly configured, the saved cards associated with the customer will be displayed in the checkout page, allowing the customer to select a saved card for payment or enter new card details if they prefer.

Once the customer selects a saved card and submits the payment, the embedded SDK will handle the payment processing, and you can listen for the result to provide appropriate feedback to the customer based on whether the payment was successful or if there were any errors.