Wupex — WPX (top-up)
Direct-topup vendor adapter — static API-key, gaming/game-account recharge with server/zone lookup, rich HTTP-status failure mapping.
Quick facts
Code WPX · Line direct-topup · Auth static x-api-key (via CustomHeaderAuth) · Model synchronous, terminal-on-success · Webhook explicitly unsupported · Adapter services/external_vendors/direct_topup/wupex/
The top-up half of Wupex — a separate adapter from the voucher one, sharing only the upstream API + the WPX DB row. Registered direct_topup/factory.go:180. See Order Lifecycle.
Auth
Static x-api-key header via CustomHeaderAuth. Requires api_key and merchant_code in Credentials (errors if absent — stricter than the voucher adapter's Username fallback), plus host.
Catalog
Both methods back onto POST /api/product/merchant/invited/list {page, pageSize:100, search:[cardType=topup]}. No Cloudflare/429 backoff here (unlike the voucher adapter).
GetCategories— groups topup products byproductType; builds a requireduserIdfield, and probes each SKU viaPOST /api/order/topup-listservers?sku=Xto optionally add aserverIdselect field.GetProducts(categoryID)— filterscardType=topupANDproductType=categoryID;CurrencyCode:"USD"hardcoded,MinAmount=MaxAmount=FaceValue,IsFixedAmount:true,AutoDelivery:true,VendorDiscount:DiscountRate.ShouldSyncVariants()=true(small catalog).
Server-list shape mismatch: the adapter parses JSON-RPC data.result.servers[].{id,name}, but the mocky route returns [{serverId, serverName, region}] — the mock shape would parse to zero servers. Worth reconciling.
Order create
POST /api/order/topup {merchant, sku, quantity:1, userAccount} — quantity hardcoded 1; userAccount built from non-empty userId/serverId/zoneId. Synchronous: status:true → TopUpStatusProcessing + VendorOrderID = orderName.
Error handling (its biggest differentiator, error_messages.go): transient status (408/429/5xx) → leave Status="" (PENDING for retry); non-transient 4xx → failureFromHTTPStatus → terminal FAILED (400→InvalidRecipient, 422→AmountOutOfRange, 401/403/409/413→Unknown); 2xx with status:false → UNKNOWN (Wupex's error.code catalog is undocumented, so it carries error.message as the reason); unparseable 2xx → poll rather than burn.
Order status / poll
GET /api/order/detail?orderName=<id> — status inferred: status:true + data → complete (parses orderDate → CompletedAt); else → ErrOrderNotFound. SupportsExternalOrderRef()=false (needs the vendor orderName).
Webhook
Explicitly unsupported — VerifyWebhookSignature returns false; ParseWebhook errors "wupex does not support webhooks".
Delivery / balance
Topups are account credits — no serials; success yields VendorOrderID + a message. GetBalance = GET /api/customer/balance → creditBalance, USD (Ping reuses it). SupportsLookup()=false.
Tests, docs
Only error_messages_test.go (unit) — no topup orchestration/contract test. Mocky route frontend/mocky-balboa/src/routes/wupex.ts (shared with voucher). Docs docs/wupex/.
DT One (Top-up)
Direct-topup vendor adapter — mobile recharge over DT One DVS, Basic auth, async transactions, poll-driven (no webhook by default), reference min/max/discount mapping.
Octopus (OCTO_TOPUP) — federated top-up
Direct-topup vendor adapter consuming another Octopus's /api/v1/topups — login-token auth, composite variant IDs, mobile lookup.