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.
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:
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.
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:
- Creating a Payment Session with the
customerDetails.idfield specified to associate the payment with a specific customer; - Retrieving the list of saved cards for the customer using the customerGetPaymentMethods and ensuring that the full response is available to be passed to the embedded SDK.
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:
// 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;
});Below is a complete example of how to show saved cards to your customers using the embedded SDK:
<!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.