钱包兑换码

APay 内置钱包兑换码能力(redeem_codes 与 redeem_redemptions),用户兑换后直接充入钱包 cash(充值余额)池。

  • 记账币种强一致:兑换码币种固定为站点记账币种(getTopupRules().accountingCurrency,读取 settings.currency)。生成与兑换阶段若币种与站点记账币种不一致,均以 HTTP 409 拒绝,防止跨币种 1:1 误入账。
  • 并发闸门与幂等键:redeem_redemptions.code_id 唯一键作为并发抢占闸门(一码一兑);redeem_redemptions.event_id(redeem:<codeId>)作为入账幂等键;(batch_id, user_id) 唯一键保证同一非空批次每位用户限兑一次。
  • 钱包后端路由:本地钱包模式(APAY_WALLET_BACKEND=local)经 changeBalance(actionType: 'redeem',balanceType: 'cash')入账;外部钱包模式(APAY_WALLET_BACKEND=ainode)经主题 walletAdapter(transaction.credit,kind: 'redeem')入账。

用户兑换接口(POST /api/users/redeem)

需用户登录会话,按用户限流每分钟最多 10 次。

请求体

字段类型必填说明
codestring是兑换码字符串(服务端自动去空白并转大写)
{
  "code": "RC-2345-6789-ABCD"
}

响应示例

{
  "code": 0,
  "message": "兑换成功,已到账 10 USD",
  "data": {
    "amount": 10,
    "currency": "USD",
    "balance": 25.5,
    "alreadyProcessed": false
  }
}

若同一用户重复提交本人已完成兑换的兑换码,服务端将直接短路返回 alreadyProcessed: true,不会再次触发下游钱包入账请求。

后台管理接口

受管理员权限 redeem(redeem:view / redeem:edit)保护。

查询兑换码列表(GET /api/admin/redeem-codes)

查询参数:page、pageSize(最大 200)、status(all | active | used | disabled)、batchId、keyword。 列表查询自动左连接权威占用表 redeem_redemptions 对齐展示状态、使用者与使用时间。

批量生成兑换码(POST /api/admin/redeem-codes)

需 redeem:edit 权限。

字段类型必填说明
countnumber是生成数量(1–1000)
amountnumber是单张面值(> 0,单位为站点记账币种)
currencystring否可选;若传入则必须与站点记账币种一致
batchIdstring否批次号(可选,同一批次每人限兑一次)
expiresAtstring否有效期(可选,无时区时间按站点 settings.timezone 解析为 UTC)
remarkstring否备注(可选,最长 200 字符)

作废兑换码(DELETE /api/admin/redeem-codes/:id)

需 redeem:edit 权限。仅允许作废未使用(active)的兑换码;若 redeem_redemptions 已存在占用记录或状态为 used,返回 HTTP 409。