Now we’ve set up our first customer with a mandate, we’re ready to start taking payments. There are three ways to set up your payments - which one is right for you will depend on what you’re using GoCardless for:

  • One-off payments: Trigger a payment against a mandate at any time with the API. Great if you want to charge your customers ad-hoc amounts.
  • Subscriptions: Set up an automatic recurring payment. Great if you want to take the same payment on a regular basis (for instance £5 per week, or £20 on the first of each month).
  • Instalment Schedules: Set up a fixed number of payments, billed at a cadence of your choosing. The key distinction of an Instalment Schedule from a Subscription is that it is a finite set of payments. This is ideal for when you have a payment to collect, but it’s a little too big for a customer to afford in one fell swoop - you can break it up into regular, recurring instalments using an Instalment Schedule.

You can even use these together - once you have a mandate, you have complete flexibility to charge your customers however and whenever you want.

Let’s try each of them individually. We’d recommend spending a few minutes on each to get to understand the full power of the API.

Using one-off payments

Let’s start by collecting a payment of £10 from the customer we just created:

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

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

$payment = $client->payments()->create([
  "params" => [
      "amount" => 1000, // 10 GBP in pence
      "currency" => "GBP",
      "links" => [
          "mandate" => "MD0000XH9A3T4C"
                       // The mandate ID from last section
      ],
      // Almost all resources in the API let you store custom metadata,
      // which you can retrieve later
      "metadata" => [
          "invoice_number" => "001"
      ]
  ],
  "headers" => [
      "Idempotency-Key" => "random_payment_specific_string"
  ]
]);

// Keep hold of this payment ID - we'll use it in a minute
// It should look like "PM000260X9VKF4"
print("ID: " . $payment->id);
import os
import gocardless_pro

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

payment = client.payments.create(
    params={
        "amount" : 1000, # 10 GBP in pence
        "currency" : "GBP",
        "links" : {
            "mandate": "MD0000XH9A3T4C"
                     # The mandate ID from the last section
        },
        # Almost all resources in the API let you store custom metadata,
        # which you can retrieve later
        "metadata": {
          "invoice_number": "001"
        }
    }, headers={
        'Idempotency-Key' : 'random_key',
})

# Keep hold of this payment ID - we will use it in a minute
# It should look like "PM000260X9VKF4"
print("ID: {}".format(payment.id))
require 'gocardless_pro'

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

payment = client.payments.create(
  params: {
    amount: 1000, # 10 GBP in pence
    currency: 'GBP',
    links: {
      mandate: 'MD0000XH9A3T4C'
             # The mandate ID from last section
    },
    # Almost all resources in the API let you store custom metadata,
    # which you can retrieve later
    metadata: {
      invoice_number: '001'
    }
  },
  headers: {
    'Idempotency-Key' => 'random_payment_specific_string'
  }
)

# Keep hold of this payment ID - we will use it in a minute
# It should look like "PM000260X9VKF4"
puts "ID: #{payment.id}"
package com.gcintegration;

import com.gocardless.GoCardlessClient;
import com.gocardless.resources.Payment;
import com.gocardless.services.PaymentService.PaymentCreateRequest.Currency;

public class SinglePayment {
    public static void main(String[] args) {
        GoCardlessClient client = GoCardlessClient
            .newBuilder(System.getenv("GC_ACCESS_TOKEN"))
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        Payment payment = client.payments().create()
            .withAmount(1000) // 10 GBP in pence
            .withCurrency(Currency.GBP)
            .withLinksMandate("MD0000XH9A3T4C")
            .withMetadata("invoice_number", "001")
            .withIdempotencyKey("random_payment_specific_string")
            .execute();
        // Keep hold of this payment ID - we'll use it in a minute
        // It should look like "PM000260X9VKF4"
        System.out.println(payment.getId());
    }
}
const payment = await client.payments.create(
  {
    amount: 1000,
    currency: "GBP",
    links: {
      mandate: "MD0000XH9A3T4C"
    },
    metadata: {
      invoice_number: "001"
    }
  },
  "random_payment_specific_string"
);

// Keep hold of this payment ID - we'll use it in a minute
// It should look like "PM000260X9VKF4"
console.log(payment.id);
var createResponse = await client.Payments.CreateAsync(new PaymentCreateRequest()
{
    Amount = 1000,
    Currency = PaymentCreateRequest.PaymentCurrency.GBP,
    Links = new PaymentCreateRequest.PaymentLinks()
    {
        Mandate = "MD0000XH9A3T4C",
    },
    Metadata = new Dictionary<string, string>()
    {
        {"invoice_number", "001"}
    },
    IdempotencyKey = "random_payment_specific_string"
});

Payment payment = createResponse.Payment;

// Keep hold of this payment ID - we'll use it in a minute
// It should look like "PM000260X9VKF4"
Console.WriteLine(payment.Id);

You’ll need to use the currency appropriate for the mandate you set up. This depends on what Direct Debit scheme you are using - the scheme of your mandate set up through the redirect flow will depend on your location (though you can set it manually by specifying a scheme):

Location currency
UK GBP
Sweden SEK
Denmark DKK
Australia AUD
New Zealand NZD
Canada CAD
United States of America USD
Everywhere else EUR
You’ll notice here that we provide an Idempotency-Key header. If we provide a unique string specific to this payment (for example its ID in our own database), the API will ensure this payment is only ever created once.

This means that if an API request times out or something goes wrong on your end, you won’t ever accidentally bill a customer twice - see our blog post for more details. You can use idempotency keys whenever you create something with the API.

In the live environment, £10 would be collected from the customer’s bank account and then paid out to you. Since we’re in the sandbox, this won’t happen, but the payment will progress with realistic timings as though it was really being processed.

If you head to your dashboard, you’ll be able to see the payment you’ve just created.

If you're creating payments in bulk (or making many requests in a short period for any other reason), you'll need to make sure you don't exceed our rate limit and handle any 429 Too Many Requests errors appropriately. You can make up to 1000 requests per minute.

Much like listing customers with list() as we did earlier, we can fetch an individual resource from the API at any time using its ID.

Once you have the ID, you can also perform useful functions on the payment, like cancelling it or retrying it if it has failed.

Let’s try grabbing our payment, and then cancelling it, taking a look at its status before and after:

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

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

$payment = $client->payments()->get("PM000260X9VKF4");
                              // Payment ID from above

print("Status: " . $payment->status . "<br />");
print("Cancelling...<br />");

$payment = $client->payments()->cancel("PM000260X9VKF4");
print("Status: " . $payment->status);
import os
import gocardless_pro

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

payment = client.payments.get("PM000260X9VKF4")
                         # Payment ID from above.

print("Amount: {}".format(payment.amount))
print("Cancelling...")

payment = client.payments.cancel("PM000260X9VKF4")
print("Status: {}".format(payment.status))
require 'gocardless_pro'

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

payment = client.payments.get('PM000269VJAR6M')
                         # Payment ID from above.

puts "Status: #{payment.status}"
puts 'Cancelling...'

payment = client.payments.cancel('PM000269VJAR6M')
puts "Status: #{payment.status}"
package com.gcintegration;

import com.gocardless.GoCardlessClient;
import com.gocardless.resources.Payment;

public class CancelPayment {
    public static void main(String[] args) {
        GoCardlessClient client = GoCardlessClient
            .newBuilder(System.getenv("GC_ACCESS_TOKEN"))
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        Payment payment = client.payments().get("PM000260X9VKF4").execute();
                                              // Payment ID from above.
        System.out.printf("Amount: %s%n", payment.getAmount());
        System.out.println("Cancelling...");

        payment = client.payments().cancel("PM000260X9VKF4").execute();
        System.out.printf("Status: %s%n", payment.getStatus());
    }
}
// Payment ID from above
const paymentId = "PM000260X9VKF4";

const payment = await client.payments.find(paymentId);

console.log(`Amount: ${payment.amount}`);
console.log("Cancelling...");

const cancelPayment = await client.payments.cancel(paymentId);
console.log(`Status: ${cancelPayment.status}`);
// Payment ID from above
String paymentId = "PM000260X9VKF4";

var paymentResponse = await client.Payments.GetAsync(paymentId);
Payment payment = paymentResponse.Payment;

Console.WriteLine($"Amount: {payment.Amount}");
Console.WriteLine("Cancelling...");

var cancelResponse = await client.Payments.CancelAsync(paymentId);
payment = cancelResponse.Payment;
Console.WriteLine($"Status: {payment.Status}");

Using subscriptions

Now, let’s create a subscription. Subscriptions collect a fixed, regular amount from a customer.

Let’s try collecting £15 per month on the 5th of each month from our customer:

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

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

$subscription = $client->subscriptions()->create([
  "params" => [
      "amount" => 1500, // 15 GBP in pence
      "currency" => "GBP",
      "interval_unit" => "monthly",
      "day_of_month" => "5",
      "links" => [
          "mandate" => "MD0000XH9A3T4C"
                       // Mandate ID from the last section
      ],
      "metadata" => [
          "subscription_number" => "ABC1234"
      ]
  ],
  "headers" => [
      "Idempotency-Key" => "random_subscription_specific_string"
  ]
]);

// Keep hold of this subscription ID - we'll use it in a minute.
// It should look a bit like "SB00003GKMHFFY"
print("ID: " . $subscription->id);
import os
import gocardless_pro

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

subscription = client.subscriptions.create(
    params={
        "amount" : 1500, # 15 GBP in pence    
        "currency" : "GBP",
        "interval_unit" : "monthly",
        "day_of_month" : "5",
        "links": {
            "mandate": "MD0000XH9A3T4C"
                     # Mandate ID from the last section
        },
        "metadata": {
            "subscription_number": "ABC1234"
        }
    }, headers={
        'Idempotency-Key': "random_subscription_specific_string"
})

# Keep hold of this subscription ID - we'll use it in a minute
# It should look a bit like "SB00003GKMHFFY"
print("ID: {}".format(subscription.id))
require 'gocardless_pro'

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

subscription = client.subscriptions.create(
  params: {
    amount: 1500, # 15 GBP in pence
    currency: 'GBP',
    interval_unit: 'monthly',
    day_of_month: '5',
    links: {
      mandate: 'MD0000XH9A3T4C'
              # Mandate ID from the last section
    },
    metadata: {
      subscription_number: 'ABC1234'
    }
  },
  headers: {
    'Idempotency-Key' => 'random_subscription_specific_string'
  }
)

# Keep hold of this subscription ID - we'll use it in a minute
# It should look a bit like "SB00003GKMHFFY"
puts "ID: #{subscription.id}"
package com.gcintegration;

import com.gocardless.GoCardlessClient;
import com.gocardless.resources.Subscription;
import com.gocardless.services.SubscriptionService.SubscriptionCreateRequest.IntervalUnit;

public class CreateSubscription {
    public static void main(String[] args) {
        GoCardlessClient client = GoCardlessClient
            .newBuilder(System.getenv("GC_ACCESS_TOKEN"))
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        Subscription subscription = client.subscriptions().create()
            .withAmount(1500) // 15 GBP in Pence
            .withCurrency("GBP")
            .withIntervalUnit(IntervalUnit.MONTHLY)
            .withDayOfMonth(5)
            .withLinksMandate("MD0000YTKZKY4J")
                             // Mandate ID from the last section
            .withMetadata("subscription_number", "ABC123")
            .withIdempotencyKey("random_subscription_specific_string")
            .execute();

        // Keep hold of this subscription ID - we'll use it in a minute.
        // It should look a bit like "SB00003GKMHFFY"
        System.out.printf("ID: %s%n", subscription.getId());
    }
}
const subscription = await client.subscriptions.create(
  {
    amount: 1500,
    currency: "GBP",
    name: "Monthly subscription",
    interval: 1,
    interval_unit: "monthly",
    day_of_month: 5,
    links: {
      mandate: "MD0123"
    }
  },
  "unique_subscription_specific_string"
);

console.log(subscription.id);
var createResponse = await client.Subscriptions.CreateAsync(new SubscriptionCreateRequest
{
    Amount = 1500,
    Currency = "GBP",
    Name = "Monthly subscription",
    Interval = 1,
    IntervalUnit = SubscriptionCreateRequest.SubscriptionIntervalUnit.Monthly,
    DayOfMonth = 5,
    Links = new GoCardless.Services.SubscriptionCreateRequest.SubscriptionLinks
    {
        Mandate = "MD0123"
    },
    IdempotencyKey = "unique_subscription_specific_string"
});

var subscription = createResponse.Subscription;

Console.WriteLine(subscription.Id);

You may need to change the currency depending on your location.

Each month, on the 5th of the month, the subscription will generate a new payment for £15 to be collected from the customer.

With the subscription ID, we can grab the subscription from the API. Let’s then try cancelling it so that no further payments are taken:

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

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

$subscription = $client->subscriptions()->get("SB00003GKMHFFY");
                                        // Subscription ID from above.

print("Status: " . $subscription->status . "<br />");
print("Cancelling...<br />");

$subscription = $client->subscriptions()->cancel("SB00003GKMHFFY");

print("Status: " . $subscription->status . "<br />");
import os
import gocardless_pro

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

subscription = client.subscriptions.get("SB00003GKMHFFY")
                                   # Subscription ID from above.

print("Status: {}".format(subscription.status))
print("Cancelling...")

subscription = client.subscriptions.cancel("SB00003GKMHFFY")

print("Status: {}".format(subscription.status))
require 'gocardless_pro'

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

subscription = client.subscriptions.get('SB00003GKMHFFY')
                                   # Subscription ID from above.

puts "Status: #{subscription.status}"
puts 'Cancelling...'

subscription = client.subscriptions.cancel('SB00003GKMHFFY')
puts "Status: #{subscription.status}"
package com.gcintegration;

import com.gocardless.GoCardlessClient;
import com.gocardless.resources.Subscription;

public class CancelSubscription {
    public static void main(String[] args) {
        GoCardlessClient client = GoCardlessClient
            .newBuilder(System.getenv("GC_ACCESS_TOKEN"))
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        Subscription subscription = client.subscriptions().get("SB00003GKMHFFY").execute();

        System.out.printf("Amount: %s%n", subscription.getStatus());
        System.out.println("Cancelling...");

        subscription = client.subscriptions().cancel("SB00003GKMHFFY").execute();

        System.out.printf("Status: %s%n", subscription.getStatus());
    }
}
// Subscription ID from above
const subscriptionId = "SB00003GKMHFFY";

const subscription = await client.subscriptions.find(subscriptionId);

console.log(`Amount: ${subscription.amount}`);
console.log("Cancelling...");

const cancelSubscription = await client.subscriptions.cancel(subscriptionId);
console.log(`Status: ${cancelSubscription.status}`);
// Subscription ID from above
String subscriptionId = "SB00003GKMHFFY";

var subscriptionResponse = await client.Subscriptions.GetAsync(subscriptionId);
Subscription subscription = subscriptionResponse.Subscription;

Console.WriteLine($"Amount: {subscription.Amount}");
Console.WriteLine("Cancelling...");

var cancelResponse = await client.Subscriptions.CancelAsync(subscriptionId);
subscription = cancelResponse.Subscription;
Console.WriteLine($"Status: {subscription.Status}");

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:

<?php
require "vendor/autoload.php";

$client = new \GoCardlessPro\Client([
    "access_token" => $currentUser->gocardlessAccessToken,
    "environment" => \GoCardlessPro\Environment::SANDBOX
]);

$instalment_schedule = $client->instalment_schedules()->create([
    "params" => [
        "name" => "ACME Invoice 103",
        "amount" => 10000, // 100 GBP in pence, collected from the customer
        "app_fee" => 10, // Your 10 pence fee, applied to each instalment,
                         // to be paid out to you
        "currency" => "GBP",
        "schedule" => [
            "start_date" => "2019-08-20",
            "interval_unit" => "weekly",
            "interval" => "2",
            "amounts" => [
                3400,
                3400,
                3200
            ]
        ],
        "links" => [
            "mandate" => "MD0000XH9A3T4C"
                         // Mandate ID from the last section
        ],
        "metadata" => []
    ],
    "headers" => [
        "Idempotency-Key" => "random_instalment_schedule_specific_string"
    ]
]);

// Keep hold of this instalment schedule ID - we"ll use it in a minute
// It should look a bit like "IS00006FWHUIA"
print("ID: " . $instalment_schedule->id);
import os
import gocardless_pro

client = gocardless_pro.Client(
    access_token=current_user.gocardless_access_token,
    environment="sandbox"
)

instalment_schedule = client.instalment_schedules.create(
    params={
        "name": "ACME Invoice 103",
        "amount": 10000, # 100 GBP in pence, collected from the customer
        "app_fee": 10, # Your 10 pence fee, applied to each instalment,
                       # to be paid out to you
        "currency": "GBP",
        "schedule": {
            "start_date": "2019-08-20",
            "interval_unit": "weekly",
            "interval": "2",
            "amounts": [
                3400,
                3400,
                3200
            ]
        }
        "links": {
            "mandate": "MD0000XH9A3T4C"
                       # Mandate ID from the last section
        },
        "metadata": {}
    }, headers={
        "Idempotency-Key": "random_instalment_schedule_specific_string"
    }
)

# Keep hold of this instalment schedule ID - we"ll use it in a minute
# It should look a bit like "IS00006FWHUIA"
print("ID: {}".format(instalment_schedule.id))
require "gocardless_pro"

client = GoCardlessPro::Client.new(
  access_token: current_user.gocardless_access_token,
  environment: :sandbox,
)

instalment_schedule = client.instalment_schedules.create(
  params: {
    name: "ACME Invoice 103",
    amount: 10_000, # 100 GBP in pence, collected from the customer
    app_fee: 10, # Your 10 pence fee, applied to each instalment,
                 # to be paid out to you
    currency: "GBP",
    schedule: {
      start_date: "2019-08-20",
      interval_unit: "weekly",
      interval: "2",
      amounts: [
        3_400,
        3_400,
        3_200,
      ],
    },
    links: {
      mandate: "MD0000XH9A3T4C",
               # Mandate ID from the last section
    },
    metadata: {},
  },
  headers: {
    "Idempotency-Key": "random_instalment_schedule_specific_string",
  },
)

# Keep hold of this instalment schedule ID - we'll use it in a minute
# It should look a bit like "IS00006FWHUIA"
puts "ID: #{instalment_schedule.id}"
package com.gcintegration;

import com.gocardless.GoCardlessClient;
import com.gocardless.resources.InstalmentSchedule;
import com.gocardless.services.InstalmentScheduleService.InstalmentScheduleCreateRequest.IntervalUnit;

public class CreateInstalmentSchedule {
    public static void main(String[] args) {
        GoCardlessClient client = GoCardlessClient
            .newBuilder(CurrentUser.gocardlessAccessToken)
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        int[] amountsArray = {3400, 3400, 3200};

        InstalmentSchedule instalmentSchedule = client.instalmentSchedules().create()
            .withAmount(10000) // 100 GBP in Pence, collected from the customer.
            .withAppFee(10)  // Your 10 pence fee, applied to each instalment, to be
                             // paid out to you
            .withCurrency("GBP")
            .withIntervalUnit(IntervalUnit.WEEKLY)
            .withInterval(2)
            .withAmounts(amountsArray)
            .withLinksMandate("MD0000YTKZKY4J")
                              // Mandate ID from the last section
            .withMetadata("invoiceId", "001")
            .withIdempotencyKey("random_instalment_schedule_specific_string")
            .execute();

        // Keep hold of this instalment schedule ID - we'll use it in a minute.
        // It should look a bit like "IS00006FWHUIA"
        System.out.printf("ID: %s%n", instalmentSchedule.getId());
    }
}
const amountsArray = [3400, 3400, 3200];

const instalmentSchedule = await client.instalmentSchedules.createWithSchedule(
  {
    // 100 GBP in pence, collected from the customer
    amount: 10000,
    // Your 10 pence fee, applied to each instalment, to be paid out to you
    app_fee: 10,
    currency: "GBP",
    name: "ACME Invoice 103",
    schedule: {
      start_date: "2019-08-20",
      interval_unit: "weekly",
      interval: 2,
      amounts: amountsArray
    },
    metadata: {
      "Invoice ID": "001"
    },
    link: {
      // Mandate ID from the last section
      mandate: "MD0000XH9A3T4C"
    }
  },
  "random_instalment_schedule_specific_string"
);

// Keep hold of this instalment schedule ID - we'll use it in a minute
// It should look a bit like "IS00006FWHUIA"
console.log(instalmentSchedule.id);
int[] amountsArray = new int[]{ 3400, 3400, 3200 };

var createResponse = await client.InstalmentSchedules.CreateAsync(
    new InstalmentScheduleCreateRequest
    {
        Amount = 10000, // 100 GBP in pence, collected from the customer
        AppFee = 10, // Your 10 pence fee, applied to each instalment,
                     // to be paid out to you
        Currency = "GBP",
        Name = "ACME Invoice 103",
        Schedule = new GoCardless
            .Services
            .InstalmentScheduleCreateRequest
            .InstalmentSchedule
            {
                StartDate = "2019-08-20",
                IntervalUnit = "weekly",
                Interval = 2,
                Amounts = amountsArray
            },
        Metadata = new Dictionary<string, string>
        {
            {"Invoice ID", "001"}
        },
        Links = new GoCardless
            .Services
            .InstalmentScheduleCreateRequest
            .InstalmentScheduleLinks
            {
                Mandate = "MD0000XH9A3T4C" // Mandate ID from the last section
            },
        IdempotencyKey = "random_instalment_schedule_specific_string"
    }
);

var instalmentSchedule = createResponse.InstalmentSchedule;

// Keep hold of this instalment schedule ID - we'll use it in a minute
// It should look a bit like "IS00006FWHUIA"
Console.WriteLine(instalmentSchedule.Id);

You may need to change the currency depending on your location.

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

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:

<?php
require "vendor/autoload.php";

$client = new \GoCardlessPro\Client([
    "access_token" => $currentUser->gocardlessAccessToken,
    "environment" => \GoCardlessPro\Environment::SANDBOX
]);

$instalment_schedule = $client->instalment_schedules()->create([
    "params" => [
        "name" => "ACME Invoice 103",
        "amount" => 10000, // 100 GBP in pence, collected from the customer
        "app_fee" => 10, // Your 10 pence fee, applied to each instalment,
                         // to be paid out to you
        "currency" => 'GBP',
        "instalments" => [
            [
                "charge_date" => '2019-08-20',
                "amount" => 3400
            ],
            [
                "charge_date" => '2019-09-03',
                "amount" => 3400
            ],
            [
                "charge_date" => '2019-09-17',
                "amount" => 3200
            ],
          ],
          "links" => [
              "mandate" => 'MD0000XH9A3T4C'
                           // Mandate ID from the last section
          ],
          "metadata" => []
      ],
      "headers" => [
          'Idempotency-Key' => 'random_instalment_schedule_specific_string'
    ]
]);

// Keep hold of this instalment schedule ID - we'll use it in a minute
// It should look a bit like "IS00006FWHUIA"
print("ID: " . $instalment_schedule->id);
import os
import gocardless_pro

client = gocardless_pro.Client(
    access_token=current_user.gocardless_access_token,
    environment="sandbox"
)

instalment_schedule = client.instalment_schedules.create(
    params={
        "name": "ACME Invoice 103",
        "amount": 10000, # 100 GBP in pence, collected from the customer
        "app_fee": 10, # Your 10 pence fee, applied to each instalment,
                       # to be paid out to you
        "currency": 'GBP',
        "instalments": [
            {
                "charge_date": '2019-08-20',
                "amount": 3400
            },
            {
                "charge_date": '2019-09-03',
                "amount": 3400
            },
            {
                "charge_date": '2019-09-17',
                "amount": 3200
            },
        ],
        "links": {
            "mandate": 'MD0000XH9A3T4C'
                       # Mandate ID from the last section
        },
        "metadata": {}
    }, headers={
        'Idempotency-Key': 'random_instalment_schedule_specific_string'
    }
)

# Keep hold of this instalment schedule ID - we'll use it in a minute
# It should look a bit like "IS00006FWHUIA"
print("ID: {}".format(instalment_schedule.id))
require "gocardless_pro"

client = GoCardlessPro::Client.new(
  access_token: current_user.gocardless_access_token,
  environment: :sandbox,
)

instalment_schedule = client.instalment_schedules.create(
  params: {
    name: "ACME Invoice 103",
    amount: 10_000, # 100 GBP in pence, collected from the customer
    app_fee: 10, # Your 10 pence fee, applied to each instalment,
                 # to be paid out to you
    currency: "GBP",
    instalments: [
      {
        charge_date: "2019-08-20",
        amount: 3_400,
      },
      {
        charge_date: "2019-09-03",
        amount: 3_400,
      },
      {
        charge_date: "2019-09-17",
        amount: 3_200,
      },
    ],
    links: {
      mandate: "MD0000XH9A3T4C",
              # Mandate ID from the last section
    },
    metadata: {},
  },
  headers: {
    "Idempotency-Key": "random_instalment_schedule_specific_string",
  },
)

# Keep hold of this instalment schedule ID - we"ll use it in a minute
# It should look a bit like "IS00006FWHUIA"
puts "ID: #{instalment_schedule.id}"
package com.gcintegration;

import com.gocardless.GoCardlessClient;
import com.gocardless.resources.InstalmentSchedule;

public class CreateInstalmentSchedule {
    public static void main(String[] args) {
        GoCardlessClient client = GoCardlessClient
            .newBuilder(CurrentUser.gocardlessAccessToken)
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        int[] amountsArray = {3400, 3400, 3200};
        String[] datesArray = {"2019-08-20", "2019-09-03", "2019-09-17"};

        InstalmentSchedule instalmentSchedule = client.instalmentSchedules().create()
            .withAmount(10000) // 100 GBP in Pence, collected from the end customer.
            .withAppFee(10)  // Your 10 pence fee, applied to each instalment, to be
                             // paid out to you
            .withCurrency("GBP")
            .withAmounts(amountsArray)
            .withDates(datesArray)
            .withLinksMandate("MD0000YTKZKY4J")
                              // Mandate ID from the last section
            .withMetadata("invoiceId", "001")
            .withIdempotencyKey("random_instalment_schedule_specific_string")
            .execute();

        // Keep hold of this instalment schedule ID - we'll use it in a minute.
        // It should look a bit like "IS00006FWHUIA"
        System.out.printf("ID: %s%n", instalmentSchedule.getId());
    }
}
const amountsArray = [3400, 3400, 3200];
const datesArray = ["2019-08-20", "2019-09-03", "2019-09-17"];

const instalmentSchedule = await client.instalmentSchedules.createWithDates(
  {
    amount: 10000, // 100 GBP in pence, collected from the customer
    app_fee: 10, // Your 10 pence fee, applied to each instalment,
    // to be paid out to you
    currency: "GBP",
    name: "ACME Invoice 103",
    schedule: {
      charge_dates: datesArray,
      amounts: amountsArray
    },
    links: {
      mandate: "MD0000XH9A3T4C" // Mandate ID from the last section
    },
    Metadata: {
      "Invoice ID": "001"
    }
  },
  "random_instalment_schedule_specific_string"
);

// Keep hold of this instalment schedule ID - we'll use it in a minute.
// It should look a bit like "IS00006FWHUIA"
console.log(instalmentSchedule.id);
int[] amountsArray = new int[]{ 3400, 3400, 3200 };
String[] datesArray = new String[]{ "2019-08-20", "2019-09-03", "2019-09-17" };

var createResponse = await client.InstalmentSchedules.CreateAsync(
    new InstalmentScheduleCreateRequest
    {
        Amount = 10000, // 100 GBP in pence, collected from the customer
        AppFee = 10, // Your 10 pence fee, applied to each instalment,
                     // to be paid out to you
        Currency = "GBP",
        Name = "ACME Invoice 103",
        Schedule = new GoCardless
            .Services
            .InstalmentScheduleCreateRequest
            .InstalmentSchedule
            {
                ChargeDates = datesArray,
                Amounts = amountsArray
            },
        Links = new GoCardless
            .Services
            .InstalmentScheduleCreateRequest
            .InstalmentScheduleLinks
            {
                Mandate = "MD0000XH9A3T4C" // Mandate ID from the last section
            },
        Metadata = new Dictionary<string, string>
        {
            {"Invoice ID", "001"}
        },
        IdempotencyKey = "random_instalment_schedule_specific_string"
    }
);

var instalmentSchedule = createResponse.InstalmentSchedule;

// Keep hold of this instalment schedule ID - we'll use it in a minute.
// It should look a bit like "IS00006FWHUIA"
Console.WriteLine(instalmentSchedule.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 make the request to create the schedule:

GET https://api.gocardless.com/instalment_schedules/IS00006FWHUIA

HTTP/1.1 200 (OK)
{
  "instalment_schedules": {
    "id": "IS123",
    "name": "Bike Invoice 271",
    "currency": "GBP",
    "status": "pending",
    "amount": "10000",
    "metadata": {},
    "links": {
      "mandate": "MD0000XH9A3T4C",
      "payments": []
    }
  }
}

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.

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:

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

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

$instalment_schedule = $client->instalment_schedules()->get("IS00006FWHUIA");
                                        // Instalment Schedule ID from above.

print("Status: " . $instalment_schedule->status . "<br />");
print("Cancelling...<br />");

$instalment_schedule = $client->instalment_schedules()->cancel("IS00006FWHUIA");

print("Status: " . $instalment_schedule->status . "<br />");
import os
import gocardless_pro

client = gocardless_pro.Client(
    access_token=current_user.gocardless_access_token,
    environment='sandbox'
)

instalment_schedule = client.instalment_schedules.get("IS00006FWHUIA")
                                   # Instalment Schedule ID from above.

print("Status: {}".format(instalment_schedule.status))
print("Cancelling...")

instalment_schedule = client.instalment_schedules.cancel("IS00006FWHUIA")

print("Status: {}".format(instalment_schedule.status))
require "gocardless_pro"

client = GoCardlessPro::Client.new(
  access_token: current_user.gocardless_access_token,
  environment: :sandbox,
)

instalment_schedule = client.instalment_schedules.get("IS00006FWHUIA")
                                          # Instalment Schedule ID from above

puts "Status: #{instalment_schedule.status}"
puts "Cancelling..."

instalment_schedule = client.instalment_schedules.cancel("IS00006FWHUIA")
puts "Status: #{instalment_schedule.status}"
package com.gcintegration;

import com.gocardless.GoCardlessClient;
import com.gocardless.resources.InstalmentSchedule;

public class CancelInstalmentSchedule {
    public static void main(String[] args) {
        GoCardlessClient client = GoCardlessClient
            .newBuilder(CurrentUser.gocardlessAccessToken)
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        InstalmentSchedule instalmentSchedule = client.instalmentSchedules()
            .get("IS00006FWHUIA") // InstalmentSchedule ID from above.
            .execute();

        System.out.printf("Status: %s%n", instalmentSchedule.getStatus());
        System.out.println("Cancelling...");

        instalmentSchedule = client.instalmentSchedules()
            .cancel("IS00006FWHUIA")
            .execute();

        System.out.printf("Status: %s%n", instalmentSchedule.getStatus());
    }
}
// InstalmentSchedule ID from above
const instalmentScheduleId = "IS00006FWHUIA";

const instalmentSchedule = await client.instalmentSchedules.find(
  instalmentScheduleId
);

console.log(`Status: ${instalmentSchedule.status}`);
console.log("Cancelling...");

const cancelledInstalmentSchedule = await client.instalmentSchedules.cancel(
  instalmentScheduleId
);
console.log(`Status: ${cancelledInstalmentSchedule.status}`);
// InstalmentSchedule ID from above
const string instalmentScheduleId = "IS00006FWHUIA";

var instalmentScheduleResponse = await client.InstalmentSchedules.GetAsync(instalmentScheduleId);
var instalmentSchedule = instalmentScheduleResponse.InstalmentSchedule;

Console.WriteLine($"Status: {instalmentSchedule.Status}");
Console.WriteLine("Cancelling...");

var cancelResponse = await client.InstalmentSchedules.CancelAsync(instalmentScheduleId);
instalmentSchedule = cancelResponse.InstalmentSchedule;
Console.WriteLine($"Status: {instalmentSchedule.Status}");

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 the 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:

// @param total - the total in GBP/USD etc.
// @param number_of_instalments - the number of instalments you'd like to split the
//                                invoice into

function calculateInstalmentValue(total, number_of_instalments)
{
    // work out the value of each instalment, rounded up to the nearest whole unit
    rounded_units = ceiling(total/number_of_instalments)

    if (rounded_units * (number_of_instalments - 1) >= total) {
        return floor(total / number_of_instalments);
    } else {
        return rounded_units;
    }
}

// @param total - the invoice total in GBP/USD etc.
// @param number_of_instalments - the number of instalments you'd like to split the
//                                invoice into
// @param normal_instalment_amount - the amount in pence/cents etc. for all payments
//                                   except the final 'balancing' payment

function getInstalmentAmounts(total, number_of_instalments, normal_instalment_amount)
{
    number_of_normal_instalments = floor(total / normal_instalment_amount);
    minimum = 200 // some "reasonable minimum" instalment amount

    amounts = [];

    for (i = 1; i < number_of_normal_instalments; i++) {
        amounts.push(normal_instalment_amount);
    }

    remainder = total % normalInstalmentAmount;

    if (remainder < minimum) {
        amounts[amounts.length - 1] += remainder;
    } else {
        amounts.push(remainder);
    }

    return amounts;
}

normal_instalment_amount = calculateInstalmentValue(total, number_of_instalments)
instalment_amounts = getInstalmentAmounts(
                      total,
                      number_of_instalments,
                      normal_instalment_amount
                    )

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.

NB: If a single payment in an instalment schedule has failed, and the instalment schedule is cancelled, the failed payment will be unaffected by the cancellation of the instalment schedule, and can therefore still be retried.