# Recurring and Unscheduled Payments

## Overview

**Recurring Payments** allow you to charge a customer on a regular basis without requiring them to be present for each payment. This is particularly useful for subscription services or any scenario where you need to bill customers periodically.

**Unscheduled Payments** allow you to charge customers on an ad-hoc basis without the need for the customer to be present. This is ideal for businesses that need to process payments at irregular intervals or for one-off purchases.

Both Recurring and Unscheduled Payments require an initial payment to be processed with the customer present (CIT). This initial payment captures and securely stores the customer's payment details for future use. Subsequent payments can then be processed without the customer being present (MIT).

Processing Recurring and Unscheduled Payments using the embedded SDK involves a similar approach as described in this page.

## Requirements

Before you can process a Recurring or Unscheduled Payment, you must first set up an initial Payment Session with the customer present (CIT). This initial payment captures and securely stores the customer's payment details for future use. Subsequent payments can then be processed without the customer being present (MIT).

To set up such a Payment, you will need to follow the steps outlined in the [Initiate Recurring Payments](/documentation/get_started/initiate_payments/recurring) page or the [Initiate Unscheduled Payments](/documentation/get_started/initiate_payments/unscheduled) page, depending on your use case.

Once the Payment Session is created, you will have access to the `clientSecret` value, which is required to process the initial payment.

## Configuring the Embedded SDK

To process a Recurring or Unscheduled payment using the embedded SDK, you need to initialize the SDK with the appropriate configuration options. These include:

- The `clientSecret` obtained from the initial Payment Session
- Setting thecontroller's `paymentType` option to `Recurring` or `Unscheduled` depending on your use case
- The `accountId` option if you are processing the payment on behalf of a Sub-Account


Below is an example of how to configure the embedded SDK for processing Recurring or Unscheduled payments:

Recurring Payment

```javascript Process Recurring Payment Example
const controller = createController({
    publicKey,
    clientSecret,
    // accountId,  // If using the Platform Fee model
    paymentType: "Recurring"
});
```

Unscheduled Payment

```javascript Process Unscheduled Payment Example
const controller = createController({
    publicKey,
    clientSecret,
    // accountId,  // If using the Platform Fee model
    paymentType: "Unscheduled"
});
```

Provided that the embedded SDK is correctly configured, the embedded payment form will be rendered, allowing the customer to complete the initial payment.

## 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 Recurring and Unscheduled payments as described above



```html Complete Example for Recurring and Unscheduled 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 = "";
        // The unique identifier for the Sub-Account, if using the Platform Fee model
        const accountId = "";

        // 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
            paymentType: "Recurring"  // Or "Unscheduled"
        });

        // Create Card Form instance using the default card form configuration
        const cardForm = createCardForm(controller);

        // 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

You should now have a basic payment form set up and be able to process Recurring or Unscheduled payments using the embedded SDK via a simple card form.

You can now explore additional features and customisation options offered by the SDK. Some recommended next steps include:

- Collecting additional information from the customer during checkout, such as billing/shipping address or custom fields. See the [Field Collection](/documentation/get_started/process_payments/embedded_sdk_v3/field_collection) page for more details on how to do this.
- Adding support for other payment methods like [Apple Pay](/documentation/get_started/process_payments/embedded_sdk_v3/apple_pay) and [Google Pay](/documentation/get_started/process_payments/embedded_sdk_v3/google_pay) to provide a seamless checkout experience for your customers.
- Enabling additional configuration options to further customise the payment form and flow according to your business needs. You can refer to the [Additional Configuration](/documentation/get_started/process_payments/embedded_sdk_v3/additional_config) page for more details on the available options and how to use them.
- Using the `checkout` component to easily enable multiple payment methods and additional features all in a single component. You can refer to the [Checkout Component](https://web-sdk.ryftpay.com/docs/web/latest/documents/checkout.html) page for more details.
- Handling SDK errors: Implement comprehensive error handling to manage different types of errors that may occur during the payment process, such as validation errors, network issues, or unexpected system errors. This will help you provide a better user experience and ensure that customers are informed about any issues that arise during checkout. You can refer to the [SDK Errors](/documentation/get_started/process_payments/sdk_errors) page for best practices and examples on how to implement effective error handling in your integration.
- Webhook handling: Set up webhooks to receive real-time notifications about payment events, such as successful payments, refunds, or chargebacks. This allows you to automate order fulfillment, update inventory, and keep customers informed about their transactions. You can refer to the [Webhooks documentation](/documentation/get_started/webhooks) for more details on how to set up and manage webhooks with Ryft.