moneyhub

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 0 Imported by: 0

README

moneyhub-go

Go Reference

A production-grade, dependency-free Go client for the Moneyhub Open Finance API - Open Banking account aggregation (AIS), payment initiation (PIS), data categorisation/enrichment, affordability, and webhooks.

Built entirely on the Go standard library (net/http, crypto/rsa, encoding/json, context) - no third-party dependencies, no version conflicts, no supply-chain surface beyond the Go toolchain itself.

Features

  • OpenID Connect authentication - Pushed Authorisation Requests (PAR), private_key_jwt client assertions (signed with stdlib crypto/rsa), authorisation code exchange, client_credentials tokens for ongoing per-user access, refresh tokens, and OIDC discovery.
  • Data Aggregation (AIS) - accounts (manual balances, standing orders, sync status), transactions (manual transactions, splits, file attachments), regular transaction (subscription/rent/salary) detection, connection lifecycle (immediate sync, connection-type filtered catalogs), categories and category groups, categorisation-as-a-service, counterparties (per-user and global), beneficiaries, investment holdings with ISIN matching, spending analysis, savings/spending goals, rental records, affordability reports, Standard Financial Statements, notification thresholds, account statements, tax (SA105) data, projects, consent history, bank icons, reseller checks, and both lightweight (users) and SCIM-based (scimusers) user records.
  • Payments (PIS) - payees, single immediate payments, Variable Recurring Payments (VRP) with sweep triggering and funds confirmation, standing orders, bulk pay files, shareable pay links, and refunds.
  • Webhooks - verifies both plain-JSON and signed-JWT webhook deliveries against Moneyhub's published JWKS (RS256 signature verification implemented with stdlib crypto/rsa, no JWT library).
  • Automatic retry with backoff for 429 (honouring Retry-After) and 5xx responses, a structured *transport.Error type instead of bare errors, full context propagation, and a clean DDD package layout - one Go package per bounded context.

Installation

go get github.com/iamkanishka/moneyhub-go

Go 1.21+ required (for the any alias and generics-adjacent stdlib features used internally).

Project layout

moneyhub-go/
├── config/                    # Config type and functional options
├── internal/transport/        # Shared HTTP client, retry/backoff, errors
└── pkg/domain/                # One package per bounded context (DDD)
    ├── auth/                  # OIDC: PAR, token exchange, JWKS, id_token verify
    ├── accounts/
    ├── transactions/
    ├── connections/
    ├── ... (34 domain packages total)
    └── webhooks/

Each domain package exposes a Service type constructed via New(cfg), and has no knowledge of any other domain package (aside from auth and webhooks sharing the JWKS verification primitive).

Configuration

Build a *config.Config once and pass it to every domain package's New constructor. In production, Moneyhub requires private_key_jwt client authentication:

import (
    "github.com/iamkanishka/moneyhub-go/config"
    "github.com/iamkanishka/moneyhub-go/pkg/domain/auth"
)

privateKeyPEM, err := os.ReadFile("/path/to/private_key.pem")
if err != nil {
    log.Fatal(err)
}
privateKey, err := auth.LoadRSAPrivateKeyPEM(privateKeyPEM)
if err != nil {
    log.Fatal(err)
}

cfg, err := config.New(
    os.Getenv("MONEYHUB_CLIENT_ID"),
    config.Production,
    config.WithPrivateKeyJWT(privateKey, os.Getenv("MONEYHUB_KEY_ID")),
    config.WithRedirectURI("https://myapp.example.com/moneyhub/callback"),
)
if err != nil {
    log.Fatal(err)
}

For early sandbox development, client_secret_basic is also supported:

cfg, err := config.New(
    "my-client-id",
    config.Sandbox,
    config.WithClientSecretBasic("my-client-secret"),
    config.WithRedirectURI("https://myapp.example.com/moneyhub/callback"),
)

Quick start: connect a bank account, then read transactions

ctx := context.Background()

authSvc := auth.New(cfg)
accountsSvc := accounts.New(cfg)
transactionsSvc := transactions.New(cfg)

// 1. Build an authorisation URL for a new user (Moneyhub assigns the sub)
claims := auth.NewClaims().PutSub("")

result, err := authSvc.PushAuthorisationRequest(ctx, auth.AuthorisationURLOptions{
    Scope:  auth.AISOfflineScopes(),
    Claims: claims,
})
if err != nil {
    log.Fatal(err)
}

// 2. Redirect the user's browser to result.URL. They authenticate at
//    their bank and are redirected back to your RedirectURI with
//    ?code=...&state=...

// 3. Exchange the code for tokens and verify the id_token
tokens, err := authSvc.ExchangeCode(ctx, code, "")
if err != nil {
    log.Fatal(err)
}
idClaims, err := authSvc.VerifyIDToken(ctx, tokens.IDToken)
if err != nil {
    log.Fatal(err)
}
userID, _ := idClaims.String("sub")

// 4. From now on, fetch fresh data tokens for this user as needed
dataTokens, err := authSvc.TokenForUser(ctx, userID, "")
if err != nil {
    log.Fatal(err)
}

accountsList, err := accountsSvc.List(ctx, dataTokens.AccessToken, accounts.ListOptions{})
if err != nil {
    log.Fatal(err)
}

txs, err := transactionsSvc.List(ctx, dataTokens.AccessToken, transactions.ListOptions{
    AccountID: accountsList[0].ID,
})

Quick start: a single immediate payment

paymentsSvc := payments.New(cfg)

payment := map[string]any{
    "amount": map[string]any{"amount": 10.50, "currency": "GBP"},
    "creditorAccount": map[string]any{
        "identification": map[string]any{
            "sortCode":      "010203",
            "accountNumber": "12345678",
        },
    },
    "reference": "Invoice 123",
}

claims := auth.NewClaims().PutSub("").PutPayment(payment)

result, err := authSvc.PushAuthorisationRequest(ctx, auth.AuthorisationURLOptions{
    Scope:  auth.PaymentScopes(),
    Claims: claims,
})

// redirect the user to result.URL to authorise the payment at their bank, then:

tokens, err := authSvc.ExchangeCode(ctx, code, "")
idClaims, err := authSvc.VerifyIDToken(ctx, tokens.IDToken)
paymentInfo := idClaims["mh:payment"]

Webhooks

webhooksVerifier := webhooks.New(cfg)

func handleWebhook(w http.ResponseWriter, r *http.Request) {
    body, err := io.ReadAll(r.Body)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        return
    }

    event, err := webhooksVerifier.Parse(r.Context(), body)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        return
    }

    switch event.ID {
    case "newTransactions":
        go processNewTransactions(event.Payload)
    default:
        go handleGenericEvent(event)
    }

    w.WriteHeader(http.StatusOK)
}

Moneyhub's webhook delivery has a 5 second response timeout and at most one retry - acknowledge with 200 immediately and do slow processing in a goroutine afterwards.

Error handling

Every function that can fail returns a *transport.Error (which implements error) with a structured Reason (ReasonConfig/ReasonNetwork/ReasonAPI/ReasonRateLimited/ ReasonDecode/ReasonJWT/ReasonValidation) instead of an opaque error string:

import "errors"

accountsList, err := accountsSvc.List(ctx, token, accounts.ListOptions{})
if err != nil {
    var apiErr *transport.Error
    if errors.As(err, &apiErr) {
        switch apiErr.Reason {
        case transport.ReasonRateLimited:
            time.Sleep(time.Duration(apiErr.RetryAfter) * time.Second)
        case transport.ReasonAPI:
            log.Printf("moneyhub API error %d: %s", apiErr.Status, apiErr.Code)
        }
    }
}

Testing

The whole test suite uses only net/http/httptest - no mocking framework, no third-party assertion library:

go test ./...
go test ./... -race

Documentation

Full package documentation: https://pkg.go.dev/github.com/iamkanishka/moneyhub-go.

License

MIT

Documentation

Overview

Package moneyhub documents the moneyhub-go module as a whole; it exports no symbols of its own.

moneyhub-go is a client for the Moneyhub Open Finance API (https://docs.moneyhubenterprise.com/), organised as one Go package per bounded context (DDD-style) under pkg/domain. There is no single "god object" client - each domain package's Service is constructed directly from a shared *config.Config:

cfg, err := config.New("my-client-id", config.Production,
    config.WithPrivateKeyJWT(privateKey, "key-id"),
    config.WithRedirectURI("https://myapp.example.com/callback"),
)
if err != nil {
    log.Fatal(err)
}

authSvc := auth.New(cfg)
accountsSvc := accounts.New(cfg)
transactionsSvc := transactions.New(cfg)

Package groups

Authentication (OpenID Connect flows, JWKS, id_token/webhook verification): pkg/domain/auth.

Data Aggregation (AIS): pkg/domain/accounts, transactions, connections, categories, counterparties, globalcounterparties, beneficiaries, holdings, regulartransactions, rentalrecords, savingsgoals, spendinggoals, spendinganalysis, affordability, standardfinancialstatements, notificationthresholds, statements, tax, projects, consenthistory, discovery, bankicons, resellercheck, users, scimusers, authrequests.

Payments (PIS): pkg/domain/payees, payments, recurringpayments, standingorders, paylinks, payfile.

Webhooks: pkg/domain/webhooks.

Example: connect a bank account, then read transactions

claims := auth.NewClaims().PutSub("")

result, err := authSvc.PushAuthorisationRequest(ctx, auth.AuthorisationURLOptions{
    Scope:  auth.AISOfflineScopes(),
    Claims: claims,
})
// redirect the user's browser to result.URL; they authenticate at
// their bank and are redirected back to RedirectURI with
// ?code=...&state=...

tokens, err := authSvc.ExchangeCode(ctx, code, "")
idClaims, err := authSvc.VerifyIDToken(ctx, tokens.IDToken)
userID, _ := idClaims.String("sub")

dataTokens, err := authSvc.TokenForUser(ctx, userID, "")
accountsList, err := accountsSvc.List(ctx, dataTokens.AccessToken, accounts.ListOptions{})
txs, err := transactionsSvc.List(ctx, dataTokens.AccessToken, transactions.ListOptions{
    AccountID: accountsList[0].ID,
})

See the auth, claims (auth.Claims), and scopes (auth package constants) documentation for the full range of supported flows, including single-use (no persistent user) connections, payments, VRP, and standing orders.

Directories

Path Synopsis
Package config provides the configuration type shared by every domain package in moneyhub-go.
Package config provides the configuration type shared by every domain package in moneyhub-go.
internal
transport
Package transport implements the low-level HTTP client shared by every domain package: request building, retry/backoff for 429 and 5xx responses, and structured error normalisation.
Package transport implements the low-level HTTP client shared by every domain package: request building, retry/backoff for 429 and 5xx responses, and structured error normalisation.
pkg
domain/accounts
Package accounts implements account data access: list/get/create/ update/delete accounts, manual account balances, AIS-reported standing orders, and account sync status.
Package accounts implements account data access: list/get/create/ update/delete accounts, manual account balances, AIS-reported standing orders, and account sync status.
domain/affordability
Package affordability implements affordability and income verification reports, used in lending and collections workflows.
Package affordability implements affordability and income verification reports, used in lending and collections workflows.
domain/auth
Package auth implements Moneyhub's OpenID Connect authentication flows: Pushed Authorisation Requests, authorisation URL building, authorisation code exchange, client_credentials tokens for ongoing per-user access, refresh tokens, private_key_jwt client assertions, and id_token / webhook JWT verification against Moneyhub's published JWKS.
Package auth implements Moneyhub's OpenID Connect authentication flows: Pushed Authorisation Requests, authorisation URL building, authorisation code exchange, client_credentials tokens for ongoing per-user access, refresh tokens, private_key_jwt client assertions, and id_token / webhook JWT verification against Moneyhub's published JWKS.
domain/authrequests
Package authrequests implements the Auth Requests API: an alternative to building authorisation URLs and Pushed Authorisation Requests by hand (see package auth) - send the desired scope/claims to this endpoint with a client_credentials token, and Moneyhub returns a ready-to-use authorisation URL.
Package authrequests implements the Auth Requests API: an alternative to building authorisation URLs and Pushed Authorisation Requests by hand (see package auth) - send the desired scope/claims to this endpoint with a client_credentials token, and Moneyhub returns a ready-to-use authorisation URL.
domain/bankicons
Package bankicons implements fetching a bank/institution's icon image by its bank reference, for use in bank-chooser UIs alongside package connections' Available* methods.
Package bankicons implements fetching a bank/institution's icon image by its bank reference, for use in bank-chooser UIs alongside package connections' Available* methods.
domain/beneficiaries
Package beneficiaries implements access to beneficiaries: payees the user has previously sent money to from a connected account, as detected from open banking data (distinct from package payees, which are payees the integrator creates for initiating payments).
Package beneficiaries implements access to beneficiaries: payees the user has previously sent money to from a connected account, as detected from open banking data (distinct from package payees, which are payees the integrator creates for initiating payments).
domain/categories
Package categories implements the Moneyhub category and category-group taxonomy used to classify transactions, plus business/personal categorisation-as-a-service for data not connected through Moneyhub.
Package categories implements the Moneyhub category and category-group taxonomy used to classify transactions, plus business/personal categorisation-as-a-service for data not connected through Moneyhub.
domain/connections
Package connections implements connection lifecycle management: listing a user's bank connections, checking sync status, triggering immediate sync, removing connections, and querying the available connection catalog (optionally filtered by connection type).
Package connections implements connection lifecycle management: listing a user's bank connections, checking sync status, triggering immediate sync, removing connections, and querying the available connection catalog (optionally filtered by connection type).
domain/consenthistory
Package consenthistory implements access to the historical record of consent grants/revocations across a user's connections and payment authorisations - useful for compliance and audit trails.
Package consenthistory implements access to the historical record of consent grants/revocations across a user's connections and payment authorisations - useful for compliance and audit trails.
domain/counterparties
Package counterparties implements counterparty data access: the merchant/payee/payer identified behind a transaction, including logos, categories, and an explicit "is this a recognised business" check.
Package counterparties implements counterparty data access: the merchant/payee/payer identified behind a transaction, including logos, categories, and an explicit "is this a recognised business" check.
domain/discovery
Package discovery implements OpenID Connect discovery: fetches Moneyhub's published OIDC provider metadata (/oidc/well-known/openid-configuration) - endpoint URLs, supported scopes, signing algorithms, and so on.
Package discovery implements OpenID Connect discovery: fetches Moneyhub's published OIDC provider metadata (/oidc/well-known/openid-configuration) - endpoint URLs, supported scopes, signing algorithms, and so on.
domain/globalcounterparties
Package globalcounterparties implements access to Moneyhub's shared, user-independent reference database of known merchants/businesses, as distinct from package counterparties (counterparties seen in a specific user's transaction history).
Package globalcounterparties implements access to Moneyhub's shared, user-independent reference database of known merchants/businesses, as distinct from package counterparties (counterparties seen in a specific user's transaction history).
domain/holdings
Package holdings implements investment account holdings access, with ISIN code matching against a reference database to enrich each holding with identified security details.
Package holdings implements investment account holdings access, with ISIN code matching against a reference database to enrich each holding with identified security details.
domain/notificationthresholds
Package notificationthresholds implements balance notification thresholds on an account - configure a balance level which, when crossed, triggers the balanceThreshold webhook (see package webhooks).
Package notificationthresholds implements balance notification thresholds on an account - configure a balance level which, when crossed, triggers the balanceThreshold webhook (see package webhooks).
domain/payees
Package payees implements payee management for payments.
Package payees implements payee management for payments.
domain/payfile
Package payfile implements Pay Files: bulk/batch payment submission - initiate many payments from a single account in one authorisation, instead of one payments.Service authorisation per payment.
Package payfile implements Pay Files: bulk/batch payment submission - initiate many payments from a single account in one authorisation, instead of one payments.Service authorisation per payment.
domain/paylinks
Package paylinks implements Pay Links: shareable, hosted single-payment links that don't require embedding a widget - useful for invoicing flows where you just need to send a customer a URL.
Package paylinks implements Pay Links: shareable, hosted single-payment links that don't require embedding a widget - useful for invoicing flows where you just need to send a customer a URL.
domain/payments
Package payments implements Single Immediate Payments (SIP): initiating a payment authorisation (driven through the auth package with a mh:payment claim) and checking payment status afterwards.
Package payments implements Single Immediate Payments (SIP): initiating a payment authorisation (driven through the auth package with a mh:payment claim) and checking payment status afterwards.
domain/projects
Package projects implements projects: a user-defined grouping construct (similar in spirit to a manual account) that can be created, read, updated, and deleted via the API.
Package projects implements projects: a user-defined grouping construct (similar in spirit to a manual account) that can be created, read, updated, and deleted via the API.
domain/recurringpayments
Package recurringpayments implements Variable Recurring Payments (VRP): set up a recurring payment consent once (via the auth package with a mh:recurring_payment claim), then trigger individual payments ("sweeps") against it without further user interaction, up to the consented limits.
Package recurringpayments implements Variable Recurring Payments (VRP): set up a recurring payment consent once (via the auth package with a mh:recurring_payment claim), then trigger individual payments ("sweeps") against it without further user interaction, up to the consented limits.
domain/regulartransactions
Package regulartransactions implements regular transaction series detection: recurring payments (subscriptions, rent, salary) automatically identified from transaction history.
Package regulartransactions implements regular transaction series detection: recurring payments (subscriptions, rent, salary) automatically identified from transaction history.
domain/rentalrecords
Package rentalrecords implements rental payment record submission - reporting a tenant's verified rent payment history (typically derived from detected regular transactions) to a credit reference agency such as Experian, to help build their credit file.
Package rentalrecords implements rental payment record submission - reporting a tenant's verified rent payment history (typically derived from detected regular transactions) to a credit reference agency such as Experian, to help build their credit file.
domain/resellercheck
Package resellercheck implements reseller check: validates a reseller/partner relationship as part of certain onboarding flows.
Package resellercheck implements reseller check: validates a reseller/partner relationship as part of certain onboarding flows.
domain/savingsgoals
Package savingsgoals implements savings goals: user-defined targets tracked against the combined balance of one or more accounts, surfacing progress as both an amount and a percentage.
Package savingsgoals implements savings goals: user-defined targets tracked against the combined balance of one or more accounts, surfacing progress as both an amount and a percentage.
domain/scimusers
Package scimusers implements SCIM users: a SCIM-style user identity resource that can hold personally identifiable information (name, email, etc), as distinct from package users (the lightweight data-only user record sub claims point at).
Package scimusers implements SCIM users: a SCIM-style user identity resource that can hold personally identifiable information (name, email, etc), as distinct from package users (the lightweight data-only user record sub claims point at).
domain/spendinganalysis
Package spendinganalysis implements aggregated spending and income statistics over arbitrary date ranges, grouped by category - useful for "this month vs last month" comparisons without manually summing transactions client-side.
Package spendinganalysis implements aggregated spending and income statistics over arbitrary date ranges, grouped by category - useful for "this month vs last month" comparisons without manually summing transactions client-side.
domain/spendinggoals
Package spendinggoals implements spending and income goals: budgeting targets scoped to a category and date range (for example "spend less than £500/month on groceries").
Package spendinggoals implements spending and income goals: budgeting targets scoped to a category and date range (for example "spend less than £500/month on groceries").
domain/standardfinancialstatements
Package standardfinancialstatements implements Standard Financial Statement (SFS) reports: a pre-filled financial statement report, used alongside affordability and income-verification reports in lending and collections workflows.
Package standardfinancialstatements implements Standard Financial Statement (SFS) reports: a pre-filled financial statement report, used alongside affordability and income-verification reports in lending and collections workflows.
domain/standingorders
Package standingorders implements standing order creation and management via Payment Initiation.
Package standingorders implements standing order creation and management via Payment Initiation.
domain/statements
Package statements implements access to account statements - periodic statement documents/metadata for a connected account, where the provider exposes them.
Package statements implements access to account statements - periodic statement documents/metadata for a connected account, where the provider exposes them.
domain/tax
Package tax implements access to SA105-relevant transaction data (the UK Self Assessment questions for property income) for HMRC reporting.
Package tax implements access to SA105-relevant transaction data (the UK Self Assessment questions for property income) for HMRC reporting.
domain/transactions
Package transactions implements transaction data access: list/get/ create/update/delete transactions, category correction, splits, and file attachments.
Package transactions implements transaction data access: list/get/ create/update/delete transactions, category correction, splits, and file attachments.
domain/users
Package users implements Moneyhub user management, for the "ongoing access" integration pattern where the integrator maintains a long-lived mapping between their own user records and a Moneyhub sub.
Package users implements Moneyhub user management, for the "ongoing access" integration pattern where the integrator maintains a long-lived mapping between their own user records and a Moneyhub sub.
domain/webhooks
Package webhooks implements verification and parsing of incoming Moneyhub webhook deliveries.
Package webhooks implements verification and parsing of incoming Moneyhub webhook deliveries.

Jump to

Keyboard shortcuts

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