railsr

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: MIT Imports: 4 Imported by: 0

README

railsr-go

CI Go Reference Go Report Card

Production-grade Go client for the Railsr Embedded Finance API.


Features

Feature Detail
Full API coverage All Railsr v1/v2 endpoints — 10 resource groups, 86 endpoints
OAuth 2.0 token management In-memory cache with automatic pre-expiry refresh, mutex-safe
Full-jitter exponential backoff Configurable max retries and base backoff
Circuit breaker Three-state (closed/open/half-open), thread-safe
Client-side rate limiter Token-bucket, mutex-safe
Idempotency keys Auto-generated (128-bit random hex) on all POST/PUT/PATCH
Telemetry hooks Pluggable Hook func called after every request
HMAC-SHA256 webhook verification Constant-time comparison
Functional options Clean, extensible configuration
Race-safe All state uses sync primitives; tested with -race
Zero non-stdlib dependencies Only golang.org/x/time and github.com/google/uuid
Polling helpers WaitForActive, WaitForStatus, WaitForTerminal with context support

Installation

go get github.com/iamkanishka/railsr-go

Requires Go 1.25.


Quick Start

package main

import (
    "context"
    "log"
    "os"

    railsgo "github.com/iamkanishka/railsr-go"
)

func main() {
    r, err := railsgo.New(
        os.Getenv("RAILSR_CLIENT_ID"),
        os.Getenv("RAILSR_CLIENT_SECRET"),
        railsgo.WithEnvironment(railsgo.EnvironmentLive),
    )
    if err != nil {
        log.Fatal(err)
    }

    ctx := context.Background()

    // Create an enduser
    eu, err := r.Endusers.Create(ctx, &railsgo.CreateEnduserParams{
        Person: &railsgo.Person{
            Name:        &railsgo.PersonName{FamilyName: "Smith", GivenName: "Alice"},
            Email:       "alice@example.com",
            DateOfBirth: "1990-01-15",
            Nationality: "GB",
            CountryOfResidence: []string{"GB"},
            Address: &railsgo.Address{
                Number:     "14",
                Street:     "High Street",
                City:       "London",
                PostalCode: "EC1A 1BB",
                ISOCountry: "GB",
            },
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    // Trigger KYC
    _, err = r.Endusers.CreateKYCCheck(ctx, eu.EnduserID, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Wait for KYC to pass (webhook-driven in production)
    eu, err = r.Endusers.WaitForStatus(ctx, eu.EnduserID, railsgo.WaitForStatusOpts{
        TargetStatuses: []string{"active"},
    })
    if err != nil {
        log.Fatal(err)
    }

    // Create a GBP ledger
    ledger, err := r.Ledgers.Create(ctx, &railsgo.CreateLedgerParams{
        HolderID:   eu.EnduserID,
        LedgerType: "standard-gbp",
        AssetClass: "currency",
        AssetType:  "gbp",
    })
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Ledger created: %s  sort=%s account=%s",
        ledger.LedgerID, ledger.UKSortCode, ledger.UKAccountNumber)
}

Configuration

r, err := railsgo.New(clientID, clientSecret,
    railsgo.WithEnvironment(railsgo.EnvironmentLive), // :play | :play_live | :live
    railsgo.WithTimeout(30*time.Second),
    railsgo.WithMaxRetries(3),
    railsgo.WithBaseBackoff(200*time.Millisecond),
    railsgo.WithRateLimitRPS(50),
)

All options have sensible defaults. Never hard-code credentials — use environment variables or a secrets manager.


Sending Money

// Create a beneficiary
ben, err := r.Beneficiaries.Create(ctx, &railsgo.CreateBeneficiaryParams{
    Name:            "Bob Jones",
    UKAccountNumber: "87654321",
    UKSortCode:      "204514",
    Currency:        "GBP",
    EnduserID:       eu.EnduserID,
})

// Run Confirmation of Payee (CoP)
ben, err = r.Beneficiaries.Verify(ctx, ben.BeneficiaryID, &railsgo.VerifyBeneficiaryParams{
    PaymentType: "faster-payment",
})
// ben.COPResult: "matched" | "close_match" | "no_match"

// Send £10.00
tx, err := r.Transactions.SendMoney(ctx, &railsgo.SendMoneyParams{
    LedgerID:      ledger.LedgerID,
    BeneficiaryID: ben.BeneficiaryID,
    Amount:        1000, // pence
    Currency:      "GBP",
    PaymentType:   "faster-payment",
    Reason:        "Invoice #42",
})

// Poll for terminal status
tx, err = r.Transactions.WaitForTerminal(ctx, tx.TransactionID, railsgo.WaitForTerminalOpts{})

Cards

// Issue a virtual card
card, err := r.Cards.Create(ctx, &railsgo.CreateCardParams{
    LedgerID:        ledger.LedgerID,
    CardType:        "virtual",
    CardProgrammeID: "cp_xxx",
})

// Freeze / unfreeze
r.Cards.Freeze(ctx, card.CardID)
r.Cards.Unfreeze(ctx, card.CardID)

// Daily spend limit £100
r.Cards.CreateRule(ctx, card.CardID, &railsgo.CreateCardRuleParams{
    RuleType:      "amount_limit",
    LimitAmount:   10_000,
    LimitCurrency: "GBP",
    LimitInterval: "daily",
})

// Block gambling MCCs
r.Cards.CreateRule(ctx, card.CardID, &railsgo.CreateCardRuleParams{
    RuleType: "mcc_block",
    MCCList:  []string{"7995", "7801"},
})

Direct Debit

// Create mandate
mandate, err := r.Mandates.Create(ctx, &railsgo.CreateMandateParams{
    EnduserID:         eu.EnduserID,
    LedgerID:          ledger.LedgerID,
    AccountNumber:     "12345678",
    SortCode:          "040004",
    AccountHolderName: "Alice Smith",
})

// Wait for BACS activation
mandate, err = r.Mandates.WaitForActive(ctx, mandate.MandateID, railsgo.WaitForMandateActiveOpts{})

// Collect £50
payment, err := r.Payments.Create(ctx, &railsgo.CreatePaymentParams{
    MandateID: mandate.MandateID,
    Amount:    5_000,
    Reason:    "Wallet top-up",
})

Compliance Firewall

// Set rules
r.Firewall.SetRules(ctx, &railsgo.SetRulesParams{
    Rules: []railsgo.FirewallRule{
        {
            Name:     "Quarantine large international",
            Rule:     `(and (> (transaction.amount) 500000) (not (= (beneficiary.country) "GB")))`,
            Action:   "quarantine",
            Priority: 10,
        },
    },
})

// Review quarantined transactions
quarantined, _ := r.Transactions.ListQuarantined(ctx)
for _, tx := range quarantined {
    if approve(tx) {
        r.Transactions.Approve(ctx, tx.TransactionID)
    } else {
        r.Transactions.Reject(ctx, tx.TransactionID, "Policy violation")
    }
}

Webhooks

// Configure endpoint
r.Webhooks.Configure(ctx, &railsgo.ConfigureParams{
    URL:    "https://myapp.com/webhooks/railsr",
    Secret: os.Getenv("RAILSR_WEBHOOK_SECRET"),
})

// Verify incoming payloads (in your HTTP handler)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    sig := r.Header.Get("X-Railsr-Signature")
    secret := os.Getenv("RAILSR_WEBHOOK_SECRET")

    if err := railsgo.VerifySignature(body, sig, secret); err != nil {
        http.Error(w, "invalid signature", 401)
        return
    }
    // process event...
}

Telemetry

import (
    "log/slog"
    railsgo "github.com/iamkanishka/railsr-go"
    "github.com/iamkanishka/railsr-go/telemetry"
)

// Log every request at DEBUG level
hook := telemetry.SlogHook(slog.LevelDebug)

r, err := railsgo.NewWithTelemetry(clientID, clientSecret, hook,
    railsgo.WithEnvironment(railsgo.EnvironmentLive),
)

// Or compose multiple hooks
hook := telemetry.Multi(
    telemetry.SlogHook(slog.LevelDebug),
    myPrometheusHook,
    myTracingHook,
)

Error Handling

tx, err := r.Transactions.SendMoney(ctx, params)
if err != nil {
    var apiErr *railsgo.APIError
    if errors.As(err, &apiErr) {
        switch apiErr.Type {
        case "not_found":
            // ledger or beneficiary missing
        case "rate_limited":
            // back off and retry
        case "circuit_open":
            // Railsr API temporarily unavailable
        case "unauthorized":
            // credentials invalid
        }
        log.Printf("Railsr error: %s [%s] request_id=%s",
            apiErr.Message, apiErr.Code, apiErr.RequestID)
    }
}

// Or use sentinel errors
if errors.Is(err, railsgo.ErrNotFound) { ... }
if errors.Is(err, railsgo.ErrRateLimited) { ... }

API Coverage

Resource Endpoints
Endusers (v2) Create, Get, List, Update, Patch, KYC ×3, FirewallRecalc, WaitForStatus
Ledgers Create, Get, List, Update, FindByUKAccount, FindByIBAN, ListEntries, CreditVirtual, DebitVirtual, DevCredit, WaitForActive
Transactions SendMoney, InterLedger, FX, Get, List, ListQuarantined, ResolveQuarantine, Approve, Reject, Retry, WaitForTerminal
Beneficiaries Create, Get, List, Update, Verify (CoP), RecalculateFirewall
Cards Create, Get, List, Activate, Freeze, Unfreeze, Cancel, Suspend, UpdateStatus, Replace, GetPAN, ResetPINAttempts, ListTransactions, CreateRule, ListRules, GetRule, DeleteRule, ListProgrammes, GetProgramme, CreatePaymentToken (Labs), ListPaymentTokens (Labs)
Mandates Create, Get, List, Cancel, WaitForActive
Payments Create, Get, List
Firewall SetRules, GetRules, CreateDataset, ListDatasets, UpdateDataset, DeleteDataset, GetFunctions
Webhooks Configure, GetConfig, ListHistory, Retry, VerifySignature, EventTypes (34 events)
Customer Get, Update, ListProducts, ListLedgers

Development

# Run all tests with race detector
go test -race ./...

# Run tests with coverage
go test -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

# Lint
golangci-lint run

# Vet
go vet ./...

License

MIT — see LICENSE.

Documentation

Overview

Package railsr is a production-grade Go client for the Railsr Embedded Finance API.

Quick Start

rails, err := railsgo.New("client_id", "client_secret",
    railsgo.WithEnvironment(railsgo.EnvironmentLive),
)
if err != nil { log.Fatal(err) }

ctx := context.Background()

// Create an enduser
eu, err := rails.Endusers.Create(ctx, &railsgo.CreateEnduserParams{
    Person: &railsgo.Person{
        Name:        &railsgo.PersonName{FamilyName: "Smith", GivenName: "Alice"},
        Email:       "alice@example.com",
        DateOfBirth: "1990-01-15",
        Nationality: "GB",
    },
})

// Create a GBP ledger
ledger, err := rails.Ledgers.Create(ctx, &railsgo.CreateLedgerParams{
    HolderID:   eu.EnduserID,
    LedgerType: "standard-gbp",
    AssetClass: "currency",
    AssetType:  "gbp",
})

Architecture

The SDK is structured as independent service objects hanging off a root Railsr struct. Each service corresponds to one Railsr API domain:

  • Railsr.Endusers — v2 enduser CRUD + KYC
  • Railsr.Ledgers — ledger CRUD + entries + virtual credit/debit
  • Railsr.Transactions — send, inter-ledger, FX, quarantine
  • Railsr.Beneficiaries — CRUD + CoP verification
  • Railsr.Cards — full card lifecycle + rules + Labs tokens
  • Railsr.Mandates — BACS Direct Debit mandates
  • Railsr.Payments — DD payment collection
  • Railsr.Firewall — compliance rules + datasets
  • Railsr.Webhooks — config + history + HMAC verification
  • Railsr.Customer — self account + products

Index

Constants

View Source
const (
	EnvironmentPlay     = client.EnvironmentPlay
	EnvironmentPlayLive = client.EnvironmentPlayLive
	EnvironmentLive     = client.EnvironmentLive
)

Variables

View Source
var (
	WithEnvironment  = client.WithEnvironment
	WithTimeout      = client.WithTimeout
	WithMaxRetries   = client.WithMaxRetries
	WithBaseBackoff  = client.WithBaseBackoff
	WithRateLimitRPS = client.WithRateLimitRPS
	WithHTTPClient   = client.WithHTTPClient
	WithUserAgent    = client.WithUserAgent
)

Re-export option constructors.

View Source
var (
	WithIdempotencyKey = client.WithIdempotencyKey
	WithQuery          = client.WithQuery
)
View Source
var (
	ErrUnauthorized     = types.ErrUnauthorized
	ErrNotFound         = types.ErrNotFound
	ErrRateLimited      = types.ErrRateLimited
	ErrCircuitOpen      = types.ErrCircuitOpen
	ErrInvalidSignature = resources.ErrInvalidSignature
)

Re-export error sentinels.

View Source
var EventTypes = resources.EventTypes

EventTypes is re-exported for single-import convenience.

View Source
var VerifySignature = resources.VerifySignature

VerifySignature is re-exported for single-import convenience.

Functions

This section is empty.

Types

type APIError

type APIError = types.APIError

Error type.

type Address

type Address = types.Address

Re-export types for single-import convenience.

type Beneficiary

type Beneficiary = types.Beneficiary

Re-export types for single-import convenience.

type Card

type Card = types.Card

Re-export types for single-import convenience.

type CardProgramme

type CardProgramme = types.CardProgramme

Re-export types for single-import convenience.

type CardRule

type CardRule = types.CardRule

Re-export types for single-import convenience.

type Company

type Company = types.Company

Re-export types for single-import convenience.

type ConfigureParams

type ConfigureParams = resources.ConfigureParams

Re-export types for single-import convenience.

type CreateBeneficiaryParams

type CreateBeneficiaryParams = resources.CreateBeneficiaryParams

Re-export types for single-import convenience.

type CreateCardParams

type CreateCardParams = resources.CreateCardParams

Re-export types for single-import convenience.

type CreateCardRuleParams

type CreateCardRuleParams = resources.CreateCardRuleParams

Re-export types for single-import convenience.

type CreateDatasetParams

type CreateDatasetParams = resources.CreateDatasetParams

Re-export types for single-import convenience.

type CreateEnduserParams

type CreateEnduserParams = resources.CreateEnduserParams

Request param types.

type CreateKYCCheckParams

type CreateKYCCheckParams = resources.CreateKYCCheckParams

Re-export types for single-import convenience.

type CreateLedgerParams

type CreateLedgerParams = resources.CreateLedgerParams

Re-export types for single-import convenience.

type CreateMandateParams

type CreateMandateParams = resources.CreateMandateParams

Re-export types for single-import convenience.

type CreatePaymentParams

type CreatePaymentParams = resources.CreatePaymentParams

Re-export types for single-import convenience.

type CreatePaymentTokenParams

type CreatePaymentTokenParams = resources.CreatePaymentTokenParams

Re-export types for single-import convenience.

type Customer

type Customer = types.Customer

Re-export types for single-import convenience.

type Enduser

type Enduser = types.Enduser

Re-export types for single-import convenience.

type Environment

type Environment = client.Environment

Environment aliases.

type FXParams

type FXParams = resources.FXParams

Re-export types for single-import convenience.

type FirewallDataset

type FirewallDataset = types.FirewallDataset

Re-export types for single-import convenience.

type FirewallRule

type FirewallRule = types.FirewallRule

Re-export types for single-import convenience.

type FirewallRules

type FirewallRules = types.FirewallRules

Re-export types for single-import convenience.

type InterLedgerParams

type InterLedgerParams = resources.InterLedgerParams

Re-export types for single-import convenience.

type KYCCheck

type KYCCheck = types.KYCCheck

Re-export types for single-import convenience.

type Ledger

type Ledger = types.Ledger

Re-export types for single-import convenience.

type LedgerEntry

type LedgerEntry = types.LedgerEntry

Re-export types for single-import convenience.

type ListBeneficiariesParams

type ListBeneficiariesParams = resources.ListBeneficiariesParams

Re-export types for single-import convenience.

type ListCardsParams

type ListCardsParams = resources.ListCardsParams

Re-export types for single-import convenience.

type ListEndusersParams

type ListEndusersParams = resources.ListEndusersParams

Re-export types for single-import convenience.

type ListEntriesParams

type ListEntriesParams = resources.ListEntriesParams

Re-export types for single-import convenience.

type ListHistoryParams

type ListHistoryParams = resources.ListHistoryParams

Re-export types for single-import convenience.

type ListLedgersParams

type ListLedgersParams = resources.ListLedgersParams

Re-export types for single-import convenience.

type ListMandatesParams

type ListMandatesParams = resources.ListMandatesParams

Re-export types for single-import convenience.

type ListPaymentsParams

type ListPaymentsParams = resources.ListPaymentsParams

Re-export types for single-import convenience.

type ListTransactionsParams

type ListTransactionsParams = resources.ListTransactionsParams

Re-export types for single-import convenience.

type Mandate

type Mandate = types.Mandate

Re-export types for single-import convenience.

type Option

type Option = client.Option

Option alias.

type PatchEnduserParams

type PatchEnduserParams = resources.PatchEnduserParams

Re-export types for single-import convenience.

type Payment

type Payment = types.Payment

Re-export types for single-import convenience.

type Person

type Person = types.Person

Re-export types for single-import convenience.

type PersonName

type PersonName = types.PersonName

Re-export types for single-import convenience.

type Railsr

type Railsr struct {
	// Endusers manages person and company endusers (v2 API).
	Endusers *resources.EndusersService

	// Ledgers manages GBP, EUR, and virtual ledgers.
	Ledgers *resources.LedgersService

	// Transactions manages all money movements.
	Transactions *resources.TransactionsService

	// Beneficiaries manages external payees.
	Beneficiaries *resources.BeneficiariesService

	// Cards manages virtual and physical cards, rules, and programmes.
	Cards *resources.CardsService

	// Mandates manages BACS Direct Debit mandates.
	Mandates *resources.MandatesService

	// Payments manages Direct Debit payment collections.
	Payments *resources.PaymentsService

	// Firewall manages compliance rules and datasets.
	Firewall *resources.FirewallService

	// Webhooks manages webhook configuration, history, and verification.
	Webhooks *resources.WebhooksService

	// Customer provides access to the authenticated customer account.
	Customer *resources.CustomerService
	// contains filtered or unexported fields
}

Railsr is the root SDK client holding all service groups.

func New

func New(clientID, clientSecret string, opts ...Option) (*Railsr, error)

New constructs a Railsr SDK client with the given credentials and options.

Example:

r, err := railsgo.New(os.Getenv("RAILSR_CLIENT_ID"), os.Getenv("RAILSR_CLIENT_SECRET"),
    railsgo.WithEnvironment(railsgo.EnvironmentLive),
    railsgo.WithMaxRetries(3),
)

func NewWithTelemetry

func NewWithTelemetry(clientID, clientSecret string, hook telemetry.Hook, opts ...Option) (*Railsr, error)

NewWithTelemetry constructs a Railsr SDK client with a custom telemetry hook. Pass nil to use the default no-op hook.

func (*Railsr) HTTP

func (r *Railsr) HTTP() *client.HTTPClient

HTTP returns the underlying HTTPClient for advanced use (e.g. custom endpoints or direct request construction).

type ReplaceCardParams

type ReplaceCardParams = resources.ReplaceCardParams

Re-export types for single-import convenience.

type RequestOption

type RequestOption = client.RequestOption

Re-export request option.

type ResolveQuarantineParams

type ResolveQuarantineParams = resources.ResolveQuarantineParams

Re-export types for single-import convenience.

type SendMoneyParams

type SendMoneyParams = resources.SendMoneyParams

Re-export types for single-import convenience.

type SetRulesParams

type SetRulesParams = resources.SetRulesParams

Re-export types for single-import convenience.

type Token

type Token = types.Token

Domain structs.

type Transaction

type Transaction = types.Transaction

Re-export types for single-import convenience.

type UpdateBeneficiaryParams

type UpdateBeneficiaryParams = resources.UpdateBeneficiaryParams

Re-export types for single-import convenience.

type UpdateCustomerParams

type UpdateCustomerParams = resources.UpdateCustomerParams

Re-export types for single-import convenience.

type UpdateDatasetParams

type UpdateDatasetParams = resources.UpdateDatasetParams

Re-export types for single-import convenience.

type UpdateEnduserParams

type UpdateEnduserParams = resources.UpdateEnduserParams

Re-export types for single-import convenience.

type UpdateLedgerParams

type UpdateLedgerParams = resources.UpdateLedgerParams

Re-export types for single-import convenience.

type VerifyBeneficiaryParams

type VerifyBeneficiaryParams = resources.VerifyBeneficiaryParams

Re-export types for single-import convenience.

type VirtualCreditParams

type VirtualCreditParams = resources.VirtualCreditParams

Re-export types for single-import convenience.

type VirtualDebitParams

type VirtualDebitParams = resources.VirtualDebitParams

Re-export types for single-import convenience.

type WaitForLedgerActiveOpts

type WaitForLedgerActiveOpts = resources.WaitForLedgerActiveOpts

Re-export types for single-import convenience.

type WaitForMandateActiveOpts

type WaitForMandateActiveOpts = resources.WaitForMandateActiveOpts

Re-export types for single-import convenience.

type WaitForStatusOpts

type WaitForStatusOpts = resources.WaitForStatusOpts

Re-export types for single-import convenience.

type WaitForTerminalOpts

type WaitForTerminalOpts = resources.WaitForTerminalOpts

Re-export types for single-import convenience.

type WebhookConfig

type WebhookConfig = types.WebhookConfig

Re-export types for single-import convenience.

type WebhookEvent

type WebhookEvent = types.WebhookEvent

Re-export types for single-import convenience.

Directories

Path Synopsis
Package auth provides OAuth 2.0 client-credentials token management.
Package auth provides OAuth 2.0 client-credentials token management.
Package client provides the core HTTP client, configuration, and transport for the Railsr SDK.
Package client provides the core HTTP client, configuration, and transport for the Railsr SDK.
internal
idempotency
Package idempotency generates cryptographically random idempotency keys.
Package idempotency generates cryptographically random idempotency keys.
retry
Package retry provides full-jitter exponential back-off logic.
Package retry provides full-jitter exponential back-off logic.
Package middleware provides HTTP middleware for the Railsr SDK: circuit breaker and client-side rate limiter.
Package middleware provides HTTP middleware for the Railsr SDK: circuit breaker and client-side rate limiter.
Package resources implements all Railsr API resource groups.
Package resources implements all Railsr API resource groups.
Package telemetry provides structured observability hooks for the Railsr SDK.
Package telemetry provides structured observability hooks for the Railsr SDK.
Package testutil provides shared test helpers for the railsr-go SDK.
Package testutil provides shared test helpers for the railsr-go SDK.
Package types defines all Railsr domain structs used across the SDK.
Package types defines all Railsr domain structs used across the SDK.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL