heleket

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 16 Imported by: 0

README

Heleket Go integration (reference)

CI

A production-grade reference Go SDK for the Heleket cryptocurrency payment API. Covers the full documented surface (payments, payouts, balance, services, exchange rates), ships with typed request/response structs, unit tests (including -race), runnable examples, a debug flag wired into log/slog, automatic retry on transport / 5xx errors, a webhook inspector CLI, and a Docker harness.

Built to be go get'd directly into your project. Zero runtime dependencies — only the Go standard library.

Quickstart

package main

import (
    "context"
    "fmt"
    "log"

    heleket "github.com/heleket/go-sdk"
)

func main() {
    client, err := heleket.NewPaymentClient(merchantID, paymentKey)
    if err != nil {
        log.Fatal(err)
    }

    invoice, err := client.CreateInvoice(context.Background(), heleket.CreateInvoiceRequest{
        Amount:   "15.00",
        Currency: "USD",
        OrderID:  "order-42",
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(invoice.URL) // → https://pay.heleket.com/pay/<uuid>
}

Install

go get github.com/heleket/go-sdk

Requirements: Go 1.22+.

Documentation

Full reference lives in docs/:

What's in the box

go.mod / *.go            Production code — zero deps beyond the standard library
webhook/                 Subpackage for incoming webhook verification
internal/testutil/       FakeTransport for offline tests
examples/                Twelve runnable programs covering every endpoint
cmd/heleket-webhook-inspect/  CLI for verifying and dumping any webhook payload
docker/                  golang:1.22-alpine multi-stage build
docs/                    Full module documentation

Common tasks

make install              # go mod download
make test                 # go test ./...
make race                 # go test -race ./...
make vet                  # go vet ./...
make staticcheck          # staticcheck ./...
make fmt                  # gofmt -w .
make qa                   # All quality gates
make example-invoice      # Create a real invoice (needs .env)
make example-webhook      # Run the webhook listener on :8000
make docker-shell         # Drop into a containerized Go shell
make build                # Compile heleket-webhook-inspect to bin/
make help                 # Full target list

Built-in resilience

  • Retries. Transport errors (DNS, timeouts, broken connections) and HTTP 5xx responses are retried up to 3 times by default with exponential backoff. Tune via heleket.WithMaxRetries(n) or disable with n = 0. Heleket rejects duplicate OrderIDs and returns the existing record, so retrying create-* calls is safe.
  • Response body cap. The SDK refuses to read more than 16 MiB per response by default to protect against memory-exhaustion from a misbehaving server. Tune via heleket.WithMaxResponseBytes.
  • No cross-host redirects. The default *http.Client blocks all redirects so the signed sign header never reaches an unexpected host.
  • HTTPS-only base URL. WithBaseURL accepts https:// for production and http://localhost / 127.0.0.1 for local testing — nothing else.
  • User-Agent. Every request carries heleket-go-sdk/<version>; append your own identifier via heleket.WithUserAgent("myapp/1.0").

Security notes (read this)

  • Always verify webhook signatures. See docs/06-webhooks.md. Never trust the payload otherwise.
  • De-duplicate replays. Use a (uuid, status) key in your DB before doing side-effect work — pattern documented in docs/06-webhooks.md.
  • Whitelist Heleket's webhook source IP 31.133.220.8 at your reverse proxy or firewall.
  • Two separate API keys — payments and payouts. Mixing them breaks webhook verification. (One exception: /v1/payment/refund uses the payout key — call PayoutClient.Refund.)
  • The SDK never logs API keys. Debug-mode output via log/slog includes URL, method, and body — but the sign header and API key are explicitly excluded.

Releasing

Releases are cut from git tags. The version reported in the User-Agent header is the Version constant in config.go, so it moves in lockstep with the tag.

  1. Land changes on main; make sure make qa is green.
  2. Bump Version in config.go and update CHANGELOG.md.
  3. Tag and push — Go tags must be prefixed with v: git tag v0.1.0 && git push origin v0.1.0.

The Go module proxy and pkg.go.dev pick up the tag automatically; consumers then get it with go get github.com/heleket/go-sdk@v0.1.0.

Pre-1.0. While the SDK is in 0.x the public API may still change between minor versions. It is frozen at 1.0.0.

Major versions. Go encodes the major version in the import path: from v2 onward the module path gains a /vN suffix (e.g. github.com/heleket/go-sdk/v2) per the Go module rules. Never delete or move a published tag.

License

MIT — see LICENSE.

Documentation

Overview

Package heleket is a Go SDK for the Heleket cryptocurrency payment API.

See https://doc.heleket.com for the upstream documentation.

Quickstart

import "github.com/heleket/go-sdk"

client, err := heleket.NewPaymentClient(merchantID, paymentKey)
if err != nil {
    log.Fatal(err)
}

invoice, err := client.CreateInvoice(ctx, heleket.CreateInvoiceRequest{
    Amount:   "15.00",
    Currency: "USD",
    OrderID:  "order-42",
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(invoice.URL)

Webhooks

The webhook subpackage handles incoming Heleket callbacks:

import "github.com/heleket/go-sdk/webhook"

verifier := webhook.NewVerifier(paymentKey)
payload, err := verifier.VerifyRaw(rawBody)

Concurrency

PaymentClient and PayoutClient are safe for concurrent use across goroutines.

Errors

All API failures return typed errors that can be inspected with errors.As:

var ve *heleket.ValidationError
if errors.As(err, &ve) {
    for field, msgs := range ve.Fields { /* ... */ }
}

Or matched against sentinel values with errors.Is:

if errors.Is(err, heleket.ErrTransport)  { /* retry */ }
if errors.Is(err, heleket.ErrValidation) { /* show fields to user */ }

Index

Constants

View Source
const DefaultBaseURL = "https://api.heleket.com"

DefaultBaseURL is the production Heleket API base URL.

View Source
const DefaultMaxResponseBytes = 16 << 20 // 16 MiB

DefaultMaxResponseBytes caps the response body the SDK is willing to read, preventing a hostile or misconfigured server from exhausting memory.

View Source
const DefaultMaxRetries = 3

DefaultMaxRetries is the default retry count for transport errors and HTTP 5xx responses. Retries use exponential backoff (100ms, 200ms, 400ms).

View Source
const DefaultTimeout = 30 * time.Second

DefaultTimeout is the default total request timeout.

View Source
const Version = "0.3.0"

Version is the SDK version, surfaced in the User-Agent header.

Variables

View Source
var (
	// ErrAPI matches any *APIError, including *ValidationError.
	ErrAPI = errors.New("heleket: api error")
	// ErrValidation matches *ValidationError specifically (HTTP 422).
	ErrValidation = errors.New("heleket: validation error")
	// ErrTransport matches *HTTPError (DNS, TCP, TLS, timeout).
	ErrTransport = errors.New("heleket: transport error")
	// ErrSignature matches *SignatureError from the webhook subpackage.
	ErrSignature = errors.New("heleket: signature error")
	// ErrPayloadDecode matches *PayloadDecodeError when a signature-valid
	// webhook payload cannot be decoded into the typed Payload struct.
	ErrPayloadDecode = errors.New("heleket: payload decode error")

	// ErrIdentifierRequired is returned when an endpoint requires UUID OR
	// OrderID and neither was set.
	ErrIdentifierRequired = errors.New("heleket: one of UUID or OrderID must be set")
	// ErrInvalidTestWebhookType is returned when TestWebhookRequest.Type is
	// neither "payment" nor "wallet".
	ErrInvalidTestWebhookType = errors.New(`heleket: TestWebhookRequest.Type must be "payment" or "wallet"`)

	// ErrRefundMoved is returned by the deprecated PaymentClient.Refund. The
	// /v1/payment/refund endpoint is now signed with the payout API key, which a
	// PaymentClient does not hold, so refunds moved to PayoutClient.Refund. See
	// UPGRADING.md.
	ErrRefundMoved = errors.New("heleket: refunds moved to PayoutClient.Refund — /v1/payment/refund is now signed with the payout API key; call NewPayoutClient(merchantID, payoutKey).Refund(ctx, req)")
)

Sentinel errors. Use with errors.Is to detect error categories without extracting a typed pointer.

if errors.Is(err, heleket.ErrValidation) { ... }
if errors.Is(err, heleket.ErrTransport)  { ... }

For field-level details (ValidationError.Fields, APIError.HTTPStatus, ...) still use errors.As to recover the concrete type.

Functions

func Sign

func Sign(body []byte, apiKey string) string

Sign returns the Heleket request signature for the given JSON body and API key.

Formula (per https://doc.heleket.com/general/request-format):

sign = md5( base64_encode(json_body) . apiKey )

For requests with no parameters, pass an empty byte slice — base64-encoding it yields the empty string, so the hash collapses to md5(apiKey).

The body argument MUST be the exact bytes that will be sent over the wire. The same Sign function is used to produce outgoing request signatures and to verify incoming webhook signatures.

func SignatureEqual

func SignatureEqual(expected, actual string) bool

SignatureEqual performs a constant-time comparison of two hex signatures. Use this in webhook verification to avoid timing side-channels.

Types

type APIError

type APIError struct {
	Message    string
	HTTPStatus int
	RawBody    []byte
}

APIError is returned when Heleket responds with state != 0 (a business error). HTTPStatus and RawBody carry the underlying response so callers can log or surface the exact server reply.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Is

func (e *APIError) Is(target error) bool

Is reports whether target is ErrAPI. errors.Is(err, ErrAPI) recognises any APIError regardless of the underlying HTTPStatus.

type AmlLink struct {
	Link      string        `json:"link"`
	ExpiredAt string        `json:"expired_at,omitempty"`
	Status    AmlLinkStatus `json:"status,omitempty"`
}

AmlLink is one questionnaire link returned by GetAmlLinks for a blocked (locked) payment. Hand Link to the end user to complete the AML/KYC/SoF questionnaire; completing it can unblock the held payment. ExpiredAt is an ISO-8601 timestamp, and Status is one of the AmlLinkStatus constants.

type AmlLinkStatus

type AmlLinkStatus string

AmlLinkStatus is the status of an AML/KYC/SoF questionnaire link returned per item by PaymentClient.GetAmlLinks for a blocked (locked) payment. "Final" statuses are terminal — the link will not transition further; intermediate statuses may still change while the user works through the questionnaire.

const (
	// AmlLinkStatusInit: link created. Intermediate.
	AmlLinkStatusInit AmlLinkStatus = "init"
	// AmlLinkStatusPending: questionnaire in progress. Intermediate.
	AmlLinkStatusPending AmlLinkStatus = "pending"
	// AmlLinkStatusCompleted: questionnaire completed. Final + successful.
	AmlLinkStatusCompleted AmlLinkStatus = "completed"
	// AmlLinkStatusExpired: link expired before completion. Final.
	AmlLinkStatusExpired AmlLinkStatus = "expired"
)

func (AmlLinkStatus) IsFinal

func (s AmlLinkStatus) IsFinal() bool

IsFinal reports whether the AML link status is terminal.

func (AmlLinkStatus) IsSuccessful

func (s AmlLinkStatus) IsSuccessful() bool

IsSuccessful reports whether the questionnaire was completed successfully.

type Balance

type Balance struct {
	Merchant []WalletBalance `json:"merchant"`
	User     []WalletBalance `json:"user"`
}

Balance is the response of POST /v1/balance.

type BlockStaticWalletRequest

type BlockStaticWalletRequest struct {
	UUID     string `json:"uuid,omitempty"`
	OrderID  string `json:"order_id,omitempty"`
	IsRefund bool   `json:"is_refund,omitempty"`
}

BlockStaticWalletRequest is the body of POST /v1/wallet/block-address.

type BlockedWallet

type BlockedWallet struct {
	UUID    string `json:"uuid"`
	Status  string `json:"status"`
	OrderID string `json:"order_id,omitempty"`
}

BlockedWallet is the response of POST /v1/wallet/block-address.

type CalculateRequest

type CalculateRequest struct {
	Currency   string `json:"currency"`
	Network    string `json:"network"`
	Amount     string `json:"amount"`
	IsSubtract bool   `json:"is_subtract,omitempty"`
}

CalculateRequest is the body of POST /v1/payout/calculate.

type Calculation

type Calculation struct {
	Commission     string `json:"commission"`
	MerchantAmount string `json:"merchant_amount,omitempty"`
	Amount         string `json:"amount,omitempty"`
	Network        string `json:"network,omitempty"`
	Currency       string `json:"currency,omitempty"`
}

Calculation is the result of CalculateWithdrawal.

type Config

type Config struct {
	MerchantID       string
	APIKey           string
	BaseURL          string
	Timeout          time.Duration
	Debug            bool
	Logger           *slog.Logger
	Transport        Transport
	UserAgent        string
	MaxRetries       int
	MaxResponseBytes int64
}

Config holds the per-client configuration. Construct it via NewPaymentClient / NewPayoutClient with the Option-style helpers; do NOT mutate Config fields after a client has been constructed — the client snapshots its config at build time and later changes are ignored at best, racy at worst.

type Conversion

type Conversion struct {
	ToCurrency string `json:"to_currency"`
	Commission string `json:"commission"`
	Rate       string `json:"rate"`
	Amount     string `json:"amount"`
}

Conversion describes an auto-conversion applied by Heleket.

type CourseSource

type CourseSource string

CourseSource identifies the exchange-rate source used when converting fiat invoice amounts. See https://doc.heleket.com/methods/payments/creating-invoice.

const (
	CourseSourceBinance    CourseSource = "Binance"
	CourseSourceBinanceP2P CourseSource = "BinanceP2P"
	CourseSourceExmo       CourseSource = "Exmo"
	CourseSourceKucoin     CourseSource = "Kucoin"
)

type CreateInvoiceRequest

type CreateInvoiceRequest struct {
	// Required.
	Amount   string `json:"amount"`
	Currency string `json:"currency"`
	OrderID  string `json:"order_id"`

	// Optional.
	Network                string            `json:"network,omitempty"`
	URLReturn              string            `json:"url_return,omitempty"`
	URLSuccess             string            `json:"url_success,omitempty"`
	URLCallback            string            `json:"url_callback,omitempty"`
	IsPaymentMultiple      *bool             `json:"is_payment_multiple,omitempty"`
	Lifetime               int               `json:"lifetime,omitempty"`
	ToCurrency             string            `json:"to_currency,omitempty"`
	Subtract               int               `json:"subtract,omitempty"`
	AccuracyPaymentPercent string            `json:"accuracy_payment_percent,omitempty"`
	AdditionalData         string            `json:"additional_data,omitempty"`
	Currencies             []CurrencyNetwork `json:"currencies,omitempty"`
	ExceptCurrencies       []CurrencyNetwork `json:"except_currencies,omitempty"`
	CourseSource           CourseSource      `json:"course_source,omitempty"`
	FromReferralCode       string            `json:"from_referral_code,omitempty"`
	DiscountPercent        int               `json:"discount_percent,omitempty"`
	IsRefresh              *bool             `json:"is_refresh,omitempty"`
	PayerEmail             string            `json:"payer_email,omitempty"`
}

CreateInvoiceRequest is the body of POST /v1/payment. Only Amount, Currency, and OrderID are required. See https://doc.heleket.com/methods/payments/creating-invoice for field semantics.

type CreatePayoutRequest

type CreatePayoutRequest struct {
	// Required.
	Amount     string `json:"amount"`
	Currency   string `json:"currency"`
	OrderID    string `json:"order_id"`
	Address    string `json:"address"`
	IsSubtract bool   `json:"is_subtract"`

	// Optional.
	Network      string `json:"network,omitempty"`
	URLCallback  string `json:"url_callback,omitempty"`
	ToCurrency   string `json:"to_currency,omitempty"`
	CourseSource string `json:"course_source,omitempty"`
	FromCurrency string `json:"from_currency,omitempty"`
	Priority     string `json:"priority,omitempty"`
	Memo         string `json:"memo,omitempty"`
}

CreatePayoutRequest is the body of POST /v1/payout. Required fields: Amount, Currency, OrderID, Address, IsSubtract.

type CreateStaticWalletRequest

type CreateStaticWalletRequest struct {
	Currency    string `json:"currency"`
	Network     string `json:"network"`
	OrderID     string `json:"order_id"`
	URLCallback string `json:"url_callback,omitempty"`
}

CreateStaticWalletRequest is the body of POST /v1/wallet.

type CurrencyNetwork

type CurrencyNetwork struct {
	Currency string `json:"currency"`
	Network  string `json:"network,omitempty"`
}

CurrencyNetwork pairs a currency with an optional network, as accepted by CreateInvoiceRequest.Currencies / ExceptCurrencies.

type ExchangeRate

type ExchangeRate struct {
	From   string `json:"from"`
	To     string `json:"to"`
	Course string `json:"course"`
	Source string `json:"source,omitempty"`
}

ExchangeRate is one entry in the response of /v1/exchange-rate/{currency}/list.

type GenerateQRCodeRequest

type GenerateQRCodeRequest struct {
	MerchantPaymentUUID string `json:"merchant_payment_uuid"`
}

GenerateQRCodeRequest is the body of POST /v1/wallet/qr.

type HTTPError

type HTTPError struct {
	Cause error
}

HTTPError wraps transport-layer failures: DNS, TCP, TLS, timeout, broken pipe. The API was either never reached or no response was received.

func (*HTTPError) Error

func (e *HTTPError) Error() string

func (*HTTPError) Is

func (e *HTTPError) Is(target error) bool

func (*HTTPError) Unwrap

func (e *HTTPError) Unwrap() error

type HTTPTransport

type HTTPTransport struct {
	Client *http.Client

	// MaxResponseBytes caps the response body the transport will read. Set
	// by the SDK from Config.MaxResponseBytes. A value <= 0 means unlimited
	// (not recommended).
	MaxResponseBytes int64
}

HTTPTransport is the default Transport. It uses an *http.Client and forwards request bodies unmodified. The caller-supplied *http.Client controls timeouts, TLS, proxies, and redirect policy.

func NewHTTPTransport

func NewHTTPTransport(client *http.Client) *HTTPTransport

NewHTTPTransport returns a Transport wrapping an *http.Client. If client is nil, an empty *http.Client is created — note that this has no timeout and follows redirects by default. Prefer letting the SDK build its own via newConfig, which sets sensible defaults.

func (*HTTPTransport) RoundTrip

func (t *HTTPTransport) RoundTrip(ctx context.Context, method, url string, headers http.Header, body []byte) (*Response, error)

RoundTrip implements Transport.

type HistoryOptions

type HistoryOptions struct {
	DateFrom string `json:"date_from,omitempty"`
	DateTo   string `json:"date_to,omitempty"`
	Cursor   string `json:"-"` // sent as ?cursor= query string
}

HistoryOptions parameterises the history endpoints. Date format is "YYYY-MM-DD HH:MM:SS". Cursor comes from a prior page's Pagination.NextCursor.

type HistoryPage

type HistoryPage[T any] struct {
	Items    []T        `json:"items"`
	Paginate Pagination `json:"paginate"`
}

HistoryPage is the paginated response shape for /v1/payment/list and /v1/payout/list. T is the per-item type (Invoice or Payout).

type InfoOptions

type InfoOptions struct {
	UUID    string `json:"uuid,omitempty"`
	OrderID string `json:"order_id,omitempty"`
}

InfoOptions identifies an invoice (or payout) for read endpoints. Set exactly one of UUID or OrderID. The server prioritises OrderID if both are sent — the SDK still rejects empty input client-side.

type Invoice

type Invoice struct {
	UUID             string        `json:"uuid"`
	OrderID          string        `json:"order_id"`
	Amount           string        `json:"amount"`
	PaymentAmount    *string       `json:"payment_amount,omitempty"`
	PaymentAmountUSD *string       `json:"payment_amount_usd,omitempty"`
	PayerAmount      string        `json:"payer_amount,omitempty"`
	PayerCurrency    string        `json:"payer_currency,omitempty"`
	Currency         string        `json:"currency"`
	MerchantAmount   *string       `json:"merchant_amount,omitempty"`
	Network          string        `json:"network,omitempty"`
	Address          string        `json:"address,omitempty"`
	From             *string       `json:"from,omitempty"`
	TxID             *string       `json:"txid,omitempty"`
	PaymentStatus    PaymentStatus `json:"payment_status,omitempty"`
	Status           PaymentStatus `json:"status,omitempty"`
	URL              string        `json:"url,omitempty"`
	ExpiredAt        int64         `json:"expired_at,omitempty"`
	IsFinal          bool          `json:"is_final"`
	AdditionalData   *string       `json:"additional_data,omitempty"`
	CreatedAt        string        `json:"created_at,omitempty"`
	UpdatedAt        string        `json:"updated_at,omitempty"`
	Commission       string        `json:"commission,omitempty"`
	AddressQRCode    string        `json:"address_qr_code,omitempty"`
	DiscountPercent  int           `json:"discount_percent,omitempty"`
	Discount         string        `json:"discount,omitempty"`
	Convert          *Conversion   `json:"convert,omitempty"`
}

Invoice is the response of POST /v1/payment and POST /v1/payment/info.

type Option

type Option func(*Config)

Option mutates a Config during client construction.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL overrides the API base URL. The URL must use https://, except for loopback hosts (localhost, 127.0.0.1, ::1) where http:// is also accepted for local testing. Trailing slashes are stripped.

func WithDebug

func WithDebug(enabled bool) Option

WithDebug toggles debug-level logging of requests and responses. When enabled and no logger has been set, the SDK writes to os.Stderr.

func WithHTTPClient

func WithHTTPClient(client *http.Client) Option

WithHTTPClient is a convenience that wraps the given *http.Client in an HTTPTransport. Use this when you want to customise *http.Client (TLS config, proxy, instrumentation) but keep the SDK's default wire behaviour.

The supplied client's CheckRedirect is NOT modified; if you set one, make sure it does not follow cross-host redirects, otherwise the SDK's signed "sign" header may be forwarded to an attacker-controlled host.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger sets the *slog.Logger used by the SDK. The logger receives debug messages only when WithDebug(true) is also active. The "sign" request header and the API key are NEVER passed to the logger.

func WithMaxResponseBytes

func WithMaxResponseBytes(n int64) Option

WithMaxResponseBytes overrides the per-response body cap. Bodies larger than this limit produce *HTTPError instead of being read fully.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets how many times the SDK will retry on transport errors (DNS, connection-reset, timeout) and HTTP 5xx responses. Set to 0 to disable retries entirely. Retries use exponential backoff and respect ctx.

Heleket rejects duplicate OrderIDs and returns the existing record, so retrying create-* endpoints with the same OrderID is safe.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout overrides the default per-request timeout.

func WithTransport

func WithTransport(t Transport) Option

WithTransport injects a custom Transport — for tests or alternative HTTP backends. When unset, the SDK uses an HTTPTransport wrapping a default http.Client with the configured timeout and a no-cross-host-redirect policy.

func WithUserAgent

func WithUserAgent(tokens string) Option

WithUserAgent appends additional tokens to the User-Agent header. The SDK always sends "heleket-go-sdk/<Version>" plus any extra tokens, in the order added. Typical use: WithUserAgent("myapp/1.2 (+https://example.com)").

type Pagination

type Pagination struct {
	Count          int    `json:"count"`
	HasPages       bool   `json:"hasPages"`
	NextCursor     string `json:"nextCursor,omitempty"`
	PreviousCursor string `json:"previousCursor,omitempty"`
	PerPage        int    `json:"perPage"`
}

Pagination is the cursor block returned alongside Items.

type PayloadDecodeError

type PayloadDecodeError struct {
	Cause error
}

PayloadDecodeError is returned by webhook.Verifier when the signature was valid but the typed Payload decode failed. This distinguishes "Heleket signed a payload we couldn't parse" from "signature did not verify."

func (*PayloadDecodeError) Error

func (e *PayloadDecodeError) Error() string

func (*PayloadDecodeError) Is

func (e *PayloadDecodeError) Is(target error) bool

func (*PayloadDecodeError) Unwrap

func (e *PayloadDecodeError) Unwrap() error

type PaymentClient

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

PaymentClient is the client for the Heleket Payments API plus the balance and exchange-rate endpoints. Construct via NewPaymentClient.

PaymentClient is safe for concurrent use across goroutines.

func NewPaymentClient

func NewPaymentClient(merchantID, apiKey string, opts ...Option) (*PaymentClient, error)

NewPaymentClient constructs a PaymentClient with the given merchant credentials.

client, err := heleket.NewPaymentClient(merchantID, paymentKey,
    heleket.WithDebug(true),
    heleket.WithTimeout(60 * time.Second),
)
if err != nil {
    log.Fatal(err)
}

func (*PaymentClient) BlockStaticWallet

func (c *PaymentClient) BlockStaticWallet(ctx context.Context, req BlockStaticWalletRequest) (*BlockedWallet, error)

BlockStaticWallet stops a static wallet from accepting further transfers.

func (*PaymentClient) CreateInvoice

func (c *PaymentClient) CreateInvoice(ctx context.Context, req CreateInvoiceRequest) (*Invoice, error)

CreateInvoice creates a new invoice.

func (*PaymentClient) CreateStaticWallet

func (c *PaymentClient) CreateStaticWallet(ctx context.Context, req CreateStaticWalletRequest) (*StaticWallet, error)

CreateStaticWallet creates a persistent top-up wallet bound to an order ID.

func (*PaymentClient) GenerateQRCode

func (c *PaymentClient) GenerateQRCode(ctx context.Context, req GenerateQRCodeRequest) (*QRCode, error)

GenerateQRCode returns a base64 QR-code image for an existing static wallet.

func (c *PaymentClient) GetAmlLinks(ctx context.Context, opts InfoOptions) ([]AmlLink, error)

GetAmlLinks returns the AML/KYC/SoF questionnaire links for a blocked (locked) payment. Set exactly one of UUID or OrderID; the server prioritises OrderID when both are sent.

Each returned AmlLink carries the questionnaire URL to hand to the end user, its expiry, and a status (see AmlLinkStatus). Completing the questionnaires unblocks a held payment.

func (*PaymentClient) GetBalance

func (c *PaymentClient) GetBalance(ctx context.Context) (*Balance, error)

GetBalance returns merchant + personal balances by currency.

func (*PaymentClient) GetExchangeRates

func (c *PaymentClient) GetExchangeRates(ctx context.Context, currency string) ([]ExchangeRate, error)

GetExchangeRates returns rates for the given fiat currency.

Read-only endpoint: issued as a GET with the currency in the path.

func (*PaymentClient) GetInfo

func (c *PaymentClient) GetInfo(ctx context.Context, opts InfoOptions) (*Invoice, error)

GetInfo looks up an invoice by UUID or OrderID. Set exactly one.

func (*PaymentClient) ListHistory

func (c *PaymentClient) ListHistory(ctx context.Context, opts HistoryOptions) (*HistoryPage[Invoice], error)

ListHistory returns paginated payment history.

func (*PaymentClient) ListServices

func (c *PaymentClient) ListServices(ctx context.Context) ([]Service, error)

ListServices returns the catalogue of supported (currency, network) pairs for payments.

func (*PaymentClient) Refund deprecated

Refund is no longer available on PaymentClient.

The /v1/payment/refund endpoint is now signed with the PAYOUT API key, which a PaymentClient does not hold, so it can no longer produce a valid signature for a refund. This stub returns ErrRefundMoved without issuing a request, rather than silently signing with the wrong key. Use PayoutClient.Refund instead:

payout, _ := heleket.NewPayoutClient(merchantID, payoutKey)
payout.Refund(ctx, req)

Deprecated: refunds moved to PayoutClient.Refund in v0.2.0. See UPGRADING.md.

func (*PaymentClient) RefundBlockedWallet

func (c *PaymentClient) RefundBlockedWallet(ctx context.Context, req RefundBlockedWalletRequest) (*RefundResult, error)

RefundBlockedWallet sends the contents of a blocked wallet to a recovery address.

func (*PaymentClient) ResendWebhook

func (c *PaymentClient) ResendWebhook(ctx context.Context, opts InfoOptions) error

ResendWebhook asks Heleket to redeliver the last webhook for the invoice.

func (*PaymentClient) TestWebhook

func (c *PaymentClient) TestWebhook(ctx context.Context, req TestWebhookRequest) error

TestWebhook sends a synthetic event to the merchant's callback URL.

type PaymentStatus

type PaymentStatus string

PaymentStatus is the status of an invoice. See https://doc.heleket.com/methods/payments/payment-statuses for details.

const (
	// PaymentStatusPaid: exact amount received. Final + successful.
	PaymentStatusPaid PaymentStatus = "paid"
	// PaymentStatusPaidOver: overpayment received. Final + successful.
	PaymentStatusPaidOver PaymentStatus = "paid_over"
	// PaymentStatusWrongAmount: underpaid, no further attempts allowed. Final.
	PaymentStatusWrongAmount PaymentStatus = "wrong_amount"
	// PaymentStatusProcess: payment is being processed. Intermediate.
	PaymentStatusProcess PaymentStatus = "process"
	// PaymentStatusConfirmCheck: seen on-chain; awaiting confirmations. Intermediate.
	PaymentStatusConfirmCheck PaymentStatus = "confirm_check"
	// PaymentStatusWrongAmountWaiting: underpaid, additional top-ups accepted. Intermediate.
	PaymentStatusWrongAmountWaiting PaymentStatus = "wrong_amount_waiting"
	// PaymentStatusCheck: waiting for the transaction to appear on-chain. Intermediate.
	PaymentStatusCheck PaymentStatus = "check"
	// PaymentStatusFail: payment error. Final.
	PaymentStatusFail PaymentStatus = "fail"
	// PaymentStatusCancel: invoice abandoned by the client. Final.
	PaymentStatusCancel PaymentStatus = "cancel"
	// PaymentStatusSystemFail: system-side error. Final.
	PaymentStatusSystemFail PaymentStatus = "system_fail"
	// PaymentStatusRefundProcess: refund in flight. Intermediate.
	PaymentStatusRefundProcess PaymentStatus = "refund_process"
	// PaymentStatusRefundFail: refund failed. Final.
	PaymentStatusRefundFail PaymentStatus = "refund_fail"
	// PaymentStatusRefundPaid: refund completed. Final.
	PaymentStatusRefundPaid PaymentStatus = "refund_paid"
	// PaymentStatusLocked: AML hold. Final.
	PaymentStatusLocked PaymentStatus = "locked"
)

func (PaymentStatus) IsFinal

func (s PaymentStatus) IsFinal() bool

IsFinal reports whether the payment status is terminal — the invoice will not transition further.

func (PaymentStatus) IsSuccessful

func (s PaymentStatus) IsSuccessful() bool

IsSuccessful reports whether the payment represents a successful payment (paid exactly or overpaid).

type Payout

type Payout struct {
	UUID           string          `json:"uuid"`
	Amount         string          `json:"amount"`
	Currency       string          `json:"currency"`
	Commission     string          `json:"commission,omitempty"`
	MerchantAmount string          `json:"merchant_amount,omitempty"`
	Network        string          `json:"network,omitempty"`
	Address        string          `json:"address,omitempty"`
	TxID           *string         `json:"txid,omitempty"`
	Status         PayoutStatus    `json:"status"`
	IsFinal        bool            `json:"is_final"`
	Balance        json.RawMessage `json:"balance,omitempty"`
	PayerCurrency  string          `json:"payer_currency,omitempty"`
	PayerAmount    json.RawMessage `json:"payer_amount,omitempty"`
	OrderID        string          `json:"order_id,omitempty"`
	CreatedAt      string          `json:"created_at,omitempty"`
	UpdatedAt      string          `json:"updated_at,omitempty"`
}

Payout is the response of POST /v1/payout and POST /v1/payout/info.

Balance and PayerAmount are typed as json.RawMessage because the Heleket server returns them in different shapes depending on the endpoint and payment state (object, string, or null). Decode into your own struct or keep as bytes for logging.

type PayoutClient

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

PayoutClient is the client for the Heleket Payouts API and balance transfers. Construct via NewPayoutClient. Uses the payout API key — distinct from the payment key.

PayoutClient is safe for concurrent use across goroutines.

func NewPayoutClient

func NewPayoutClient(merchantID, apiKey string, opts ...Option) (*PayoutClient, error)

NewPayoutClient constructs a PayoutClient with the given merchant credentials.

func (*PayoutClient) CalculateWithdrawal

func (c *PayoutClient) CalculateWithdrawal(ctx context.Context, req CalculateRequest) (*Calculation, error)

CalculateWithdrawal previews the commission and final amount for a payout.

func (*PayoutClient) CreatePayout

func (c *PayoutClient) CreatePayout(ctx context.Context, req CreatePayoutRequest) (*Payout, error)

CreatePayout creates a withdrawal.

func (*PayoutClient) GetInfo

func (c *PayoutClient) GetInfo(ctx context.Context, opts InfoOptions) (*Payout, error)

GetInfo looks up a payout by UUID or OrderID.

func (*PayoutClient) ListHistory

func (c *PayoutClient) ListHistory(ctx context.Context, opts HistoryOptions) (*HistoryPage[Payout], error)

ListHistory returns paginated payout history.

func (*PayoutClient) ListServices

func (c *PayoutClient) ListServices(ctx context.Context) ([]Service, error)

ListServices returns the catalogue of supported (currency, network) pairs for payouts.

func (*PayoutClient) Refund added in v0.2.0

func (c *PayoutClient) Refund(ctx context.Context, req RefundRequest) (*RefundResult, error)

Refund refunds a paid invoice in full or in part.

It targets POST /v1/payment/refund, but that endpoint is signed with the PAYOUT API key — which a PaymentClient does not hold — so the method lives on PayoutClient. Construct the client with your payout key:

payout, _ := heleket.NewPayoutClient(merchantID, payoutKey)
payout.Refund(ctx, heleket.RefundRequest{UUID: invoiceUUID, Address: addr, IsSubtract: true})

Required: Address, IsSubtract; one of UUID / OrderID.

func (*PayoutClient) TransferToBusiness

func (c *PayoutClient) TransferToBusiness(ctx context.Context, req TransferRequest) (*Transfer, error)

TransferToBusiness moves funds from the personal balance to the business balance.

func (*PayoutClient) TransferToPersonal

func (c *PayoutClient) TransferToPersonal(ctx context.Context, req TransferRequest) (*Transfer, error)

TransferToPersonal moves funds from the business balance to the personal wallet.

type PayoutStatus

type PayoutStatus string

PayoutStatus is the status of a withdrawal. See https://doc.heleket.com/methods/payouts/payout-statuses for details.

const (
	PayoutStatusProcess    PayoutStatus = "process"     // intermediate
	PayoutStatusCheck      PayoutStatus = "check"       // intermediate
	PayoutStatusPaid       PayoutStatus = "paid"        // final, successful
	PayoutStatusFail       PayoutStatus = "fail"        // final
	PayoutStatusCancel     PayoutStatus = "cancel"      // final
	PayoutStatusSystemFail PayoutStatus = "system_fail" // final
)

func (PayoutStatus) IsFinal

func (s PayoutStatus) IsFinal() bool

IsFinal reports whether the payout status is terminal.

func (PayoutStatus) IsSuccessful

func (s PayoutStatus) IsSuccessful() bool

IsSuccessful reports whether the payout settled successfully.

type QRCode

type QRCode struct {
	Image string `json:"image"`
}

QRCode is the response of POST /v1/wallet/qr. Image is a base64 data URI.

type RefundBlockedWalletRequest

type RefundBlockedWalletRequest struct {
	UUID    string `json:"uuid"`
	Address string `json:"address"`
}

RefundBlockedWalletRequest is the body of POST /v1/wallet/blocked-address-refund.

type RefundRequest

type RefundRequest struct {
	UUID       string `json:"uuid,omitempty"`
	OrderID    string `json:"order_id,omitempty"`
	Address    string `json:"address"`
	IsSubtract bool   `json:"is_subtract"`
}

RefundRequest is the body of POST /v1/payment/refund.

type RefundResult

type RefundResult struct {
	Commission string `json:"commission,omitempty"`
	Amount     string `json:"amount,omitempty"`
	Address    string `json:"address,omitempty"`
}

RefundResult is returned by refund endpoints.

type Response

type Response struct {
	StatusCode int
	Body       []byte
	Header     http.Header
}

Response captures the status code, raw body, and headers of an HTTP reply. Body is retained verbatim so signature checks and the debug logger see the exact bytes Heleket sent.

type Service

type Service struct {
	Network     string            `json:"network"`
	Currency    string            `json:"currency"`
	IsAvailable bool              `json:"is_available"`
	Limit       ServiceLimit      `json:"limit,omitempty"`
	Commission  ServiceCommission `json:"commission,omitempty"`
}

Service describes a (currency, network) pair available for payments or payouts.

type ServiceCommission

type ServiceCommission struct {
	FeeAmount string `json:"fee_amount,omitempty"`
	Percent   string `json:"percent,omitempty"`
}

ServiceCommission describes the per-pair fees.

type ServiceLimit

type ServiceLimit struct {
	MinAmount string `json:"min_amount,omitempty"`
	MaxAmount string `json:"max_amount,omitempty"`
}

ServiceLimit describes the per-pair amount bounds.

type SignatureError

type SignatureError struct {
	Reason string
}

SignatureError is returned by webhook.Verifier when the signature does not match. Treat as a hard security failure — never trust the payload.

func (*SignatureError) Error

func (e *SignatureError) Error() string

func (*SignatureError) Is

func (e *SignatureError) Is(target error) bool

type StaticWallet

type StaticWallet struct {
	UUID     string `json:"uuid"`
	WalletID string `json:"wallet_uuid,omitempty"`
	OrderID  string `json:"order_id"`
	Address  string `json:"address"`
	Network  string `json:"network"`
	Currency string `json:"currency"`
	URL      string `json:"url,omitempty"`
}

StaticWallet is the response of POST /v1/wallet.

type TestWebhookRequest

type TestWebhookRequest struct {
	Type        string        `json:"-"` // path segment, not body
	URLCallback string        `json:"url_callback"`
	Currency    string        `json:"currency"`
	Network     string        `json:"network"`
	Status      PaymentStatus `json:"status"`
	UUID        string        `json:"uuid,omitempty"`
	OrderID     string        `json:"order_id,omitempty"`
}

TestWebhookRequest triggers a synthetic webhook to the merchant's callback URL. Type must be "payment" or "wallet".

type Transfer

type Transfer struct {
	UserUUID     string `json:"user_wallet_uuid,omitempty"`
	MerchantUUID string `json:"merchant_wallet_uuid,omitempty"`
	Amount       string `json:"amount,omitempty"`
	Currency     string `json:"currency,omitempty"`
}

Transfer is the response of TransferToPersonal / TransferToBusiness.

type TransferRequest

type TransferRequest struct {
	Amount   string `json:"amount"`
	Currency string `json:"currency"`
}

TransferRequest is the body of POST /v1/transfer/to-personal and /v1/transfer/to-business.

type Transport

type Transport interface {
	// RoundTrip sends an HTTP request and returns the response. Implementations
	// MUST forward the body bytes verbatim — the SDK has already signed those
	// exact bytes, and any mutation would invalidate the signature.
	RoundTrip(ctx context.Context, method, url string, headers http.Header, body []byte) (*Response, error)
}

Transport is the HTTP contract the SDK relies on. The default implementation wraps *http.Client. Tests inject internal/testutil.FakeTransport and merchants who want custom HTTP behaviour (proxies, retries, instrumentation) can plug in their own implementation via WithTransport.

type ValidationError

type ValidationError struct {
	*APIError
	Fields map[string][]string
}

ValidationError is returned when Heleket responds with HTTP 422 and a field-level errors map. Fields is keyed by request field name.

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) Is

func (e *ValidationError) Is(target error) bool

Is reports whether target is ErrValidation or ErrAPI. ValidationError is-a APIError, so both sentinels match.

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() error

Unwrap exposes the embedded APIError so errors.As recovers either type.

type WalletBalance

type WalletBalance struct {
	UUID         string `json:"uuid,omitempty"`
	Balance      string `json:"balance"`
	CurrencyCode string `json:"currency_code"`
}

WalletBalance is a per-currency entry in a Balance.

Directories

Path Synopsis
cmd
heleket-webhook-inspect command
Heleket webhook inspector.
Heleket webhook inspector.
examples
aml_links command
create_invoice command
create_payout command
exchange_rates command
get_balance command
get_payout_info command
handle_webhook command
Webhook listener example.
Webhook listener example.
list_services command
refund command
internal
exampleutil
Package exampleutil is shared scaffolding for the examples/ programs.
Package exampleutil is shared scaffolding for the examples/ programs.
testutil
Package testutil provides test doubles for the Heleket SDK.
Package testutil provides test doubles for the Heleket SDK.
Package webhook handles incoming Heleket webhooks.
Package webhook handles incoming Heleket webhooks.

Jump to

Keyboard shortcuts

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