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.
Payment code should outlive the payment provider.
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.
Purchase, Verify, Refund, Inquiry, ParseCallback. Same signatures for Zarinpal and for a Shaparak acquirer.
Ask Capabilities() instead of hard-coding which provider can refund. Unsupported operations answer ErrNotSupported, never a surprise.
Toman(15_000) and Rial(150_000) are the same money. Each gateway converts to whatever unit its own API expects.
Query strings, POST forms and the casing drift between PSPs collapse into one Callback struct that feeds the verification directly.
Every gateway honours WithBaseURL, and a virtual in-memory gateway runs the whole purchase-to-refund cycle with no network and no credentials.
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 â
All twenty-five are implemented and covered by tests.
| Gateway | Constant | Kind | Verify | Refund | Inquiry | Callback | Split |
|---|---|---|---|---|---|---|---|
| Zarinpal | Zarinpal | REST | â | â | â | â | â |
| Zibal | Zibal | REST | â | â | â | â | â |
| Vandar | Vandar | REST | â | â | â | â | â |
| PayWeb | PayWeb | REST | â | â | â | â | â |
| IDPay | IDPay | REST | â | â | â | â | â |
| Pay.ir | PayIr | REST | â | â | â | â | â |
| NextPay | NextPay | REST | â | â | â | â | â |
| PayPing | PayPing | REST v3 | â | â | â | â | â |
| BitPay.ir | BitPay | REST | â | â | â | â | â |
| YekPay | YekPay | REST | â | â | â | â | â |
| Sadad ¡ Bank Melli | Sadad | REST + 3DES | â | â | â | â | â |
| Parsian | Parsian | SOAP | â | â | â | â | â |
| Iran Kish | IranKish | REST + RSA/AES | â | â | â | â | â |
| Mellat ¡ Behpardakht | Mellat | SOAP | â | â | â | â | â |
| Saman ¡ SEP | Saman | REST | â | â | â | â | â |
| Pasargad | Pasargad | REST + RSA sign | â | â | â | â | â |
| AsanPardakht | AsanPardakht | REST v1 | â | â | â | â | â |
| Sepehr ¡ Bank Saderat | Sepehr | REST | â | â | â | â | â |
| TOP | Top | REST, in-app | â | â | â | â | â |
| Jibit ¡ PPG v3 | Jibit | OAuth REST | â | â | â | â | â |
| SnappPay | SnappPay | OAuth REST, BNPL | â | â | â | â | â |
| TorobPay | TorobPay | OAuth REST, BNPL | â | â | â | â | â |
| Digipay | DigiPay | OAuth REST, wallet/BNPL | â | â | â | â | â |
| Tara | Tara | OAuth REST, club credit | â | â | â | â | â |
| Virtual | Virtual | in-memory | â | â | â | â | â |
â means the provider offers no such API to merchants; the call returns
ErrNotSupported and Capabilities() says so up front.
Identical for every provider, including the ones that need an extra settlement call.
Redirect.Send emits a 303 or an auto-submitting form, whichever the bank requires.ParseCallback normalises whatever the bank posts or appends.Guides in the wiki, the full reference on pkg.go.dev.
Install, the first payment, the callback handler and the mistakes worth avoiding.
Which credential goes in which Config field, per gateway.
The shared transport options and every provider specific switch.
The virtual gateway, fake provider servers and the failure paths worth covering.
Moving a hand written GetToken / Confirm layer onto Payvand, one adapter at a time.
Every type, function and option, generated from the source.