Unified Transaction‑Confirmation Standard
Scope Applies to all responses produced by the three live endpoints:
• /confirmation/<transactionRef> → Card (NGN & USD)
• /confirmation/pwt/<session_id> → PwT (Pay‑with‑Transfer)
• /confirmation/byCustomerRef/<ref> → aggregate wrapper that returns a txn_list composed of the above two.
This document tells integrators exactly which fields to check for fulfilment decisions and
gives sample payloads for every pathway.
Canonical Top‑Level Fields
status
string
✓
One of Successful, Pending, Unsuccessful –
already set by each endpoint.
instrument
string
✓
Card or PwT (the aggregate endpoint
forwards child objects untouched; each
child still contains its own instrument).
code
string
✓
Gateway‑result code normalised:
• NGN → response.ResponseCode (e.g. "00").
• USD → result ("SUCCESS" / "PENDING" /
"FAILURE").
• PwT → payment_status ("00" success,
"AWAITING_PAYMENT",
"EXPIRED_SESSION", …).
amount
number
✓
Amount authorised/received (decimal,
major currency unit).
currency
string
✓
ISO‑4217; for PwT this is fetched from the linked PaymentURL.
message
string
✓
Human‑readable description –
informational only.
merchant
object
✓
Contains at minimum {first_name, last_name, email, phone_number}.
customer_
transaction_ref
string
ⅹ
Passed through when available (Card).
internal_transa ction_ref
string
ⅹ
Always returned by the aggregate endpoint;
single‑instrument endpoints include it in
logs and snapshots.
error_code
string
ⅹ
Present only when status == "Unsuccessful".
error_message
string
ⅹ
Present only when status == "Unsuccessful".
Processor‑specific keys (response, status_description, virtual_account, …) are preserved for
analytics but not required for order fulfilment.
Decision Matrix (merchant validation rules)
Card
NGN
status == "Successful"
AND code == "00"
AND amount == expected_amount AND
currency == store_currency
Any other combo → failure.
Use response.ResponseCode for
decline reason (e.g. 51
insufficient funds).
Card
USD
status == "Successful"
AND
code == "SUCCESS" AND amount == expected_amount AND currency == store_currency
code == "PENDING" → wait & re‑poll;
anything else → failure.
PwT
—
status == "Successful"
AND code == "00"
AND amount == expected_amount AND
currency == store_currency
code == "AWAITING
PAYMENT" → keep polling until session
expiry. code == "EXPIRED
SESSION" or any other non‑00 → failure.
Any
Any
HTTP ≥ 500 or
missing JSON keys →
technical error; retry
API.
Quick reference – code mapping
Card
(NGN)
response.ResponseCode == "00"
"00"
Card
(NGN)
other two‑digit ISO8583 code
same code ("51","54", …)
Card
(USD)
result == "SUCCESS"
"SUCCESS"
Card
(USD)
result == "PENDING"
"PENDING"
Card
(USD)
result == "FAILURE"
"FAILURE"
PwT
payment_status == "Successful"
"00"
PwT
payment_status == "AWAITING_PAYMENT"
"AWAITING_PAYMENT"
PwT
session expired without payment
"EXPIRED_SESSION"
JSON Schema (Draft 7)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ConfirmationResponse",
"type": "object",
"required": ["status","instrument","code","amount","currency","message"],
"properties": {
"status": {
"type": "string",
"enum": ["Successful","Pending","Unsuccessful"]
},
"instrument": {
"type": "string","enum": ["Card","PwT"]
},
"code": { "type": "string" },
"amount": { "type": "number", "minimum": 0 },
"currency": { "type": "string","pattern": "^[A-Z]{3}$" },
"message": { "type": "string" },
"error_code": { "type": "string" },
"error_message": { "type": "string" },
"merchant": { "type": "object" }
},
"if": { "properties": { "status": { "const": "Unsuccessful" } } },
"then": {
"required":["error_code","error_message"]
}
}Worked Example
4.1 Card – NGN (Successful)
{
"status": "Successful",
"instrument": "Card",
"code": "00",
"amount": 1000.0,
"currency": "NGN",
"message": "Approved by Financial Institution",
"merchant": { "first_name": "zishan","last_name": "jawed","email":"dev.zishan@gmail.com","phone_number": "8979938492" },
"customer_transaction_ref": "AIRTEST004",
"internal_transaction_ref": "8199d79d-39e5-4bff-bafd-d04fa01d50d5",
"response": { "ResponseCode": "00", "...", "..."}
}4.2 Card – USD (Pending)
{
"status": "Pending",
"instrument": "Card",
"code": "PENDING",
"amount": 1000.0,
"currency": "USD",
"message": "Payment Status … is PENDING",
"merchant": { "first_name": "zishan,"last_name": "jawed","email": "dev.zishan@gmail.com","phone_number": "8979938492" }
}4.3 PwT (Successful)
{
"status": "Successful",
"instrument": "PwT",
"code": "00",
"amount": 1000.0,
"currency": "NGN",
"message": "Payment completed successfully",
"merchant": { "first_name": "zishan,"last_name": "jawed","email": "dev.zishan@gmail.com","phone_number": "8979938492" },
"session_id": "SESSION73F40FD0",
"transactions": [ { "...": "..." } ]
}4.4 PwT (Awaiting payment)
{
"status": "Pending",
"instrument": "PwT",
"code": "AWAITING_PAYMENT",
"amount": 0.0,
"currency": "NGN",
"message": "Awaiting payment of 1000.0",
"session_expires_at": "2025-07-01T09:26:57.22Z"
}4.5 Any – Server‑side exception
{
"status": "Unsuccessful",
"instrument": "Card",
"code": "X000",
"amount": 0,
"currency": "",
"message": "Internal server error",
"error_code": "X000",
"error_message": "Internal server error
}Merchant‑side Pseudocode
resp = call_confirmation_api()
if resp["status"] == "Successful":
if resp["code"] in ("00","SUCCESS") and resp["amount"] == order_amount:
fulfil_order()
else:
flag_mismatch()
elif resp["status"] == "Pending":
if resp["code"] == "AWAITING_PAYMENT":
schedule_poll()
else: # USD_PENDING or other temporary states
schedul_poll_short()
else: # Unsuccessful
log_failure(resp["code"], resp.get("error_code"))
show_failure_to_user()Last updated