monobank

package module
v1.0.0 Latest Latest
Warning

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

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

README

monobank-sdk

Go Reference CI MIT License

Go SDK for the monobank API — both the client (currency, statements, jars, webhook subscription, corporate auth) and the server-side helpers you need to actually receive webhooks safely (signature verification, ready-to-mount http.Handler, deduper for retries, typed enums).

Started as a fork of vtopc/go-monobank; diverged in scope to cover the parts the upstream library does not want to ship.

Install

go get github.com/OlexiyOdarchuk/monobank-sdk

Requires Go 1.22+.

What's in here

  • Currency ratesClient.Currency
  • Server signing keyClient.ServerKey (/bank/sync, secp256k1)
  • Webhook signature verificationVerifyWebhookSignature, ServerKey.Verify
  • WebhookHandler — drop-in http.Handler with key cache + rotation, parser, deduper, error reporting
  • Webhook parserParseWebHook
  • MemoryDeduper — LRU set so mono's 60s/600s retries don't run your callback twice
  • MCC enumTransaction.MCCCode().Category() buckets ISO 18245 codes into Groceries / Fuel / Restaurants / Telecom / Transfer / etc.
  • CurrencyCode enum — typed ISO 4217 codes (UAH/USD/EUR/...) with .String() for the alphabetic name
  • RetryWithRetry(attempts, base, max) honouring Retry-After on 5xx and 429
  • TransactionsRange — paginate /personal/statement in 31-day windows
  • Personal & Corporate API clients — token / X-Sign+X-Key-Id authorization
  • RegistrationStatusPOST /personal/auth/registration/status

Quick start: receive a signed webhook

package main

import (
    "context"
    "log"
    "net/http"

    "github.com/OlexiyOdarchuk/monobank-sdk"
)

func main() {
    client := monobank.NewClient(nil)

    h, err := monobank.NewWebhookHandler(context.Background(), monobank.WebhookHandlerOptions{
        Keys:  client,
        Dedup: monobank.NewMemoryDeduper(1024),
        OnEvent: func(_ context.Context, e *monobank.WebHookResponse) error {
            t := e.Data.Transaction
            log.Printf("%s · %d %s · %s",
                e.Data.AccountID,
                t.Amount,
                monobank.CurrencyCode(t.CurrencyCode),
                t.MCCCode().Category())
            return nil
        },
    })
    if err != nil { log.Fatal(err) }

    http.Handle("/webhook", h)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Subscribe your public URL to mono once via PersonalClient.SetWebHook(ctx, "https://you/webhook").

Quick start: personal-API client

client := monobank.NewPersonalClient(nil).
    WithAuth(monobank.NewPersonalAuthorizer(os.Getenv("MONO_TOKEN")))

info, _ := client.ClientInfo(context.Background())
fmt.Println(info.Name, len(info.Accounts), "accounts")

Relationship to vtopc/go-monobank

The upstream library is intentionally a thin client wrapper — no server helpers, no functional options, no enums. This SDK keeps every endpoint the upstream supports (so migration is a single import swap) and adds the rest. If you want minimalism, use the upstream; if you want batteries, use this one.

The MIT license carries the upstream copyright forward.

License

MIT — see LICENSE.

Documentation

Overview

Package monobank is a Go client for the monobank REST API (https://api.monobank.ua/docs/).

It covers three authorization modes:

  • Public — no auth; see PublicAPI.
  • Personal — a single user's token; see PersonalAPI.
  • Corporate / providers — service-level access via an ECDSA key pair; see CorporateAPI.

Webhooks

Personal and corporate clients can subscribe a URL to receive statement events as signed POST requests. Verify the signature before trusting the body:

sk, _ := client.ServerKey(ctx)               // cache; refresh on rotation
err := sk.Verify(body, r.Header.Get("X-Sign"))
event, _ := monobank.ParseWebHook(body)

See examples/webhook for a complete handler.

Index

Examples

Constants

View Source
const (
	// PermSt - statements(transactions) and client info of individual(фізичної особи).
	PermSt = "s"
	// PermPI - personal information(first and last names).
	PermPI = "p"
	// PermFOP - statements(transactions) and client info of private entrepreneur(ФОП).
	PermFOP = "f"
)

Permissions.

View Source
const MaxStatementWindow = 31 * 24 * time.Hour

MaxStatementWindow is the longest range /personal/statement accepts in a single call. Use [commonClient.TransactionsRange] to span longer ranges.

View Source
const (
	// WebHookTypeStatementItem — a single bank-account statement entry.
	WebHookTypeStatementItem = "StatementItem"
)

Known WebHookResponse.Type values.

Variables

View Source
var (
	ErrDecodePrivateKey  = errors.New("failed to decode private key")
	ErrEncodePublicKey   = errors.New("failed to encode public key with sha1")
	ErrNoPrivateKey      = errors.New("failed to find private key block")
	ErrInvalidEC         = errors.New("invalid elliptic curve private key value")
	ErrInvalidPrivateKey = errors.New("invalid private key length")
)

Errors.

View Source
var (
	ErrEmptyRequest = errors.New("empty request")
	ErrInvalidURL   = errors.New("invalid URL")
)

Errors.

View Source
var (
	// ErrBadSignature is returned by VerifyWebhookSignature when the
	// signature does not match. Use errors.Is to detect it.
	ErrBadSignature = errors.New("webhook signature is invalid")
	// ErrBadSignatureEncoding is returned when X-Sign is not valid base64.
	ErrBadSignatureEncoding = errors.New("X-Sign is not valid base64")
	// ErrMissingPubKey is returned when verification is attempted with a nil key.
	ErrMissingPubKey = errors.New("missing public key")
	// ErrUnknownWebHookType is returned by ParseWebHook when the payload's
	// top-level "type" is not a recognised WebHookType* constant.
	ErrUnknownWebHookType = errors.New("unknown webhook type")
)

Webhook errors.

View Source
var (
	ErrNilKeyProvider = errors.New("WebhookHandlerOptions.Keys is required")
	ErrNilOnEvent     = errors.New("WebhookHandlerOptions.OnEvent is required")
)

Webhook-handler configuration errors.

View Source
var ErrEmptyAuthMaker = errors.New("authMaker is nil")
View Source
var ErrInvalidPubKey = errors.New("invalid serverPubKey: not an uncompressed secp256k1 point")

ErrInvalidPubKey is returned by Client.ServerKey when /bank/sync returns a serverPubKey that isn't a valid uncompressed secp256k1 point.

Functions

func VerifyWebhookSignature

func VerifyWebhookSignature(pub *ecdsa.PublicKey, body []byte, xSign string) error

VerifyWebhookSignature returns nil iff xSign is a valid ECDSA signature of body produced by the bank's serverPubKey.

Mono currently encodes the signature as a raw 64-byte r||s pair (base64); ASN.1 DER is accepted as a fallback so future encoding changes don't break callers.

Types

type APIError

type APIError struct {
	Method              string
	URL                 string
	StatusCode          int
	ExpectedStatusCodes []int
	Body                []byte
}

APIError is returned when monobank's HTTP response does not match any of the expected status codes.

func (*APIError) Error

func (e *APIError) Error() string

type Account

type Account struct {
	AccountID    string   `json:"id"`
	SendID       string   `json:"sendId"`
	Balance      int64    `json:"balance"`
	CreditLimit  int64    `json:"creditLimit"`
	CurrencyCode int      `json:"currencyCode"`
	CashbackType string   `json:"cashbackType"` // enum: None, UAH, Miles
	CardMasks    []string `json:"maskedPan"`    // card number masks
	Type         CardType `json:"type"`
	IBAN         string   `json:"iban"`
}

type Accounts

type Accounts []Account

type Authorizer

type Authorizer interface {
	// SetAuth modifies http.Request and sets authorization tokens
	SetAuth(*http.Request) error
}

type CardType

type CardType string
const (
	Black    CardType = "black"    //
	White    CardType = "white"    //
	Platinum CardType = "platinum" //
	Iron     CardType = "iron"     //
	FOP      CardType = "fop"      // ФОП
	Yellow   CardType = "yellow"   //
	EAid     CardType = "eAid"     // єПідтримка
	Diia     CardType = "diia"     // Дія.Картка
)

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the unauthenticated base client. PersonalClient and CorporateClient embed it.

func New

func New(opts ...Option) Client

New returns a public Client built from the supplied options. Extensible counterpart to NewClient.

c := monobank.New(
    monobank.WithHTTPClient(myHTTP),
    monobank.WithRetry(5, 0, 0),
)
Example (WithRetry)
// New + WithRetry — automatic backoff on 5xx and 429, honouring
// Retry-After when mono sends it.
client := monobank.New(
	monobank.WithRetry(5, 500*time.Millisecond, 30*time.Second),
)
_ = client

func NewClient

func NewClient(client *http.Client) Client

NewClient returns a public Client using the given *http.Client (nil → default). Prefer New for new code.

Example
client := monobank.NewClient(nil)

rates, err := client.Currency(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Printf("%d currency pairs\n", len(rates))

func (Client) Currency

func (c Client) Currency(ctx context.Context) (Currencies, error)

func (Client) ServerKey

func (c Client) ServerKey(ctx context.Context) (*ServerKey, error)

ServerKey fetches the bank's public key used to sign webhooks.

The /bank/sync endpoint is public — no token is required. Cache the result and refresh whenever an incoming X-Key-Id stops matching ServerKey.ID; that is mono's signal that it rotated the key.

Example
client := monobank.NewClient(nil)

sk, err := client.ServerKey(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Printf("keyId=%s serverTime=%s\n", sk.ID, sk.ServerTime.UTC().Format(time.RFC3339))

func (*Client) WithBaseURL

func (c *Client) WithBaseURL(uri string)

WithBaseURL overrides the base URL. Kept for backwards compatibility; new code passes WithBaseURL to New.

type ClientInfo

type ClientInfo struct {
	ID         string   `json:"clientId"`
	Name       string   `json:"name"`
	WebHookURL string   `json:"webHookUrl"`
	Accounts   Accounts `json:"accounts"`
	Jars       Jars     `json:"jars"`
}

ClientInfo - client/user info Personal API - https://api.monobank.ua/docs/#/definitions/UserInfo Corporate API - https://api.monobank.ua/docs/corporate.html#/definitions/UserInfo

type CommonAPI

type CommonAPI interface {
	PublicAPI

	// SetWebHook - sets webhook for statements
	SetWebHook(ctx context.Context, uri string) error
}

type CorpAuth

type CorpAuth struct {
	*CorpAuthMaker
	// contains filtered or unexported fields
}

func (CorpAuth) SetAuth

func (a CorpAuth) SetAuth(r *http.Request) error

type CorpAuthMaker

type CorpAuthMaker struct {
	KeyID string // X-Key-Id - ID key of the service
	// contains filtered or unexported fields
}

func NewCorpAuthMaker

func NewCorpAuthMaker(secKey []byte) (*CorpAuthMaker, error)

func (*CorpAuthMaker) New

func (c *CorpAuthMaker) New(requestID string) Authorizer

func (*CorpAuthMaker) NewPermissions

func (c *CorpAuthMaker) NewPermissions(permissions ...string) Authorizer

type CorpAuthMakerAPI

type CorpAuthMakerAPI interface {
	// New returns corp Authorizer for endpoints with Request ID.
	New(requestID string) Authorizer

	// NewPermissions returns corp Authorizer for Auth endpoint to get Request ID.
	// Omitting permissions means all permissions.
	NewPermissions(permissions ...string) Authorizer
}

type CorpSettings

type CorpSettings struct {
	Pubkey     string  `json:"pubkey"`
	Name       string  `json:"name"`
	Permission string  `json:"permission"`
	Webhook    *string `json:"webhook"`
}

type CorporateAPI

type CorporateAPI interface {
	CommonAPI

	// Settings returns information about company.
	// https://api.monobank.ua/docs/corporate.html#tag/Avtorizaciya-ta-nalashtuvannya-kompaniyi/paths/~1personal~1corp~1settings/get
	Settings(ctx context.Context) (*CorpSettings, error)

	// RegistrationStatus checks whether a corporate-API registration has
	// been approved by the bank. pubkeyPEM is the PEM-encoded secp256k1
	// public key originally submitted via /personal/auth/registration.
	// https://api.monobank.ua/docs/corporate.html#tag/Avtorizaciya-ta-nalashtuvannya-kompaniyi/paths/~1personal~1auth~1registration~1status/post
	RegistrationStatus(ctx context.Context, pubkeyPEM []byte) (*RegistrationStatusResponse, error)

	// Auth initializes client access.
	// https://api.monobank.ua/docs/corporate.html#tag/Kliyentski-personalni-dani/paths/~1personal~1auth~1request/post
	Auth(ctx context.Context, callbackURL string, permissions ...string) (*TokenRequest, error)

	// CheckAuth checks status of request for client's personal data.
	// https://api.monobank.ua/docs/corporate.html#tag/Kliyentski-personalni-dani/paths/~1personal~1auth~1request/get
	CheckAuth(ctx context.Context, requestID string) error

	// ClientInfo
	// https://api.monobank.ua/docs/corporate.html#tag/Kliyentski-personalni-dani/paths/~1personal~1client-info/get
	ClientInfo(ctx context.Context, requestID string) (*ClientInfo, error)

	// Transactions - gets bank account statements(transactions)
	// https://api.monobank.ua/docs/corporate.html#tag/Kliyentski-personalni-dani/paths/~1personal~1statement~1{account}~1{from}~1{to}/get
	Transactions(ctx context.Context, requestID, accountID string, from, to time.Time) (Transactions, error)
}

type CorporateClient

type CorporateClient struct {
	// contains filtered or unexported fields
}

func NewCorporate

func NewCorporate(authMaker CorpAuthMakerAPI, opts ...Option) (CorporateClient, error)

NewCorporate returns a CorporateClient built from the supplied options. authMaker is required.

c, err := monobank.NewCorporate(authMaker, monobank.WithRetry(5, 0, 0))

func NewCorporateClient

func NewCorporateClient(client *http.Client, authMaker CorpAuthMakerAPI) (CorporateClient, error)

NewCorporateClient returns corporate client

func (CorporateClient) Auth

func (c CorporateClient) Auth(ctx context.Context, callbackURL string, permissions ...string) (*TokenRequest, error)

Auth initializes access.

func (CorporateClient) CheckAuth

func (c CorporateClient) CheckAuth(ctx context.Context, requestID string) error

func (CorporateClient) ClientInfo

func (c CorporateClient) ClientInfo(ctx context.Context, requestID string) (*ClientInfo, error)

func (CorporateClient) RegistrationStatus

func (c CorporateClient) RegistrationStatus(ctx context.Context, pubkeyPEM []byte) (
	*RegistrationStatusResponse, error)

func (CorporateClient) SetWebHook

func (c CorporateClient) SetWebHook(ctx context.Context, uri string) error

SetWebHook sets webhook for corporate API.

func (CorporateClient) Settings

func (c CorporateClient) Settings(ctx context.Context) (*CorpSettings, error)

func (CorporateClient) Transactions

func (c CorporateClient) Transactions(ctx context.Context, requestID, accountID string, from, to time.Time) (Transactions, error)

func (CorporateClient) TransactionsRange

func (c CorporateClient) TransactionsRange(ctx context.Context, accountID string, from, to time.Time) (
	Transactions, error)

TransactionsRange returns statements across an arbitrary date range by splitting it into successive 31-day windows (mono's single-call limit) and concatenating the results in chronological order.

If to is zero or before from the call returns nil, nil.

type Currencies

type Currencies []Currency

type Currency

type Currency struct {
	CurrencyCodeA int           `json:"currencyCodeA"`
	CurrencyCodeB int           `json:"currencyCodeB"`
	Date          epoch.Seconds `json:"date"`
	RateSell      float64       `json:"rateSell"`
	RateBuy       float64       `json:"rateBuy"`
	RateCross     float64       `json:"rateCross"`
}

type CurrencyCode

type CurrencyCode int

CurrencyCode is an ISO 4217 numeric currency code. The bank returns it as a plain int in payloads (Account.CurrencyCode, Transaction.CurrencyCode, Jar.CurrencyCode, Currency.CurrencyCodeA/B); callers can convert with monobank.CurrencyCode(t.CurrencyCode) for type-safe comparisons.

Example
c := monobank.CurrencyCode(840)
fmt.Println(c)
fmt.Println(c == monobank.USD)
Output:
USD
true
const (
	UAH CurrencyCode = 980 // Ukrainian hryvnia (account default)
	USD CurrencyCode = 840 // US dollar
	EUR CurrencyCode = 978 // Euro
	GBP CurrencyCode = 826 // Pound sterling
	PLN CurrencyCode = 985 // Polish złoty
	CHF CurrencyCode = 756 // Swiss franc
	JPY CurrencyCode = 392 // Japanese yen
	CZK CurrencyCode = 203 // Czech koruna
	CAD CurrencyCode = 124 // Canadian dollar
	AUD CurrencyCode = 36  // Australian dollar
	CNY CurrencyCode = 156 // Chinese yuan
)

Currency codes seen in monobank payloads. Not exhaustive — extend as needed.

func (CurrencyCode) String

func (c CurrencyCode) String() string

String returns the ISO 4217 alphabetic code (e.g. "UAH") if known, otherwise the numeric code as a decimal string.

type Deduper

type Deduper interface {
	// Has reports whether id has been recorded by a previous Add.
	Has(id string) bool
	// Add records id as processed. It is safe to call Add for the same
	// id more than once.
	Add(id string)
}

Deduper remembers transaction IDs the handler has already processed successfully so it can short-circuit retries from mono.

Mono retries failed deliveries after 60s and 600s. The handler calls Has(id) before invoking OnEvent and Add(id) only after OnEvent succeeds — that way a transient OnEvent failure (which produces HTTP 500) does not poison the deduper and prevent the next retry from being processed.

The default LRU implementation (NewMemoryDeduper) is safe for concurrent use. Plug in your own (Redis, SQLite, etc.) by satisfying the interface — useful for sharing dedup state across replicas.

type HTTPDoer

type HTTPDoer interface {
	Do(req *http.Request) (*http.Response, error)
}

HTTPDoer is the subset of *http.Client that Client depends on. Any transport implementing it (the stdlib client, a custom round-tripped one, a test fake) plugs in via WithHTTPClient.

type Jar

type Jar struct {
	ID           string `json:"id"`
	SendID       string `json:"sendId"`
	Title        string `json:"title"`
	Description  string `json:"description"`
	CurrencyCode int    `json:"currencyCode"`
	Balance      int64  `json:"balance"`
	Goal         int64  `json:"goal"`
}

type Jars

type Jars []Jar

type KeyProvider

type KeyProvider interface {
	ServerKey(ctx context.Context) (*ServerKey, error)
}

KeyProvider is anything that can fetch the bank's current signing key. Client satisfies it; tests can swap in a fake.

type MCC

type MCC int

MCC is an ISO 18245 Merchant Category Code. Mono populates it on every statement transaction (Transaction.MCC, Transaction.OriginalMCC); use this type's helpers to bucket spending by category without memorising four-digit codes.

func (MCC) Category

func (c MCC) Category() MCCCategory

Category returns a high-level bucket for the MCC based on the ISO 18245 range tables. Unknown codes return CategoryUnknown.

Specific codes are matched before broader ranges (first-match wins).

type MCCCategory

type MCCCategory string

MCCCategory groups merchant categories at the level most apps want for reporting (groceries vs restaurants, fuel vs transport, etc.).

const (
	CategoryUnknown      MCCCategory = "Unknown"
	CategoryAgriculture  MCCCategory = "Agriculture"
	CategoryContracted   MCCCategory = "ContractedServices"
	CategoryTransport    MCCCategory = "Transport"
	CategoryFuel         MCCCategory = "Fuel"
	CategoryUtilities    MCCCategory = "Utilities"
	CategoryRetail       MCCCategory = "Retail"
	CategoryGroceries    MCCCategory = "Groceries"
	CategoryClothing     MCCCategory = "Clothing"
	CategoryEntertain    MCCCategory = "Entertainment"
	CategoryRestaurants  MCCCategory = "Restaurants"
	CategoryHotels       MCCCategory = "Hotels"
	CategoryHealth       MCCCategory = "Health"
	CategoryEducation    MCCCategory = "Education"
	CategoryProfessional MCCCategory = "ProfessionalServices"
	CategoryFinancial    MCCCategory = "Financial"
	CategoryTransfer     MCCCategory = "MoneyTransfer"
	CategoryGovernment   MCCCategory = "Government"
	CategoryTelecom      MCCCategory = "Telecom"
	CategoryCharity      MCCCategory = "Charity"
)

Known categories. Not exhaustive — extend as needed.

type MemoryDeduper

type MemoryDeduper struct {
	// contains filtered or unexported fields
}

MemoryDeduper is a fixed-size LRU set of strings, safe for concurrent use.

func NewMemoryDeduper

func NewMemoryDeduper(capacity int) *MemoryDeduper

NewMemoryDeduper returns an in-memory LRU Deduper of the given capacity. Capacity <= 0 falls back to 1024.

func (*MemoryDeduper) Add

func (d *MemoryDeduper) Add(id string)

Add records id as seen. No-op for empty ids.

func (*MemoryDeduper) Has

func (d *MemoryDeduper) Has(id string) bool

Has reports whether id is currently tracked.

func (*MemoryDeduper) Len

func (d *MemoryDeduper) Len() int

Len returns the number of currently-tracked ids; useful for diagnostics.

type Option

type Option func(*Client)

Option configures a Client. Pass options to New, NewPersonal or NewCorporate. Designed to be additive — future options slot in without breaking existing callers.

func WithBaseURL

func WithBaseURL(uri string) Option

WithBaseURL overrides the default https://api.monobank.ua base URL. Useful for testing against a recorded server or monobank's sandbox.

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) Option

WithHTTPClient sets a custom *http.Client. If nil or not provided, http.DefaultClient is used.

func WithHTTPDoer

func WithHTTPDoer(d HTTPDoer) Option

WithHTTPDoer accepts any HTTPDoer. Useful for plugging in middlewares (circuit breakers, custom transports, test fakes).

func WithRetry

func WithRetry(attempts int, baseDelay, maxDelay time.Duration) Option

WithRetry enables automatic retry for transient HTTP failures (5xx and 429). Backoff is exponential with full jitter; the Retry-After response header is honoured when present.

Pass attempts == 0 to inherit defaults (4 attempts, 500ms base, 30s ceiling). Pass attempts <= 1 to disable retry explicitly. Non-positive baseDelay / maxDelay inherit defaults.

type PersAuth

type PersAuth struct {
	// contains filtered or unexported fields
}

func NewPersonalAuthorizer

func NewPersonalAuthorizer(token string) PersAuth

func (PersAuth) SetAuth

func (a PersAuth) SetAuth(req *http.Request) error

type PersonalAPI

type PersonalAPI interface {
	CommonAPI

	// ClientInfo - https://api.monobank.ua/docs/#/definitions/UserInfo
	ClientInfo(context.Context) (*ClientInfo, error)

	// Transactions - gets bank account statements
	// https://api.monobank.ua/docs/#/definitions/StatementItems
	Transactions(ctx context.Context, accountID string, from, to time.Time) (Transactions, error)
}

type PersonalClient

type PersonalClient struct {
	// contains filtered or unexported fields
}

func NewPersonal

func NewPersonal(opts ...Option) PersonalClient

NewPersonal returns a PersonalClient built from the supplied options. Auth is set up via the WithAuth method on the returned value.

c := monobank.NewPersonal(monobank.WithRetry(5, 0, 0)).
    WithAuth(monobank.NewPersonalAuthorizer(token))

func NewPersonalClient

func NewPersonalClient(client *http.Client) PersonalClient
Example
token := os.Getenv("MONO_TOKEN")
client := monobank.NewPersonalClient(nil).
	WithAuth(monobank.NewPersonalAuthorizer(token))

info, err := client.ClientInfo(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Printf("hello, %s; %d accounts\n", info.Name, len(info.Accounts))

func (PersonalClient) ClientInfo

func (c PersonalClient) ClientInfo(ctx context.Context) (*ClientInfo, error)

func (PersonalClient) SetWebHook

func (c PersonalClient) SetWebHook(ctx context.Context, uri string) error

func (PersonalClient) Transactions

func (c PersonalClient) Transactions(ctx context.Context, accountID string, from, to time.Time) (
	Transactions, error)

func (PersonalClient) TransactionsRange

func (c PersonalClient) TransactionsRange(ctx context.Context, accountID string, from, to time.Time) (
	Transactions, error)

TransactionsRange returns statements across an arbitrary date range by splitting it into successive 31-day windows (mono's single-call limit) and concatenating the results in chronological order.

If to is zero or before from the call returns nil, nil.

func (PersonalClient) WithAuth

func (c PersonalClient) WithAuth(auth Authorizer) PersonalClient

WithAuth returns copy of PersonalClient with authorizer

type PublicAPI

type PublicAPI interface {
	// Currency https://api.monobank.ua/docs/#/definitions/CurrencyInfo
	Currency(context.Context) (Currencies, error)

	// ServerKey fetches the bank's current public key, its identifier and
	// server time. Use it together with VerifyWebhookSignature.
	// https://api.monobank.ua/docs/#tag/Publichni-dani/paths/~1bank~1sync/get
	ServerKey(context.Context) (*ServerKey, error)
}

type PublicAuthorizer

type PublicAuthorizer struct{}

func NewPublicAuthorizer

func NewPublicAuthorizer() PublicAuthorizer

func (PublicAuthorizer) SetAuth

func (a PublicAuthorizer) SetAuth(_ *http.Request) error

type RegistrationStatus

type RegistrationStatus string

RegistrationStatus is the state of a corporate-API registration request.

const (
	RegistrationStatusNew      RegistrationStatus = "New"
	RegistrationStatusDeclined RegistrationStatus = "Declined"
	RegistrationStatusApproved RegistrationStatus = "Approved"
)

Possible RegistrationStatus values.

type RegistrationStatusRequest

type RegistrationStatusRequest struct {
	Pubkey []byte `json:"pubkey"`
}

RegistrationStatusRequest is the body of POST /personal/auth/registration/status. Pubkey is the PEM file containing the secp256k1 public key submitted during registration. encoding/json base64-encodes []byte fields automatically, so the wire format is the base64 string the bank expects.

type RegistrationStatusResponse

type RegistrationStatusResponse struct {
	Status RegistrationStatus `json:"status"`
	KeyID  string             `json:"keyId"`
}

RegistrationStatusResponse is the body returned by POST /personal/auth/registration/status. KeyID is set once mono approves the registration and corresponds to the X-Key-Id the corporate client must use thereafter.

type ServerKey

type ServerKey struct {
	ID         string
	PubKey     *ecdsa.PublicKey
	ServerTime time.Time
}

ServerKey is the bank's current ECDSA (secp256k1) public key together with its identifier and the server time at the moment of the call. Returned by Client.ServerKey and consumed by VerifyWebhookSignature / ServerKey.Verify.

The X-Key-Id header on every incoming webhook equals [ServerKey.ID] for the key that signed it; when it stops matching, mono has rotated the key and the caller should re-fetch.

func (*ServerKey) Verify

func (k *ServerKey) Verify(body []byte, xSign string) error

Verify is a convenience wrapper around VerifyWebhookSignature using this key as the verifier.

type TokenRequest

type TokenRequest struct {
	RequestID string `json:"tokenRequestId"` // Unique token request ID.
	AcceptURL string `json:"acceptUrl"`      // URL to redirect client or build QR on top of it.
}

type Transaction

type Transaction struct {
	ID          string        `json:"id"`
	Time        epoch.Seconds `json:"time"`
	Description string        `json:"description"`
	MCC         int32         `json:"mcc"`
	OriginalMCC int32         `json:"originalMcc"`
	Hold        bool          `json:"hold"`
	// Amount in the account currency
	Amount int64 `json:"amount"`
	// OperationAmount in the transaction currency or amount after double conversion
	OperationAmount int64 `json:"operationAmount"`
	// ISO 4217 numeric code
	CurrencyCode   int    `json:"currencyCode"`
	CommissionRate int64  `json:"commissionRate"`
	CashbackAmount int64  `json:"cashbackAmount"`
	Balance        int64  `json:"balance"`
	Comment        string `json:"comment"`
	// For withdrawal only.
	ReceiptID string `json:"receiptId"`
	// For fop(ФОП) accounts only.
	InvoiceID string `json:"invoiceId"`
	// For fop(ФОП) accounts only.
	EDRPOU string `json:"counterEdrpou"`
	// For fop(ФОП) accounts only.
	IBAN string `json:"counterIban"`
}

Transaction - bank account statement

func (Transaction) MCCCode

func (t Transaction) MCCCode() MCC

MCC returns the typed MCC for a transaction's numeric code.

func (Transaction) OriginalMCCCode

func (t Transaction) OriginalMCCCode() MCC

OriginalMCCCode is the pre-categorisation MCC from the acquirer; mono sometimes remaps MCC (e.g. for cashback), keeping the original here.

type Transactions

type Transactions []Transaction

Transactions - transactions

type WebHookData

type WebHookData struct {
	AccountID   string      `json:"account"`
	Transaction Transaction `json:"statementItem"`
}

type WebHookRequest

type WebHookRequest struct {
	WebHookURL string `json:"webHookUrl"`
}

type WebHookResponse

type WebHookResponse struct {
	Type string      `json:"type"` // see WebHookType* constants
	Data WebHookData `json:"data"`
}

func ParseWebHook

func ParseWebHook(body []byte) (*WebHookResponse, error)

ParseWebHook decodes a raw webhook body into a WebHookResponse. If the payload's "type" is not a known WebHookType* constant, the response is still returned but wrapped in ErrUnknownWebHookType so callers can opt out of processing unfamiliar events.

type WebhookHandler

type WebhookHandler struct {
	// contains filtered or unexported fields
}

WebhookHandler is a ready-to-mount http.Handler that receives signed webhooks from monobank. Behaviour:

  • GET /your-webhook-path — returns 200 (mono pings the URL with GET when you subscribe a webhook to confirm it's alive).
  • POST — reads the body, verifies X-Sign against the cached server key (re-fetching if X-Key-Id changed), decodes the payload and calls OnEvent. Bad signature → 401; OnEvent error → 500; otherwise 200.

Mount it on any router (net/http, gin via http.HandlerFunc, chi, …):

h, err := monobank.NewWebhookHandler(monobank.WebhookHandlerOptions{
    Keys:    client,
    OnEvent: func(ctx context.Context, e *monobank.WebHookResponse) error {
        log.Printf("%+v", e.Data.Transaction)
        return nil
    },
})
if err != nil { log.Fatal(err) }
http.Handle("/webhook", h)

func NewWebhookHandler

func NewWebhookHandler(ctx context.Context, opts WebhookHandlerOptions) (*WebhookHandler, error)

NewWebhookHandler validates opts and primes the key cache. Use a non-cancellable context here unless start-up time is bounded by the caller — the handler is unusable without an initial key.

Example
client := monobank.NewClient(nil)

h, err := monobank.NewWebhookHandler(context.Background(), monobank.WebhookHandlerOptions{
	Keys: client,
	OnEvent: func(_ context.Context, e *monobank.WebHookResponse) error {
		t := e.Data.Transaction
		log.Printf("%s %d %q", e.Data.AccountID, t.Amount, t.Description)
		return nil
	},
})
if err != nil {
	log.Fatal(err)
}
http.Handle("/webhook", h)

func (*WebhookHandler) KeyID

func (h *WebhookHandler) KeyID() string

KeyID returns the cached server-key identifier. Mainly for diagnostics.

func (*WebhookHandler) ServeHTTP

func (h *WebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

type WebhookHandlerOptions

type WebhookHandlerOptions struct {
	// Keys is required. The handler calls Keys.ServerKey on start-up to
	// load the bank's public key, and again whenever an incoming
	// X-Key-Id stops matching the cached one (i.e. mono rotated).
	Keys KeyProvider

	// OnEvent is required. It receives the verified, parsed webhook
	// payload. Returning a non-nil error makes the handler respond with
	// HTTP 500 so mono retries the delivery; returning nil acks with 200.
	OnEvent func(ctx context.Context, event *WebHookResponse) error

	// OnUnknownType is optional. When set, it receives payloads that pass
	// signature verification but whose top-level "type" is not a known
	// WebHookType* constant. If left nil the handler silently ACKs (200);
	// payloads are authentic, just unfamiliar.
	OnUnknownType func(ctx context.Context, raw []byte)

	// OnError is optional. It is invoked for internal failures the
	// handler can't surface through HTTP (e.g. a key refresh that fails
	// while still trying to verify a request). Use it for logging.
	OnError func(err error)

	// Dedup is optional. When set, the handler consults it before calling
	// OnEvent and skips duplicates (responding 200 so mono stops retrying).
	// Mono retries failed deliveries after 60s and 600s — without a deduper,
	// an event your OnEvent successfully processed but failed to ACK might
	// be processed twice.
	Dedup Deduper
}

WebhookHandlerOptions configures NewWebhookHandler.

Directories

Path Synopsis
examples
webhook command
Command webhook receives signed monobank personal-API webhooks and prints a one-line summary of each transaction.
Command webhook receives signed monobank personal-API webhooks and prints a one-line summary of each transaction.

Jump to

Keyboard shortcuts

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