Appearance
Data model
The transaction schemas and the status / method enums the two backends share. Enum values are quoted verbatim from the admin mono-backend Mongoose models (models/DepositTransaction.js, models/WithdrawalTransaction.js, models/BankAccount.js, models/PaymentSetting.js).
Deposit transaction
Collection deposittransactions. tx_id format TXN-YYYYMMDD-NNNN.
Status
Default created. Full enum:
created, pending, intent, started, authorized, received, settled,
completed, success, failed, expired, disputed, pending_confirmation,
refunded, confirmed, canceled, reversing, reversedThe inline comments in the model explain the provider-oriented ones: intent (payment process started), started (player on the payment page), authorized (card authorized), received (fiat received by the provider), settled (crypto sent), completed (final success), success (legacy/alternative success), pending_confirmation (awaiting 3DS or a manual check), disputed (chargeback).
success vs completed
Both success and completed are terminal success states — success is the legacy one and is what manual admin credits are written as. Treat either as "the money is in." Don't assume a single canonical success value.
Method
Required. Enum:
crypto, bank, manual_bank, card, correction, bonus, bank_account,
upi, voucher, admin, pix, mobile_money, neteller, skrill, cash,
apple_pay, google_payA manual admin Fiat credit is stored as cash (not fiat); Correction Up as correction; Bonus as bonus. See Admin panel API.
Notable fields
| Field | Notes |
|---|---|
amount | Decimal128 |
afterBalance | Wallet total after the credit |
provider | Free string; "Admin" for manual credits |
paymentType / paymentSystemName | Alternative provider fields used by some gateways |
external_tx_id | Provider's own id |
bonusEligible | Boolean, default false |
rejection | { rejectedAmount, rejectedAt, rejectedBy, reasonCode, reason, rejectedTransactionRefId } |
refund | { refundedAmount, refundedAt, refundedBy, reason, refundedTransactionRefId } |
idempotency_key | Unique, sparse |
createdBy | { adminId, name } — who ran a manual credit |
note | Internal note |
confirmed_at / failed_at / refunded_at | Timestamps |
Index: { playerId, status, currency }.
Withdrawal transaction
Collection withdrawaltransactions. tx_id format WDR-YYYYMMDD-NNNN.
Status
Default created. Enum:
created, pending, processing, confirmed, rejected, success, error, refundedcreated is the pre-finalisation state; pending means held and awaiting review/payout; processing means a provider is paying it out; error is a failed payout that can be retried (up to retry_count, default 5).
Method
Enum (note: no manual_bank here, unlike deposits — and it does include cash):
crypto, correction, bank, card, upi, voucher, admin, pix,
mobile_money, neteller, skrill, cash, apple_pay, google_payA manual admin Fiat debit is stored as cash; Correction Down as correction.
Rejection reasons
rejection.reject_reason_code enum:
KYC_INCOMPLETE, FRAUD_SUSPECTED, BALANCE_TOO_LOW, LIMIT_EXCEEDED, ADMIN_REJECTEDA manual rejection from the review queue is recorded as ADMIN_REJECTED. The rejection object also holds rejected_by, rejected_at, reject_reason, player_message, and player_notification_id.
Notable fields
| Field | Notes |
|---|---|
amount | Decimal128, required |
isApproved | Boolean, default false; true for manual admin debits and approved payouts |
approved | { approved_at, approved_by } |
retry_count | Default 5, min 0 |
addressData | { crypto_address, network, account_number, ifsc_code, account_holder } — bank fields encrypted; null for manual admin debits |
cryptoDetails | { payment_id, network, wallet_address, wallet_type } |
rollback_finalized_at | When the refund-on-failure completed |
createdBy | { adminId, name } |
confirmed_at | Success timestamp |
Bank account
Collection for manual bank accounts (models/BankAccount.js). One per brand + currency, typically.
| Field | Notes |
|---|---|
brand | Required |
bank_name | Required |
account_number, account_holder_name, ifsc_code | Required; encrypted at the application layer |
currency | Required, uppercased |
deposit_min_amount / deposit_max_amount | Default null; max ≥ min |
withdrawal_min_amount / withdrawal_max_amount | Default null; max ≥ min |
instructions | Shown to the player |
order | Display order, default 0 |
isActive | Default true |
isDeleted | Soft-delete flag |
Payment setting
Singleton keyed global (models/PaymentSetting.js). All toggles default true.
deposit.cryptoEnabled → enableCryptoDeposit
deposit.localCurrencyEnabled → enableLocalCurrencyDeposit
deposit.manualBankEnabled → enableManualBankDeposit
withdraw.cryptoEnabled → allowCryptoWithdraw
withdraw.localCurrencyEnabled → enableLocalCurrencyWithdrawThe right-hand column is the player-app flag each maps to, surfaced through GET /player/payment-config.
Permissions
The permission strings gating payment actions, gathered from the route files:
| Permission | Gates |
|---|---|
management.payment.view | Read payment settings and the method catalogue |
management.payment.create | Sync the method catalogue |
management.payment.edit | Update settings, reorder / toggle methods |
management.payment.manualBank.view | List manual bank accounts |
management.payment.manualBank.create | Create a bank account |
management.payment.manualBank.edit | Edit accounts; approve/reject/refund manual bank requests |
management.payment.manualBank.delete | Delete a bank account |
players.list.detail.payment.deposit.create | Manually credit a player (panel) |
players.list.detail.payment.withdrawal.create | Manually debit a player (panel) |
Permissions are modelled as { resource, action, category, description } and grouped into roles ({ roleName, permissions[] }). See Roles and access.