OBHPayments
↑↓ navigate↵ openSearches titles and topics
Documentation

Building

Gateways & installments

OBH offers more than one payment gateway. Call listGateways() to show them on your checkout page, then pass the customer's choice to startPayment(). Requires @obhco/payments 0.2.0.

Available gateways

CodeKindWhat the customer does
sadadcard (default)Pays the full amount with any Shetab bank card through Sadad (Bank Melli Iran).
zarinpalcardPays the full amount with any Shetab bank card through Zarinpal. Minimum 10,000 R.
mellatcardPays the full amount with any Shetab bank card through Bank Mellat (Behpardakht).
digipayinstallmentBuys now and pays later in installments with Digipay credit (BNPL), or pays with a Digipay wallet or card. Requires the customer's mobile number.

Do not hard-code this list

OBH lists only the gateways that are currently enabled. A gateway without LIVE credentials disappears from the live list, and new gateways appear without an SDK change.

List gateways

ts
class OBHPayments {  listGateways(options?: { simulator?: boolean }): Promise<PaymentGateway[]>}
GET/api/v1/gateways?simulator=false
PropertyTypeDescription
coderequired"sadad" | "zarinpal" | "mellat" | "digipay" | stringPass this to startPayment as gateway.
displayNamerequiredstringPersian label ready for a checkout page, e.g. خرید اقساطی با دیجی‌پی.
kindrequired"card" | "installment"Use it to group or label options.
requiresCustomerMobilerequiredbooleanWhen true, collect the customer's mobile number and pass customerMobile.
isDefaultrequiredbooleanThe gateway used when you omit gateway.
minAmountrequiredstring | nullMinimum whole Rial amount, or null when there is none.
Responsejson
{  "data": [    {      "code": "sadad",      "name": "Sadad (Bank Melli Iran)",      "displayName": "پرداخت با کارت بانکی (سداد)",      "kind": "card",      "requiresCustomerMobile": false,      "minAmount": null,      "isDefault": true,      "environment": "live"    },    {      "code": "zarinpal",      "name": "Zarinpal",      "displayName": "پرداخت با زرین‌پال",      "kind": "card",      "requiresCustomerMobile": false,      "minAmount": "10000",      "isDefault": false,      "environment": "live"    },    {      "code": "mellat",      "name": "Mellat (Behpardakht)",      "displayName": "پرداخت با کارت بانکی (به‌پرداخت ملت)",      "kind": "card",      "requiresCustomerMobile": false,      "minAmount": null,      "isDefault": false,      "environment": "live"    },    {      "code": "digipay",      "name": "Digipay (Installments / BNPL)",      "displayName": "خرید اقساطی با دیجی‌پی",      "kind": "installment",      "requiresCustomerMobile": true,      "minAmount": null,      "isDefault": false,      "environment": "live"    }  ]}

Build a checkout picker

1

Load the gateways on your server

ts
const gateways = await obh.listGateways()
2

Let the customer choose

Render one option per gateway using displayName. If the chosen gateway has requiresCustomerMobile, ask for the customer's mobile number.

3

Start the payment with that gateway

app/actions/checkout.tsts
"use server" import { redirect } from "next/navigation" import { obh } from "@/lib/obh" export async function checkout(form: FormData) {  const gateways = await obh.listGateways()  const chosen = gateways.find((g) => g.code === form.get("gateway"))  if (!chosen) throw new Error("Unknown gateway")   const payment = await obh.startPayment({    externalOrderId: String(form.get("orderId")),    amount: "15000000",    description: "Pro subscription (yearly)",    gateway: chosen.code,    customerMobile: chosen.requiresCustomerMobile      ? String(form.get("mobile"))      : undefined,    idempotencyKey: `order-${form.get("orderId")}-${chosen.code}`,  })   redirect(payment.checkoutUrl!)}
4

Verify the return the same way as always

verifyReturn() and getPayment() work for every gateway. Trust only payment.status. payment.gateway tells you which gateway processed the payment.

A new gateway is a new request

The gateway and mobile number are part of the idempotency fingerprint. If the customer switches gateway, use a new idempotency key (for example, include the gateway code). Reusing the old key returns 409 IDEMPOTENCY_CONFLICT.

Digipay installments

With Digipay the customer pays the first installment at checkout and the rest later. For your service nothing changes: once Digipay verifies the purchase, the payment is completed for the full amount, and you can fulfil the order immediately. Digipay owes the remaining installments to OBH, not your customer to you.

  • customerMobile is required. 09…, +989… and 989… forms are accepted.
  • Receipt fields map to Digipay values: referenceNumber is the Digipay trackingCode and orderNumber is the providerId.
  • A customer may also choose a Digipay wallet or card on Digipay's page. Those are full payments, but the result looks the same to you.

Testing with the simulator

With simulator: true, listGateways() returns every gateway, and Digipay payments open the OBH Digipay simulator at /pay/digipay/<ticket>. There you can simulate a BNPL, credit, wallet or card purchase, or a failure. Zarinpal payments open /pay/zarinpal/<authority> and Mellat payments open /pay/mellat/<refId>.

Errors

CodeMeaning
INVALID_ARGUMENTThe SDK rejected a malformed gateway or customerMobile before calling the API.
GATEWAY_NOT_SUPPORTED422 — the gateway code is not one OBH offers.
CUSTOMER_MOBILE_REQUIRED422 — the gateway needs the customer's mobile number.
GATEWAY_NOT_AVAILABLE503 — the gateway is not enabled for LIVE payments. Offer another one from listGateways().

Everything else behaves as described in Create a payment and Errors.