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 ¶
- Constants
- Variables
- func VerifyWebhookSignature(pub *ecdsa.PublicKey, body []byte, xSign string) error
- type APIError
- type Account
- type Accounts
- type Authorizer
- type CardType
- type Client
- type ClientInfo
- type CommonAPI
- type CorpAuth
- type CorpAuthMaker
- type CorpAuthMakerAPI
- type CorpSettings
- type CorporateAPI
- type CorporateClient
- func (c CorporateClient) Auth(ctx context.Context, callbackURL string, permissions ...string) (*TokenRequest, error)
- func (c CorporateClient) CheckAuth(ctx context.Context, requestID string) error
- func (c CorporateClient) ClientInfo(ctx context.Context, requestID string) (*ClientInfo, error)
- func (c CorporateClient) RegistrationStatus(ctx context.Context, pubkeyPEM []byte) (*RegistrationStatusResponse, error)
- func (c CorporateClient) SetWebHook(ctx context.Context, uri string) error
- func (c CorporateClient) Settings(ctx context.Context) (*CorpSettings, error)
- func (c CorporateClient) Transactions(ctx context.Context, requestID, accountID string, from, to time.Time) (Transactions, error)
- func (c CorporateClient) TransactionsRange(ctx context.Context, accountID string, from, to time.Time) (Transactions, error)
- type Currencies
- type Currency
- type CurrencyCode
- type Deduper
- type HTTPDoer
- type Jar
- type Jars
- type KeyProvider
- type MCC
- type MCCCategory
- type MemoryDeduper
- type Option
- type PersAuth
- type PersonalAPI
- type PersonalClient
- func (c PersonalClient) ClientInfo(ctx context.Context) (*ClientInfo, error)
- func (c PersonalClient) SetWebHook(ctx context.Context, uri string) error
- func (c PersonalClient) Transactions(ctx context.Context, accountID string, from, to time.Time) (Transactions, error)
- func (c PersonalClient) TransactionsRange(ctx context.Context, accountID string, from, to time.Time) (Transactions, error)
- func (c PersonalClient) WithAuth(auth Authorizer) PersonalClient
- type PublicAPI
- type PublicAuthorizer
- type RegistrationStatus
- type RegistrationStatusRequest
- type RegistrationStatusResponse
- type ServerKey
- type TokenRequest
- type Transaction
- type Transactions
- type WebHookData
- type WebHookRequest
- type WebHookResponse
- type WebhookHandler
- type WebhookHandlerOptions
Examples ¶
Constants ¶
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.
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.
const (
// WebHookTypeStatementItem — a single bank-account statement entry.
WebHookTypeStatementItem = "StatementItem"
)
Known WebHookResponse.Type values.
Variables ¶
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.
var ( ErrEmptyRequest = errors.New("empty request") ErrInvalidURL = errors.New("invalid URL") )
Errors.
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.
var ( ErrNilKeyProvider = errors.New("WebhookHandlerOptions.Keys is required") ErrNilOnEvent = errors.New("WebhookHandlerOptions.OnEvent is required") )
Webhook-handler configuration errors.
var ErrEmptyAuthMaker = errors.New("authMaker is nil")
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 ¶
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.
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 Authorizer ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the unauthenticated base client. PersonalClient and CorporateClient embed it.
func New ¶
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 ¶
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) ServerKey ¶
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 ¶
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 CorpAuth ¶
type CorpAuth struct {
*CorpAuthMaker
// contains filtered or unexported fields
}
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 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 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 ¶
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 KeyProvider ¶
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 ¶
WithBaseURL overrides the default https://api.monobank.ua base URL. Useful for testing against a recorded server or monobank's sandbox.
func WithHTTPClient ¶
WithHTTPClient sets a custom *http.Client. If nil or not provided, http.DefaultClient is used.
func WithHTTPDoer ¶
WithHTTPDoer accepts any HTTPDoer. Useful for plugging in middlewares (circuit breakers, custom transports, test fakes).
func WithRetry ¶
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 ¶
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 (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
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 ¶
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.
type TokenRequest ¶
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 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.