Direct Debit: Taking Instalment Payments
Using instalment schedules
Finally, let’s try collecting payments using an instalment schedule. Instalment schedules collect a finite set of recurring payments from a customer.
Let’s say we have an invoice for £100. We want to collect this in 3 instalments, with a cadence of 1 instalment every 2 weeks:
1<?php
2require "vendor/autoload.php";
3
4$client = new \GoCardlessPro\Client([
5 "access_token" => $currentUser->gocardlessAccessToken,
6 "environment" => \GoCardlessPro\Environment::SANDBOX
7]);
8
9$instalment_schedule = $client->instalmentSchedules()->createWithSchedule([
10 "params" => [
11 "name" => "ACME Invoice 103",
12 "total_amount" => 10000, // 100 GBP in pence, collected from the customer
13 "app_fee" => 10, // Your 10 pence fee, applied to each instalment,
14 // to be paid out to you
15 "currency" => "GBP",
16 "schedule" => [
17 "start_date" => "2019-08-20",
18 "interval_unit" => "weekly",
19 "interval" => "2",
20 "amounts" => [
21 3400,
22 3400,
23 3200
24 ]
25 ],
26 "links" => [
27 "mandate" => "MD0000XH9A3T4C"
28 // Mandate ID from the last section
29 ],
30 "metadata" => []
31 ],
32 "headers" => [
33 "Idempotency-Key" => "random_instalment_schedule_specific_string"
34 ]
35]);
36
37// Keep hold of this instalment schedule ID - we"ll use it in a minute
38// It should look a bit like "IS00006FWHUIA"
39print("ID: " . $instalment_schedule->id);
GoCardless will take responsibility for calculating the estimated charge dates of these payments. The charge date scheduling semantics are as follows:
We will create the first payment as close to the specified start date as possible.
The subsequent payment will be as close to two weeks after the specified start date as possible.
The important clarification is that we will always schedule from the originally specified start date, not the realised charge date of the first payment.
We will then debit £34, £34, and £32 from the end customer’s bank account in the 6 weeks following the specified charge date.
Charged on | 2019-08-20 | 2019-09-03 | 2019-09-17 |
Amounts charged | £34.00 | £34.00 | £32.00 |
Scheduling Instalments
For your convenience, we also provide you with the ability to schedule the payments yourself if you have a specific or irregular cadence with which you’d like to collect instalments:
1<?php
2require "vendor/autoload.php";
3
4$client = new \GoCardlessPro\Client([
5 "access_token" => $currentUser->gocardlessAccessToken,
6 "environment" => \GoCardlessPro\Environment::SANDBOX
7]);
8
9$instalment_schedule = $client->instalmentSchedules()->createWithDates([
10 "params" => [
11 "name" => "ACME Invoice 103",
12 "total_amount" => 10000, // 100 GBP in pence, collected from the customer
13 "app_fee" => 10, // Your 10 pence fee, applied to each instalment,
14 // to be paid out to you
15 "currency" => 'GBP',
16 "instalments" => [
17 [
18 "charge_date" => '2019-08-20',
19 "amount" => 3400
20 ],
21 [
22 "charge_date" => '2019-09-03',
23 "amount" => 3400
24 ],
25 [
26 "charge_date" => '2019-09-17',
27 "amount" => 3200
28 ],
29 ],
30 "links" => [
31 "mandate" => 'MD0000XH9A3T4C'
32 // Mandate ID from the last section
33 ],
34 "metadata" => []
35 ],
36 "headers" => [
37 'Idempotency-Key' => 'random_instalment_schedule_specific_string'
38 ]
39]);
40
41// Keep hold of this instalment schedule ID - we'll use it in a minute
42// It should look a bit like "IS00006FWHUIA"
43print("ID: " . $instalment_schedule->id);
We will try to create the payments as close to the charge dates you specify as possible.
The process of setting up all the payments for an instalment schedule is quite expensive. For this reason, the instalment schedule starts off in the “pending” state and remains in this state while we create all the instalments for it. If you want to inspect the created instalment schedule yourself, we can do this using the instalment schedule ID which is returned when making the request to create the schedule:
1GET https://api.gocardless.com/instalment_schedules/IS00006FWHUIA
2
3HTTP/1.1 200 (OK)
4{
5 "instalment_schedules": {
6 "id": "IS123",
7 "name": "Bike Invoice 271",
8 "currency": "GBP",
9 "status": "pending",
10 "amount": "10000",
11 "metadata": {},
12 "links": {
13 "mandate": "MD0000XH9A3T4C",
14 "payments": []
15 }
16 }
17}
This should return the instalment schedule object we just created.
Once all the related payments for an instalment schedule have been created, it will transition into the “active” state. We will send you a webhook to notify you of this change. If you do not wish to rely on webhooks, we’d recommend polling the GET endpoint to check when an instalment schedule becomes active. Once all the instalments for a schedule have been created, their IDs will be included under the payments key in the “links” attribute of the response object.
Listing and cancelling a schedule
Let’s imagine that we no longer want to collect the payment in instalments, and we need to cancel the instalment schedule. This is accomplished as follows:
1<?php
2require 'vendor/autoload.php';
3
4$client = new \GoCardlessPro\Client([
5 'access_token' => $currentUser->gocardlessAccessToken,
6 'environment' => \GoCardlessPro\Environment::SANDBOX
7]);
8
9$instalment_schedule = $client->instalment_schedules()->get("IS00006FWHUIA");
10 // Instalment Schedule ID from above.
11
12print("Status: " . $instalment_schedule->status . "<br />");
13print("Cancelling...<br />");
14
15$instalment_schedule = $client->instalment_schedules()->cancel("IS00006FWHUIA");
16
17print("Status: " . $instalment_schedule->status . "<br />");
Cancelling an instalment schedule will cancel all remaining payments (i.e. payments that are not submitted, collected or failed) in the schedule. If you wish to collect the leftover amount for a given invoice, you can do so by creating a single payment to collect the remainder, or by splitting the remaining amount into a new instalment schedule. Instalment schedules cannot be cancelled if they are already cancelled, or completed. You will receive a webhook notifying you of when the instalment schedule and its remaining payments have been cancelled.
Advice on calculating your instalment amounts
You may have noticed that whilst GoCardless takes responsibility for figuring out the precise dates upon which payments will be collected, we are not prescriptive on how the original invoice amount is split between instalments. How much you charge your customers is, after all, your decision entirely!
We will use the top-level amount parameter as a checksum, to make sure all the individual instalment amounts add up to the right total. With that in mind, it’s crucial for you as the integrator to ensure you’ve worked out the right split between your instalment amounts. Below, we provide a sample algorithm (in pseudocode) to help you accomplish this:
1// @param total - the total in GBP/USD etc.
2// @param number_of_instalments - the number of instalments you'd like to split the
3// invoice into
4
5function calculateInstalmentValue(total, number_of_instalments)
6{
7 // work out the value of each instalment, rounded up to the nearest whole unit
8 rounded_units = ceiling(total/number_of_instalments)
9
10 if (rounded_units * (number_of_instalments - 1) >= total) {
11 return floor(total / number_of_instalments);
12 } else {
13 return rounded_units;
14 }
15}
16
17// @param total - the invoice total in GBP/USD etc.
18// @param number_of_instalments - the number of instalments you'd like to split the
19// invoice into
20// @param normal_instalment_amount - the amount in pence/cents etc. for all payments
21// except the final 'balancing' payment
22
23function getInstalmentAmounts(total, number_of_instalments, normal_instalment_amount)
24{
25 number_of_normal_instalments = floor(total / normal_instalment_amount);
26 minimum = 200 // some "reasonable minimum" instalment amount
27
28 amounts = [];
29
30 for (i = 1; i < number_of_normal_instalments; i++) {
31 amounts.push(normal_instalment_amount);
32 }
33
34 remainder = total % normalInstalmentAmount;
35
36 if (remainder < minimum) {
37 amounts[amounts.length - 1] += remainder;
38 } else {
39 amounts.push(remainder);
40 }
41
42 return amounts;
43}
44
45normal_instalment_amount = calculateInstalmentValue(total, number_of_instalments)
46instalment_amounts = getInstalmentAmounts(
47 total,
48 number_of_instalments,
49 normal_instalment_amount
50 )
Instalment schedule modes of failure
There are two distinct “failure” states an instalment schedule can fall into. The first is creation_failed
, which indicates we could not create the instalment schedule and its payments. You would be notified of this in the response to the POST create request.
The second is slightly more nuanced and is called errored
. This indicates that one or more of the instalments is in a failed state. We will serve you a webhook should this occur, but you will need to reconcile the failed payment(s) yourself.
More Guides
Taking instalment payments against a mandate