Once you’ve set up Direct Debit mandates with your users’ end customers, you can start collecting payments from them using either automatically-recurring subscriptions or one-off payments.

With the mandate created, you’re ready to go - there’s no need to wait for it to become active, as GoCardless will automatically submit payments at the right time to charge the end customer as soon as possible.

Two ways to collect payments

There are two ways to set up payments - which one is right for you will depend on the kinds of payments your users want to take, and how you want to manage the process through your product:

Subscriptions
Set up an automatic recurring payment. This works great for users that want to take the same payment on a regular basis (for instance £5 per week, or £20 on the first of each month).

You can add an app_fee on top of GoCardless's fees or receive a share of GoCardless's collected fees.

When you setup an app fee, your fee will be taken from every payment created for that subscription.

One-off payments
Trigger a payment against a mandate at any time with the API. This allows your user to charge their end customers ad-hoc amounts.

You can add an app_fee on top of GoCardless's fees or receive a share of GoCardless's collected fees.

You can use one-off payments to build your own subscriptions logic, for example if you want to add additional amounts based on usage to a fixed regular payment.

Your integration may either apply app fees or receive a revenue share from GoCardless's fees. No app can have both app fees and a revenue share. For more information or to set up a revenue share, contact the partnerships team.

Collecting one-off payments

Let’s start by collecting a one-off payment of £10 from an end customer:

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

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

$payment = $client->payments()->create([
  "params" => [
      "amount" => 1000, // 10 GBP in pence collected from end customer
      "app_fee" => 10, // 10 pence, to be paid out to you
      "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=current_user.gocardless_access_token,
    environment='sandbox'
)

payment = client.payments.create(
    params={
        "amount" : 1000, # 10 GBP in pence collected from end customer.
        "app_fee": 10, # 10 pence, to be paid out to you.
        "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: current_user.gocardless_access_token,
	environment: :sandbox
)

payment = client.payments.create(
  params: {
    amount: 1000, # 10 GBP in pence, collected from the end customer.
    app_fee: 10, # 10 pence, to be paid out to you.
    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(CurrentUser.gocardlessAccessToken)
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        Payment payment = client.payments().create()
            .withAmount(1000) // 10 GBP in pence collected from end customer.
            .withAppFee(10) // 10 pence, to be paid out to you.
            .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());
    }
}
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"
});

var 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. When an end customer goes through the Redirect Flow, we'll choose the right Direct Debit scheme based on their bank account, and each scheme supports a single currency. You will likely want to use the mandates API to read the mandate's scheme attribute, and translate that into a currency using this table:

scheme currency
bacs GBP
autogiro SEK
betalingsservice DKK
becs AUD
becs_nz NZD
pad CAD
sepa_core 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, we would debit £10.00 from the end customer’s bank account. From that, before paying it out to your user, GoCardless would deduct its fees, and any app_fee you’ve specified which would be deducted and passed on to you.

Taken from end customer's bank account £10.00
GoCardless fee £0.20
Your app_fee £0.10
Paid out to your user £9.70
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 for each of your access tokens (i.e. for each of your users).

You can earn revenue from payments collected through your product either by adding an app_fee to what GoCardless charges, or by receiving a share of GoCardless’s fees.

The choice of whether you want to charge an app_fee, and if so, how much, is yours. However, it may not be more than half the value of the payment and must be made clear to your user.

For any questions about app fees, GoCardless fees or revenue sharing, please don’t hesitate to contact our partnerships team.

Creating a payment returns details on the payment you’ve set up, including 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' => $currentUser->gocardlessAccessToken,
    '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=current_user.gocardless_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: current_user.gocardless_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(CurrentUser.gocardlessAccessToken)
            .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 string paymentId = "PM000260X9VKF4";

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

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

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

Using recurring 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 an end customer:

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

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

$subscription = $client->subscriptions()->create([
    "params" => [
        "amount" => 1500, // 15 GBP in pence, collected from the end customer
        "app_fee" => 10, // 10 pence, to be paid out to you
        "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=current_user.gocardless_access_token,
    environment='sandbox'
)

subscription = client.subscriptions.create(
    params={
        "amount" : 1500, # 15 GBP in pence, collected from the end customer.
        "app_fee": 10, # 10 pence, to be paid out to you.
        "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: current_user.gocardless_access_token,
	environment: :sandbox
)

subscription = client.subscriptions.create(
  params: {
    amount: 1500, # 15 GBP in pence, collected from the customer
    app_fee: 10, # 10 pence, to be paid out to you
    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(CurrentUser.gocardlessAccessToken)
            .withEnvironment(GoCardlessClient.Environment.SANDBOX)
            .build();

        Subscription subscription = client.subscriptions().create()
            .withAmount(1500) // 15 GBP in Pence, collected from the end customer.
            .withAppFee(10) // 10 pence, to be paid out to you.
            .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());
    }
}
var createResponse = await client.Subscriptions.CreateAsync(new SubscriptionCreateRequest
{
    Amount = 1500,
    AppFee = 10,
    Currency = "GBP",
    Name = "Monthly subscription",
    Interval = 1,
    DayOfMonth = 5,
    IntervalUnit = SubscriptionCreateRequest.SubscriptionIntervalUnit.Monthly,
    Links = new GoCardless.Services.SubscriptionCreateRequest.SubscriptionLinks
    {
        Mandate = "MD0123"
    },
    IdempotencyKey = "unique_subscription_specific_string"
});

var subscription = createResponse.Subscription;

Console.WriteLine(subscription.Id);

You’ll need to set the currency depending on the customer’s mandate’s scheme.

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

We will debit £15.00 from the end customer’s bank account. From that, before paying it out to your user, GoCardless would deduct its fees, and any app_fee you’ve specified which would be deducted and passed on to you.

Taken from end customer's bank account £15.00
GoCardless fee £0.20
Your app_fee £0.10
Paid out to your user £14.70

With the subscription ID which is returned when we create the subscription, we can grab it from the API.

If you want to stop charging the customer or change the amount they’re being debited, you’ll need to cancel the subscription. Let’s then try doing that via the API so no further payments are taken:

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

$client = new \GoCardlessPro\Client([
    'access_token' => $currentUser->gocardlessAccessToken,
    '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=current_user.gocardless_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: current_user.gocardless_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(CurrentUser.gocardlessAccessToken)
            .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 string subscriptionId = "SB00003GKMHFFY";

var subscriptionResponse = await client.Subscriptions.GetAsync(subscriptionId);
var 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}");

With the existing subscription cancelled, you can set up a new one using the same mandate.

Whenever a subscription creates a payment, you’ll receive a webhook with resource_type “subscriptions” and action “payment_created”.

POST https://example.com/webhooks HTTP/1.1
User-Agent: gocardless-webhook-service/1.1
Content-Type: application/json
Webhook-Signature: 68cfef92b143649f4ce9b9e4b3d182b879be77625bcd0b3ff1f37849d01f76e6
{
  "events": [
    {
      "id": "EV123",
      "created_at": "2014-08-04T12:00:00.000Z",
      "action": "payment_created",
      "resource_type": "subscriptions",
      "links": {
        "subscription": "SB123",
        "payment": "PM123",
        "organisation": "OR123"
      }
    }
  ]
}

It’s likely that you’ll want to record this newly-created payment in your database to provide your user with visibility over payments being collected, and so you can track its status over time, taking action if it fails later.

Handling payment failures

Payments collected via Direct Debit are not confirmed immediately - when a payment is created, it must be submitted to the banking system, and GoCardless won’t know for a few days whether the payment has been successful. For more details on how long payments take to process, see our guides for Bacs, SEPA, Autogiro, BECS BECS NZ, Betalingsservice, and PAD.

Payment failures are rare - less than 1% of payments fail - but building a great experience for your users means handling these failures. A payment may fail, for example, if the end customer has insufficient funds in their account.

You’ll find out about a payment failure when you receive a webhook that includes an event with the resource_type of “payments” and action of “failed”:

POST https://example.com/webhooks HTTP/1.1
User-Agent: gocardless-webhook-service/1.1
Content-Type: application/json
Webhook-Signature: 86f8bb84a4de63cff4af48f22b64b20970b415b066e3d21459ea515052860514
{
  "events": [
    {
      "id": "EV456",
      "created_at": "2014-08-03T12:00:00.000Z",
      "action": "failed",
      "resource_type": "payments",
      "links": {
        "payment": "PM456",
        "organisation": "OR123"
      },
      "details": {
        "origin": "bank",
        "cause": "mandate_cancelled",
        "description": "Customer cancelled the mandate at their bank branch.",
        "scheme": "bacs",
        "reason_code": "ARUDD-1"
      }
    },
    {
      "id": "EV123",
      "created_at": "2014-08-03T12:00:00.000Z",
      "action": "confirmed",
      "resource_type": "payments",
      "links": {
        "payment": "PM123",
        "organisation": "OR123"
      },
      "details": {
        "origin": "gocardless",
        "cause": "payment_confirmed",
        "description": "Payment was confirmed as collected"
      }
    }
  ]
}

You’ll need to think about how to handle failures in your product - the right thing to do will depend on your product and your users’ preferences. You might consider:

  • Updating your UI to show that a payment failed, and offering the option to try again
  • Offering an option which allows your users to have failed payments automatically retried
  • Sending the end customer an email telling them what happened and what they need to do next

The details object inside the event includes more details on why the payment failed. The cause field is especially useful, providing more context on why the payment was unsuccessful. The most common causes of payment failure you’ll want to handle include:

refer_to_payer
For UK payments using the Bacs scheme, this means the end customer had insufficient funds. For Eurozone payments using the SEPA scheme, the customer must contact their bank to find our what went wrong.
insufficient_funds
Only used for SEPA, this means that the customer did not have sufficient funds.
mandate_cancelled
The mandate was cancelled between when the payment was submitted to the banks and when it was due to be debited from the end customer's account.

You can find a full list of the cause values for payment-related webhook events, including failures in our reference documentation.

You’ll need to think about how to handle failures in your product - the right thing to do will depend on your product and your users’ preferences. You might consider:

  • Updating your UI to show that a payment failed, and offering the option to try again
  • Including a setting which allows your users to have failed payments automatically retried
  • Sending the customer an email telling them what happened and what they need to do next

You can read more about reporting the status of payments to your users in our user experience guide.

If a payment has failed due to insufficient funds, you may wish to retry it. You don’t need to create a new payment to do this, but can rather retry the existing one through a simple API call:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

public class RetryPayment {
    public static void main(String[] args) {
        GoCardlessClient client = GoCardlessClient
            .newBuilder(CurrentUser.gocardlessAccessToken)
            .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("Retrying...");

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

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

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

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