> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://ixopay.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://ixopay.ferndocs.com/_mcp/server.

# Recurring payments

Recurring payments, also known as subscription payments, are transactions that occur on a regular basis. These types of payments are common in industries such as streaming services, subscription boxes, and software as a service (SaaS) providers. With IXOPAY platform, you can easily set up recurring payments for your customers.

```mermaid
%%{ init: { "sequence": {"mirrorActors": false} } }%%
sequenceDiagram
  participant M as Merchant backend
  participant G as IXOPAY platform
  participant PSP
  M->>G: Create initial transaction with schedule
  activate M
  activate G
  G-->>+PSP: Perform payment
  PSP-->>-G: Payment complete
  G-->>M: Payment complete
  deactivate G
  M->>M: Handle the initial transaction response and store uuid
  deactivate M
  loop IXOPAY platform scheduler
  activate G
  G->>+M: Follow-up transactions: callback
  M-->>M: Update internal state
  M->>-G: Callback OK
  deactivate G
  end
```

> **Reference**
>
> For more details on the  scheduler, check out the in-depth article on the [scheduler](https://documentation.ixopay.com/docs/reference/features/scheduler "Scheduler reference article") in the reference.

To use recurring payments, follow these simple steps:

## Step 1: Creating the initial transaction

To start a recurring payment, you'll need to create an initial [Debit](https://documentation.ixopay.com/api/transaction/debit) transaction with the `withRegister` flag set to `true`. This flag allows you to register the payment instrument for future charges. The `transactionIndicator` field should be set to `INITIAL` to indicate that this is the first transaction in the [recurring payments series](https://documentation.ixopay.com/docs/reference/concepts/transactions/indicators#initial).

> **Transaction criteria**
>
> `INITIAL` and `RECURRING` indicators should only be used if the merchant intends to process payments at regular, consistent intervals, and always for the same amount.

Alternatively, a [Register](https://documentation.ixopay.com/api/transaction/register) transaction can be used. The register transaction will not debit funds, the subsequent follow-up transactions will debit funds from the customer. You can use this to set start the payment schedule at a later time.

You can also include a `schedule` object in the initial transaction to schedule follow-up transactions. The scheduler object should include the `amount`, `currency`, `periodLength`, `periodUnit`, and optionally `startDateTime` fields.

The response to this transaction will contain a unique identifier called `uuid`, which you will need for subsequent transactions.

Here's an example of how to create an initial [Debit](https://documentation.ixopay.com/api/transaction/debit) transaction with the `withRegister` flag set to true:

* curl
* Python
* PHP
* Java

```sh
curl --request POST -sL \
  --url "https://gateway.ixopay.com/api/v3/transaction/${API_KEY}/debit" \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --header "Authorization: Basic $(echo -n "$USERNAME:$PASSWORD" | base64)" \
  --data-raw '{
    "merchantTransactionId": "your-unique-identifier",
    "withRegister": true,
    "transactionIndicator": "INITIAL",
    "schedule": {
      "amount": "9.99",
      "currency": "EUR",
      "periodLength": 1,
      "periodUnit": "MONTH",
      "callbackUrl": "https://api.example.org/callback"
    },
    "description": "My subscription.",
    "amount": "9.99",
    "currency": "EUR",
    "successUrl": "https://shop.example.org/checkout/success",
    "cancelUrl": "https://shop.example.org/checkout/cancelled",
    "errorUrl": "https://shop.example.org/checkout/error",
    "callbackUrl": "https://api.example.org/callback",
  }'
```

```python
import requests
import json
import base64
import os

url = "https://gateway.ixopay.com/api/v3/transaction/{apiKey}/debit".format(
    apiKey=os.environ["API_KEY"]
)
auth = base64.b64encode("%s:%s" % (os.environ["USERNAME"], os.environ["PASSWORD"]))

payload = json.dumps(
    {
        "merchantTransactionId": "your-unique-identifier",
        "withRegister": true,
        "transactionIndicator": "INITIAL",
        "schedule": {
          "amount": "9.99",
          "currency": "EUR",
          "periodLength": 1,
          "periodUnit": "MONTH",
          "callbackUrl": "https://api.example.org/callback"
        },
        "description": "My subscription.",
        "amount": "9.99",
        "currency": "EUR",
        "successUrl": "https://shop.example.org/checkout/success",
        "cancelUrl": "https://shop.example.org/checkout/cancelled",
        "errorUrl": "https://shop.example.org/checkout/error",
        "callbackUrl": "https://api.example.org/callback",
    }
)
headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Basic {auth}".format(auth=auth),
}

response = requests.request("POST", url, headers=headers, data=payload)
```

```php
<?php

$auth = base64_encode("$USERNAME:$PASSWORD");

$curl = curl_init("https://gateway.ixopay.com/api/v3/transaction/$API_KEY/debit");
curl_setopt_array($curl, array(
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_POSTFIELDS =>'{
    "merchantTransactionId": "your-unique-identifier",
    "withRegister": true,
    "transactionIndicator": "INITIAL",
    "schedule": {
      "amount": "9.99",
      "currency": "EUR",
      "periodLength": 1,
      "periodUnit": "MONTH",
      "callbackUrl": "https://api.example.org/callback"
    },
    "description": "My subscription.",
    "amount": "9.99",
    "currency": "EUR",
    "successUrl": "https://shop.example.org/checkout/success",
    "cancelUrl": "https://shop.example.org/checkout/cancelled",
    "errorUrl": "https://shop.example.org/checkout/error",
    "callbackUrl": "https://api.example.org/callback",
  }',
  CURLOPT_HTTPHEADER => array(
    'Content-Type: application/json',
    'Accept: application/json',
    "Authorization: Basic $auth"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
```

```java
OkHttpClient client = new OkHttpClient().newBuilder().build();
RequestBody body = RequestBody.create(
  MediaType.parse("application/json"),
  "{" +
    "\"merchantTransactionId\": \"your-unique-identifier\"," +
    "\"withRegister\"": true," +
    "\"transactionIndicator\": \"INITIAL\"," +
    "\"schedule\": {" +
      "\"amount\": \"9.99\"," +
      "\"currency\": \"EUR\"," +
      "\"periodLength\": 1," +
      "\"periodUnit\": \"MONTH\"," +
      "\"callbackUrl\": \"https://api.example.org/callback\"" +
    "}," +
    "\"description\": \"My subscription.\"," +
    "\"amount\": \"9.99\"," +
    "\"currency\": \"EUR\"," +
    "\"successUrl\": \"https://shop.example.org/checkout/success\"," +
    "\"cancelUrl\": \"https://shop.example.org/checkout/cancelled\"," +
    "\"errorUrl\": \"https://shop.example.org/checkout/error\"," +
    "\"callbackUrl\": \"https://api.example.org/callback\"" +
  "}"
);
String auth = Base64.getEncoder().encodeToString(
  "%s:%s".format(System.getenv("USERNAME"), System.getenv("PASSWORD")));
Request request = new Request.Builder()
  .url("https://gateway.ixopay.com/api/v3/transaction/%s/debit"
    .format(System.getenv("API_KEY")))
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Basic %s".format(auth))
  .build();
Response response = client.newCall(request).execute();
```

In the above example, we set the `amount` and `currency` fields to the amount we want to charge the customer for each transaction. The `periodLength` and `periodUnit` fields are used to specify how often we want to charge the customer. In this case, we want to charge the customer every month, so we set the periodUnit to `MONTH`.

## Step 2: Handle the initial transaction response

When the initial transaction is created, you'll receive a response that includes a unique `uuid` for the transaction and `scheduleData.scheduleId` for the transaction. You can use these fields to identify the transaction and schedule in future API calls.

Depending on your integration methods, handle the result of the initial transaction:

* For hosted payment pages you need to [Redirect your customer](https://documentation.ixopay.com/docs/guides/getting-started/accept-payments/hosted-payment-pages#step-2-redirect-your-customer).
* For hosted fields, you have to include the [`transactionToken`](https://documentation.ixopay.com/docs/guides/getting-started/accept-payments/payment.js#step-2-perform-transaction) in the initial transaction, then the payment should be complete.

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "success": true,
  "uuid": "4d40738b1194869734f7",
  "purchaseId": "20260922-4d40738b1194869734f7",
  "returnType": "FINISHED",
  "paymentMethod": "Creditcard"
}
```

## Step 3: Follow-up transactions

IXOPAY platform will automatically schedule follow-up transactions according to the schedule specified in the `schedule` object. You will receive a notification via the callback URL specified in the initial transaction when a follow-up transaction is made, see [Callbacks](https://documentation.ixopay.com/docs/guides/getting-started/callbacks) on how to handle these notifications.

Depending on the result of the notifications you can manage your customers access to the subscription.

### Making follow-up transactions without the scheduler

If you do not want to use the IXOPAY platform scheduler, you can implement your own scheduler to make the follow-up transactions. You will need to remove the `schedule` and store the `uuid` of the initial transaction and schedule follow-up transactions using your own scheduler.

When the scheduled time for a follow-up transaction arrives, you will need to create a new [Debit](https://documentation.ixopay.com/api/transaction/debit) transaction with the `uuid` of the initial transaction specified in the `referenceUuid` field and the `transactionIndicator` field set to `RECURRING`. You can then process the transaction and repeat the process for subsequent follow-up transactions.

* curl
* Python
* PHP
* Java

```sh
curl --request POST -sL \
  --url "https://gateway.ixopay.com/api/v3/transaction/${API_KEY}/debit" \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --header "Authorization: Basic $(echo -n "$USERNAME:$PASSWORD" | base64)" \
  --data-raw '{
    "merchantTransactionId": "your-unique-identifier",
    "referenceUuid": "4d40738b1194869734f7",
    "transactionIndicator": "RECURRING",
    "description": "My subscription.",
    "amount": "9.99",
    "currency": "EUR",
    "successUrl": "https://shop.example.org/checkout/success",
    "cancelUrl": "https://shop.example.org/checkout/cancelled",
    "errorUrl": "https://shop.example.org/checkout/error",
    "callbackUrl": "https://api.example.org/callback",
  }'
```

```python
import requests
import json
import base64
import os

url = "https://gateway.ixopay.com/api/v3/transaction/{apiKey}/debit".format(
    apiKey=os.environ["API_KEY"]
)
auth = base64.b64encode("%s:%s" % (os.environ["USERNAME"], os.environ["PASSWORD"]))

payload = json.dumps(
    {
        "merchantTransactionId": "your-unique-identifier",
        "referenceUuid": "4d40738b1194869734f7",
        "transactionIndicator": "RECURRING",
        "description": "My subscription.",
        "amount": "9.99",
        "currency": "EUR",
        "successUrl": "https://shop.example.org/checkout/success",
        "cancelUrl": "https://shop.example.org/checkout/cancelled",
        "errorUrl": "https://shop.example.org/checkout/error",
        "callbackUrl": "https://api.example.org/callback",
    }
)
headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Basic {auth}".format(auth=auth),
}

response = requests.request("POST", url, headers=headers, data=payload)
```

```php
<?php

$auth = base64_encode("$USERNAME:$PASSWORD");

$curl = curl_init("https://gateway.ixopay.com/api/v3/transaction/$API_KEY/debit");
curl_setopt_array($curl, array(
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_POSTFIELDS =>'{
    "merchantTransactionId": "your-unique-identifier",
    "referenceUuid": "4d40738b1194869734f7",
    "transactionIndicator": "RECURRING",
    "description": "My subscription.",
    "amount": "9.99",
    "currency": "EUR",
    "successUrl": "https://shop.example.org/checkout/success",
    "cancelUrl": "https://shop.example.org/checkout/cancelled",
    "errorUrl": "https://shop.example.org/checkout/error",
    "callbackUrl": "https://api.example.org/callback",
  }',
  CURLOPT_HTTPHEADER => array(
    'Content-Type: application/json',
    'Accept: application/json',
    "Authorization: Basic $auth"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
```

```java
OkHttpClient client = new OkHttpClient().newBuilder().build();
RequestBody body = RequestBody.create(
  MediaType.parse("application/json"),
  "{" +
    "\"merchantTransactionId\": \"your-unique-identifier\"," +
    "\"referenceUuid\": \"4d40738b1194869734f7\"," +
    "\"transactionIndicator\": \"RECURRING\"," +
    "\"description\": \"My subscription.\"," +
    "\"amount\": \"9.99\"," +
    "\"currency\": \"EUR\"," +
    "\"successUrl\": \"https://shop.example.org/checkout/success\"," +
    "\"cancelUrl\": \"https://shop.example.org/checkout/cancelled\"," +
    "\"errorUrl\": \"https://shop.example.org/checkout/error\"," +
    "\"callbackUrl\": \"https://api.example.org/callback\"" +
  "}"
);
String auth = Base64.getEncoder().encodeToString(
  "%s:%s".format(System.getenv("USERNAME"), System.getenv("PASSWORD")));
Request request = new Request.Builder()
  .url("https://gateway.ixopay.com/api/v3/transaction/%s/debit"
    .format(System.getenv("API_KEY")))
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Basic %s".format(auth))
  .build();
Response response = client.newCall(request).execute();
```

By using your own scheduling logic, you have the most control over when and how often your customers are invoiced. You can implement your own Dunning process and handle failures gracefully.

## Conclusion

Recurring payments can be a powerful tool for merchants to increase customer retention and drive revenue. By using the `withRegister` flag on a [Debit](https://documentation.ixopay.com/api/transaction/debit) transaction or by using the [Register](https://documentation.ixopay.com/api/transaction/register) transaction, you can easily create recurring payments. Either use IXOPAY platform's scheduler or bring your own.

## Next steps

Now that you've integrated IXOPAY platform's recurring payments, you can look into …

* … handling changes to the payments status with [callbacks](https://documentation.ixopay.com/docs/guides/getting-started/callbacks).
* … [testing your setup](https://documentation.ixopay.com/docs/guides/getting-started/testing) to make sure you've set up everything correctly.
* … checking out the [scheduler reference](https://documentation.ixopay.com/docs/reference/features/scheduler) to understand the intricacies of scheduling recurring payments.