OBHPayments
↑↓ navigate↵ openSearches titles and topics
Documentation

Reference

Configuration

Everything the client accepts, how the base URL is normalized, and the checks that run in the constructor. Configuration errors fail immediately instead of halfway through a checkout.

Client options

ts
new OBHPayments(options: OBHPaymentsOptions) type OBHPaymentsOptions = {  secretKey: string  simulator?: boolean  baseUrl?: string  timeoutMs?: number  fetch?: typeof fetch}
PropertyTypeDescription
secretKeyrequiredstringA server-side service credential beginning with svc_. The constructor rejects anything else.
simulatoroptional
default: process.env.OBH_SIMULATOR === "true"
booleanWhen true, routes payments to the OBH Sadad simulator instead of the live Sadad IPG.
baseUrloptional
default: "https://obhco.ir/api/v1"
stringHardcoded to https://obhco.ir/api/v1 by default. Can only be overridden for local OBH core development.
timeoutMsoptional
default: 15000
numberPer-request timeout in milliseconds. Must be a finite positive number.
fetchoptional
default: globalThis.fetch
typeof fetchCustom fetch implementation, useful for tests, proxies, or instrumented HTTP clients.

The constructor validates all four options synchronously. A bad credential, an unsafe URL, a non-positive timeout, or a missing fetch implementation throws before any request is made.

Base URL rules

The SDK normalizes baseUrl once, in the constructor. The rules are strict on purpose:

  • It must parse as an absolute URL.
  • It must not contain credentials, a query string, or a fragment (user:pass@, ?x=1, #hash).
  • It must use https:, unless the hostname is localhost, 127.0.0.1, or [::1], where plain http: is allowed for development.
  • Trailing slashes are removed.
  • If the resulting path does not end with /api/v1, that suffix is appended.
You passRequests go toWhy
http://localhost:3100http://localhost:3100/api/v1/...Localhost may use HTTP; suffix appended.
https://pay.obh.examplehttps://pay.obh.example/api/v1/...Standard production form.
https://pay.obh.example/api/v1https://pay.obh.example/api/v1/...Already suffixed, not doubled.
https://pay.obh.example/gateway/https://pay.obh.example/gateway/api/v1/...Trailing slash removed, suffix appended to the path.
http://pay.obh.exampleRejectedOnly localhost may use plain HTTP.
pay.obh.exampleRejectedNot an absolute URL.
https://pay.obh.example?env=testRejectedQuery strings are not allowed.

Prefer an origin, not an endpoint

Pass https://pay.obh.example and let the SDK add the version. Passing the full endpoint works too, but an origin is harder to break when the API version is mentioned elsewhere.

Timeouts

Each request gets its own AbortController and a timer. The default is 15 seconds. When the timer fires, the request is aborted and the SDK throws { code: "TIMEOUT", retryable: true }.

ts
const obh = new OBHPayments({  secretKey: process.env.OBH_SERVICE_KEY!,  timeoutMs: 10_000,})

A timeout is ambiguous, not a failure: the server may have processed the request. For creation, retry with the same idempotency key. For retrieval, simply retry.

Custom fetch

The package uses the global fetch by default. Supply your own to add tracing, route through a proxy, or simulate responses in tests.

Instrumented fetchts
const obh = new OBHPayments({  secretKey: process.env.OBH_SERVICE_KEY!,  fetch: async (input, init) => {    const startedAt = performance.now()     try {      return await fetch(input, init)    } finally {      metrics.observe("obh.request.ms", performance.now() - startedAt)    }  },})

Your fetch must honour AbortSignal

The SDK passes signal on every request. A custom fetch that ignores it will not time out correctly. If you wrap the platform fetch, forward init unchanged apart from what you intentionally add.

Server-only guard

The constructor checks for a global window and throws when one exists. This is a development guard, not a security boundary: it catches the common mistake of importing the SDK into a client component.

ts
// In a browser bundle, this throws:// "@obhco/payments is server-only and must not run in browser code."const obh = new OBHPayments({ /* ... */ })

In Next.js, keep the client in a module that is only imported from server code. Adding import "server-only" to that module gives you a second, compile-time guard.

lib/obh.tsts
import "server-only" import { OBHPayments } from "@obhco/payments" export const obh = new OBHPayments({  secretKey: process.env.OBH_SERVICE_KEY!,})

Environment variables

Use names that make the trust boundary obvious. Never use a NEXT_PUBLIC_ (or equivalent) prefix for secret values.

VariablePurpose
OBH_SERVICE_KEYThe svc_ credential generated for your service. Server-only.
OBH_SIMULATOROptional. When set to "true", routes payments to the OBH Sadad simulator instead of the live Sadad gateway.
OBH_WEBHOOK_SECRETUsed only by your webhook route, never by the SDK.

A missing variable is not a nice error

If OBH_SERVICE_KEY is undefined, the constructor throws “secretKey must be an OBH service credential.” Validate configuration at startup so you learn about it before a customer does.

Multiple services

One client represents one credential and one service. Create a separate client for each service you operate; do not share a key across services.

ts
export const igraphical = new OBHPayments({  secretKey: process.env.IGRAPHICAL_SERVICE_KEY!,}) export const otherService = new OBHPayments({  secretKey: process.env.OTHER_SERVICE_KEY!,})

A service can only see its own payments. Using the wrong client does not leak data; it produces 404 PAYMENT_NOT_FOUND.

Runtime and module format

  • Node.js 20+. The package declares engines.node >= 20.
  • ESM only. It exports import with TypeScript declarations. CommonJS consumers need a dynamic import() or an ESM entrypoint.
  • No runtime dependencies. Node built-ins and web platform globals are enough.
  • Edge runtimes. Not officially tested. The client uses standard fetch, AbortController, and crypto, but the server-only guard assumes a window-less environment. Use a Node.js runtime for now.

Continue with Errors for the failure surface.