payments

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 20, 2026 License: AGPL-3.0 Imports: 18 Imported by: 0

Documentation

Overview

Package payments is the payment-provider registry: one Descriptor per provider (see registry.go), each wrapping that provider's HTTP API behind a Client — create a payment for an order, query its status (the polling fallback), and authenticate a webhook. All amounts are in whole rubles; crypto providers are invoiced in RUB so the payer sends the crypto equivalent.

Index

Constants

View Source
const (
	ProviderYooKassa  = keyYooKassa
	ProviderCryptoBot = keyCryptoBot
)

Provider identifiers for the two providers the panel shipped with. Every other provider's key lives in its own file; use payments.Get to look one up.

View Source
const DisplayNameKey = "display_name"

DisplayNameKey is a reserved config key holding the operator's custom label for this provider's pay button (shown to users in the bot and on the subscription page). Empty ⇒ fall back to the descriptor's Label. It's a display setting, not a credential, so it isn't part of Fields — the settings API injects it into the form for every provider.

Variables

View Source
var ErrNoStatusAPI = errors.New("провайдер не поддерживает опрос статуса")

ErrNoStatusAPI is returned by Status for providers that expose no way to query a payment. The polling fallback skips them (the webhook is the only confirmation).

Functions

func Label added in v1.0.0

func Label(key string) string

Label is the display name for a provider key ("" ⇒ a manual order).

Types

type AuraPay added in v1.0.0

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

AuraPay is a minimal AuraPay client.

func (*AuraPay) Create added in v1.0.0

func (a *AuraPay) Create(ctx context.Context, req CreateReq) (string, string, error)

Create opens an invoice and returns its id plus the hosted pay URL. The payment method is left unset so the payer picks it on the AuraPay page.

func (*AuraPay) Status added in v1.0.0

func (a *AuraPay) Status(ctx context.Context, providerID string) (Result, error)

Status re-reads an invoice by its id.

func (*AuraPay) Webhook added in v1.0.0

func (a *AuraPay) Webhook(ctx context.Context, body []byte, _ http.Header) (string, Result, error)

Webhook doesn't trust the callback body: it re-fetches over the authenticated API.

type Client added in v1.0.0

type Client interface {
	// Create opens a payment and returns the provider's id for it plus the hosted
	// URL to send the payer to.
	Create(ctx context.Context, req CreateReq) (providerID, payURL string, err error)

	// Status re-reads a payment (the fallback for a missed webhook). Providers with
	// no status endpoint return ErrNoStatusAPI and are confirmed by webhook only.
	Status(ctx context.Context, providerID string) (Result, error)

	// Webhook authenticates a callback and reports what it says about the payment.
	// It MUST reject anything it cannot prove came from the provider: either by
	// checking a signature, or — when the provider signs nothing — by re-fetching the
	// payment over the API and reporting that instead of trusting the body.
	Webhook(ctx context.Context, body []byte, h http.Header) (providerID string, res Result, err error)
}

Client talks to one configured provider.

type Config added in v1.0.0

type Config map[string]string

Config is a provider's saved credentials: field key → value. Bools are "1"/"".

func (Config) Bool added in v1.0.0

func (c Config) Bool(key string) bool

func (Config) Get added in v1.0.0

func (c Config) Get(key string) string

type CreateReq added in v1.0.0

type CreateReq struct {
	AmountRub   int
	OrderID     int64  // our order id; providers echo it back in the webhook
	Description string // human text shown on the payment page
	ReturnURL   string // where to send the payer after paying
	WebhookURL  string // our callback URL, for providers that take it per-payment
	Email       string // payer email, for providers that require a receipt
}

CreateReq is the panel's request to open a payment for one order. Amounts are whole rubles — the panel prices plans in rubles and every provider here bills in RUB (crypto ones convert at their own rate).

type CryptoBot

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

CryptoBot is a minimal Crypto Pay API client. testnet switches to the @CryptoTestnetBot sandbox endpoint (use a testnet app token).

func NewCryptoBot

func NewCryptoBot(token string, testnet bool) *CryptoBot

func (*CryptoBot) Create added in v1.0.0

func (c *CryptoBot) Create(ctx context.Context, req CreateReq) (string, string, error)

Create implements Client.

func (*CryptoBot) CreateInvoice

func (c *CryptoBot) CreateInvoice(ctx context.Context, amountRub int, orderID int64, description string) (invoiceID, payURL string, err error)

CreateInvoice creates a fiat (RUB) invoice — the user pays the crypto equivalent — and returns its id + the pay URL to open in Telegram.

func (*CryptoBot) InvoiceStatus

func (c *CryptoBot) InvoiceStatus(ctx context.Context, invoiceID string) (Result, error)

InvoiceStatus returns the normalised status of an invoice plus the amount CryptoBot recorded for it. Invoices are created as fiat (RUB), so "amount" is the fiat amount (a decimal string) and "fiat" its currency — NOT paid_amount, which is the crypto the payer actually sent.

func (*CryptoBot) Status added in v1.0.0

func (c *CryptoBot) Status(ctx context.Context, providerID string) (Result, error)

Status implements Client.

func (*CryptoBot) VerifyWebhook

func (c *CryptoBot) VerifyWebhook(body []byte, signature string) bool

VerifyWebhook checks the crypto-pay-api-signature header: HMAC-SHA256 of the raw body keyed by SHA256(token), hex-encoded.

func (*CryptoBot) Webhook added in v1.0.0

func (c *CryptoBot) Webhook(_ context.Context, body []byte, h http.Header) (string, Result, error)

Webhook implements Client. The HMAC proves CryptoBot sent the body, so the invoice it carries (including its amount) can be trusted as-is.

type Descriptor added in v1.0.0

type Descriptor struct {
	Key    string  // stable id, stored on the order row and used as the webhook path leaf
	Label  string  // display name, e.g. "ЮКасса"
	Note   string  // one line under the name: methods, currency, who can sign up
	Fields []Field // credentials; also the settings form
	New    func(cfg Config) Client
}

Descriptor is a provider's registry entry.

func All added in v1.0.0

func All() []Descriptor

All returns every known provider, in display order.

func Get added in v1.0.0

func Get(key string) (Descriptor, bool)

Get looks up a provider by key.

func (Descriptor) Configured added in v1.0.0

func (d Descriptor) Configured(cfg Config) bool

Configured reports whether every required field has a value.

func (Descriptor) DisplayName added in v1.0.0

func (d Descriptor) DisplayName(cfg Config) string

DisplayName is the operator-set pay-button label, or the default Label if unset.

type Field added in v1.0.0

type Field struct {
	Key         string        `json:"key"`
	Label       string        `json:"label"`
	Kind        FieldKind     `json:"kind"`
	Placeholder string        `json:"placeholder,omitempty"`
	Help        string        `json:"help,omitempty"`
	Optional    bool          `json:"optional,omitempty"` // not required to consider the provider configured
	Options     []FieldOption `json:"options,omitempty"`  // choices for FieldSelect
}

Field is one credential input in the provider's settings form.

type FieldKind added in v1.0.0

type FieldKind string

FieldKind is how the panel renders a credential field.

const (
	FieldText   FieldKind = "text"   // plain value (shop id, merchant id)
	FieldSecret FieldKind = "secret" // masked; encrypted at rest; empty on save = keep current
	FieldBool   FieldKind = "bool"   // toggle (test mode, sandbox)
	FieldSelect FieldKind = "select" // one of Options (e.g. payment method)
)

type FieldOption added in v1.0.0

type FieldOption struct {
	Value string `json:"value"`
	Label string `json:"label"`
}

FieldOption is one choice in a FieldSelect.

type Heleket added in v1.0.0

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

Heleket is a minimal Heleket/Cryptomus payment client.

func (*Heleket) Create added in v1.0.0

func (h *Heleket) Create(ctx context.Context, req CreateReq) (string, string, error)

Create opens a fiat (RUB) invoice; the payer settles it in crypto.

func (*Heleket) Status added in v1.0.0

func (h *Heleket) Status(ctx context.Context, providerID string) (Result, error)

Status re-reads an invoice by its Heleket uuid.

func (*Heleket) Webhook added in v1.0.0

func (h *Heleket) Webhook(ctx context.Context, body []byte, _ http.Header) (string, Result, error)

Webhook doesn't trust the POST body: it pulls the uuid out and re-fetches the invoice over the signed API, reporting that instead.

type Invoice added in v0.13.0

type Invoice struct {
	Status string `json:"status"`
	Amount string `json:"amount"` // fiat amount for a currency_type=fiat invoice
	Fiat   string `json:"fiat"`   // fiat currency code, e.g. "RUB"
}

Invoice is the subset of CryptoBot's Invoice object we consume. It is returned by getInvoices and is also the body of an invoice_paid webhook payload.

func (Invoice) AsResult added in v0.13.0

func (inv Invoice) AsResult() Result

AsResult normalises an Invoice into a Result. Exported so a webhook payload (which is itself an Invoice) can be verified by the caller.

type Pal24 added in v1.0.0

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

Pal24 is a minimal PayPalych client.

func (*Pal24) Create added in v1.0.0

func (p *Pal24) Create(ctx context.Context, req CreateReq) (string, string, error)

Create opens a bill and returns its id plus the hosted payment page.

func (*Pal24) Status added in v1.0.0

func (p *Pal24) Status(ctx context.Context, providerID string) (Result, error)

Status re-reads a bill by its id.

func (*Pal24) Webhook added in v1.0.0

func (p *Pal24) Webhook(_ context.Context, body []byte, h http.Header) (string, Result, error)

Webhook parses the postback (JSON or form-encoded), checks the MD5 signature, and reports the bill's state.

type PayPear added in v1.0.0

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

PayPear is a minimal PayPear client.

func (*PayPear) Create added in v1.0.0

func (p *PayPear) Create(ctx context.Context, req CreateReq) (string, string, error)

Create opens a payment and returns its id plus the hosted confirmation URL.

func (*PayPear) Status added in v1.0.0

func (p *PayPear) Status(ctx context.Context, providerID string) (Result, error)

Status re-reads a payment by its id.

func (*PayPear) Webhook added in v1.0.0

func (p *PayPear) Webhook(ctx context.Context, body []byte, _ http.Header) (string, Result, error)

Webhook doesn't trust the callback body: it re-fetches over the authenticated API.

type Platega added in v1.0.0

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

Platega is a minimal Platega client.

func (*Platega) Create added in v1.0.0

func (p *Platega) Create(ctx context.Context, req CreateReq) (string, string, error)

Create opens a transaction and returns its id plus the hosted pay URL. With a specific method configured it creates a method-specific payment (v1); with none it uses the v2 endpoint that omits paymentMethod, so Platega shows its own method picker and the payer chooses card vs SBP on the payment page.

func (*Platega) Status added in v1.0.0

func (p *Platega) Status(ctx context.Context, providerID string) (Result, error)

Status re-reads a transaction by its id.

func (*Platega) Webhook added in v1.0.0

func (p *Platega) Webhook(ctx context.Context, body []byte, _ http.Header) (string, Result, error)

Webhook doesn't trust the callback body: it re-fetches the transaction over the authenticated status API and reports that.

type Result added in v0.13.0

type Result struct {
	Status        Status
	AmountKopecks int64  // amount in kopecks (exact; no float), 0 if unknown
	Currency      string // ISO-4217 code, e.g. "RUB"; "" if unknown
}

Result is a queried payment/invoice state plus the amount the provider recorded for it. The caller verifies AmountKopecks/Currency against the order's snapshot before applying a plan, so a tampered or misrouted callback can't grant a plan that was paid for with the wrong amount. AmountKopecks is 0 and Currency "" when the provider response carried no readable amount (the caller then fails open).

type RioPay added in v1.0.0

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

RioPay is a minimal RioPay client.

func (*RioPay) Create added in v1.0.0

func (r *RioPay) Create(ctx context.Context, req CreateReq) (string, string, error)

Create opens an order. RioPay wants the amount as a decimal string in rubles and echoes our order id back in the webhook as externalId.

func (*RioPay) Status added in v1.0.0

func (r *RioPay) Status(ctx context.Context, providerID string) (Result, error)

Status re-reads an order by RioPay's order uuid.

func (*RioPay) Webhook added in v1.0.0

func (r *RioPay) Webhook(_ context.Context, body []byte, h http.Header) (string, Result, error)

Webhook authenticates the raw body against the X-Signature header (HMAC-SHA512).

type RollyPay added in v1.0.0

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

RollyPay is a minimal RollyPay client.

func (*RollyPay) Create added in v1.0.0

func (r *RollyPay) Create(ctx context.Context, req CreateReq) (string, string, error)

Create opens a payment and returns its id plus the hosted pay URL.

func (*RollyPay) Status added in v1.0.0

func (r *RollyPay) Status(ctx context.Context, providerID string) (Result, error)

Status re-reads a payment by its id.

func (*RollyPay) Webhook added in v1.0.0

func (r *RollyPay) Webhook(_ context.Context, body []byte, h http.Header) (string, Result, error)

Webhook authenticates the raw body: HMAC-SHA256 of "{timestamp}.{body}" against the X-Signature header. The timestamp is part of the signed message, so a body or timestamp tampered in transit fails the check.

type SeverPay added in v1.0.0

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

SeverPay is a minimal SeverPay client.

func (*SeverPay) Create added in v1.0.0

func (s *SeverPay) Create(ctx context.Context, req CreateReq) (string, string, error)

Create opens a payin. SeverPay requires a payer email; we synthesise one from the order when the caller has none (the panel doesn't collect payer emails).

func (*SeverPay) Status added in v1.0.0

func (s *SeverPay) Status(ctx context.Context, providerID string) (Result, error)

Status re-reads a payin by its SeverPay numeric id.

func (*SeverPay) Webhook added in v1.0.0

func (s *SeverPay) Webhook(ctx context.Context, body []byte, _ http.Header) (string, Result, error)

Webhook doesn't trust the callback body: it pulls the id and re-fetches over the signed status API, reporting that.

type Status

type Status string

Status is the normalised payment state across providers.

const (
	StatusPending  Status = "pending"  // created, not yet paid
	StatusPaid     Status = "paid"     // money received
	StatusCanceled Status = "canceled" // cancelled / expired / failed
)

type YooKassa

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

YooKassa is a minimal Checkout API client (create payment + status).

func NewYooKassa

func NewYooKassa(shopID, secretKey string) *YooKassa

func (*YooKassa) Create added in v1.0.0

func (y *YooKassa) Create(ctx context.Context, req CreateReq) (string, string, error)

Create implements Client.

func (*YooKassa) CreatePayment

func (y *YooKassa) CreatePayment(ctx context.Context, amountRub int, orderID int64, description, returnURL string) (paymentID, confirmURL string, err error)

CreatePayment creates an auto-capture payment and returns its id + the hosted confirmation URL to send the user to. amountRub is whole rubles.

func (*YooKassa) PaymentStatus

func (y *YooKassa) PaymentStatus(ctx context.Context, paymentID string) (Result, error)

PaymentStatus returns the normalised status of a payment plus the amount YooKassa recorded for it. "amount" is what the payer was charged (a decimal string like "100.00"); note this is deliberately NOT income_amount, which is the net after commission and would never match the order.

func (*YooKassa) Status added in v1.0.0

func (y *YooKassa) Status(ctx context.Context, providerID string) (Result, error)

Status implements Client.

func (*YooKassa) Webhook added in v1.0.0

func (y *YooKassa) Webhook(ctx context.Context, body []byte, _ http.Header) (string, Result, error)

Webhook implements Client. YooKassa signs nothing, so the POST body is not trusted for anything but the payment id: the payment is re-fetched over the authenticated API and that answer is what gets reported.

Jump to

Keyboard shortcuts

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