# Variable Recurring Payments

Source: https://docs.gocardless.com/docs/collect-payments/recurring-payments/variable-recurring-payments

# Variable Recurring Payments

## What are Variable Recurring Payments?

Collect real-time recurring payments of varying amounts from a single customer authorisation. The customer consents once via their banking app; you can then collect multiple payments within agreed parameters - no re-authorisations required for each charge.

## When to use Variable Recurring Payments

|                             | **Commercial VRP (cVRP)**                                                                                                                                                                  | **Sweeping VRP (sVRP)**                                                                |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| Transfer type               | Me-to-Business                                                                                                                                                                             | Me-to-me                                                                               |
| When to use                 | Usage-based billing where amounts vary each period (metered services, such as electricity, gas, water and telecoms providers) Local and central government Rail fares Charitable donations | Lending repayments Savings automation and round-ups Automated investment contributions |
| Who initiates               | Business                                                                                                                                                                                   | Customer's bank                                                                        |
| Payer account types support | Personal                                                                                                                                                                                   | Personal and business                                                                  |

> **Info:**
> Variable Recurring Payments feature is only available to approved integrators with the VRP
>   Sweeping upgrade or VRP Commercial upgrade to use either mechanism respectively. If you're
>   interested in trying it out - please [contact support](mailto:help@gocardless.com).

## Comparison between other recurring payment options

|                        | Subscriptions                                        | Instalments                                    | Variable Recurring Payments                                                          | Instant Bank Pay + Direct Debit                          |
| ---------------------- | ---------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------- |
| Mandate required       | Yes                                                  | Yes                                            | Yes (consent)                                                                        | Yes                                                      |
| Customer authorisation | Once                                                 | Once                                           | Once (with agreed limits and frequency)                                              | Once                                                     |
| Payment amounts        | Fixed (per subscription)                             | Fixed per instalment, can vary across schedule | Variable within agreed constraints                                                   | First payment fixed; subsequent payments flexible        |
| Payment schedule       | Merchant-defined recurring (weekly, monthly, yearly) | Merchant-defined schedule with a defined end   | Flexible - merchant triggers payments as needed                                      | Flexible after first payment                             |
| Confirmation speed     | 2-x business days                                    | 2-x business days                              | Seconds                                                                              | First payment: minutes; subsequent: 2-x business days    |
| Chargeback risk        | Yes                                                  | Yes                                            | None                                                                                 | First payment: none; subsequent: yes                     |
| Missed payment retries | Yes (with Success+)                                  | Yes (with Success+)                            | Yes                                                                                  | Subsequent payments: yes                                 |
| Best for               | SaaS, memberships, gym fees, insurance               | Payment plans, tuition, professional services  | Usage-based regulated billing and utilities, financial services, government services | Subscription services needing immediate, initial payment |
| Availability           | All schemes                                          | All schemes                                    | GBP only                                                                             | GBP and EUR                                              |

## How it works

1. The customer authorised a VRP consent via their banking app, setting agreed payment parameters (maximum amount, frequency)
2. You collect payments within those parameters via GoCardless API - no further customer action needed per payment
3. Payment is confirmed within seconds
4. You receive a webhook confirming the payment status
5. Funds arrive same day or next business day

## cVRP: Step-by-step guide

### Create a billing request

```http
curl -X POST https://api.gocardless.com/billing_requests \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d ']
        }
      }
    }
  }'
```

```php
<?php
require 'vendor/autoload.php';

$client = new \GoCardlessPro\Client([
    'access_token' => getenv('GC_ACCESS_TOKEN'),
    'environment' => \GoCardlessPro\Environment::SANDBOX
]);

$response = $client->billingRequests()->create([
    'params' => [
        'fallback_enabled'     => true,
        'purpose_code'         => 'loan',
        'payment_context_code' => 'billing_goods_and_services_in_arrears',
        'payment_purpose_code' => 'bank_loan_delayed_draw_funding',
        'mandate_request'      => [
            'scheme'      => 'faster_payments',
            'constraints' => [
                'start_date'             => '2024-11-01',
                'max_amount_per_payment' => 50000,
                'periodic_limits'        => [
                    [
                        'period'           => 'month',
                        'max_total_amount' => 100000,
                        'alignment'        => 'creation_date',
                    ],
                ],
            ],
        ],
    ],
]);
```

```python

client = gocardless_pro.Client(
    access_token=os.environ['GC_ACCESS_TOKEN'],
    environment='sandbox'
)

response = client.billing_requests.create(
    params=,
                ],
            },
        },
    }
)
```

```ruby
require 'gocardless_pro'

client = GoCardlessPro::Client.new(
    access_token: ENV['GC_ACCESS_TOKEN'],
    environment: :sandbox
)

response = client.billing_requests.create(
  params: 
        ]
      }
    }
  }
)
```

```java

GoCardlessClient client = GoCardlessClient
    .newBuilder(System.getenv("GC_ACCESS_TOKEN"))
    .withEnvironment(GoCardlessClient.Environment.SANDBOX)
    .build();

BillingRequestCreateResponse response = client.billingRequests()
    .create()
    .withFallbackEnabled(true)
    .withPurposeCode("loan")
    .withPaymentContextCode("billing_goods_and_services_in_arrears")
    .withPaymentPurposeCode("bank_loan_delayed_draw_funding")
    .withMandateRequestScheme("faster_payments")
    .withMandateRequestConstraintsStartDate("2024-11-01")
    .withMandateRequestConstraintsMaxAmountPerPayment(50000)
    .withMandateRequestConstraintsPeriodicLimitsBuilder()
        .withPeriod("month")
        .withMaxTotalAmount(100000)
        .withAlignment("creation_date")
    .done()
    .execute();
```

```javascript
const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");

const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);

const response = await client.billingRequests.create(,
      ],
    },
  },
});
```

```cs
using GoCardless;

var client = GoCardlessClient.Create(
    Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
    GoCardlessClient.Environment.SANDBOX
);

var response = await client.BillingRequests.CreateAsync(
    new BillingRequestCreateRequest
    
                }
            }
        }
    }
);
```

```go
package main

    "context"
    "fmt"
    "os"

    gocardless "github.com/gocardless/gocardless-pro-go/v6"
)

func main() 

    fallbackEnabled := true
    params := gocardless.BillingRequestCreateParams,
                },
            },
        },
    }

    result, err := client.BillingRequests.Create(context.TODO(), params)
    if err != nil 
    fmt.Println(result)
}
```

**Response**

```json
,
      "links": ,
      "constraints": 
        ]
      }
    },
    "payment_request": null,
    "subscription_request": null,
    "instalment_schedule_request": null,
    "metadata": ,
    "fallback_enabled": true,
    "fallback_occurred": false,
    "purpose_code": "loan",
    "payment_purpose_code": "bank_loan_delayed_draw_funding",
    "sign_flow_url": null,
    "auto_fulfil": false,
    "actions": [
      
        }
      },
      ,
      ,
      
      },
      
    ],
    "links": 
  }
}
```

### Confirm the mandate

After creating the Billing Request, direct the customer to authorise the consent. How you do this depends on your integration type:

- Hosted Pages - Create a Billing Request Flow and redirect the customer to the authorisation_url. See [Hosted Payment Pages](/integration-types/gocardless-hosted-pages).
- Custom API - Collect customer details and bank account via Billing Request actions, then confirm. See [Custom Payment Pages](/integration-types/custom-payment-pages).

### Handle the outcome

Listen for webhooks to confirm the mandate status:

| Event                     | Meaning                          |
| ------------------------- | -------------------------------- |
| billing_request_fulfilled | Customer authorised the mandate  |
| mandates_created          | Mandate created and ready to use |
| mandates_active           | Mandate active                   |

### Collect a variable payment

You can collect a payment with the customer not present (recurring billing etc.):

```http
curl -X POST https://api.gocardless.com/payments \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: UNIQUE_KEY" \
  -d '
    }
  }'
```

```php
$response = $client->payments()->create(
    [
        'params' => [
            'amount'               => 5000,
            'currency'             => 'GBP',
            'psu_interaction_type' => 'off_session',
            'charge_date'          => '2024-11-01',
            'description'          => 'November subscription',
            'links'                => [
                'mandate' => 'MD123',
            ],
        ],
    ],
    'UNIQUE_KEY'
);
```

```python
response = client.payments.create(
    params=,
    },
    headers=
)
```

```ruby
response = client.payments.create(
  params: 
  },
  headers: 
)
```

```java
Payment payment = client.payments()
    .create()
    .withAmount(5000)
    .withCurrency("GBP")
    .withPsuInteractionType("off_session")
    .withChargeDate("2024-11-01")
    .withDescription("November subscription")
    .withLinksMandate("MD123")
    .withIdempotencyKey("UNIQUE_KEY")
    .execute();

```

```javascript
const payment = await client.payments.create(
  ,
  },
  "UNIQUE_KEY",
);
```

```cs
var payment = await client.Payments.CreateAsync(
    new PaymentCreateRequest
    
    },
    new RequestSettings 
);
```

```go
params := gocardless.PaymentCreateParams,
}

payment, err := client.Payments.Create(context.TODO(), params, gocardless.WithIdempotencyKey("UNIQUE_KEY"))
```

Or when the customer is present for top-ups and one-off purchases:

```http
curl -X POST https://api.gocardless.com/payments \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '
    }
  }'
```

```php
$response = $client->payments()->create([
    'params' => [
        'amount'               => 5000,
        'currency'             => 'GBP',
        'psu_interaction_type' => 'in_session',
        'description'          => 'One-off top-up',
        'links'                => [
            'mandate' => 'MD123',
        ],
    ],
]);
```

```python
response = client.payments.create(
    params=,
    }
)
```

```ruby
response = client.payments.create(
  params: 
  }
)
```

```java
Payment payment = client.payments()
    .create()
    .withAmount(5000)
    .withCurrency("GBP")
    .withPsuInteractionType("in_session")
    .withDescription("One-off top-up")
    .withLinksMandate("MD123")
    .execute();
```

```javascript
const payment = await client.payments.create(,
});
```

```cs
var payment = await client.Payments.CreateAsync(
    new PaymentCreateRequest
    
    }
);
```

```go
params := gocardless.PaymentCreateParams,
}

payment, err := client.Payments.Create(context.TODO(), params)
```

To cancel a future-dated payment (off-session only): `POST /payments//actions/cancel`. In-session payments cannot be cancelled once submitted.

Listen for webhooks to confirm the variable payment status:

| Event              | Meaning                       |
| ------------------ | ----------------------------- |
| payments_confirmed | Payment confirmed and settled |
| payments_failed    | Payment failed                |

### Handle the consent being cancelled

Listen for webhooks to confirm the consent status:

| **Event**          | **Meaning**                                                      |
| ------------------ | ---------------------------------------------------------------- |
| mandates_cancelled | Consent cancelled (by you or the customer via their banking app) |

## sVRP: Step-by-step guide

### Create a Billing Request

```http
curl -X POST https://api.gocardless.com/billing_requests \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d ']
        }
      }
    }
  }'
```

```php
<?php
require 'vendor/autoload.php';

$client = new \GoCardlessPro\Client([
    'access_token' => getenv('GC_ACCESS_TOKEN'),
    'environment' => \GoCardlessPro\Environment::SANDBOX
]);

$response = $client->billingRequests()->create([
    'params' => [
        'mandate_request' => [
            'scheme'      => 'faster_payments',
            'sweeping'    => true,
            'constraints' => [
                'start_date'             => '2024-11-01',
                'end_date'               => '2025-11-01',
                'max_amount_per_payment' => 50000,
                'periodic_limits'        => [
                    [
                        'period'           => 'month',
                        'max_total_amount' => 100000,
                        'alignment'        => 'creation_date',
                    ],
                ],
            ],
        ],
    ],
```

```python

client = gocardless_pro.Client(
    access_token=os.environ['GC_ACCESS_TOKEN'],
    environment='sandbox'
)

response = client.billing_requests.create(
    params=,
                ],
            },
        }
    }
)
```

```ruby
require 'gocardless_pro'

client = GoCardlessPro::Client.new(
    access_token: ENV['GC_ACCESS_TOKEN'],
    environment: :sandbox
)

response = client.billing_requests.create(
  params: 
        ]
      }
    }
  }
)
```

```java

GoCardlessClient client = GoCardlessClient
    .newBuilder(System.getenv("GC_ACCESS_TOKEN"))
    .withEnvironment(GoCardlessClient.Environment.SANDBOX)
    .build();

BillingRequestCreateResponse response = client.billingRequests()
    .create()
    .withMandateRequestScheme("faster_payments")
    .withMandateRequestSweeping(true)
    .withMandateRequestConstraintsStartDate("2024-11-01")
    .withMandateRequestConstraintsEndDate("2025-11-01")
    .withMandateRequestConstraintsMaxAmountPerPayment(50000)
    .withMandateRequestConstraintsPeriodicLimitsBuilder()
        .withPeriod("month")
        .withMaxTotalAmount(100000)
        .withAlignment("creation_date")
    .done()
    .execute();
```

```javascript
const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");

const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);

const response = await client.billingRequests.create(,
      ],
    },
  },
});
```

```cs
using GoCardless;

var client = GoCardlessClient.Create(
    Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
    GoCardlessClient.Environment.SANDBOX
);

var response = await client.BillingRequests.CreateAsync(
    new BillingRequestCreateRequest
    
                }
            }
        }
    }
);
```

```go
package main

    "context"
    "fmt"
    "os"

    gocardless "github.com/gocardless/gocardless-pro-go/v6"
)

func main() 

    sweeping := true
    params := gocardless.BillingRequestCreateParams,
                },
            },
        },
    }

    result, err := client.BillingRequests.Create(context.TODO(), params)
    if err != nil 
    fmt.Println(result)
}
```

**Response:**

```json
,
    "links": ,
    "actions": [
      ,
      ,
      
    ]
  }
}
```

### Confirm the consent

After creating the Billing Request, direct the customer to authorise the consent. How you do this depends on your integration type:

- Hosted Pages - Create a Billing Request Flow and redirect the customer to the authorisation_url. See [Hosted Payment Pages](/integration-types/gocardless-hosted-pages).
- Custom API - Collect customer details and bank account via Billing Request actions, then confirm. See [Custom Payment Pages](/integration-types/custom-payment-pages).

### Handle the outcome

Listen for webhooks to confirm the mandate status:

| Event                     | Meaning                          |
| ------------------------- | -------------------------------- |
| billing_request_fulfilled | Customer authorised the mandate  |
| mandates_created          | Mandate created and ready to use |
| mandates_active           | Mandate active                   |

### Collect a variable payment

Retrieve the mandate ID from:

```http
curl https://api.gocardless.com/billing_requests/BRQ000123 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

```php
<?php
require 'vendor/autoload.php';

$client = new \GoCardlessPro\Client([
    'access_token' => getenv('GC_ACCESS_TOKEN'),
    'environment' => \GoCardlessPro\Environment::SANDBOX
]);

$response = $client->billingRequests()->get('BRQ000123');

$mandateId = $response->mandate_request->links->mandate;
```

```python

client = gocardless_pro.Client(
    access_token=os.environ['GC_ACCESS_TOKEN'],
    environment='sandbox'
)

response = client.billing_requests.get('BRQ000123')

mandate_id = response.mandate_request.links.mandate
```

```ruby
require 'gocardless_pro'

client = GoCardlessPro::Client.new(
    access_token: ENV['GC_ACCESS_TOKEN'],
    environment: :sandbox
)

response = client.billing_requests.get('BRQ000123')

mandate_id = response.mandate_request.links.mandate
```

```java

GoCardlessClient client = GoCardlessClient
    .newBuilder(System.getenv("GC_ACCESS_TOKEN"))
    .withEnvironment(GoCardlessClient.Environment.SANDBOX)
    .build();

BillingRequest billingRequest = client.billingRequests()
    .get("BRQ000123")
    .execute();

String mandateId = billingRequest.getMandateRequest().getLinks().getMandate();
```

```javascript
const gocardless = require("gocardless-nodejs");
const constants = require("gocardless-nodejs/constants");

const client = gocardless(process.env.GC_ACCESS_TOKEN, constants.Environments.Sandbox);

const billingRequest = await client.billingRequests.find("BRQ000123");

const mandateId = billingRequest.mandate_request.links.mandate;
```

```cs
using GoCardless;

var client = GoCardlessClient.Create(
    Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
    GoCardlessClient.Environment.SANDBOX
);

var billingRequest = await client.BillingRequests.GetAsync("BRQ000123");

var mandateId = billingRequest.MandateRequest.Links.Mandate;
```

```go
package main

    "context"
    "fmt"
    "os"

    gocardless "github.com/gocardless/gocardless-pro-go/v6"
)

func main() 

    billingRequest, err := client.BillingRequests.Get(context.TODO(), "BRQ000123")
    if err != nil 

    mandateId := billingRequest.MandateRequest.Links.Mandate
    fmt.Println(mandateId)
}
```

**Response:**

```json

    }
  }
}
```

And collect a variable payment from the consent within the agreed constraints:

```http
curl -X POST https://api.gocardless.com/payments \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '
    }
  }'
```

```php
$response = $client->payments()->create([
    'params' => [
        'amount'      => 10000,
        'currency'    => 'GBP',
        'description' => 'Monthly savings sweep',
        'links'       => [
            'mandate' => 'MD123',
        ],
    ],
]);

```

```python
response = client.payments.create(
    params=,
    }
)
```

```ruby
response = client.payments.create(
  params: 
  }
)
```

```java
Payment payment = client.payments()
    .create()
    .withAmount(10000)
    .withCurrency("GBP")
    .withDescription("Monthly savings sweep")
    .withLinksMandate("MD123")
    .execute();

```

```javascript
const payment = await client.payments.create(,
});
```

```cs
var payment = await client.Payments.CreateAsync(
    new PaymentCreateRequest
    
    }
);
```

```go
params := gocardless.PaymentCreateParams,
}

payment, err := client.Payments.Create(context.TODO(), params)
```

**Response:**

```json

  }
}
```

### Handle the consent being cancelled

Listen for webhooks to confirm the consent status:

| **Event**          | **Meaning**                                                      |
| ------------------ | ---------------------------------------------------------------- |
| mandates_cancelled | Consent cancelled (by you or the customer via their banking app) |

## What's next?

  
#### [Setting Up Mandates](/docs/collect-payments/setting-up-mandates)

Create mandates and collect bank account details via the API.

  
#### [GoCardless Hosted Pages](/docs/collect-payments/integration-types/gocardless-hosted-pages)

Use hosted pages for a no-code VRP integration.

  
#### [VRP Events](/docs/collect-payments/events-and-webhooks/billing-request-events)

Understand the webhook events fired during the VRP consent lifecycle.

  
#### [Scheme Guidance](/docs/collect-payments/scheme-guidance/billing-request-purpose-codes)

Purpose codes and scheme-specific requirements for VRP.