Wallet Redeem Codes
APay provides built-in wallet redeem codes (redeem_codes and redeem_redemptions) that credit a user's cash balance pool.
- Currency alignment: Every redeem code's currency is locked to the site's accounting currency (
getTopupRules().accountingCurrency, backed bysettings.currency). Both code creation and redemption reject mismatched currencies with HTTP409. - Idempotency & concurrency gate:
redeem_redemptions.code_id(unique) acts as the concurrency gate so each code can only be claimed once;redeem_redemptions.event_id(redeem:<codeId>) is the deterministic ledger idempotency key;(batch_id, user_id)enforces one redemption per user within the same non-null batch. - Wallet backend routing: Local wallet sites (
APAY_WALLET_BACKEND=local) credit viachangeBalance(actionType: 'redeem',balanceType: 'cash'); external wallet sites (APAY_WALLET_BACKEND=ainode) credit via the theme'swalletAdapter(transaction.credit,kind: 'redeem').
User Redemption (POST /api/users/redeem)
Requires an authenticated user session. Rate-limited to 10 requests per minute per user.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
code | string | yes | Redeem code string (whitespace is stripped and normalized to uppercase) |
{
"code": "RC-2345-6789-ABCD"
}
Response
{
"code": 0,
"message": "兑换成功,已到账 10 USD",
"data": {
"amount": 10,
"currency": "USD",
"balance": 25.5,
"alreadyProcessed": false
}
}
If the same user submits a code they have already completed redeeming, the endpoint short-circuits without calling downstream wallet services and returns alreadyProcessed: true.
Admin Endpoints
Protected by the redeem (redeem:view / redeem:edit) admin permission.
List Redeem Codes (GET /api/admin/redeem-codes)
Query parameters: page, pageSize (max 200), status (all | active | used | disabled), batchId, keyword.
Effective status, usedBy, and usedAt are reconciled with redeem_redemptions.
Batch Generate Redeem Codes (POST /api/admin/redeem-codes)
Requires redeem:edit.
| Field | Type | Required | Description |
|---|---|---|---|
count | number | yes | Number of codes to generate (1–1000) |
amount | number | yes | Face value per code (> 0) in the site's accounting currency |
currency | string | no | Optional; if provided, must match the site's accounting currency |
batchId | string | no | Optional batch identifier (limits each user to 1 code per batch) |
expiresAt | string | no | Optional expiration timestamp; timezone-less strings are parsed in settings.timezone |
remark | string | no | Optional operator note (max 200 chars) |
Disable Redeem Code (DELETE /api/admin/redeem-codes/:id)
Requires redeem:edit. Disables an unused (active) redeem code. Returns HTTP 409 if the code has already been claimed in redeem_redemptions or marked used.