Skip to content

Repository files navigation

saltedge-client-go

Go Reference CI Go Report Card License: MIT

A complete, production-grade Go SDK for the SaltEdge API v6, covering all three product areas:

  • AIS — Account Information Service
  • PIS — Payment Initiation Service
  • Data Enrichment Platform — Categorisation, Merchant ID & Financial Insights

Features

Category What's included
Full API v6 coverage AIS, PIS, Data Enrichment — every endpoint from the official docs
Entry point Single salt_edge.go with saltedge.New() and three service groups: AIS, PIS, Enrichment
Authentication App-id + Secret auto-injected; optional HMAC-SHA256 request signing
Resilience Exponential-backoff retries (configurable), 429/5xx handled, context cancellation
Generics ListResponse[T], SingleResponse[T], Paginator[T], pagination.All, pagination.ForEach
Typed errors APIError with errors.Is/errors.As, sentinel vars, IsNotFound, IsRetryable, IsClass
Webhooks webhook.Handler — HMAC validation, event routing by type, typed On() / OnAny()
Middleware Logging, User-Agent injection, debug mode via log/slog
Functional options WithTimeout, WithMaxRetries, WithPrivateKey, WithLogger, WithHTTPClient, WithDebug
Tests 57 unit tests (race-detector clean), integration test suite

Installation

go get github.com/iamkanishka/saltedge-client-go@latest

Requires Go 1.25+.


Quick Start

import (
    saltedge "github.com/iamkanishka/saltedge-client-go"
    "github.com/iamkanishka/saltedge-client-go/pkg/models/ais"
    "github.com/iamkanishka/saltedge-client-go/pkg/pagination"
)

client := saltedge.New("YOUR_APP_ID", "YOUR_SECRET",
    saltedge.WithTimeout(20 * time.Second),
    saltedge.WithMaxRetries(3),
    saltedge.WithDebug(true),
)

// ── AIS ──────────────────────────────────────────────────────────────────────
customer, err := client.AIS.Customers.Create(ctx, ais.CreateCustomerParams{
    Identifier: "alice@example.com",
})

session, err := client.AIS.Connections.Connect(ctx, ais.ConnectParams{
    CustomerID: customer.ID,
    Consent:    ais.ConsentObject{Scopes: []string{"accounts", "transactions"}},
    Attempt:    &ais.AttemptObject{ReturnTo: "https://yourapp.com/callback"},
})
fmt.Println("Connect URL:", session.ConnectURL)

// Paginate all accounts
accounts, err := pagination.All(ctx, func(ctx context.Context, fromID string) ([]ais.Account, string, error) {
    return client.AIS.Accounts.List(ctx, ais.ListAccountsParams{
        ConnectionID: "conn-id",
        FromID:       fromID,
    })
})

// ── PIS ──────────────────────────────────────────────────────────────────────
payment, err := client.PIS.Payments.Create(ctx, pis.CreatePaymentParams{
    CustomerID:   customer.ID,
    ProviderCode: "fake_client_xf",
    TemplateCode: "sepa_credit_transfer",
    PaymentAttributes: map[string]any{
        "amount":        "100.00",
        "currency_code": "EUR",
        "creditor_name": "Acme Corp",
        "creditor_iban": "DE89370400440532013000",
    },
})

// ── Data Enrichment ───────────────────────────────────────────────────────────
bucket, err := client.Enrichment.Buckets.Create(ctx, enrichment.CreateBucketParams{
    CustomerID: customer.ID,
})

API Coverage

AIS (Account Information Service)

Resource Operations
Countries List
Providers List, Show
Customers Create, Show, List, Remove
Connections Show, List, Connect, Reconnect, Refresh, BackgroundRefresh, Update, Remove
Consents List, Show, Revoke
Accounts List
Transactions List, Update
Exchange Rates List

PIS (Payment Initiation Service)

Resource Operations
Customers Create, Show, List, Remove
Providers List, Show
Payments Create, Show, List, Refresh
Payment Templates Show, List
Bulk Payments Create, Show, List, Refresh

Data Enrichment Platform

Resource Operations
Buckets Create, Show, Remove
Accounts Import, List, Remove
Transactions Import, StartCategorization, ListCategorized
Merchants Show
Categories List, ListByType, Learn
Customer Rules List, Show, Remove
Financial Insights Create, Show, List, Remove

Error Handling

import saltErr "github.com/iamkanishka/saltedge-client-go/pkg/errors"

_, err := client.AIS.Customers.Show(ctx, "missing-id")

// Helper functions
if saltErr.IsNotFound(err)   { /* 404 */ }
if saltErr.IsRateLimit(err)  { /* 429 */ }
if saltErr.IsServerError(err){ /* 5xx */ }
if saltErr.IsRetryable(err)  { /* auto-retried but still failed */ }
if saltErr.IsClass(err, "InvalidCredentials") { /* specific class */ }

// Sentinel matching with errors.Is
if errors.Is(err, saltErr.ErrNotFound) { ... }
if errors.Is(err, saltErr.ErrCustomerNotFound) { ... }

// Full details via errors.As
var apiErr *saltErr.APIError
if errors.As(err, &apiErr) {
    fmt.Printf("status=%d class=%s message=%s\n",
        apiErr.StatusCode, apiErr.Class, apiErr.Message)
}

Pagination

// Option A: collect everything
all, err := pagination.All(ctx, func(ctx context.Context, fromID string) ([]ais.Transaction, string, error) {
    return client.AIS.Transactions.List(ctx, ais.ListTransactionsParams{
        ConnectionID: "conn-id",
        FromID:       fromID,
    })
})

// Option B: streaming with ForEach (memory-efficient)
err = pagination.ForEach(ctx, fetchFn, func(tx ais.Transaction) error {
    fmt.Println(tx.Description, tx.Amount)
    return nil
})

// Option C: manual iteration
pager := pagination.New(fetchFn)
for pager.Next(ctx) {
    for _, item := range pager.Page().Items {
        process(item)
    }
}
if err := pager.Err(); err != nil { ... }

Webhooks

import (
    "github.com/iamkanishka/saltedge-client-go/internal/signer"
    "github.com/iamkanishka/saltedge-client-go/pkg/webhook"
)

s := signer.New("YOUR_WEBHOOK_SECRET")
handler := webhook.New(s)

handler.On(webhook.AISSuccess, func(ev *webhook.Event) error {
    var cb ais.SuccessCallback
    json.Unmarshal(ev.Data, &cb.Data)
    fmt.Println("Connection synced:", cb.Data.ConnectionID, "stage:", cb.Data.Stage)
    return nil
})

handler.On(webhook.AISFailure,     handleFail)
handler.On(webhook.AISNotify,      handleNotify)
handler.On(webhook.AISDestroy,     handleDestroy)
handler.On(webhook.PISPaymentSuccess, handlePaymentSuccess)
handler.OnAny(logUnhandled) // fallback

http.Handle("/webhook/saltedge", handler)

Configuration Options

client := saltedge.New(appID, secret,
    saltedge.WithBaseURL("https://custom-proxy.example.com/api/v6"),
    saltedge.WithTimeout(15 * time.Second),
    saltedge.WithMaxRetries(5),
    saltedge.WithRetry(httpclient.RetryConfig{
        MaxAttempts: 4,
        BaseDelay:   200 * time.Millisecond,
        MaxDelay:    30 * time.Second,
    }),
    saltedge.WithPrivateKey(hmacPrivateKey), // enables request signing
    saltedge.WithLogger(logger.DefaultSlog()),
    saltedge.WithHTTPClient(myCustomHTTPClient), // for tests or proxying
    saltedge.WithDebug(true),
)

Project Structure

saltedge-client-go/
├── salt_edge.go              # ← Entry point: saltedge.New(), Client, service groups
├── client.go                 # Core HTTP executor, Config, functional options
│
├── pkg/
│   ├── errors/               # APIError, sentinels, IsNotFound, IsRetryable, …
│   ├── logger/               # Logger interface, Noop, Slog adapters
│   ├── models/
│   │   ├── ais/              # AIS models: Country, Provider, Customer, Connection,
│   │   │                     #   Consent, Account, Transaction, ExchangeRate, Callbacks
│   │   ├── pis/              # PIS models: Customer, Provider, Payment, BulkPayment,
│   │   │                     #   PaymentTemplate, Callbacks
│   │   └── enrichment/       # Enrichment models: Bucket, Account, Transaction,
│   │                         #   Merchant, Category, CustomerRule, FinancialInsight
│   ├── pagination/           # Paginator[T], All(), ForEach()
│   └── webhook/              # Handler, EventType constants, Event struct
│
├── services/
│   ├── requester.go          # Requester interface (Get/Post/Put/Delete)
│   ├── ais/                  # CountriesService, ProvidersService, CustomersService,
│   │                         #   ConnectionsService, ConsentsService, AccountsService,
│   │                         #   TransactionsService, RatesService
│   ├── pis/                  # CustomersService, ProvidersService, PaymentsService,
│   │                         #   TemplatesService, BulkPaymentsService
│   └── enrichment/           # BucketsService, AccountsService, TransactionsService,
│                             #   MerchantsService, CategoriesService,
│                             #   CustomerRulesService, FinancialInsightsService
│
├── internal/
│   ├── httpclient/           # Doer interface, retry engine, connection pool
│   └── signer/               # HMAC-SHA256 request/webhook signing
│
├── examples/
│   ├── ais_quickstart/       # Customer → connect session → paginate accounts + txns
│   ├── pis_payment/          # SEPA payment + bulk payment
│   ├── webhook_server/       # Full HTTP webhook server
│   └── data_enrichment/      # Bucket → import → categorize → insights
│
├── tests/
│   ├── unit/                 # 57 tests, race-detector clean, httptest-based mocks
│   └── integration/          # Build-tagged real-API tests (needs credentials)
│
└── .github/workflows/        # CI (lint → test → build) + release automation

Running Tests

# Unit tests (no credentials needed)
go test -v -race ./tests/unit/...

# With coverage
go test -race -coverpkg=./... -coverprofile=coverage.out ./tests/unit/...
go tool cover -html=coverage.out

# Integration tests (requires real credentials)
SALTEDGE_APP_ID=xxx SALTEDGE_SECRET=yyy \
  go test -tags=integration -v ./tests/integration/...

Versioning

This module follows Semantic Versioning. Breaking changes are introduced only in major version bumps (v2, v3, …). The current version is v2.0.0.


License

MIT

About

A complete, production-grade Go SDK for the SaltEdge API v6, covering all three product areas: AIS — Account Information Service PIS — Payment Initiation Service Data Enrichment Platform — Categorisation, Merchant ID & Financial Insights

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages