Legacy form adapter

Drop-in form-encoded endpoint for existing shopping-cart plugins.

payzum exposes a form-encoded endpoint at /legacy/api.php for shopping-cart plugins that speak a command-based, HMAC-signed wire format. If your cart plugin already works with a gateway using this protocol, pointing it at your payzum host and entering your payzum public/private key pair is usually all you need to migrate.

New integrations should prefer the REST API at POST /v1/payment. The legacy adapter exists to keep existing shopping-cart plugins working without code changes.

Endpoint

POST /legacy/api.php
Content-Type: application/x-www-form-urlencoded

All parameters are sent as a standard URL-encoded form body. Every request must be signed; the signature goes in the HMAC request header.

/legacy/api.php is the only legacy route — any other /legacy/* path returns 404.

Signing requests

Compute the signature over the raw body bytes exactly as you will send them:

HMAC: hex(HMAC-SHA-512(privateKey, rawBody))

Do not re-encode the form between signing and transmitting — the bytes you sign must match the bytes you send.

Example (curl)

BODY="version=1&cmd=create_transaction&key=<PUBLIC_KEY>&nonce=$(date +%s%3N)&amount=25.00&currency1=USD&currency2=USDT.TRC20&invoice=ORDER-5678&ipn_url=https%3A%2F%2Fmerchant.example.com%2Fipn"
HMAC=$(printf '%s' "$BODY" | openssl dgst -sha512 -hmac "<PRIVATE_KEY>" | sed 's/^.*= //')
curl -X POST "$PAYZUM_BASE/legacy/api.php" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "HMAC: $HMAC" \
  -d "$BODY"

Set $PAYZUM_BASE to your payzum API host: https://merchant.payzum.com (production) or https://staging.payzum.com (staging/sandbox).

Supported commands

Pass the command name as the cmd field. Each command maps to a REST equivalent.

| cmd value | REST equivalent | Description | |---|---|---| | create_transaction | POST /v1/payment | Create a new invoice | | get_tx_info | GET /v1/payment/:id | Read a single invoice by its payzum id | | get_tx_info_multi | GET /v1/payment/:id (batched) | Read up to 25 invoices in one call | | rates | GET /v1/currencies | Fetch the current rate table |

cmd=create_transaction

Creates an invoice. Returns a payzum payment id (txid) and a checkout URL.

BODY="version=1&cmd=create_transaction&key=<PUBLIC_KEY>&nonce=$(date +%s%3N)&amount=10.00&currency1=USD&currency2=USDT.TRC20&invoice=ORDER-1234&ipn_url=https%3A%2F%2Fmerchant.example.com%2Fipn"
HMAC=$(printf '%s' "$BODY" | openssl dgst -sha512 -hmac "<PRIVATE_KEY>" | sed 's/^.*= //')
curl -X POST "$PAYZUM_BASE/legacy/api.php" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "HMAC: $HMAC" \
  -d "$BODY"

cmd=get_tx_info

Reads a single invoice by its payzum id (txid).

BODY="version=1&cmd=get_tx_info&key=<PUBLIC_KEY>&nonce=$(date +%s%3N)&txid=pzi_..."
HMAC=$(printf '%s' "$BODY" | openssl dgst -sha512 -hmac "<PRIVATE_KEY>" | sed 's/^.*= //')
curl -X POST "$PAYZUM_BASE/legacy/api.php" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "HMAC: $HMAC" \
  -d "$BODY"

cmd=get_tx_info_multi

Reads up to 25 invoices in one call. Pass the payzum ids as a |-separated list in txid (URL-encoded as %7C). The result is a map keyed by txid; ids that are not found carry a per-entry { "error": "tx not found" }.

BODY="version=1&cmd=get_tx_info_multi&key=<PUBLIC_KEY>&nonce=$(date +%s%3N)&txid=pzi_aaa%7Cpzi_bbb"
HMAC=$(printf '%s' "$BODY" | openssl dgst -sha512 -hmac "<PRIVATE_KEY>" | sed 's/^.*= //')
curl -X POST "$PAYZUM_BASE/legacy/api.php" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "HMAC: $HMAC" \
  -d "$BODY"

cmd=rates

Returns the current rate table for supported currencies. No per-transaction parameters required beyond version, cmd, and key.

BODY="version=1&cmd=rates&key=<PUBLIC_KEY>&nonce=$(date +%s%3N)"
HMAC=$(printf '%s' "$BODY" | openssl dgst -sha512 -hmac "<PRIVATE_KEY>" | sed 's/^.*= //')
curl -X POST "$PAYZUM_BASE/legacy/api.php" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "HMAC: $HMAC" \
  -d "$BODY"

Error responses

Every response uses the stable envelope { "error": "ok", "result": ... } on success and { "error": "<message>", "result": null } on error. Always check the error field: application-level errors can arrive with HTTP 200 (the error field is the authoritative signal), while protocol-level errors also carry an HTTP error status — 401 for HMAC/key failures, 400 for a bad nonce, missing fields, or wrong content-type, and 429 for rate limiting.

Rate limits

The adapter shares the same per-merchant rate limit as /v1/*: 60 requests per minute.

IPN (webhook) delivery

IPN payloads for legacy-adapter merchants are delivered in the CoinPayments dialect: a form-urlencoded body (keys sorted alphabetically, RFC-3986 percent-encoding) signed with HMAC-SHA-512, with the lowercase hex signature in the fixed HMAC header.

The IPN is signed with your merchant webhook secretnot your private key. The private key signs your requests to payzum; outbound IPNs are signed with the webhook secret shown at merchant creation or rotation.

Deduplicate on the ipn_id field, which is stable across retries. See Overview & verification for the full field list, status mapping, and verification steps.

Limitations

  • The adapter covers the common command surface: create_transaction, get_tx_info, get_tx_info_multi, and rates — 4 commands. Advanced commands such as mass withdrawals are not supported — use the REST API for those workflows.