Wallet
Panel Guide
QR & Terminals

Panel — QR & Terminals

Who calls these endpoints?Merchant panel
Base path
/api/panel/v1/wallet
Identity
Panel session (JWT) — Authorization: Bearer <panel access token>. The token comes from panel login; an s2s signing key does NOT open these endpoints.
Authorization
RBAC permission: wallet:qr:read · wallet:qr:manage · wallet:terminal:read · wallet:terminal:manage · wallet:sponsor-policy:manage · wallet:sponsor-policy:approveFour-eyes: the requester and the approver cannot be the same person — this is enforced by identity comparison, not by a role gate.
Cross-surface
Only four QR endpoints are shared (POST qr, GET qr, GET qr/:id, POST qr/:id/revoke); s2s additionally carries GET qr/:id/image and POST qr/parse. The whole terminal-management and QR producer-code surface (18 endpoints) is panel-only — device provisioning, credential issue/rotate/revoke, suspend, bulk actions and producer-code approval cannot be called with a signing key.

Where you manage your physical acceptance points and QR codes.

QR management

GET  qr                     # codes (filter by programId, qrType, status, targetEndUserId)
POST qr                     # generate
GET  qr/:id
POST qr/:id/revoke          # revoke

Generation and payment flow: QR.

Track active codes here rather than keeping your own code ledger. A suspicious code can be invalidated instantly with revoke.

Producer codes

GET  qr/producer-codes
POST qr/producer-codes
POST qr/producer-codes/:id/approve
POST qr/producer-codes/:id/retire

A producer code identifies the institution inside a standard QR scheme. It is managed with approval and retirement steps.

Coming soon — common-standard QR scheme

Producer codes become meaningful with a standard QR scheme. Today's format is closed loop and carries no institution identity. This section activates when a standard scheme is supported. Status: Roadmap · QR — Interoperability.

Terminals

GET   terminals                        # filter by q, storeCode, status; cursor pagination
POST  terminals                        # create
GET   terminals/:id
PATCH terminals/:id
GET   terminals/:id/events             # terminal event history

Terminal fields: bound business account, label, form factor, store and lane code, per-transaction cap (perTxnCap + perTxnCapCurrency), credential id.

The currency of the per-transaction cap

⚠️

perTxnCap is not a bare number: perTxnCapCurrency always travels with it. That field is not accepted from the caller — it is derived from the currency of the business account the terminal is bound to.

The reason is directly a security one: if the request body could set the cap's currency, writing "cap = 10000 USD" would be a way to widen a TRY cap, and the cap would lose its meaning.

The two exist together or not at all: you cannot store a cap without a denomination, or a denomination without a cap. Removing the cap clears the currency too.

In a multi-currency program the same "10,000" means a very different real ceiling on a TRY terminal and on a USD one. Choosing the terminal's business account is also choosing the currency the cap is enforced in.

If the cap's currency differs from the operation currency, the operation is rejected — no FX conversion is performed.

Fill in storeCode and laneCode from the start. As the estate grows, reporting and bulk actions rely on them; backfilling later becomes manual cleanup.

Activation and credentials

POST terminals/:id/activation           # issue an activation code ({ ttlMinutes })
POST terminals/:id/credential           # bind a credential
POST terminals/:id/rotate-credential    # rotate
POST terminals/:id/revoke-credential    # revoke

A terminal is commissioned in the field with a short-lived activation code.

⚠️

When a terminal is lost or stolen, revoke its credential (revoke-credential) — suspending is not enough. Requests carrying a revoked credential are rejected.

Lifecycle

POST terminals/:id/suspend      # { reason }
POST terminals/:id/resume
POST terminals/:id/retire
ActionEffect
SuspendThe terminal cannot transact; reversible
ResumeLifts the suspension
RetirePermanently decommissions it

Bulk actions

POST terminals/bulk             # bulk creation (row list + activation TTL)
POST terminals/bulk-suspend     # bulk suspension

Bulk suspension works by terminal id list, store code, or the whole tenant.

⚠️

allInTenant: true suspends every terminal — acceptance stops completely. Use it only for deliberate situations such as a security incident, and record the reason.

Reporting

GET reports/terminals

Per-terminal transaction reporting, filterable by store code, status and date range.

Checklist

  • Terminal labels, store and lane codes filled in consistently
  • Per-transaction cap (perTxnCap) configured and perTxnCapCurrency is the currency you expect
  • Activation codes delivered to the field over a secure channel
  • Credentials revoked for lost/stolen terminals
  • Retired terminals cleaned out of the list