rollback — Debit Cancellation
Cancels a previously sent debit and returns its amount to the player. The platform retries rollbacks until it receives OK. Because the original debit may never have reached the operator, you must handle that case as well.
POST {wallet_base_url}/rollbackWhen It Is Called
reason | When | Does the operator have the original debit? |
|---|---|---|
cancel_bet | The player cancels a bet before betting closes | Yes (a debit that received OK) |
round_void | The round is voided (game progression error, platform server recovery, etc.). One per unsettled debit in that round | Usually yes. May be missing if the debit's outcome was uncertain |
timeout | The debit outcome is uncertain (3-second timeout, non-HTTP-200, connection failure, UNKNOWN_ERROR). Sent immediately after the debit fails | Unknown — it may never have arrived, may have been processed late, or may still be in progress |
system | Cleanup of debits whose result has been unknown for more than 30 seconds because a platform server stopped mid-debit, and cleanup of buy-ins interrupted mid-seating during server recovery | Unknown |
- A rollback cancels only debits. It never cancels credits.
- The
tx_idof the rollback for a given debit is fixed and unique. The same debit is never cancelled twice under different reasons. - New
reasonvalues may be added. If you receive an unknown value, do not reject it.
Request
Headers are the same as the common headers.
| Field | Type | Required | Description |
|---|---|---|---|
tx_id | string | ● | ID of the rollback transaction itself. Idempotency key |
ref_tx_id | string | ● | The tx_id of the debit to cancel |
external_id | string | ● | Operator-side player ID |
player_id | string | ● | Platform player ID (pl_…), for reference |
currency | string | ● | Currency code |
amount | integer | ● | Amount of the original debit (1 or more). There are no partial rollbacks |
reason | string | ● | A value from the table above |
game_code | string | ● | Game code. May be an empty string on platform recovery paths |
table_id | string | ● | Table ID (tbl_…) |
round_id | string | ● | Round ID (rnd_…) or table session ID (tss_…) of the original debit |
operator_session_id | string | null | ● | The value sent at game launch. May be null on platform recovery paths (system, round_void) |
created_at | string | ● | Time the platform created the rollback transaction (UTC, milliseconds). Does not change on retries |
{
"amount": 5000,
"created_at": "2026-10-19T02:10:03.131Z",
"currency": "PHP",
"external_id": "u-102938",
"game_code": "live_baccarat",
"operator_session_id": "s-88af21",
"player_id": "pl_0199e8b4f2a37c1d8e5b6a4f3c2d1e0b",
"reason": "timeout",
"ref_tx_id": "tx_0199f2a1c3d47e8b9a6f1e2d3c4b5a69",
"round_id": "rnd_0199f2a0b1c27d3e8f4a5b6c7d8e9f01",
"table_id": "tbl_0199a1b2c3d47e5f8a9b0c1d2e3f4a5b",
"tx_id": "tx_3b7e1c9a5d2f4e8b6a0c2d4f6e8a0b1c"
}{
"amount": 5000,
"created_at": "2026-10-19T02:10:09.870Z",
"currency": "PHP",
"external_id": "u-102938",
"game_code": "live_baccarat",
"operator_session_id": "s-88af21",
"player_id": "pl_0199e8b4f2a37c1d8e5b6a4f3c2d1e0b",
"reason": "cancel_bet",
"ref_tx_id": "tx_0199f2a1c3d47e8b9a6f1e2d3c4b5a70",
"round_id": "rnd_0199f2a0b1c27d3e8f4a5b6c7d8e9f01",
"table_id": "tbl_0199a1b2c3d47e5f8a9b0c1d2e3f4a5b",
"tx_id": "tx_9d1f3a5c7e9b0d2f4a6c8e0b2d4f6a8c"
}Response
| Field | Type | Required | Description |
|---|---|---|---|
status | string | ● | Must be OK for the transaction to finish |
balance | integer | ● | Balance after processing |
operator_tx_id | string | Operator-side transaction ID |
{ "status": "OK", "balance": 1250000, "operator_tx_id": "w-99814" }Handling by Case
Handling depends on the state of the debit referenced by ref_tx_id. Return OK in every case.
| Original debit state (operator record) | Handling | Response |
|---|---|---|
| Succeeded (deducted) | Refund the original debit amount and mark the debit as "rolled back" | OK + balance after the refund |
| No record (never arrived, or not yet processed) | Do not refund; record ref_tx_id in the rollback markers | OK + current balance |
A debit that was rejected (INSUFFICIENT_FUNDS, etc.) | Do not refund; record it in the rollback markers | OK + current balance |
| Already rolled back | Do nothing | OK + current balance |
Repeated request with the same rollback tx_id | Do not process again | The original response as is |
- For the refund amount, use the original debit amount recorded by the operator. The request's
amountis the same value, but if it differs from the original record, the original record takes precedence. - Refund even if the player is blocked, logged out, or has an expired session.
When the original debit does not exist yet
timeout and system rollbacks may be processed before the original debit. For example, while the operator takes more than 3 seconds to process a debit, the platform has already treated it as a failure and sent a rollback.
Platform Operator
debit tx_10 (5,000) ─────────────────▶ processing delayed …
── 3 seconds elapsed, bet rejected ──
rollback tx_11 (ref=tx_10) ─────────▶ no record of tx_10 → OK without refund, mark tx_10 as rolled back
(delayed debit tx_10 continues processing)
tx_10 is in rollback markers → TX_ALREADY_ROLLED_BACK, no deductionConversely, if the debit commits first and the rollback arrives afterwards, it becomes the "succeeded → refund" case. Either way, the final balance must be the same as if the debit had never happened.
To guarantee this:
- Process debits and rollbacks inside the same player-row lock.
- A debit checks the rollback markers after acquiring the lock. If it checks before the lock, and a rollback commits in between, the debit misses the rollback marker and deducts the amount anyway.
- Do not delete rollback markers (keep them for at least a few days). Late debits usually arrive within a few seconds, but they can be delayed further by operator-side outages.
Rollbacks cannot be rejected
If you return anything other than OK for a rollback (including TX_ALREADY_ROLLED_BACK and PLAYER_NOT_FOUND), the transaction does not finish and is retried at 1 s → 2 s → 5 s → 15 s → 1 min → 5 min, then every 5 minutes indefinitely. Return OK even if you cannot find the original debit or have already rolled it back.
Processing Order
- Verify the signature → on failure,
INVALID_SIGNATURE(it will be retried, so fix the cause right away) - Validate fields (
tx_id,ref_tx_id, andexternal_idpresent,amountis an integer of 1 or more) → on failure,INVALID_REQUEST - If a record for the same rollback
tx_idexists → return the stored response as is - Lock the player row
- Look up (and lock) the debit for
ref_tx_id→ see Handling by Case above - Record the rollback marker, record the rollback
tx_idwith its response, and returnOK