Memory Appendix
Vendor Amount Discount
Every direct_topup vendor mapper must populate MinAmount, MaxAmount, and VendorDiscount on types.Product — these fields drive pricing, margin, and the variant U
Source memory file:
feedback_vendor_amount_discount.md· Category: Feedback / working-preference This is a verbatim dump of Claude's persistent memory for the Octopus project. Rendered inside a code block so nothing is altered.
---
name: Vendor Amount & Discount mapping is business-critical
description: Every direct_topup vendor mapper must populate MinAmount, MaxAmount, and VendorDiscount on types.Product — these fields drive pricing, margin, and the variant UI.
type: feedback
originSessionId: b0e6c877-5dbd-4595-8081-bf10f1463e62
---
When mapping a vendor's wire response → `services/external_vendors/direct_topup/types.Product`, the mapper MUST populate:
- `MinAmount` and `MaxAmount` (`*float64`) — the denomination/face value of the variant. For fixed-amount products both equal the face value (e.g. `60` for "60 UC", `50` for a $50 gift card). For variable-amount products they are the buy bounds.
- `VendorDiscount` (`float64`, percentage 0–100) — computed as `((retail - wholesale) / retail) * 100`. This is the margin we earn from the vendor's wholesale price vs. retail/origin price.
The handler at `http/handler/admin_topup.go` `syncVariantsForProduct` reads these directly off `types.Product` and writes them onto `models.TopupVariant`. The admin UI (`views/admin/topup_product_detail.jet`) renders `v.MinAmount`, `v.MaxAmount`, and `v.VendorDiscount` — if the mapper omits them, the variants table shows blank Amount and 0% Discount.
**Why:** Amount and Discount aren't display niceties — they are how the business runs. Margin (vendor_discount) drives client-facing pricing, profitability, and routing decisions. A mapper that returns zero/null silently breaks pricing for every variant from that vendor. This bit us on SEAGM: the initial mapper set only `Price` and `FixedAmounts` and skipped the three fields above, so variants synced with empty amount and 0% discount.
**How to apply:**
- Reference implementation: `services/external_vendors/direct_topup/dtone/dtone_vendor.go` `mapProduct` — sets `MinAmount = MaxAmount = destAmount` when fixed; computes `vendorDiscount = ((retail - wholesale) / retail) * 100`.
- For any new vendor (or audit of an existing one): grep the mapper for `MinAmount`, `MaxAmount`, `VendorDiscount`. If any of the three is missing, the integration is broken regardless of whether it compiles or sync succeeds.
- If the vendor reports a pre-computed discount field (e.g. SEAGM's `discount_rate`), prefer recomputing from `(origin - unit) / origin * 100` for consistency; fall back to the vendor-reported field only when origin price is missing.
- Mirror this in any mock (mocky-balboa) used to test the integration: return non-zero `origin_price` and `discount_rate` so a fresh sync produces non-zero values end-to-end.Use Bun Not Npm
For all JS/TS package management and test running in this repo (grasshopper, client, octopus-website, mocky-balboa, octopus-docs), use bun -- never npm.
Vendor Chaos And Tests
Every new vendor adapter (direct_topup, vouchers, esim, payouts) must add a mocky-balboa route with chaos hooks AND ship contract + service tests in the same PR