Appendix – Response Codes & Messages
This appendix focuses ONLY on the unified response codes (status_code) and messages (status_message) you will receive from Snapcart PPOB & Game Voucher APIs (Inquiry, Payment, Payment Status, Catalog endpoints, and Callbacks).
Use this as a quick reference for:
- Determining whether a transaction is final or still in progress.
- Showing user‑friendly status.
- Deciding next action (retry, stop, prompt user).
Standard Response Envelope
Successful or error responses follow:
{
"status_code": "200",
"status_message": "Transaction successful",
"data": { ... },
"error": ""
}
Field rules:
- status_code: Always a string, Unified code (e.g. "200", "400").
- status_message: Human-readable, High-level status description.
- data: Object or array; may be omitted on certain failures.
- error: Present and non-empty only when there is a client/input/system/biller error.
IDs and correlation:
- request_id: Snapcart‑generated; use for Payment Status queries and callback matching.
- reference_id: Client‑supplied; echoed verbatim when sent in Inquiry/Payment; store alongside request_id for reconciliation.
Ignore unknown future fields (forward compatibility).
Status Classification Cheat Sheet
| Classification | Meaning | Examples (status_code + status_message) |
|---|---|---|
| Final Success | Transaction completed successfully | 200 + Transaction successful |
| Final Failure | Will not recover; start a new flow or inform user | 400 Transaction failed, 402 Insufficient balance, 404 Product not found, 410 Inquiry expired, 500 Third party error |
| In Progress | Still processing; poll or await callback | 200 Transaction is being processed |
| Action Required | Client must do something before continuing | 402 Insufficient balance (top-up), 202 Waiting for payment (perform payment) |
Unified Response Codes & Messages
| status_code | status_message | Classification | Typical When | Client Action |
|---|---|---|---|---|
| 200 | Transaction successful | Final Success | Inquiry (bill OK), Payment status final, Callback | Stop polling; mark success |
| 200 | Transaction is being processed | In Progress | Payment acknowledged / vendor processing | Poll or await callback |
| 202 | Waiting for payment | In Progress | Two-step products after inquiry (before payment) | Execute Payment before expiry |
| 400 | Bad request | Final Failure | Missing/invalid JSON / fields | Fix request & retry |
| 400 | Transaction failed | Final Failure | Vendor processed & failed | Show failure; allow new attempt |
| 400 | Incorrect bill number | Final Failure | Invalid destination/account number | Prompt user to re-enter |
| 400 | Product and number mismatch | Final Failure | Wrong product for destination (e.g. phone number prefix mismatch) | Let user choose correct product |
| 401 | Unauthorized | Final Failure | Invalid/missing API key | Correct API key configuration |
| 403 | Invalid IP address | Final Failure | IP not registered/blocked | Register or fix client IP |
| 403 | Client Inactive | Final Failure | Client/API key disabled | Contact support / reactivate |
| 402 | Insufficient balance | Final Failure (requires new flow) | Wallet < required amount | Top-up then start new inquiry/payment |
| 404 | Product not found | Final Failure | Unknown/inactive product_code/item_code | Refresh catalog / correct code |
| 404 | Transaction not found | Final Failure | Invalid request_id in Payment Status | Verify ID / use correct reference |
| 404 | Vendor not found | Final Failure | No vendor mapped for product | Contact support / configuration fix |
| 409 | Product out of stock | Final Failure | Inventory/quota depleted | Hide option; suggest alternative |
| 409 | Bill already paid | Final Failure | Duplicate bill payment attempt | Inform user; stop |
| 409 | Outstanding bill | Final Failure | Postpaid/BPJS arrears present | Prompt to settle arrears first |
| 410 | Inquiry expired | Final Failure | Payment performed after inquiry TTL | Restart from Inquiry |
| 429 | Too many requests, please try again later | Final Failure (retry after wait) | Rate limit exceeded (polling too fast) | Backoff & retry later |
| 500 | Third party error | Final Failure | Unmapped vendor issue / generic fallback | Retry new flow (limited attempts) |
| 500 | Configuration error | Final Failure | Missing route/product mapping internal | Contact support |
| 500 | Database error | Final Failure | Internal persistence failure | Retry new flow / escalate if persistent |
| 502 | General error | Final Failure | Gateway / intermediate failure | Retry new flow |
| 503 | System maintenance | Final Failure | Maintenance window | Retry after maintenance |
| 408 | Request timeout | In Progress / Transitional | Internal operation timeout (rare surface) | Poll again; escalate if persistent |
Note: All codes are strings. Treat unknown (future) status_message values conservatively: if status_code != "200" or the message is not clearly success, handle as failure/in‑progress according to the numeric code.
Client Actions Cheat Sheet
| Scenario | Action |
|---|---|
| Processing persists beyond SLA | Escalate / show delayed message |
| Insufficient balance (402) | Prompt top-up, restart fresh inquiry/payment |
| Inquiry expired (410) | Restart Inquiry → Payment flow |
| Rate limited (429) | Increase poll interval / exponential backoff |
| Vendor/system error (500/502/503) | Limited retries (e.g., 1–2) then fail |
| Product out of stock (409) | Remove option; suggest alternative |
| Incorrect bill number (400) | Prompt user to correct destination |
| Transaction not found (404) | Verify request_id; if wrong discard |
Callback vs Polling
- If callback returns a final status, stop polling immediately.
- If callback returns an in‑progress status (rare), continue polling until final.
- Always trust the latest updated_at (if provided) or treat any final status as authoritative.
- Strongly recommended: rely on callback alone for production (minimal polling) to reduce load and latency; use polling only as a fallback or during initial integration/testing.