Payment orders (/TrexAdmin/GatewayOrders)
Route: /TrexAdmin/GatewayOrders (filtered by PaymentOrder)
TrexWallet exposes GatewayOrders as a first-class admin page alongside wallets, wallet
transactions, and tariffs. Since May 2026, the payer flow is available at /pay; see the
PWA changelog. This admin page is the canonical surface for
inspecting and reconciling those orders.
Operator view vs payment flow
Customer and merchant side
- End users open the universal
/pay?code=…page. It picks a terminal, callsorder_start(aliaspayment_order_start), and either renders a built-in widget (Crypto, Stripe / T-Bank, Internal) or redirects the browser to the merchant continuation page usingterminal flow path. - Merchants integrate through
/payorders/v1/using merchant authentication:put_payment_order,get_app_payments,wait_app_payment,get_app_payment,refund_payment,get_refund_status. Anonymous-by-code endpoints (get_order_info,get_terminals) live under/payment/v1/. - Bank wire and custom card flows use merchant continuation pages. The merchant integration performs provider-specific work and reports the result to TrexWallet.
Operator / admin side
/TrexAdmin/GatewayOrdersis the staff view. Operators see the order, chosen terminal, applied tariff, linked transactions, and provider-facing status. It is separate from the payer page and merchant API.- Typical operator actions: search by code /
externalId, inspect state (Draft,Predicted,Finished,Canceled), trace payment record bits (SendedToNetwork,NetworkIn,NetworkConfirmed,Failed | Cancelation | Timeout), and follow up failed deliveries. - The top Payment order guardrails card manages the maximum number of open orders per payer identifier and the conversion-limit rules.
So: /pay + /payorders/v1/ = how orders are created and paid; /TrexAdmin/GatewayOrders = how operators inspect and manage those orders inside TrexAdmin.
Payment order guardrails
The page includes a small settings block above the filters:
| Field | Meaning |
|---|---|
| Open orders per payer | Limits open PaymentOrder operations for the same non-empty partner_info. 0 disables the guard. |
| Conversion limits JSON | Per-app conversion guard rules; key 0 is the default rule. Empty JSON disables the guard. |
Example:
{
"0": { "minTotal": 1000, "maxBadToSuccessRatio": 10 },
"123456789012345": { "minTotal": 200, "maxBadToSuccessRatio": 5 }
}
The conversion guard uses PaymentOrder statistics for the previous completed UTC calendar day.
It aggregates all currencies for the current appId; there is no merchant-wallet dimension.
It rejects new orders when:
total > minTotal
and (total - finished) / finished > maxBadToSuccessRatio
If finished == 0 after minTotal is exceeded, the guard rejects immediately. The same conversion guard runs both at order creation and at order_start, so old draft orders are checked again when a buyer actually starts payment.
Lifecycle seen from the admin page
Draft put_payment_order without terminalId (open order)
Predicted order_start applied tariff and created payment record
Finished provider/network confirmation completed the payment
Canceled order expired, failed, or was canceled
Predicted orders with SendedToNetwork but no NetworkIn indicate that checkout details were
issued but the provider has not reported incoming funds. SendedToModule without progress usually
means the merchant/provider integration has not reported a later status.
put_payment_order flow model and field semantics
put_payment_order supports the merchant scenarios below. Use this section as the source of truth for amount/currency semantics and terminal locking behavior.
Field semantics
currency— order currency (merchant side): what the merchant denominates the order in and receives. Required. For cross-currency it must additionally allow cross-payment. The buyer-pay currency comes from the terminal, not from this field.orderAmount/payAmount— the two conversion sides. Provide exactly one; the side you set decides the direction.orderAmount(order side, incurrency) →payAmount = orderAmount * rate(forward).payAmount(pay side, in the terminal currency) →orderAmount = payAmount / rate(reverse).rateis the effectivecurrency → terminalCurrencyrate (mid-rate ×(1 + terminalCurrency.payment_spread)).amount— legacy single field (incurrency). Accepted only when neitherorderAmountnorpayAmountis set: mapped toorderAmount.terminalId— null for open orders (A/B), required for locked orders (C1/C forward/C reverse) and for the reverse (payAmount) side.
Flow A: single-currency open order
terminalId = null, amount viaorderAmount(order side).- Buyer chooses an eligible same-currency terminal on
/pay. - Final tariff is applied when buyer starts payment.
Flow B: open order with cross-currency buyer choice
- Same request shape as flow A.
- Buyer can choose a non-matching payment currency where the operator enabled cross-payment.
- Cross-currency rate is captured at payment start, not at order creation.
Flow C1: same-currency merchant lock
terminalIdis set; terminal currency equalscurrency.- Buyer sees only the selected terminal.
- Effective rate is 1 (same currency path).
Flow C reverse: cross-currency merchant lock, fixed payment
terminalIdset (different-currency terminal), amount viapayAmount(in the terminal currency).- The buyer pays exactly
payAmount; the merchant receivespayAmount / rateincurrency. - Rate snapshot is captured at creation and stays fixed for this order instance.
Flow C forward: cross-currency merchant lock, fixed order
terminalIdset (different-currency terminal), amount viaorderAmount(incurrency).- The merchant fixes what it receives (
orderAmount); the buyer paysorderAmount * ratein the terminal currency. - Mirror of C reverse with the same rate snapshot behavior. Cross-currency locks get a short TTL because the captured rate goes stale against the market.
Validation notes
- Sending the
payAmountside withoutterminalIdis rejected (PaymentOrderTerminalRequired), because the pay side is denominated in the terminal currency and the rate snapshot depends on a concrete terminal tariff. - Both or neither of
orderAmount/payAmountset (after legacyamountmapping) is rejected (PaymentOrderAmountSideAmbiguous). - For merchant payments (
app_id > 0), the authenticated merchant must own the app. - For self-topup (
app_id = 0), payer identity comes from the authenticated session.
Related operator pages
| Page | Role |
|---|---|
/TrexAdmin/Tariffs | Tariff rules used by order_start: terminal-specific rules and per-merchant overrides. |
/TrexAdmin/TokenNetworks | Terminal type, availability, display settings, continuation path, and partner-info requirement. |
/TrexAdmin/Currencies | payment_spread and AllowCrossPayment for cross-currency orders. The same spread applies to outgoing payouts with the inverse sign. |
/TrexAdmin/Refunds | OuterRefund (tx_type=25) lifecycle: Created -> Confirmed -> Finished. |
/TrexAdmin/Transacts | Detailed transaction state. Use this when GatewayOrders does not show enough state-bit detail. |