Phoenix Prediction Docs

Idempotency

Prevent double debit and double credit in wallet processing

Wallet callbacks can be delivered more than once. If your backend processes the same tx_id twice, players can be charged or credited incorrectly.

Required Behavior

For every callback:

  1. Verify the Phoenix signature.
  2. Start a database transaction.
  3. Look up tx_id in your wallet transaction table.
  4. If it already exists, return the stored result without changing balance again.
  5. Lock the player balance row or use an atomic balance update.
  6. Apply the debit, credit, or rollback once.
  7. Store the tx_id, request body, result, and final balance.
  8. Commit.

Debit

Debit is synchronous. The player is waiting for the bet result.

If Phoenix receives a clear transport error before your service processes the debit, Phoenix may retry briefly. If Phoenix times out, it does not keep retrying inline because your backend may still process the first request.

Your side should still be safe if the same debit tx_id appears again.

Credit

Credit happens after settlement. Phoenix retries temporary delivery failures with backoff.

If you receive the same credit tx_id more than once, return success without crediting the player again.

Rollback

Rollback reverses a previous debit. It must be idempotent independently from the debit.

Your ledger should make these states clear:

StateMeaning
Debit acceptedPlayer funds were reserved or deducted
Debit rejectedBet was not accepted
Credit appliedSettlement payout was recorded
Rollback appliedPreviously accepted debit was refunded

SQL Pattern

BEGIN;

SELECT * FROM wallet_transactions WHERE tx_id = $1 FOR UPDATE;
-- if found, return stored response

SELECT balance FROM players WHERE id = $2 FOR UPDATE;
-- validate and update balance once

INSERT INTO wallet_transactions (
  tx_id,
  player_id,
  kind,
  amount_cents,
  response_json
) VALUES ($1, $2, $3, $4, $5);

COMMIT;

Use your own schema, but keep the same behavior: one tx_id, one balance effect.

On this page