OctoWiki
Vendor AdaptersVoucher Vendors

TRS — The Reward Store

Voucher vendor adapter — HTTP Basic auth, async create + poll delivery, no webhook, and several under-tested rough edges.

Quick facts

Code TRS · Line voucher · Auth HTTP Basic (username/password) · Model async (create → poll) · Webhook none (poll-driven) · Delivery inline code/pin/claim_url · Adapter services/external_vendors/vouchers/trs_vendor.go (single file, ~880 lines) · Tests/mock/docs ⚠️ none

TRSVendor (trs_vendor.go:18) embeds BaseVendor + an *HTTPClient. Registered in vouchers/factory.go:52 (case types.VendorTRS). See Order Lifecycle for how the orchestrator drives it.

Auth

HTTP Basic — no token/API key. getBasicAuth() (trs_vendor.go:118) returns a basic AuthConfig applied on every call via httpReq.SetBasicAuth (http_client.go:110). Auth(ctx) has no login endpoint — it validates by calling GET {Host}/wallets and reading the status (401 → "invalid credentials"). RedoAuth just re-calls Auth (nothing to refresh).

vendor_attributes read: host, username, password, timeout (default 30), delay (default 1000). api_key is loaded generically but never used. Nothing from Extra.

Catalog

Only GetCatalogBatched (trs_vendor.go:621); no separate GetProducts. GET {Host}/products?limit=50&page=N, page-based, pageSize=50 hardcoded. Loop ends on empty page, X-Pagination-Has-More-Pages != "true", or a short page. Built-in retry (max 3, 2s base backoff, special-cases 429) and a 500 ms inter-page sleep.

Mapping (trsCatalogProduct): product name reused as brand; IsActive hardcoded true; DeliveryTime = Instant unless DELIVERED_TYPE=="DELAYED". Denomination: starts IsFixedDenomination=true, flips to variable if the first denom has min!=max or there is >1 denomination.

Discount is taken only from Denominations[0] — per-tier discounts on other denominations are dropped.

Order create

CreateOrderPOST {Host}/orders (trs_vendor.go:191). Body {product_id, denomination, quantity, reference_code, wallet_id?}product_id is parsed from VendorProductSKU (defaults 0 if unparseable). Async: returns no codes at create (Data.Vouchers always empty); OrderID is the TRS numeric order id (not the reference code). Status map: DELIVERED→complete, CANCELLED→failed, else→pending+retry.

Order status / poll

GetOrderStatusGET {Host}/orders/{id|reference} (trs_vendor.go:299). 404 → types.ErrOrderNotFound. Status map: DELIVERED→complete; PARTIALLY_DELIVERED→complete (⚠️ treated as fully complete); CANCELLED→failed; PENDING→processing+retry.

On CANCELLED, TRS sets ShouldReCreate=true (trs_vendor.go:396) — the orchestrator recreates the order rather than failing it (the recreate protocol).

Webhook

None. TRS does not override NormalizeWebhookPayload, so it inherits BaseVendor's generic parser, but in practice TRS is poll-driven with no signature verification. Don't expect callbacks.

Delivery

Vouchers come inline in the order-details response (GetVouchers reuses GET /orders/{id}). trsVoucher maps card_number→Code, pin_code→Pin, claim_url→ClaimURL; reference = TRS_{id}. If expires_at is missing it synthesizes now + 365 days (trs_vendor.go:477). TRS uses strongly-typed JSON, so the generic *_mapped_field columns are not used.

Balance

GetBalanceGET {Host}/wallets (trs_vendor.go:548); optional currency filter is embedded as raw ?filters={"currency":"XXX"} (⚠️ not URL-encoded). Amount decoded via json:"amount,string".

Quirks & tech-debt

  • CancelVoucher is a no-op that lies — makes no HTTP call, always returns success/CANCELLED (trs_vendor.go:511, marked TODO).
  • ProductAvailability is mocked — always IsAvailable: true (trs_vendor.go:598).
  • Ping/health = GetBalance(nil).
  • Reference codes are timestamp-based (TRS_{UnixNano}[_R{retry}][_{ref}]), not deterministic.
  • No TRS-specific error table (unlike neo_errors.go/runa_errors.go).
  • http_client.go logs full response bodies and the Basic-Auth header at Info level — a credential-in-logs concern.
  • ⚠️ No unit/contract tests, no mocky-balboa route, no docs/ — TRS is the least-covered adapter. Call this out as tech-debt.

On this page