Order states, refunds, and an easy accounting mistake:
completeddoes not mean the full amount remains settled.
Orders and Refunds
GET /v1/qrpay/orders ← 列表
GET /v1/qrpay/orders/{id} ← 详情(含逐笔退款明细)
States
| Value | Meaning | Location of funds |
|---|---|---|
pending | Funds locked; upstream not yet notified (very brief) | Locked |
processing | Upstream notified; awaiting a final state | Locked |
completed | Upstream succeeded | Settled |
failed | Upstream definitively failed | Unfrozen |
expired | Timed out without a final state | Unfrozen |
canceled | Member abandoned before upstream notification | Unfrozen |
refunded | Fully refunded | Returned |
⚠
completedis not an absolute terminal state. The recipient merchant may initiate a refund later;a
completedorder can becomerefunded(or remaincompletedwhilerefunded_totalincreases).Do not permanently remove an order from synchronization after it reaches
completed.
⚠ Alert and suspend handling for unknown states; never default them to “processing.”
Showing a failed order as still running leaves the user waiting indefinitely.
The Key Point: status Cannot Tell You How Much Was Collected
Partial refunds do not change status. Only a full refund (settled amount minus our revenue) moves it to refunded.
Therefore:
这一单最终收了多少 = customer_total − refunded_total
⚠ Checking only state treats an order with a 90% refund as fully settled. Reports show inflated revenue,
even though each order looks individually valid—no assertion catches it.
Per-refund details—when and how much was refunded, including the acquirer-side amount—are available from the detail endpoint.
Amount Formats Differ Between the Two Endpoints
| Endpoint | refunded_total format |
|---|---|
GET /v1/qrpay/orders (list) | Fixed-point decimal string ("12.34") |
GET /v1/qrpay/orders/{id} (detail) | Integer string in smallest units ("12340000") |
⚠ Do not reuse one parser for both. This is a historical inconsistency, not a design goal—
but it is now part of the contract, and changing it would break existing merchant integrations.
failure_code May Be Empty
Upstream failure reasons are free text. If no public code can be mapped, we return an empty string, not api_error.
⚠ A consistently wrong value is worse than an absent field—you would branch on it.
Refunds: You Cannot Initiate Them, but Must Handle Them
- There is no refund-initiation endpoint—the upstream API does not provide one. Only the recipient merchant can initiate a refund.
- There is no supplementary charge—funds are released when the member personally selects Pay.
Your responsibility is to handle incoming refunds:
- Subscribe to the QR Payment Order Status webhook
- Query the order after receipt to read
refunded_total(the webhook only signals a change) - Merge forward using
status_version; do not let a slow response overwrite newer state
The Scheme List Is Live
GET /v1/qrpay/schemes
It is not a static constant—the upstream scheme table changes, and we synchronize it. Schemes no longer returned upstream disappear from the list, while historical orders retain their original scheme information.
⚠ Do not copy the scheme table into your code. If you do, your interface will continue prompting users
to scan a scheme after the upstream has removed it.