Alphabet BridgePayments Docs
API reference

Payments

Create, fetch and cancel payments; every field, idempotency and the order-reference rule.

Method Path Does
POST /api/v1/payments Create a payment and get its checkout URL
GET /api/v1/payments/{id} Fetch a payment
POST /api/v1/payments/{id}/cancel Cancel a pending payment
POST /api/v1/payments/{id}/refunds Refund a payment (test mode), see Refunds

{id} is the payment's id from the create response. Every request needs your API key; see Authentication.

Create a payment

POST /api/v1/payments

bash
curl -s -X POST "$BRIDGE_URL/api/v1/payments" \
  -H "Authorization: Bearer $BRIDGE_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: 7f9c2b1e-order-1001" \
  -d @payment.json

with payment.json:

json
{
  "amount": 25000,
  "currency": "IQD",
  "method": "aps_card",
  "order_reference": "ORD-1001",
  "description": "Two coffees",
  "customer": {"name": "Ali", "phone": "+9647700000000", "email": "ali@example.com"},
  "payer_name": "Ali Hassan",
  "payer_phone": "+9647701234567",
  "payer_email": "ali.hassan@example.com",
  "locale": "en",
  "return_url": "https://shop.example/return",
  "metadata": {"cart_id": "c-42"}
}

Request fields

Field Type Required Rules
amount integer yes Whole IQD, at least 250. Your application may also have its own minimum and maximum.
currency string no IQD, the only currency today. Default IQD.
method string no aps_card. Required only if your application offers more than one method.
order_reference string yes Your order id, up to 64 characters. See the rule below.
description string no Up to 512 characters. Shown to the customer on the checkout page.
customer object no Only name (up to 255), phone (up to 32) and email.
payer_name string no Who pays, up to 255 characters. Shown in the dashboards.
payer_phone string no Up to 20 characters.
payer_email string no A valid email address.
locale string no ar or en: the language of the checkout and card pages. Default: your application's language.
return_url string no Where the customer is sent afterwards, up to 2048 characters. Default: your application's return URL. Must be https:// with a live key, and its host must equal your application's own return-URL host. See the rule below.
metadata object no Any JSON object up to 4 KB. Returned as it was sent.

Response

201 Created:

json
{
  "id": "01a13c8c-8592-70f4-9ef9-d3767e74d54c",
  "status": "pending",
  "mode": "test",
  "amount": 25000,
  "refunded_amount": 0,
  "currency": "IQD",
  "method": "aps_card",
  "order_reference": "ORD-1001",
  "description": "Two coffees",
  "customer": {"name": "Ali", "phone": "+9647700000000", "email": "ali@example.com"},
  "payer": {"name": "Ali Hassan", "phone": "+9647701234567", "email": "ali.hassan@example.com", "ip": "203.0.113.10", "user_agent": "GuzzleHttp/7"},
  "locale": "en",
  "return_url": "https://shop.example/return",
  "metadata": {"cart_id": "c-42"},
  "checkout_url": "https://<your-bridge-domain>/checkout/01a13c8c-8592-70f4-9ef9-d3767e74d54c",
  "expires_at": "2026-10-14T23:03:24+00:00",
  "paid_at": null,
  "created_at": "2026-10-14T22:33:24+00:00",
  "refunds": []
}

Payment fields

Field Type Meaning
id string (UUID) The payment's id. Use it in every other call.
status string pending, paid, failed, cancelled, expired, partially_refunded or refunded. See Statuses.
mode string test or live, from the key that created it.
amount integer Whole IQD.
refunded_amount integer Whole IQD refunded so far.
currency string IQD.
method string aps_card.
order_reference string Your order id.
description string or null As sent.
customer object name, phone, email, each null when not sent.
payer object name, phone, email as sent, plus ip and user_agent of the server that created the payment.
locale string ar or en.
return_url string or null Where the customer is sent afterwards.
metadata object or null As sent.
checkout_url string Send the customer here to pay.
expires_at string ISO 8601. Unpaid after this time, the payment expires. 30 minutes after creation by default.
paid_at string or null ISO 8601, set when the payment becomes paid.
created_at string ISO 8601.
refunds array Every confirmed refund: id, amount, source, provider_reference, note, created_at.

refunds[].source is api (a test-mode refund through the API), manual (a live refund confirmed in the dashboard) or provider_detected (the provider reported a refund the bridge had no record of).

The order-reference rule

The same order_reference cannot be paid twice. While a payment with that reference is pending, paid, partially_refunded or refunded, creating another one with the same reference answers 409 order_reference_already_paid, with the existing payment's id:

json
{
  "error": {
    "code": "order_reference_already_paid",
    "message": "A payment for this order_reference is still pending. Reuse it or wait until it fails, is cancelled or expires.",
    "payment_id": "01a13c8c-8592-70f4-9ef9-d3767e74d54c"
  }
}

Send the customer to that payment's checkout_url if it is still pending, or treat the order as paid. Once a payment is failed, cancelled or expired, you can create a new payment with the same reference. Test and live references are separate.

The return_url rule

A return_url you send must have the same host as your application's own return URL (any path or query is fine); otherwise the bridge answers 422 return_url_not_allowed:

json
{
  "error": {
    "code": "return_url_not_allowed",
    "message": "The return_url host must match this application's own return-URL host."
  }
}

Leave return_url out to use your application's own one. This holds in both test and live mode. If you need a second domain, ask Alphabet to change the return URL on your application.

Idempotency

Network errors happen. To retry POST /api/v1/payments safely, send an Idempotency-Key header (any string up to 255 characters, unique per order attempt, for example a UUID).

You send You get
A new key The payment is created as usual. The answer is stored with the key.
The same key and the same body The stored answer again, with the header Idempotent-Replayed: true. No second payment.
The same key and a different body 409 idempotency_key_reused.
The same key while the first request is still running 409 idempotency_request_in_progress. Retry shortly.

A 422 validation_failed answer is not stored, so you can fix the body and retry with the same key. Keys are per application and per mode.

Fetch a payment

GET /api/v1/payments/{id}

bash
curl -s "$BRIDGE_URL/api/v1/payments/01a13c8c-8592-70f4-9ef9-d3767e74d54c" \
  -H "Authorization: Bearer $BRIDGE_KEY" \
  -H "Accept: application/json"

200 OK with the payment, in the same shape as above. Here it has been paid and partly refunded:

json
{
  "id": "01a13c8c-8592-70f4-9ef9-d3767e74d54c",
  "status": "partially_refunded",
  "mode": "test",
  "amount": 25000,
  "refunded_amount": 10000,
  "currency": "IQD",
  "method": "aps_card",
  "order_reference": "ORD-1001",
  "description": "Two coffees",
  "customer": {"name": "Ali", "phone": "+9647700000000", "email": "ali@example.com"},
  "payer": {"name": "Ali Hassan", "phone": "+9647701234567", "email": "ali.hassan@example.com", "ip": "203.0.113.10", "user_agent": "GuzzleHttp/7"},
  "locale": "en",
  "return_url": "https://shop.example/return",
  "metadata": {"cart_id": "c-42"},
  "checkout_url": "https://<your-bridge-domain>/checkout/01a13c8c-8592-70f4-9ef9-d3767e74d54c",
  "expires_at": "2026-10-14T23:03:24+00:00",
  "paid_at": "2026-10-14T22:35:02+00:00",
  "created_at": "2026-10-14T22:33:24+00:00",
  "refunds": [
    {"id": 1, "amount": 10000, "source": "api", "provider_reference": "01a13c8c-8595-723d-90e3-978995c21827", "note": "One item out of stock", "created_at": "2026-10-14T22:40:11+00:00"}
  ]
}

A payment of another application, of the other mode, or an id that does not exist answers 404 not_found.

Cancel a payment

POST /api/v1/payments/{id}/cancel, no body.

bash
curl -s -X POST "$BRIDGE_URL/api/v1/payments/01a13c8c-85e7-718d-ad61-4dc03208c5f0/cancel" \
  -H "Authorization: Bearer $BRIDGE_KEY" \
  -H "Accept: application/json"

Only a pending payment can be cancelled. The bridge first asks the provider for the latest result, so a payment the customer has just paid is not cancelled. On success the answer is 200 OK with the payment, "status": "cancelled", and a payment.cancelled webhook is sent.

If the payment is no longer pending, the answer is 409 with its current status:

json
{
  "error": {
    "code": "payment_not_cancellable",
    "message": "Only a pending payment can be cancelled; this one is paid.",
    "status": "paid"
  }
}

A cancelled payment can still turn paid until its original expires_at if the customer completes the card page that was already open. You then receive payment.paid and must deliver the order or refund it. See Payment flow.