One interface for every
Iranian payment gateway

Payvand puts twenty-five IPGs — bank acquirers, PSPs, aggregators and the buy-now-pay-later providers — behind a single Go interface. Switching provider is a value change, not a code change.

25 gateways zero dependencies Go 1.26+ MIT
Get started Source Documentation
go get github.com/amiranmanesh/payvand

Why

Payment code should outlive the payment provider.

Zero dependencies

Nothing but the Go standard library. The SOAP client, the 3DES and RSA envelopes and the retrying HTTP client are all built in — and CI fails if a dependency ever appears.

One interface

Purchase, Verify, Refund, Inquiry, ParseCallback. Same signatures for Zarinpal and for a Shaparak acquirer.

Capability aware

Ask Capabilities() instead of hard-coding which provider can refund. Unsupported operations answer ErrNotSupported, never a surprise.

Amount safety

Toman(15_000) and Rial(150_000) are the same money. Each gateway converts to whatever unit its own API expects.

Callbacks normalised

Query strings, POST forms and the casing drift between PSPs collapse into one Callback struct that feeds the verification directly.

Testable offline

Every gateway honours WithBaseURL, and a virtual in-memory gateway runs the whole purchase-to-refund cycle with no network and no credentials.

Quick start

Create the payment, send the payer, verify what comes back.

// 1. Initialise once, with the settings shared by the application.
pv := payvand.Init(payvand.WithTimeout(20 * time.Second))

// 2. Build the gateway of the terminal you charge on.
gw, err := pv.Gateway(payvand.Zarinpal, payvand.Config{
    MerchantKey: merchantID,
})

// 3. Create the payment and send the payer to the bank.
purchase, err := gw.Purchase(ctx, payvand.PurchaseRequest{
    Amount:      payvand.Toman(15_000),
    OrderID:     "1001",
    CallbackURL: "https://shop.example/payments/callback",
})
purchase.Redirect.Send(w, r)  // 303, or an auto-posting form — the gateway decides
// 4. The payer returns. This handler never changes when the provider does.
cb, err := gw.ParseCallback(r)
if err != nil || !cb.Succeeded {
    http.Error(w, "payment canceled", http.StatusPaymentRequired)
    return
}

order := orders.ByToken(cb.Token)

// The amount comes from your records, never from the browser.
verified, err := gw.Verify(ctx, cb.VerifyRequest(order.Amount))

Swapping payvand.Zarinpal for payvand.Mellat, payvand.Parsian or payvand.Virtual changes the first call and nothing else. Full guide →

Supported gateways

All twenty-five are implemented and covered by tests.

GatewayConstantKindVerifyRefundInquiryCallbackSplit
ZarinpalZarinpalREST✓—✓✓✓
ZibalZibalREST✓—✓✓✓
VandarVandarREST✓✓—✓—
PayWebPayWebREST✓——✓—
IDPayIDPayREST✓—✓✓—
Pay.irPayIrREST✓——✓—
NextPayNextPayREST✓✓—✓—
PayPingPayPingREST v3✓✓✓✓✓
BitPay.irBitPayREST✓——✓—
YekPayYekPayREST✓——✓—
Sadad · Bank MelliSadadREST + 3DES✓——✓—
ParsianParsianSOAP✓✓—✓✓
Iran KishIranKishREST + RSA/AES✓——✓—
Mellat · BehpardakhtMellatSOAP✓✓✓✓—
Saman · SEPSamanREST✓✓—✓—
PasargadPasargadREST + RSA sign✓✓✓✓—
AsanPardakhtAsanPardakhtREST v1✓✓✓✓✓
Sepehr · Bank SaderatSepehrREST✓✓—✓—
TOPTopREST, in-app✓—✓——
Jibit · PPG v3JibitOAuth REST✓✓✓✓—
SnappPaySnappPayOAuth REST, BNPL✓✓✓✓—
TorobPayTorobPayOAuth REST, BNPL✓✓✓✓—
DigipayDigiPayOAuth REST, wallet/BNPL✓✓—✓✓
TaraTaraOAuth REST, club credit✓——✓✓
VirtualVirtualin-memory✓✓✓✓—

— means the provider offers no such API to merchants; the call returns ErrNotSupported and Capabilities() says so up front.

The flow

Identical for every provider, including the ones that need an extra settlement call.

  1. Purchase — the provider issues a token; persist it next to the order.
  2. Redirect — Redirect.Send emits a 303 or an auto-submitting form, whichever the bank requires.
  3. Callback — ParseCallback normalises whatever the bank posts or appends.
  4. Verify — mandatory: an unverified transaction is reversed by most Iranian gateways. Mellat, AsanPardakht, Vandar and Pasargad need several calls here, and Payvand makes them for you.
  5. Refund or inquiry — where the provider supports them, behind the same interface.

Documentation

Guides in the wiki, the full reference on pkg.go.dev.

Getting started

Install, the first payment, the callback handler and the mistakes worth avoiding.

Configuration

Which credential goes in which Config field, per gateway.

Options

The shared transport options and every provider specific switch.

Testing

The virtual gateway, fake provider servers and the failure paths worth covering.

Migration

Moving a hand written GetToken / Confirm layer onto Payvand, one adapter at a time.

API reference

Every type, function and option, generated from the source.