Skip to content

fintech-sdk/gocardless-client-go

Repository files navigation

gocardless-client-go

Go Reference Go Report Card License: MIT

A production-ready, fully idiomatic Go SDK for the GoCardless API.
Zero external dependencies — stdlib only.


Features

Capability Detail
Complete API coverage All 47 resource services matching the GoCardless API reference
Open Banking / Pay by Bank Billing Requests, Bank Authorisations, Institutions
Outbound Payments Full send-money flow with ECDSA/RSA request signing
OAuth2 Partner auth-URL builder, token exchange, lookup, disconnect
Resilience Exponential backoff + full jitter, circuit breaker, retry budgets
Rate-limit awareness Parses X-RateLimit-* headers, respects Retry-After on 429
Pagination Generic Paginator[T] — stream via channels or collect to slice
Webhook verification HMAC-SHA256 + typed event router + IP allowlist middleware
Middleware chain Composable http.RoundTripper wrappers for tracing, metrics, signing
Request correlation WithRequestID(ctx, id) propagates as X-Request-ID
Zero dependencies Only the Go standard library is required
Go 1.22+ Uses generics for the paginator

Installation

go get github.com/iamkanishka/gocardless-client-go

Requires Go 1.22 or later.


Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    gocardless "github.com/iamkanishka/gocardless-client-go"
    "github.com/iamkanishka/gocardless-client-go/resources"
)

func main() {
    client := gocardless.New(
        gocardless.WithAccessToken("sandbox_token_here"),
        gocardless.WithEnvironment(gocardless.Sandbox),
    )

    customer, err := client.Customers.Create(context.Background(), resources.CreateCustomerParams{
        Email:       "alice@example.com",
        GivenName:   "Alice",
        FamilyName:  "Smith",
        CountryCode: "GB",
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Created customer:", customer.ID)
}

Project Structure

gocardless-client-go/
├── gocardless.go                      # Root package — re-exports all public types
├── go.mod
├── client/
│   └── client.go                      # Functional-options constructor, all 47 services
├── resources/
│   ├── types.go                       # Shared enums, Metadata, status constants
│   ├── helpers.go                     # struct → url.Values conversion
│   ├── customers.go
│   ├── mandates.go
│   ├── payments.go
│   ├── subscriptions.go
│   ├── redirect_flows.go
│   ├── billing_requests.go
│   ├── events.go
│   ├── payouts.go
│   ├── creditors_bankaccounts_refunds.go
│   ├── remaining_resources.go
│   ├── new_resources.go
│   ├── extensions.go
│   └── resources_test.go
├── webhooks/
│   ├── webhooks.go                    # HMAC verification + typed Handler
│   ├── event_types.go                 # 123 event action constants
│   ├── ip_allowlist.go                # IP allowlist middleware
│   └── webhooks_test.go
├── oauth/
│   └── oauth.go                       # OAuth2 partner flow
├── signing/
│   └── signing.go                     # ECDSA P-256 / RSA request signing
├── docs/
│   ├── usage.md                       # Usage guide
│   └── webhooks.md                    # Webhook integration guide
└── internal/
    └── httpclient/
        ├── client.go                  # Resilient HTTP client (retries, backoff, CB)
        ├── errors.go                  # APIError types + Is* helpers
        ├── paginator.go               # Generic Paginator[T]
        ├── middleware.go              # Middleware chain, rate-limit state
        └── idempotency.go             # Crypto-random key generator

Client Configuration

client := gocardless.New(
    gocardless.WithAccessToken("your-token"),
    gocardless.WithEnvironment(gocardless.Live),
    gocardless.WithTimeout(30*time.Second),
    gocardless.WithMaxRetries(3),
    gocardless.WithRetryDelays(500*time.Millisecond, 30*time.Second),
    gocardless.WithCircuitBreaker(),
    gocardless.WithLogger(myLogger),
    gocardless.WithMiddleware(myTracingMiddleware),
)
Option Default Description
WithAccessToken(token) Required. Bearer access token
WithEnvironment(env) Sandbox Sandbox or Live
WithHTTPClient(c) built-in Custom *http.Client
WithTimeout(d) 30s Per-request timeout
WithMaxRetries(n) 3 Max automatic retry attempts
WithRetryDelays(base, max) 500ms / 30s Exponential backoff range
WithCircuitBreaker() disabled Opens after 5 consecutive failures
WithLogger(l) noop Inject structured logger
WithMiddleware(m...) none Transport middleware chain

Error Handling

import "errors"

_, err := client.Customers.Get(ctx, "CU_MISSING")
if err != nil {
    var apiErr *gocardless.APIError
    switch {
    case gocardless.IsNotFound(err):
        fmt.Println("not found")
    case gocardless.IsUnprocessable(err):
        if errors.As(err, &apiErr) {
            for _, fe := range apiErr.Errors {
                fmt.Printf("field %q: %s\n", fe.Field, fe.Message)
            }
        }
    case gocardless.IsRateLimited(err):
        fmt.Println("rate limited — auto-retried until budget exhausted")
    case gocardless.IsInvalidState(err):
        fmt.Println("resource in wrong state")
    default:
        log.Fatal(err)
    }
}

Idempotency

key := gocardless.NewIdempotencyKey()

payment, err := client.Payments.Create(ctx, params,
    gocardless.WithIdempotencyKey(key),
)

Pagination

// Stream via channel (memory-efficient)
paginator := client.Payments.All(resources.ListPaymentsParams{Limit: 100})
ch, errCh := paginator.All(ctx)
for payment := range ch {
    process(payment)
}
if err := <-errCh; err != nil {
    log.Fatal(err)
}

// Or collect to slice
all, err := client.Payments.All(resources.ListPaymentsParams{}).Collect(ctx)

Webhook Verification

handler := webhooks.NewHandler(webhookSecret).
    OnPaymentPaidOut(func(e webhooks.Event) { /* … */ }).
    OnPaymentFailed(func(e webhooks.Event) { /* … */ }).
    OnMandateCancelled(func(e webhooks.Event) { /* … */ }).
    OnBillingRequestFulfilled(func(e webhooks.Event) { /* … */ }).
    OnAny(func(e webhooks.Event) { auditLog(e) })

http.Handle("/webhooks", webhooks.IPAllowlistMiddleware(handler))

OAuth2

cfg := &oauth.Config{
    ClientID:    "your-client-id",
    ClientSecret: "your-secret",
    RedirectURI: "https://example.com/callback",
    Environment: oauth.Live,
}

// Step 1: redirect user
http.Redirect(w, r, cfg.AuthURL(oauth.WithScope("read_write")), http.StatusFound)

// Step 2: exchange code
token, err := cfg.Exchange(ctx, r.URL.Query().Get("code"))

// Step 3: use token
merchantClient := gocardless.New(gocardless.WithAccessToken(token.AccessToken))

Request Signing (Outbound Payments)

signer := signing.NewECDSASigner("key-id", privateKey)

client := gocardless.New(
    gocardless.WithAccessToken(token),
    gocardless.WithMiddleware(signing.NewMiddleware(signer)),
)

Running Tests

go test ./...            # All tests
go test -race ./...      # With race detector
go test -cover ./...     # With coverage

Security

See SECURITY.md for vulnerability disclosure policy and security notes.


Changelog

See CHANGELOG.md for release history.


License

MIT — see LICENSE.

Packages

 
 
 

Contributors

Languages