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
CreateOrder → POST {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
GetOrderStatus → GET {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
GetBalance → GET {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
CancelVoucheris a no-op that lies — makes no HTTP call, always returns success/CANCELLED(trs_vendor.go:511, marked TODO).ProductAvailabilityis mocked — alwaysIsAvailable: 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.gologs 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.
Vendor Adapters
Per-vendor implementation notes — one page per adapter, grouped by product line, documenting each vendor's auth, catalog, order, webhook, delivery, and unique quirks.
Runa — RUNA
Voucher vendor adapter — async, link-based (Runa-hosted payout URL), Svix-signed webhook + polling. The reference orchestration test.