Skip to content
Amir Iranmanesh edited this page Jul 31, 2026 · 3 revisions

Payvand

One Go interface for every Iranian payment gateway.

Payvand (پیوند — the link) puts twenty-five internet payment gateways — bank acquirers, PSPs, aggregators and the buy-now-pay-later providers — behind a single interface. Swapping Zarinpal for Mellat, or for SnappPay, is a value change, not a code change.

pv := payvand.Init(payvand.WithTimeout(20 * time.Second))
gw, _ := pv.Gateway(payvand.Zarinpal, payvand.Config{MerchantKey: merchantID})

purchase, _ := gw.Purchase(ctx, payvand.PurchaseRequest{
    Amount:      payvand.Toman(15_000),
    OrderID:     "1001",
    CallbackURL: "https://shop.example/payments/callback",
})
purchase.Redirect.Send(w, r)

The package imports nothing outside the Go standard library.

Start here

Page What it covers
Getting Started install, first payment, callback handler, the mistakes worth avoiding
Supported Gateways the twenty-five providers, what each supports, and their quirks
Configuration which credential goes in which Config field, per gateway
Options shared transport options and every provider specific switch
Callbacks and Verification the part that decides whether you get paid
Errors sentinels, provider codes, and what to do about each
Testing the virtual gateway, fake provider servers, failure paths
Migration Guide moving an existing GetToken/Confirm layer onto Payvand
Extending registering a gateway of your own
FAQ short answers to the recurring questions
Roadmap what is done and what is next

The interface

type Gateway interface {
	Name() Name
	Capabilities() Capabilities

	Purchase(ctx context.Context, req PurchaseRequest) (PurchaseResponse, error)
	Verify(ctx context.Context, req VerifyRequest) (VerifyResponse, error)
	Refund(ctx context.Context, req RefundRequest) (RefundResponse, error)
	Inquiry(ctx context.Context, req InquiryRequest) (InquiryResponse, error)
	ParseCallback(r *http.Request) (Callback, error)
}

Operations a provider does not offer return an error wrapping payvand.ErrNotSupported, and Capabilities() tells you before you call.

Three rules that save money

  1. Always verify. Most Iranian gateways reverse a transaction that is never verified. Callback.Succeeded is a hint; only a successful Verify means the money is yours.
  2. Never take the amount from the callback. Read it from your own order — Callback.VerifyRequest(amount) is shaped to make that the easy path.
  3. Persist the token before redirecting. Once the payer is on the bank page, that token is your only handle on the payment.

Links

Clone this wiki locally