Skip to main content

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, calls order_start (alias payment_order_start), and either renders a built-in widget (Crypto, Stripe / T-Bank, Internal) or redirects the browser to the merchant continuation page using terminal 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/GatewayOrders is 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:

FieldMeaning
Open orders per payerLimits open PaymentOrder operations for the same non-empty partner_info. 0 disables the guard.
Conversion limits JSONPer-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, in currency) → payAmount = orderAmount * rate (forward). payAmount (pay side, in the terminal currency) → orderAmount = payAmount / rate (reverse). rate is the effective currency → terminalCurrency rate (mid-rate × (1 + terminalCurrency.payment_spread)).
  • amount — legacy single field (in currency). Accepted only when neither orderAmount nor payAmount is set: mapped to orderAmount.
  • 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 via orderAmount (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

  • terminalId is set; terminal currency equals currency.
  • Buyer sees only the selected terminal.
  • Effective rate is 1 (same currency path).

Flow C reverse: cross-currency merchant lock, fixed payment

  • terminalId set (different-currency terminal), amount via payAmount (in the terminal currency).
  • The buyer pays exactly payAmount; the merchant receives payAmount / rate in currency.
  • Rate snapshot is captured at creation and stays fixed for this order instance.

Flow C forward: cross-currency merchant lock, fixed order

  • terminalId set (different-currency terminal), amount via orderAmount (in currency).
  • The merchant fixes what it receives (orderAmount); the buyer pays orderAmount * rate in 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 payAmount side without terminalId is 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 / payAmount set (after legacy amount mapping) 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.
PageRole
/TrexAdmin/TariffsTariff rules used by order_start: terminal-specific rules and per-merchant overrides.
/TrexAdmin/TokenNetworksTerminal type, availability, display settings, continuation path, and partner-info requirement.
/TrexAdmin/Currenciespayment_spread and AllowCrossPayment for cross-currency orders. The same spread applies to outgoing payouts with the inverse sign.
/TrexAdmin/RefundsOuterRefund (tx_type=25) lifecycle: Created -> Confirmed -> Finished.
/TrexAdmin/TransactsDetailed transaction state. Use this when GatewayOrders does not show enough state-bit detail.