Home/Integrating a 3rd-party top-up / code API
Developer docs

Integrating a 3rd-party top-up / code API

Last updated: 2026-10-08

TL;DR

By default it uses the built-in Mock vendor. To use a real upstream: set env vars, restart, change no code.

1. An upstream only needs five capabilities

CapabilityMethodPurpose
Pull catalogpullCatalog()Sync prices, stock, sold-out status
Check stockfetchStock(ids)Validate before ordering
Create ordercreateOrder(req)Idempotency key; sync return or async fulfilment
Query orderqueryOrder(id)Polling fallback outside of webhooks
CallbackverifyWebhook()Primary path for async delivery results

2. Environment variables

UPSTREAM_ENABLED=true
UPSTREAM_ADAPTER=rest
UPSTREAM_BASE_URL=https://api.supplier.example
UPSTREAM_API_KEY=xxxxxxxx
UPSTREAM_API_SECRET=yyyyyyyy
UPSTREAM_SIGNATURE=hmac_sha256   # none|bearer|header_key|query_key|hmac_sha256|md5

UPSTREAM_PATH_CATALOG=/api/products
UPSTREAM_PATH_CREATE_ORDER=/api/order/create
UPSTREAM_PATH_QUERY_ORDER=/api/order/query

UPSTREAM_MAPPING_JSON='{"itemsPath":"data.list","fields":{"externalId":"id","title":"name","price":"price","stock":"stock","soldOutFlag":"sold_out"},"order":{"orderIdPath":"data.order_no","contentPath":"data.content"}}'

3. What the signature does

The hmac_sha256 strategy sorts the request body's params into k1=v1&k2=v2, appends the API Secret, then hashes. This is how the vast majority of domestic platforms work; md5 is a legacy variant.

Webhook verification uses a constant-time comparison and checks a timestamp tolerance window to prevent replay.

4. How to fill in the field mapping

  • itemsPath: the path to the array in the response, e.g. data.list.
  • fields.externalId: the product's unique id, aligned with the site's upstream_ref.
  • soldOutFlag / soldOutWhen: platforms express "out of stock" in countless ways (boolean, string, zero stock); these two fields unify them.
  • order.contentPath: where delivery content lives; split a string by lines, or an array by elements.

5. How fast is switching vendors

If the new platform still fits those five capabilities: only env vars change, zero code changes.

If it's unusual (SOAP, a private binary protocol, session login required): add a file implementing UpstreamAdapter, register it in the factory, and every other layer stays unaware.

6. Self-check

GET /api/upstream/health

Returns the active config summary, health status, and catalog count. Check this first after wiring a new vendor.

7. Stability strategy

  • Timeout + backoff retry: 10s default timeout, up to 2 retries.
  • No aggressive order retries: avoid double-shipping; retry only once.
  • Catalog cache: 60s default. On upstream jitter, return the previous result marked stale so the page never goes blank.
  • Idempotency key: order-no-line-no; one duplicate submit yields one upstream order.

8. Where the code lives

lib/server/upstream/types.ts         # interface contract (five capabilities)
lib/server/upstream/mock-adapter.ts  # local vendor
lib/server/upstream/rest-adapter.ts  # generic REST adapter
lib/server/upstream/index.ts         # facade: cache, degradation, error handling
lib/server/env.ts                    # all config
lib/server/http.ts                   # timeout/retry/idempotency
lib/server/signature.ts              # signing & webhook verification
app/api/upstream/health/route.ts     # self-check endpoint
app/api/upstream/webhook/route.ts    # async callback entry