Request Router
Routes deposit and withdrawal requests. Input validation before the Orchestrator.
POST /payments/withdrawal endpoint returns 501 Not Implemented in Phase 2. The stub exists so the frontend can build without type errors. Full implementation is Phase 3.Endpoints
/payments/depositInitiates a new deposit. Validates the request, then forwards to the Orchestrator.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Amount in the smallest currency unit (e.g. cents) |
currency | string | Yes | ISO 4217 code, e.g. "USD" |
method | string | Yes | Payment method slug, e.g. "btc" |
metadata | object | No | Arbitrary key-value pairs passed through to transaction.metadata |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
transaction_id | string | UUID of the created transaction |
status | string | "INITIATED" -- initial status |
wallet_address | string | Crypto address to show the player |
destination_tag | string | null | Required memo for XRP/TON, null otherwise |
expires_at | string | ISO 8601 datetime when the invoice expires |
amount_crypto | string | Exact crypto amount the player must send |
/payments/withdrawalInitiates a withdrawal. Requires KYC gate and balance check before forwarding to Orchestrator. Phase 3 feature -- stub in Phase 2.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Amount to withdraw in the smallest currency unit |
currency | string | Yes | ISO 4217 currency code |
method | string | Yes | Payment method slug |
wallet_address | string | Yes | Player's destination wallet address |
destination_tag | string | No | Memo / destination tag for XRP, TON |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
transaction_id | string | UUID of the created transaction |
status | string | "INITIATED" -- pending KYC/manual review in Phase 3 |
/payments/:transaction_id/statusReturns current transaction status. Polled by the frontend. See Status Polling spec.
Response (200 OK)
| Field | Type | Description |
|---|---|---|
transaction_id | string | Transaction UUID |
status | string | Current UnifiedStatus |
direction | string | "deposit" | "withdrawal" |
amount_requested | number | Original requested amount (smallest unit) |
amount_credited | number | null | Credited amount, null until COMPLETED |
wallet_address | string | null | For deposits: address to show player |
expires_at | string | null | Invoice expiry, null if already expired/completed |
created_at | string | ISO 8601 transaction creation time |
updated_at | string | ISO 8601 last status change time |
/payments/methodsReturns available payment methods for the player's brand + geo. Read from payment_methods cache table.
Response (200 OK)
| Field | Type | Description |
|---|---|---|
methods | Method[] | Array of available methods: { slug, name, network, min_amount, max_amount, fee_pct, logo_url, requires_tag } |
Validation Pipeline
Checks run in sequence. The first failing check stops the pipeline and returns an error. The Orchestrator is called only if all checks pass.
UNAUTHORIZEDAuth middleware already ran -- Router trusts the injected user context
BRAND_NOT_RESOLVEDbrand_id must be present in JWT claims. Router rejects requests without a resolved brand.
VALIDATION_ERRORChecks that amount, currency, method are non-null and match expected types
CURRENCY_NOT_SUPPORTEDISO 4217 code must be in the brand's allowed currency list
METHOD_NOT_AVAILABLEChecks payment_methods cache: method must be active for the player's country
AMOUNT_OUT_OF_RANGEamount must be ≥ min_amount and ≤ max_amount from payment_methods cache
INSUFFICIENT_BALANCEFor withdrawals only: player balance must be ≥ amount. Phase 3 -- stub returns 501 in Phase 2.
Request Context
The Router enriches every request before forwarding it downstream. These fields are available to the Orchestrator, Adapters, and State Machine without additional calls.
| Field | Source | Description |
|---|---|---|
user_id | JWT claim | Authenticated player identifier |
brand_id | JWT claim | Tenant that owns this request |
session_id | JWT claim | Session ID for fraud correlation |
geo | IP geolocation | ISO 3166-1 alpha-2 country code resolved from client IP |
ip | Request header | Client IP (X-Forwarded-For or socket, sanitized) |
direction | Route path | "deposit" | "withdrawal" -- derived from which endpoint was called |
Geolocation is resolved once in the Router and cached in the request context. It is not re-resolved in the Orchestrator or Adapters.
Error Codes
| HTTP | Code | When | Retry? |
|---|---|---|---|
401 | UNAUTHORIZED | JWT missing, expired, or invalid signature | No |
400 | BRAND_NOT_RESOLVED | brand_id not found in JWT claims | No |
422 | VALIDATION_ERROR | Required fields missing or wrong type | No |
422 | CURRENCY_NOT_SUPPORTED | Currency not in brand's allowed list | No |
422 | METHOD_NOT_AVAILABLE | Method inactive for player's geo | No |
422 | AMOUNT_OUT_OF_RANGE | amount < min_amount or > max_amount | No |
429 | RATE_LIMIT_EXCEEDED | Rate limiter blocked the request (see Rate Limiting spec) | Yes (after retry_after) |
422 | INSUFFICIENT_BALANCE | Withdrawal amount exceeds player balance (Phase 3) | No |
501 | NOT_IMPLEMENTED | Withdrawal endpoint stub in Phase 2 | No |