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.

  1. Canonical Top‑Level Fields

Field
Type
Always Present?
Notes

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.

  1. Decision Matrix (merchant validation rules)

Instrument
Currency
Accept (fulfil order) IF ALL true
Decline/Retry 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

Instrument
Condition
Value

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"

  1. 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"]
        }
}
  1. 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
}
  1. 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