Custom Payment Pages#
Build your own payment flow from scratch using the GoCardless API directly. You have complete control over the user experience, design, and logic.
This is the highest-effort integration. Consider starting with Hosted Pages to validate your integration — the underlying API is identical.
Supports: Direct Debit, Pay By Bank, VRP.
Custom Payment Pages require the Fully custom checkout paid add-on. It's enabled in the Sandbox environment so you can build and test freely, but you'll need to add it to your Live account to use it for real payments. See Pricing for details.
Best for#
- White-label or embedded product requirements
- Complex, multi-step user journeys
- Deep integration with your application
- Full control over UX and brand experience
How it works#
- Build your own UI for collecting customer and bank account details
- Use the Billing Requests API to create and progress through the payment flow
- Call API actions to collect customer details, collect bank account information, and confirm
- Handle all states, errors, and edge cases in your application
What you need to handle:
- Input validation and error messaging
- Scheme-specific bank account fields per country
- Compliance requirements (e.g., Direct Debit Guarantee display)
- Loading states and timeouts
- Mobile responsiveness
Step-by-step guide#
Create a billing request#
The Billing Request defines what you're collecting and applies to all flows. The payload differs by payment type:
curl -X POST https://api.gocardless.com/billing_requests \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"billing_requests": {
"mandate_request": {
"scheme": "bacs"
}
}
}'<?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' => 'bacs',
],
],
]);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.create(
params={
'mandate_request': {
'scheme': 'bacs',
}
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.create(
params: {
mandate_request: {
scheme: 'bacs'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCreateResponse response = client.billingRequests()
.create()
.withMandateRequestScheme("bacs")
.execute();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({
mandate_request: {
scheme: "bacs",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CreateAsync(
new BillingRequestCreateRequest
{
MandateRequest = new BillingRequestCreateRequest.BillingRequestMandateRequest
{
Scheme = "bacs"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCreateParams{
MandateRequest: &gocardless.BillingRequestCreateParamsMandateRequest{
Scheme: "bacs",
},
}
result, err := client.BillingRequests.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000301",
"status": "pending",
"mandate_request": {
"scheme": "bacs",
"currency": "GBP"
},
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completed": false
},
{
"type": "collect_bank_account",
"required": true,
"completed": false
},
{
"type": "confirm_payer_details",
"required": true,
"completed": false
}
]
}
}curl -X POST https://api.gocardless.com/billing_requests \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"billing_requests": {
"payment_request": {
"description": "Order #9012",
"amount": 12000, "currency": "GBP", "scheme": "faster_payments"
}
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->create([
'params' => [
'payment_request' => [
'description' => 'Order #9012',
'amount' => 12000,
'currency' => 'GBP',
'scheme' => 'faster_payments',
],
],
]);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.create(
params={
'payment_request': {
'description': 'Order #9012',
'amount': 12000,
'currency': 'GBP',
'scheme': 'faster_payments',
}
}
)
require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.create(
params: {
payment_request: {
description: 'Order #9012',
amount: 12000,
currency: 'GBP',
scheme: 'faster_payments'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCreateResponse response = client.billingRequests()
.create()
.withPaymentRequestDescription("Order #9012")
.withPaymentRequestAmount(12000)
.withPaymentRequestCurrency("GBP")
.withPaymentRequestScheme("faster_payments")
.execute();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({
payment_request: {
description: "Order #9012",
amount: 12000,
currency: "GBP",
scheme: "faster_payments",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CreateAsync(
new BillingRequestCreateRequest
{
PaymentRequest = new BillingRequestCreateRequest.BillingRequestPaymentRequest
{
Description = "Order #9012",
Amount = 12000,
Currency = "GBP",
Scheme = "faster_payments"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCreateParams{
PaymentRequest: &gocardless.BillingRequestCreateParamsPaymentRequest{
Description: "Order #9012",
Amount: 12000,
Currency: "GBP",
Scheme: "faster_payments",
},
}
result, err := client.BillingRequests.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000302",
"status": "pending",
"payment_request": {
"amount": 12000,
"currency": "GBP",
"scheme": "faster_payments"
},
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completed": false
},
{
"type": "select_institution",
"required": true,
"completed": false
},
{
"type": "bank_authorisation",
"required": true,
"completed": false
}
]
}
}curl -X POST https://api.gocardless.com/billing_requests \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"billing_requests": {
"payment_request": {
"description": "First payment",
"amount": 5000, "currency": "GBP", "scheme": "faster_payments"
},
"mandate_request": { "scheme": "bacs" }
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->create([
'params' => [
'payment_request' => [
'description' => 'First payment',
'amount' => 5000,
'currency' => 'GBP',
'scheme' => 'faster_payments',
],
'mandate_request' => [
'scheme' => 'bacs',
],
],
]);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.create(
params={
'payment_request': {
'description': 'First payment',
'amount': 5000,
'currency': 'GBP',
'scheme': 'faster_payments',
},
'mandate_request': {
'scheme': 'bacs',
},
}
) require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.create(
params: {
payment_request: {
description: 'First payment',
amount: 5000,
currency: 'GBP',
scheme: 'faster_payments'
},
mandate_request: {
scheme: 'bacs'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCreateResponse response = client.billingRequests()
.create()
.withPaymentRequestDescription("First payment")
.withPaymentRequestAmount(5000)
.withPaymentRequestCurrency("GBP")
.withPaymentRequestScheme("faster_payments")
.withMandateRequestScheme("bacs")
.execute();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({
payment_request: {
description: "First payment",
amount: 5000,
currency: "GBP",
scheme: "faster_payments",
},
mandate_request: {
scheme: "bacs",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CreateAsync(
new BillingRequestCreateRequest
{
PaymentRequest = new BillingRequestCreateRequest.BillingRequestPaymentRequest
{
Description = "First payment",
Amount = 5000,
Currency = "GBP",
Scheme = "faster_payments"
},
MandateRequest = new BillingRequestCreateRequest.BillingRequestMandateRequest
{
Scheme = "bacs"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCreateParams{
PaymentRequest: &gocardless.BillingRequestCreateParamsPaymentRequest{
Description: "First payment",
Amount: 5000,
Currency: "GBP",
Scheme: "faster_payments",
},
MandateRequest: &gocardless.BillingRequestCreateParamsMandateRequest{
Scheme: "bacs",
},
}
result, err := client.BillingRequests.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000303",
"status": "pending",
"payment_request": {
"amount": 5000,
"currency": "GBP",
"scheme": "faster_payments"
},
"mandate_request": {
"scheme": "bacs",
"currency": "GBP"
},
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completed": false
},
{
"type": "collect_bank_account",
"required": true,
"completed": false
},
{
"type": "confirm_payer_details",
"required": true,
"completed": false
},
{
"type": "select_institution",
"required": true,
"completed": false
},
{
"type": "bank_authorisation",
"required": true,
"completed": false
}
]
}
}curl -X POST https://api.gocardless.com/billing_requests \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"billing_requests": {
"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"
}]
}
}
}
}'<?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',
],
],
],
],
],
]);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.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',
},
],
},
},
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.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'
}
]
}
}
}
)import com.gocardless.GoCardlessClient;
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();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({
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",
},
],
},
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CreateAsync(
new BillingRequestCreateRequest
{
FallbackEnabled = true,
PurposeCode = "loan",
PaymentContextCode = "billing_goods_and_services_in_arrears",
PaymentPurposeCode = "bank_loan_delayed_draw_funding",
MandateRequest = new BillingRequestCreateRequest.BillingRequestMandateRequest
{
Scheme = "faster_payments",
Constraints = new BillingRequestCreateRequest.BillingRequestMandateRequestConstraints
{
StartDate = "2024-11-01",
MaxAmountPerPayment = 50000,
PeriodicLimits = new List<BillingRequestCreateRequest.BillingRequestMandateRequestConstraintsPeriodicLimit>
{
new BillingRequestCreateRequest.BillingRequestMandateRequestConstraintsPeriodicLimit
{
Period = "month",
MaxTotalAmount = 100000,
Alignment = "creation_date"
}
}
}
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
fallbackEnabled := true
params := gocardless.BillingRequestCreateParams{
FallbackEnabled: &fallbackEnabled,
PurposeCode: "loan",
PaymentContextCode: "billing_goods_and_services_in_arrears",
PaymentPurposeCode: "bank_loan_delayed_draw_funding",
MandateRequest: &gocardless.BillingRequestCreateParamsMandateRequest{
Scheme: "faster_payments",
Constraints: &gocardless.BillingRequestCreateParamsMandateRequestConstraints{
StartDate: "2024-11-01",
MaxAmountPerPayment: 50000,
PeriodicLimits: []gocardless.BillingRequestCreateParamsMandateRequestConstraintsPeriodicLimit{
{
Period: "month",
MaxTotalAmount: 100000,
Alignment: "creation_date",
},
},
},
},
}
result, err := client.BillingRequests.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000301",
"created_at": "2024-11-01T00:00:00.000Z",
"status": "pending",
"mandate_request": {
"scheme": "faster_payments",
"currency": "GBP",
"verify": "recommended",
"consent_type": "recurring",
"description": null,
"sweeping": false,
"payer_requested_dual_signature": false,
"metadata": {},
"links": {},
"constraints": {
"start_date": "2024-11-01",
"end_date": null,
"max_amount_per_payment": 50000,
"periodic_limits": [
{
"period": "month",
"max_total_amount": 100000,
"alignment": "creation_date",
"max_amount_per_period": null,
"max_payments": null
}
]
}
},
"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": [
{
"type": "collect_customer_details",
"required": true,
"completes_actions": [],
"requires_actions": [],
"status": "pending",
"collect_customer_details": {
"default_country_code": "GB",
"incomplete_fields": {
"customer": ["given_name", "family_name", "email"],
"customer_billing_detail": ["address_line1", "city", "postal_code", "country_code"]
}
}
},
{
"type": "select_institution",
"required": true,
"completes_actions": [],
"requires_actions": [],
"status": "pending",
"institution_guess_status": "pending"
},
{
"type": "collect_bank_account",
"required": true,
"completes_actions": [],
"requires_actions": [],
"status": "pending",
"available_country_codes": ["GB"]
},
{
"type": "bank_authorisation",
"required": true,
"completes_actions": [],
"requires_actions": ["select_institution", "collect_customer_details"],
"status": "pending",
"bank_authorisation": {
"authorisation_type": "mandate"
}
},
{
"type": "confirm_payer_details",
"required": true,
"completes_actions": [],
"requires_actions": ["collect_customer_details", "collect_bank_account"],
"status": "pending"
}
],
"links": {
"mandate_request": "MRQ0000301"
}
}
}Collect Customer Details#
Applies to all flows
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000301/actions/collect_customer_details \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"customer": {
"email": "frank.osborne@example.com",
"given_name": "Frank", "family_name": "Osborne"
},
"customer_billing_detail": {
"address_line1": "27 Acer Road", "city": "London",
"postal_code": "E8 3GX", "country_code": "GB"
}
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->collectCustomerDetails(
'BRQ0000301',
[
'data' => [
'customer' => [
'email' => 'frank.osborne@example.com',
'given_name' => 'Frank',
'family_name' => 'Osborne',
],
'customer_billing_detail' => [
'address_line1' => '27 Acer Road',
'city' => 'London',
'postal_code' => 'E8 3GX',
'country_code' => 'GB',
],
],
]
);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.collect_customer_details(
'BRQ0000301',
params={
'data': {
'customer': {
'email': 'frank.osborne@example.com',
'given_name': 'Frank',
'family_name': 'Osborne',
},
'customer_billing_detail': {
'address_line1': '27 Acer Road',
'city': 'London',
'postal_code': 'E8 3GX',
'country_code': 'GB',
},
}
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.collect_customer_details(
'BRQ0000301',
data: {
customer: {
email: 'frank.osborne@example.com',
given_name: 'Frank',
family_name: 'Osborne'
},
customer_billing_detail: {
address_line1: '27 Acer Road',
city: 'London',
postal_code: 'E8 3GX',
country_code: 'GB'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCollectCustomerDetailsResponse response = client.billingRequests()
.collectCustomerDetails("BRQ0000301")
.withCustomerEmail("frank.osborne@example.com")
.withCustomerGivenName("Frank")
.withCustomerFamilyName("Osborne")
.withCustomerBillingDetailAddressLine1("27 Acer Road")
.withCustomerBillingDetailCity("London")
.withCustomerBillingDetailPostalCode("E8 3GX")
.withCustomerBillingDetailCountryCode("GB")
.execute();
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.collectCustomerDetails("BRQ0000301", {
data: {
customer: {
email: "frank.osborne@example.com",
given_name: "Frank",
family_name: "Osborne",
},
customer_billing_detail: {
address_line1: "27 Acer Road",
city: "London",
postal_code: "E8 3GX",
country_code: "GB",
},
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CollectCustomerDetailsAsync(
"BRQ0000301",
new BillingRequestCollectCustomerDetailsRequest
{
Data = new BillingRequestCollectCustomerDetailsRequest.CollectCustomerDetailsData
{
Customer = new BillingRequestCollectCustomerDetailsRequest.CustomerData
{
Email = "frank.osborne@example.com",
GivenName = "Frank",
FamilyName = "Osborne"
},
CustomerBillingDetail = new BillingRequestCollectCustomerDetailsRequest.CustomerBillingDetailData
{
AddressLine1 = "27 Acer Road",
City = "London",
PostalCode = "E8 3GX",
CountryCode = "GB"
}
}
}
);package main
import (
"context"
"fmt"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCollectCustomerDetailsParams{
Data: gocardless.BillingRequestCollectCustomerDetailsParamsData{
Customer: gocardless.BillingRequestCollectCustomerDetailsParamsCustomer{
Email: "frank.osborne@example.com",
GivenName: "Frank",
FamilyName: "Osborne",
},
CustomerBillingDetail: gocardless.BillingRequestCollectCustomerDetailsParamsCustomerBillingDetail{
AddressLine1: "27 Acer Road",
City: "London",
PostalCode: "E8 3GX",
CountryCode: "GB",
},
},
}
result, err := client.BillingRequests.CollectCustomerDetails(context.TODO(), "BRQ0000301", params)
if err != nil {
panic(err)
}
fmt.Println(result)Response: Billing Request with collect_customer_details marked completed.
Collect a bank account#
Applies to all flows.
Required fields vary by country and scheme. Example for UK (Bacs / Faster Payments):
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000301/actions/collect_bank_account \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"country_code": "GB",
"account_holder_name": "Frank Osborne",
"account_number": "55779911",
"branch_code": "200000"
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->collectBankAccount(
'BRQ0000301',
[
'data' => [
'country_code' => 'GB',
'account_holder_name' => 'Frank Osborne',
'account_number' => '55779911',
'branch_code' => '200000',
],
]
);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.collect_bank_account(
'BRQ0000301',
params={
'data': {
'country_code': 'GB',
'account_holder_name': 'Frank Osborne',
'account_number': '55779911',
'branch_code': '200000',
}
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.collect_bank_account(
'BRQ0000301',
data: {
country_code: 'GB',
account_holder_name: 'Frank Osborne',
account_number: '55779911',
branch_code: '200000'
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestCollectBankAccountResponse response = client.billingRequests()
.collectBankAccount("BRQ0000301")
.withCountryCode("GB")
.withAccountHolderName("Frank Osborne")
.withAccountNumber("55779911")
.withBranchCode("200000")
.execute();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.collectBankAccount("BRQ0000301", {
data: {
country_code: "GB",
account_holder_name: "Frank Osborne",
account_number: "55779911",
branch_code: "200000",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.CollectBankAccountAsync(
"BRQ0000301",
new BillingRequestCollectBankAccountRequest
{
Data = new BillingRequestCollectBankAccountRequest.CollectBankAccountData
{
CountryCode = "GB",
AccountHolderName = "Frank Osborne",
AccountNumber = "55779911",
BranchCode = "200000"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestCollectBankAccountParams{
Data: gocardless.BillingRequestCollectBankAccountParamsData{
CountryCode: "GB",
AccountHolderName: "Frank Osborne",
AccountNumber: "55779911",
BranchCode: "200000",
},
}
result, err := client.BillingRequests.CollectBankAccount(context.TODO(), "BRQ0000301", params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response: Billing Request with collect_bank_account marked completed.
Confirm payer details#
Applies to: all flows including mandates (Mandate-only flow and Payment + Mandate flow
The customer confirms their consent to set up the mandate. No request body required.
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000301/actions/confirm_payer_details \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->confirmPayerDetails('BRQ0000301');import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.confirm_payer_details('BRQ0000301')require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.confirm_payer_details('BRQ0000301')import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestConfirmPayerDetailsResponse response = client.billingRequests()
.confirmPayerDetails("BRQ0000301")
.execute();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.confirmPayerDetails("BRQ0000301");using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.ConfirmPayerDetailsAsync("BRQ0000301");package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
result, err := client.BillingRequests.ConfirmPayerDetails(context.TODO(), "BRQ0000301", gocardless.BillingRequestConfirmPayerDetailsParams{})
if err != nil {
panic(err)
}
fmt.Println(result)
}Response: Billing Request with collect_bank_account marked completed.
{
"billing_requests": {
"id": "BRQ0000301",
"status": "ready_to_fulfil",
"actions": [
{
"type": "collect_customer_details",
"required": true,
"completed": true
},
{ "type": "collect_bank_account", "required": true, "completed": true },
{ "type": "confirm_payer_details", "required": true, "completed": true }
]
}
}Select Institution#
Applies to: all flows including payments (Payment-only flow and Payment + Mandate flow
Identify the customer's bank for the open banking authorisation. Use the list institutions endpoint to present a bank picker to the customer.
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000302/actions/select_institution \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"institution": "MONZO_MONZGB2L",
"country_code": "GB"
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->selectInstitution(
'BRQ0000302',
[
'data' => [
'institution' => 'MONZO_MONZGB2L',
'country_code' => 'GB',
],
]
);import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.select_institution(
'BRQ0000302',
params={
'data': {
'institution': 'MONZO_MONZGB2L',
'country_code': 'GB',
}
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.select_institution(
'BRQ0000302',
data: {
institution: 'MONZO_MONZGB2L',
country_code: 'GB'
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestSelectInstitutionResponse response = client.billingRequests()
.selectInstitution("BRQ0000302")
.withInstitution("MONZO_MONZGB2L")
.withCountryCode("GB")
.execute();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.selectInstitution("BRQ0000302", {
data: {
institution: "MONZO_MONZGB2L",
country_code: "GB",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.SelectInstitutionAsync(
"BRQ0000302",
new BillingRequestSelectInstitutionRequest
{
Data = new BillingRequestSelectInstitutionRequest.SelectInstitutionData
{
Institution = "MONZO_MONZGB2L",
CountryCode = "GB"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BillingRequestSelectInstitutionParams{
Data: gocardless.BillingRequestSelectInstitutionParamsData{
Institution: "MONZO_MONZGB2L",
CountryCode: "GB",
},
}
result, err := client.BillingRequests.SelectInstitution(context.TODO(), "BRQ0000302", params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response: Billing Request with select_institution marked completed.
Create a bank authorisation#
Applies to: all flows including payments (Payment-only flow and Payment + Mandate flow.
curl -X POST https://api.gocardless.com/bank_authorisations \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bank_authorisations": {
"redirect_uri": "https://yoursite.com/payment-complete",
"links": { "billing_request": "BRQ0000302" }
}
}'<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->bankAuthorisations()->create([
'params' => [
'redirect_uri' => 'https://yoursite.com/payment-complete',
'links' => [
'billing_request' => 'BRQ0000302',
],
],
]);
import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.bank_authorisations.create(
params={
'redirect_uri': 'https://yoursite.com/payment-complete',
'links': {
'billing_request': 'BRQ0000302',
},
}
)require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.bank_authorisations.create(
params: {
redirect_uri: 'https://yoursite.com/payment-complete',
links: {
billing_request: 'BRQ0000302'
}
}
)import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BankAuthorisationCreateResponse response = client.bankAuthorisations()
.create()
.withRedirectUri("https://yoursite.com/payment-complete")
.withLinksBuilder()
.withBillingRequest("BRQ0000302")
.done()
.execute();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.bankAuthorisations.create({
redirect_uri: "https://yoursite.com/payment-complete",
links: {
billing_request: "BRQ0000302",
},
});using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BankAuthorisations.CreateAsync(
new BankAuthorisationCreateRequest
{
RedirectUri = "https://yoursite.com/payment-complete",
Links = new BankAuthorisationCreateRequest.BankAuthorisationLinks
{
BillingRequest = "BRQ0000302"
}
}
);package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
params := gocardless.BankAuthorisationCreateParams{
RedirectUri: "https://yoursite.com/payment-complete",
Links: gocardless.BankAuthorisationCreateParamsLinks{
BillingRequest: "BRQ0000302",
},
}
result, err := client.BankAuthorisations.Create(context.TODO(), params)
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"bank_authorisations": {
"id": "BAU0000101",
"url": "https://pay.gocardless.com/obauth/BAU0000101",
"redirect_uri": "https://yoursite.com/payment-complete",
"expires_at": "2024-06-01T18:15:00.000Z",
"links": { "billing_request": "BRQ0000302" }
}
}Redirect customer to their bank#
Applies to: all flows including payments (Payment-only flow and Payment + Mandate flow
Redirect your customer to the url from the bank authorisation response. The customer approves the payment in their banking app, then is returned to your redirect_uri.
Don't use the redirect to confirm the outcome. Always use webhooks.
Fulfill the billing request#
Applies to: Mandate-only flow
Once the Billing Request status is ready_to_fulfil, call the fulfil action to activate the mandate.
curl -X POST https://api.gocardless.com/billing_requests/BRQ0000301/actions/fulfil \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"<?php
require 'vendor/autoload.php';
$client = new \GoCardlessPro\Client([
'access_token' => getenv('GC_ACCESS_TOKEN'),
'environment' => \GoCardlessPro\Environment::SANDBOX
]);
$response = $client->billingRequests()->fulfil('BRQ0000301');import os
import gocardless_pro
client = gocardless_pro.Client(
access_token=os.environ['GC_ACCESS_TOKEN'],
environment='sandbox'
)
response = client.billing_requests.fulfil('BRQ0000301')require 'gocardless_pro'
client = GoCardlessPro::Client.new(
access_token: ENV['GC_ACCESS_TOKEN'],
environment: :sandbox
)
response = client.billing_requests.fulfil('BRQ0000301')import com.gocardless.GoCardlessClient;
GoCardlessClient client = GoCardlessClient
.newBuilder(System.getenv("GC_ACCESS_TOKEN"))
.withEnvironment(GoCardlessClient.Environment.SANDBOX)
.build();
BillingRequestFulfilResponse response = client.billingRequests()
.fulfil("BRQ0000301")
.execute();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.fulfil("BRQ0000301");using GoCardless;
var client = GoCardlessClient.Create(
Environment.GetEnvironmentVariable("GC_ACCESS_TOKEN"),
GoCardlessClient.Environment.SANDBOX
);
var response = await client.BillingRequests.FulfilAsync("BRQ0000301");package main
import (
"context"
"fmt"
"os"
gocardless "github.com/gocardless/gocardless-pro-go/v6"
)
func main() {
opts := gocardless.WithEndpoint(gocardless.SandboxEndpoint)
client, err := gocardless.New(os.Getenv("GC_ACCESS_TOKEN"), opts)
if err != nil {
panic(err)
}
result, err := client.BillingRequests.Fulfil(context.TODO(), "BRQ0000301", gocardless.BillingRequestFulfilParams{})
if err != nil {
panic(err)
}
fmt.Println(result)
}Response:
{
"billing_requests": {
"id": "BRQ0000301",
"status": "fulfilled",
"links": { "mandate_request_mandate": "MD0000301" }
}
}Handle Webhooks#
| Event | Meaning | Applies to |
|---|---|---|
| billing_requests.fulfilled | Billing Request completed | All flows |
| mandates.active | Mandate is active and ready | Mandate-only, payment + mandate |
| payments.confirmed | Pay By Bank payment settled | Payment-only, payment + mandate |
| payments.failed | Payment failed | Payment-only, payment + mandate |
| mandates.cancelled | Mandate cancelled | Mandate-only, payment + mandate |
What's next?#
Guide to creating mandates and handling all mandate actions.
Understand the events fired during a billing request lifecycle.
Pre-populate customer data to reduce steps in your custom flow.
Start with hosted pages to validate your integration — same underlying API.