# Subscriptions

## Overview

**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.

## Requirements

Before proceeding, you must ensure that you have completed the Subscription initial setup as described in the [Initial Setup](/documentation/get_started/initiate_payments/subscriptions) 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](https://api-reference.ryftpay.com/#tag/Subscriptions/operation/subscriptionGetById) endpoint.
- If you have created a **Subscription using the Ryft Portal**, you can retrieve the Subscription details using the [subscriptionGetById](/documentation/api/reference/openapi#operation/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](/documentation/get_started/initiate_payments/subscriptions) of the Initiate Payments section.

## Configuring the Embedded SDK

To **process** a payment for a Subscription, you will need to configure the embedded SDK accordingly by providing the following parameters:

1. the `clientSecret` option with the `paymentSessions.initial.clientSecret` value returned from the API
2. the controller's `paymentType` option set to `Recurring` to indicate that the payment being processed is for a Subscription
3. 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 `cardFormConfig` object. You can find all the necessary details in the Subscription object returned from the API. More info on how to configure the `RyftSubscriptionConfig` object can be found on [this](https://web-sdk.ryftpay.com/docs/web/latest/interfaces/Web_SDK.RyftSubscriptionConfig.html) page.


Below is an example of how to configure the embedded SDK for processing a Subscription payment using the card form component:


```javascript Process Subscription Payment Example
// 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.

## Handling Failed Payments

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](/documentation/get_started/webhooks/subscriptions) 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](/documentation/get_started/process_payments/embedded_sdk_v3/additional_config#handling-required-actions) page for more information on how to handle this scenario.

## Complete Example

The following is a complete working example that combines:

- all the features covered in the [Quick Start](/documentation/get_started/process_payments/embedded_sdk_v3/quick_start#complete-example) guide
- the configuration for processing a Subscription payment



```html Complete Example for Processing Subscription Payments
<!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>
```

## Next Steps

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](/documentation/get_started/manage_payments/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](/documentation/get_started/webhooks/subscriptions) to stay informed about important events related to Subscriptions, such as payment failures, cancellations, or updates.