Merchant API

Idempotency Keys

Make every money-entry POST safe to retry with a required Idempotency-Key and a unique merchant_reference.

Two layers protect a merchant from accidental duplicate intents: the Idempotency-Key header owns transport retries and merchant_reference owns unique business identity. Use both.

The Idempotency-Key header

Payment, Checkout, and Payout creation require an Idempotency-Key header. The server recognises the key for 24 hours. A semantically identical request with the same key replays the response during that period.

POST /payment_intents
Authorization: Basic <base64(pk_test_...:sk_test_...)>
Idempotency-Key: 7c2a9e3f-...
Content-Type: application/json

Rules:

  • Up to 255 printable ASCII characters. A UUID v4 is the safe default.
  • Scope is (api_key_id, key). Two API keys can use the same idempotency string without collision.
  • A key expires after 24 hours. Use a new key for a new request after that period.
  • A repeat with the same key but different canonical fields returns 409 idempotency_conflict.
  • A repeat sent while the original is still in flight returns 409 idempotency_conflict.
  • A 5xx response on the original request releases the lock so the retry reaches a fresh handler.

Send an Idempotency-Key on every supported money-entry POST. Payment, Checkout, and Payout creation reject a missing key before persistence.

merchant_reference on intents

merchant_reference is your application's unique order or payout id. The platform enforces uniqueness on (merchant_id, environment, merchant_reference) per intent resource:

  • Reusing a Payment, Checkout, or Payout merchant_reference under another Idempotency-Key returns 409 duplicate_merchant_reference, even when the create fields are unchanged.
  • Only a retry with the original Idempotency-Key can replay the original response.
  • The reference does not replace Idempotency-Key; both identities are required.

For direct Payment requests, the fingerprint covers amount, currency, country, customer, payment_method, payment_method_options, return_url, device fingerprint, metadata, and Merchant reference. Checkout requests fingerprint their smaller contract separately: amount, currency, country, optional customer, return_url, metadata, and Merchant reference. Normalised values are fingerprinted canonically, so equivalent requests with the same key replay.

The header protects transport retries while merchant_reference preserves business identity. Store both values in your own system before sending the request.

Pattern

const idempotencyKey = crypto.randomUUID()
const merchantReference = `order_${myInternalOrderId}`

const auth = "Basic " + Buffer.from(`${publicKey}:${secretKey}`).toString("base64")

await fetch(`${base}/payment_intents`, {
  method: "POST",
  headers: {
    Authorization: auth,
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: "10000",
    currency: "MYR",
    country: "MY",
    merchant_reference: merchantReference,
    customer: { email: "aisyah@example.com" },
    payment_method: "FPX",
  }),
})

Persist idempotencyKey next to your internal order before you POST. On retry after a crash, replay the same key. The platform replays the response.

On this page