Documentation
¶
Overview ¶
Package payvand is a dependency-free Go client for the Iranian internet payment gateways (IPG).
Every provider — bank acquirers and PSPs alike — is reached through one interface, Gateway, so the call sites of an application never change when the provider does. Choosing a provider is choosing a value:
pv := payvand.Init(payvand.WithTimeout(20 * time.Second))
gw, err := pv.Gateway(payvand.Zarinpal, payvand.Config{MerchantKey: merchantID})
if err != nil {
return err
}
purchase, err := gw.Purchase(ctx, payvand.PurchaseRequest{
Amount: payvand.Toman(15_000),
OrderID: "10245",
CallbackURL: "https://shop.example/payments/callback",
})
if err != nil {
return err
}
purchase.Redirect.Send(w, r) // GET redirect or auto-posting form
After the payer returns, the callback is parsed and the payment verified:
callback, _ := gw.ParseCallback(r) verified, err := gw.Verify(ctx, callback.VerifyRequest(payvand.Toman(15_000)))
Swapping Zarinpal for Mellat, Parsian or the in-memory payvand.Virtual gateway changes the first line and nothing else.
Provider specific behaviour is opt-in through options declared by the gateway packages, and composes with the shared ones:
gw, err := pv.Gateway(payvand.Zibal, cfg,
payvand.WithSandbox(true),
zibal.WithFeeMode(1),
zibal.WithMultiplexing(zibal.Share{BankAccount: iban, Amount: 50_000}),
)
The package imports nothing outside the Go standard library.
Example ¶
The virtual gateway keeps the examples runnable without a merchant account; swapping it for a real name is the only change a production program needs.
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/amiranmanesh/payvand"
)
func main() {
pv := payvand.Init(payvand.WithTimeout(20 * time.Second))
gw, err := pv.Gateway(payvand.Virtual, payvand.Config{MerchantKey: "merchant-key"})
if err != nil {
log.Fatal(err)
}
purchase, err := gw.Purchase(context.Background(), payvand.PurchaseRequest{
Amount: payvand.Toman(15_000),
OrderID: "1001",
CallbackURL: "https://shop.example/payments/callback",
Description: "Wallet top-up",
})
if err != nil {
log.Fatal(err)
}
fmt.Println("gateway:", gw.Name())
fmt.Println("amount:", purchase.Amount.Rial(), "Rial")
fmt.Println("redirect method:", purchase.Redirect.Method)
}
Output: gateway: virtual amount: 150000 Rial redirect method: GET
Index ¶
- Constants
- Variables
- func IsRegistered(name Name) bool
- func Register(name Name, factory core.Factory)
- type Callback
- type Capabilities
- type Client
- type Config
- type Currency
- type Doer
- type Error
- type Factory
- type Gateway
- type InquiryRequest
- type InquiryResponse
- type Logger
- type Money
- type Name
- type NopLogger
- type Option
- func WithBaseURL(baseURL string) Option
- func WithHTTPClient(client Doer) Option
- func WithHeader(key, value string) Option
- func WithLogger(l Logger) Option
- func WithRetry(maxAttempts int, backoff time.Duration) Option
- func WithSandbox(enabled bool) Option
- func WithSkipTLSVerify(skip bool) Option
- func WithTimeout(d time.Duration) Option
- func WithUserAgent(ua string) Option
- type Options
- type PurchaseRequest
- type PurchaseResponse
- type Redirect
- type RefundRequest
- type RefundResponse
- type SlogLogger
- type Status
- type VerifyRequest
- type VerifyResponse
Examples ¶
Constants ¶
const ( // AsanPardakht is the AsanPardakht PSP (REST v1). AsanPardakht = asanpardakht.Name // BitPay is the BitPay.ir aggregator. BitPay = bitpay.Name // DigiPay is the Digipay wallet, credit and BNPL gateway. DigiPay = digipay.Name // IDPay is the IDPay PSP. IDPay = idpay.Name // IranKish is the Iran Kish acquirer (Bank Kar Afarin group). IranKish = irankish.Name // Jibit is the Jibit proxy payment gateway. Jibit = jibit.Name // Mellat is the Behpardakht Mellat acquirer. Mellat = mellat.Name // NextPay is the NextPay PSP. NextPay = nextpay.Name // Parsian is the Parsian Bank acquirer. Parsian = parsian.Name // Pasargad is the Bank Pasargad acquirer. Pasargad = pasargad.Name // PayIr is the Pay.ir PSP. PayIr = payir.Name // PayPing is the PayPing PSP. PayPing = payping.Name // PayWeb is the PayWeb PSP. PayWeb = payweb.Name // Sadad is the Sadad / Bank Melli acquirer. Sadad = sadad.Name // Saman is the Saman Bank (SEP) acquirer. Saman = saman.Name // Sepehr is the Sepehr / Bank Saderat (Mabna) acquirer. Sepehr = sepehr.Name // SnappPay is the SnappPay online instalment (BNPL) gateway. SnappPay = snapppay.Name // Tara is the Tara club credit gateway. Tara = tara.Name // Top is the TOP (Taban Ati Pardaz) in-app gateway. Top = top.Name // TorobPay is the TorobPay online credit (BNPL) gateway. TorobPay = torobpay.Name // Vandar is the Vandar PSP. Vandar = vandar.Name // Virtual is the in-memory gateway used for development and tests. Virtual = virtual.Name // YekPay is the YekPay multi-currency PSP. YekPay = yekpay.Name // Zarinpal is the Zarinpal PSP. Zarinpal = zarinpal.Name // Zibal is the Zibal PSP. Zibal = zibal.Name )
Names of the supported gateways. Pass one to [Client.Gateway] or New.
const ( // IRR is the Iranian Rial. IRR = core.IRR // IRT is the Iranian Toman. IRT = core.IRT )
Currency values.
const ( // StatusUnknown means the provider reported no mappable state. StatusUnknown = core.StatusUnknown // StatusPending means the payer has not finished yet. StatusPending = core.StatusPending // StatusPaid means the money was taken but not settled. StatusPaid = core.StatusPaid // StatusVerified means the payment is settled. StatusVerified = core.StatusVerified // StatusFailed means the payment failed. StatusFailed = core.StatusFailed // StatusCanceled means the payer aborted. StatusCanceled = core.StatusCanceled // StatusRefunded means the payment was returned. StatusRefunded = core.StatusRefunded )
Transaction statuses.
Variables ¶
var ( // ErrNotSupported is returned by an operation the provider lacks. ErrNotSupported = core.ErrNotSupported // ErrGatewayNotRegistered is returned for an unknown gateway name. ErrGatewayNotRegistered = core.ErrGatewayNotRegistered // ErrInvalidConfig is returned when credentials are missing. ErrInvalidConfig = core.ErrInvalidConfig // ErrInvalidRequest is returned for an unusable request. ErrInvalidRequest = core.ErrInvalidRequest // ErrPaymentFailed is returned when the provider rejected the payment. ErrPaymentFailed = core.ErrPaymentFailed // ErrPaymentCanceled is returned when the payer aborted. ErrPaymentCanceled = core.ErrPaymentCanceled // ErrAlreadyVerified is returned for a repeated verification. ErrAlreadyVerified = core.ErrAlreadyVerified // ErrVerificationPending is returned when the provider is still settling // the payment and Verify must be called again. ErrVerificationPending = core.ErrVerificationPending // ErrAmountMismatch is returned when the settled amount differs. ErrAmountMismatch = core.ErrAmountMismatch // ErrUnexpectedResponse is returned for an unreadable provider answer. ErrUnexpectedResponse = core.ErrUnexpectedResponse )
Sentinel errors, comparable with errors.Is.
Functions ¶
func IsRegistered ¶
IsRegistered reports whether a gateway is available.
Types ¶
type Capabilities ¶
type Capabilities = core.Capabilities
Capabilities describes what a gateway supports.
type Gateway ¶
Gateway is the interface every provider implements.
Example (ParseCallback) ¶
A callback handler is written once and works for every provider: the parsed callback carries the token, and the amount always comes from the merchant's own records.
package main
import (
"fmt"
"log"
"net/http"
"github.com/amiranmanesh/payvand"
)
func main() {
gw, err := payvand.New(payvand.Virtual, payvand.Config{})
if err != nil {
log.Fatal(err)
}
handler := func(w http.ResponseWriter, r *http.Request) {
callback, err := gw.ParseCallback(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !callback.Succeeded {
http.Error(w, "the payer canceled the payment", http.StatusPaymentRequired)
return
}
// The amount is read from the order, never from the query string.
verified, err := gw.Verify(r.Context(), callback.VerifyRequest(payvand.Toman(15_000)))
if err != nil {
http.Error(w, err.Error(), http.StatusPaymentRequired)
return
}
fmt.Fprintln(w, "reference number:", verified.ReferenceNumber)
}
_ = handler
fmt.Println("ready")
}
Output: ready
Example (Refund) ¶
Operations a provider does not offer report payvand.ErrNotSupported, so a caller can branch on capability instead of on provider name.
package main
import (
"context"
"errors"
"fmt"
"log"
"github.com/amiranmanesh/payvand"
)
func main() {
gw, err := payvand.New(payvand.Zarinpal, payvand.Config{MerchantKey: "merchant-id"})
if err != nil {
log.Fatal(err)
}
if !gw.Capabilities().Refund {
fmt.Println("refunds must be issued from the provider panel")
}
_, err = gw.Refund(context.Background(), payvand.RefundRequest{Token: "A1"})
fmt.Println(errors.Is(err, payvand.ErrNotSupported))
}
Output: refunds must be issued from the provider panel true
type InquiryRequest ¶
type InquiryRequest = core.InquiryRequest
InquiryRequest is the input of [Gateway.Inquiry].
type InquiryResponse ¶
type InquiryResponse = core.InquiryResponse
InquiryResponse is the output of [Gateway.Inquiry].
type Money ¶
Money is an amount plus the unit it is expressed in.
func SettledAmount ¶ added in v1.2.0
SettledAmount reconciles the amount a provider reports for a payment with the amount that was ordered, returning an error wrapping ErrAmountMismatch when they disagree. Gateways apply it inside [Gateway.Verify]; it is exported for the same check against an InquiryResponse.
type Name ¶
Name identifies a gateway in the registry.
func Registered ¶
func Registered() []Name
Registered returns the sorted names of the linked gateways.
type Option ¶
Option configures a gateway.
func WithBaseURL ¶
WithBaseURL overrides the provider host, for sandboxes and tests.
func WithHTTPClient ¶
WithHTTPClient sets the HTTP client used for every call.
func WithHeader ¶
WithHeader adds a header sent with every request.
func WithSandbox ¶
WithSandbox switches gateways that have a test environment to it.
func WithSkipTLSVerify ¶
WithSkipTLSVerify disables TLS certificate verification. Only reach for it when a Shaparak host serves a chain your trust store cannot complete.
func WithTimeout ¶
WithTimeout bounds a single gateway call.
func WithUserAgent ¶
WithUserAgent overrides the User-Agent header.
type PurchaseRequest ¶
type PurchaseRequest = core.PurchaseRequest
PurchaseRequest is the input of [Gateway.Purchase].
type PurchaseResponse ¶
type PurchaseResponse = core.PurchaseResponse
PurchaseResponse is the output of [Gateway.Purchase].
type RefundRequest ¶
type RefundRequest = core.RefundRequest
RefundRequest is the input of [Gateway.Refund].
type RefundResponse ¶
type RefundResponse = core.RefundResponse
RefundResponse is the output of [Gateway.Refund].
type SlogLogger ¶
type SlogLogger = core.SlogLogger
SlogLogger adapts a standard library slog logger.
type VerifyRequest ¶
type VerifyRequest = core.VerifyRequest
VerifyRequest is the input of [Gateway.Verify].
type VerifyResponse ¶
type VerifyResponse = core.VerifyResponse
VerifyResponse is the output of [Gateway.Verify].
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
basic
command
Command basic runs one full payment cycle — purchase, callback, verify, refund — against the in-memory virtual gateway, so it works offline.
|
Command basic runs one full payment cycle — purchase, callback, verify, refund — against the in-memory virtual gateway, so it works offline. |
|
multigateway
command
Command multigateway shows the point of the package: a table of terminals read from configuration, every one of them driven by the same code.
|
Command multigateway shows the point of the package: a table of terminals read from configuration, every one of them driven by the same code. |
|
webshop
command
Command webshop is a miniature shop showing the two HTTP handlers a payment integration needs: one that starts a payment and one that finishes it.
|
Command webshop is a miniature shop showing the two HTTP handlers a payment integration needs: one that starts a payment and one that finishes it. |
|
gateway
|
|
|
asanpardakht
Package asanpardakht implements the AsanPardakht IPG (REST v1, ipgrest.asanpardakht.ir).
|
Package asanpardakht implements the AsanPardakht IPG (REST v1, ipgrest.asanpardakht.ir). |
|
bitpay
Package bitpay implements the BitPay.ir gateway (REST, bitpay.ir).
|
Package bitpay implements the BitPay.ir gateway (REST, bitpay.ir). |
|
digipay
Package digipay implements the Digipay universal payment gateway (UPG, REST, api.mydigipay.com).
|
Package digipay implements the Digipay universal payment gateway (UPG, REST, api.mydigipay.com). |
|
idpay
Package idpay implements the IDPay gateway (REST, api.idpay.ir).
|
Package idpay implements the IDPay gateway (REST, api.idpay.ir). |
|
irankish
Package irankish implements the Iran Kish IPG (REST + RSA/AES envelope, ikc.shaparak.ir).
|
Package irankish implements the Iran Kish IPG (REST + RSA/AES envelope, ikc.shaparak.ir). |
|
jibit
Package jibit implements the Jibit Proxy Payment Gateway (PPG v3, REST, napi.jibit.ir).
|
Package jibit implements the Jibit Proxy Payment Gateway (PPG v3, REST, napi.jibit.ir). |
|
mellat
Package mellat implements the Behpardakht Mellat IPG (SOAP, bpm.shaparak.ir).
|
Package mellat implements the Behpardakht Mellat IPG (SOAP, bpm.shaparak.ir). |
|
nextpay
Package nextpay implements the NextPay gateway (REST, nextpay.org).
|
Package nextpay implements the NextPay gateway (REST, nextpay.org). |
|
parsian
Package parsian implements the Parsian Bank IPG (SOAP, pec.shaparak.ir).
|
Package parsian implements the Parsian Bank IPG (SOAP, pec.shaparak.ir). |
|
pasargad
Package pasargad implements the Bank Pasargad IPG (REST with RSA signed bodies, pep.shaparak.ir).
|
Package pasargad implements the Bank Pasargad IPG (REST with RSA signed bodies, pep.shaparak.ir). |
|
payir
Package payir implements the Pay.ir gateway (REST, pay.ir).
|
Package payir implements the Pay.ir gateway (REST, pay.ir). |
|
payping
Package payping implements the PayPing gateway (REST v3, api.payping.ir).
|
Package payping implements the PayPing gateway (REST v3, api.payping.ir). |
|
payweb
Package payweb implements the PayWeb IPG (REST, ipg.payweb.ir).
|
Package payweb implements the PayWeb IPG (REST, ipg.payweb.ir). |
|
sadad
Package sadad implements the Sadad / Bank Melli IPG (REST + 3DES signature, sadad.shaparak.ir).
|
Package sadad implements the Sadad / Bank Melli IPG (REST + 3DES signature, sadad.shaparak.ir). |
|
saman
Package saman implements the Saman Bank (SEP) IPG (REST, sep.shaparak.ir).
|
Package saman implements the Saman Bank (SEP) IPG (REST, sep.shaparak.ir). |
|
sepehr
Package sepehr implements the Sepehr / Bank Saderat (Mabna) IPG (REST, sepehr.shaparak.ir).
|
Package sepehr implements the Sepehr / Bank Saderat (Mabna) IPG (REST, sepehr.shaparak.ir). |
|
snapppay
Package snapppay implements the SnappPay online instalment gateway (REST, api.snapppay.ir).
|
Package snapppay implements the SnappPay online instalment gateway (REST, api.snapppay.ir). |
|
tara
Package tara implements the Tara club credit gateway (REST, pay.tara360.ir).
|
Package tara implements the Tara club credit gateway (REST, pay.tara360.ir). |
|
top
Package top implements the TOP (Taban Ati Pardaz) in-app gateway (REST, merchantapi.top.ir).
|
Package top implements the TOP (Taban Ati Pardaz) in-app gateway (REST, merchantapi.top.ir). |
|
torobpay
Package torobpay implements the TorobPay online credit gateway (REST, api.torobpay.com).
|
Package torobpay implements the TorobPay online credit gateway (REST, api.torobpay.com). |
|
vandar
Package vandar implements the Vandar IPG (REST, ipg.vandar.io).
|
Package vandar implements the Vandar IPG (REST, ipg.vandar.io). |
|
virtual
Package virtual implements an in-memory gateway for development and tests.
|
Package virtual implements an in-memory gateway for development and tests. |
|
yekpay
Package yekpay implements the YekPay gateway (REST, gate.yekpay.com).
|
Package yekpay implements the YekPay gateway (REST, gate.yekpay.com). |
|
zarinpal
Package zarinpal implements the Zarinpal payment gateway (REST, api.zarinpal.com).
|
Package zarinpal implements the Zarinpal payment gateway (REST, api.zarinpal.com). |
|
zibal
Package zibal implements the Zibal payment gateway (REST, gateway.zibal.ir).
|
Package zibal implements the Zibal payment gateway (REST, gateway.zibal.ir). |
|
internal
|
|
|
cryptox
Package cryptox holds the cryptographic primitives the Iranian PSPs ask for: 3DES-ECB signatures (Sadad), AES-CBC plus RSA envelopes (IranKish) and RSA signatures over the request body (Pasargad).
|
Package cryptox holds the cryptographic primitives the Iranian PSPs ask for: 3DES-ECB signatures (Sadad), AES-CBC plus RSA envelopes (IranKish) and RSA signatures over the request body (Pasargad). |
|
gwopt
Package gwopt carries the gateway specific option state that lives inside core.Options, so every gateway package expresses its own options with the shared core.Option type instead of inventing a parallel one.
|
Package gwopt carries the gateway specific option state that lives inside core.Options, so every gateway package expresses its own options with the shared core.Option type instead of inventing a parallel one. |
|
soap
Package soap is the minimal SOAP 1.1 client used by the bank gateways that still expose a webservice (Parsian, Mellat).
|
Package soap is the minimal SOAP 1.1 client used by the bank gateways that still expose a webservice (Parsian, Mellat). |
|
testutil
Package testutil holds the fake gateway server the package's own tests are written against.
|
Package testutil holds the fake gateway server the package's own tests are written against. |
|
tokenauth
Package tokenauth caches the short lived bearer tokens the OAuth style gateways hand out, so a gateway value can be built once at start-up and then shared by every request without re-authenticating on each call.
|
Package tokenauth caches the short lived bearer tokens the OAuth style gateways hand out, so a gateway value can be built once at start-up and then shared by every request without re-authenticating on each call. |
|
transport
Package transport is the HTTP plumbing shared by every gateway: JSON, form and raw calls with timeout, retry, logging and header handling.
|
Package transport is the HTTP plumbing shared by every gateway: JSON, form and raw calls with timeout, retry, logging and header handling. |