Subscriptions allows merchants to set up recurring payments for their customers, enabling a seamless and automated billing process.
This page will guide you throgh the steps to process Subscriptions using Ryft.
Before proceeding, you must ensure that you have completed the Subscription initial setup as described in the Initial Setup page.
Once the initial setup is complete, you will need the subscription details that you will use to process the payment:
- If you have created a Subscription using the API, you should have already available the response payload to use in the next steps. If not, you can retrieve the Subscription details using the subscriptionGetById endpoint.
- If you have created a Subscription using the Ryft Portal, you can retrieve the Subscription details using the subscriptionGetById endpoint. The Subscription ID can be found in the Portal under the Subscriptions section.
You can view an example of a Subscription object in the Subscription page of the Initiate Payments section.
To process a payment for a Subscription, you will need to configure the embedded SDK accordingly by providing the following parameters:
- the
clientSecretoption with thepaymentSessions.initial.clientSecretvalue returned from the API - the controller's
paymentTypeoption set toRecurringto indicate that the payment being processed is for a Subscription - if you want to show your customer the details of the Subscription (which is highly recommended, unless you have a custom UI to display the Subscription details outside of the embedded SDK payment form), you can provide the subscription details in the
cardFormConfigobject. You can find all the necessary details in the Subscription object returned from the API. More info on how to configure theRyftSubscriptionConfigobject can be found on this page.
Below is an example of how to configure the embedded SDK for processing a Subscription payment using the card form component:
// Instantiate the `controller` object
const controller = createController({
publicKey,
clientSecret,
paymentType: "Recurring" // Required for Subscription payments
});
// Instantiate the `cardForm` object
const cardForm = createCardForm(controller, {
// You can find all the necessary details in the Subscription object
// returned from the API.
subscription: {
description: "Weekly Subscription",
price: {
amount: 1000,
currency: "GBP",
interval: {
unit: "Days",
count: 7,
times: 52
}
},
billingDetail: {
billingCycleTimestamp: 1772755200
}
}
});Provided that the embedded SDK is correctly configured, the embedded payment form will be rendered, allowing the customer to complete the payment for the Subscription.
Once the customer submits the payment form, the embedded SDK will handle the payment processing and start the recurring billing cycle as defined in the Subscription details.
Some payments made throughout the Subscription lifecycle may fail. This can happen for various reasons, such as insufficient funds, expired payment methods, or network issues.
In order to handle these failures, you should:
- Ensure you listen to the relevant webhooks to be notified when a payment fails.
- Bring the failed payment to the customer's attention, and request them to update their payment method or take any necessary actions to resolve the issue.
Please note: Ryft will automatically retry failed payments in which the decline code is considered as retryiable. Typically you can just wait until all the retries are exhausted before reaching out to the customer. If a retry in the current cycle succeeds, no further action is required and the subscription will transition back to an Active status.
However, in case of hard declines (e.g., insufficient funds), you should contact the customer immediately to update their payment method. In such cases, you should use the clientSecret and requiredAction values from the latest paymentSessions object in the Subscription to re-initiate the payment process. See the Required Actions page for more information on how to handle this scenario.
The following is a complete working example that combines:
- all the features covered in the Quick Start guide
- the configuration for processing a Subscription payment
<!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 = "";
// 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,
paymentType: "Recurring" // Required for Subscription payments
});
// Create Card Form instance with Subscription configuration
const cardForm = createCardForm(controller, {
subscription: {
description: "Weekly Subscription",
price: {
amount: 1000,
currency: "GBP",
interval: {
unit: "Days",
count: 7,
times: 52
}
},
billingDetail: {
billingCycleTimestamp: 1773187200
}
}
});
// Listen to validation changes to enable/disable the PAY button
cardForm.on("validationChange", (event) => {
payButton.disabled = !event.isValid;
});
// 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>After successfully processing a payment for a Subscription, you can manage and monitor the Subscription through the Ryft Portal or by using the Ryft API. You can view details such as payment history, upcoming billing dates, and customer information.
There may be situations where you need to update the Subscription details, such as changing the billing amount, updating the payment method, pausing or cancelling a Subscription. Please refer to the Manage Subscriptions section of the Ryft API documentation for more information on how to make these updates.
Additionally, as mentioned earlier, make sure to register for the relevant webhooks to stay informed about important events related to Subscriptions, such as payment failures, cancellations, or updates.