1.配置新版支付模块-菜单和接口都已重构
2.优化充值提现页面 3.菜单翻译问题 4.备份数据库
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
- **请求方法**:所有移动端业务接口(`/api/*`,不含 `/api/v1/authToken`)一律使用 `POST` 调用;查询类接口同时兼容 `GET`(便于浏览器/调试工具直接访问),客户端统一走 `POST`
|
||||
- `POST` 时请求头 `Content-Type: application/json`,参数放在 JSON body
|
||||
- `GET` 兼容模式下,参数走 URL query string
|
||||
- **例外**:公告模块 `/api/notice/noticeList`、`/api/notice/noticeDetail`、`/api/notice/noticeConfirm` 与模拟收银台页 `/api/finance/depositMockPayPage` **仅支持 `GET`**,参数一律走 URL query string
|
||||
- **例外**:公告模块 `/api/notice/noticeList`、`/api/notice/noticeDetail`、`/api/notice/noticeConfirm` **仅支持 `GET`**,参数一律走 URL query string
|
||||
- 鉴权类接口 `/api/v1/authToken` 仍为 `GET`
|
||||
- 时间:UTC 时间戳(秒) + 服务端时区配置
|
||||
- 金额:数字传输(如 `"100.00"`),客户端展示统一保留两位小数(存储仍为 `decimal(18,2)`)
|
||||
@@ -426,28 +426,66 @@
|
||||
- `processing_note`:string(到账提示文案)
|
||||
- `fee_note`:string(手续费提示文案)
|
||||
- `rate_mode`:string(`fixed` / `live`)
|
||||
- `fields`:object(提现表单必填项开关)
|
||||
- `fields`:object(**与 DDPay / `withdrawCreate` 一致**,不由后台「支付/收款配置」开关维护;用于客户端展示必填项)
|
||||
- `receive_type_bank_only`:bool(当前固定 `true`,仅支持银行卡出金)
|
||||
- `require_receiver_name`:bool(固定 `true`,对应 `receiver_name`)
|
||||
- `require_receive_account`:bool(固定 `true`,对应 `receive_account`)
|
||||
- `require_bank_code`:bool(固定 `true`,对应 `bank_code`)
|
||||
- `require_bank_branch`:bool(固定 `false`,`bank_branch` 选填,不传时服务端按 `N/A` 提交 DDPay)
|
||||
|
||||
### 5.4 创建充值订单
|
||||
- **POST** `/api/finance/depositCreate`
|
||||
- `Content-Type: application/json`(推荐)、`application/x-www-form-urlencoded` 或 **`multipart/form-data`**(如 Apifox 的 form-data);字段名与下表一致即可,服务端通过统一参数名读取,**不限制**为某一种 body 类型。
|
||||
|
||||
说明(与真实「创建订单 → 调起三方 → 异步回调」一致):
|
||||
- **创建单**:`depositCreate` 仅写入 **待支付** 订单(`status=pending`),**不在此请求内入账**。返回体中 `paid=false`,`pay_url` 为 **模拟第三方收银台** 完整 URL(HMAC 防篡改,见下 §5.4.1)。
|
||||
- **客户端**:在 WebView/系统浏览器中打开 `pay_url`;用户完成支付后(模拟页为「确认支付」按钮),由服务端 `depositMockNotify` 验签后调用 `DepositSettlement::settle` 入账,并推送 `wallet.changed`;客户端可轮询 `depositDetail` 或依赖推送更新余额。
|
||||
- **未来接入真实第三方支付**:将 `pay_url` 与回调 URL 替换为真网关,入账仍**仅**在回调/验签成功路径中调用 `DepositSettlement::settle`(与当前模拟回调一致)。
|
||||
- 档位与渠道取自 `depositTierList`:创建订单时须选择返回 `channels` 中某一渠道的 `code` 作为 `channel_code` 传入;服务端会校验档位存在、启用且渠道已启用。
|
||||
- **HMAC 密钥**:模拟链路与签名校验使用环境变量 **`DEPOSIT_MOCK_HMAC_KEY`**(或 `config('app.deposit_mock_hmac_key')`);生产环境务必配置,与代码中默认值区分。
|
||||
- **并发上限**:同一用户最多同时存在 **3 笔待支付充值单**(`status=0`);超过后创建接口返回 `code=2005`。
|
||||
- **超时失效**:充值单创建后 **60 秒内未支付**将自动置为失败(`status=failed`),并在订单备注记录失败原因(`[timeout] unpaid over 60s`)。
|
||||
- **定时任务兜底**:服务端进程 `depositOrderExpireTicker` 每 **10 秒**主动扫描超时待支付单,保证即使用户不访问任何充值接口也会准时失效。
|
||||
说明:
|
||||
- **仅支持 DDPay**:`channel_code` 必须为 **`ddpay`**,否则返回 `code=2004`。
|
||||
- `depositCreate` 先创建 `status=pending` 的充值单,再调用 DDPay「入金发起」;若成功返回 `payment_url`,则 `pay_url=payment_url`。
|
||||
- 入账由 **`POST /api/finance/ddpayDepositNotify`** 验签后结算,或网关同步返回 `transaction_status=completed` 时结算。
|
||||
- 档位与渠道取自 `depositTierList`:`channels` 中仅会出现 `ddpay`(与后台「支付/收款配置」一致)。
|
||||
- 同一用户最多 3 笔待支付充值单;创建后 60 秒未支付会超时失败。
|
||||
|
||||
请求参数(**三者缺一不可**,任一为空或空白即 `code=1001` 参数缺失):
|
||||
- `tier_id`:string,必填(含义:玩家选择的充值档位 ID,取自 `depositTierList` 的 `id`;也可用同义字段名 `tier_key`)
|
||||
- `channel_code`:string,必填(含义:支付渠道代码,**小写**;须与所选档位在 `depositTierList` 返回的 `channels[].code` 之一一致,例如默认内置渠道常为 `directpay`)
|
||||
- `idempotency_key`:string,必填,≤64(含义:客户端生成的唯一键,短时间内同 `idempotency_key` 不会重复下单;建议 UUID。**调试工具中若使用变量,请确保解析后非空**)
|
||||
请求参数:
|
||||
|
||||
> **常见 1001 原因**:只传了 `tier_id` + `idempotency_key`,**漏传 `channel_code`**。请先调 `depositTierList`,用对应档位下 `channels` 中某项的 `code` 作为 `channel_code`。
|
||||
**A. 通用必填参数**
|
||||
- `tier_id`:string,必填(充值档位 ID;同义字段:`tier_key`)
|
||||
- `channel_code`:string,必填,固定传 **`ddpay`**
|
||||
- `idempotency_key`:string,必填,≤64(客户端幂等键,建议 UUID)
|
||||
|
||||
**B. DDPay 渠道必填参数(`channel_code=ddpay`)**
|
||||
|
||||
> **依据**:DDPay 官方《Payment Gateway》接口说明(与仓库内 `docs/DDPay Payment Gateway_v1.1.3_zh.md` / `docs/DDPay Payment Gateway_v1.1.3.pdf` 一致;以下「官方」均指该文档)。
|
||||
|
||||
- `payment_type`:string,必填(DDPay 字段 **`payment_type`,支付方式选择**)
|
||||
- **取值须为官方枚举字符串**(文档 §3.1):**`01`** = FPX,**`02`** = duitnow,**`03`** = ewallet;**其他取值须向 DDPay 商户支持另行确认**,勿自行臆造。
|
||||
- 兼容字段:`paymentType`
|
||||
- **注意**:若传入 `FPX`、`duitnow` 等**非官方编码**,网关可能直接拒绝;移动端应传 **`01` / `02` / `03`**。
|
||||
- `payer_name`:string,必填(**付款账户持有人姓名**,须与银行登记信息一致)
|
||||
- 兼容字段:`payerName`
|
||||
- `payer_bank_name`:string,必填(对应官方字段 **`payer_bank[name]`,付款银行名称**)
|
||||
- 须使用 **DDPay 银行列表中的银行全称**(与官方附录一致):**MYR** 参考官方附录「入金银行列表(MYR)」中 **英文全称**(如 `Public Bank`、`Maybank2U`);**THB** 参考「入金银行列表(THB)」中 **英文全称**(如 `BANGKOK BANK PUBLIC COMPANY LTD.`)。勿使用简称或与列表不一致的拼写,否则可能被网关拒单。
|
||||
- 兼容字段:`payer_bank[name]`、`payerBankName`
|
||||
|
||||
**B.1 服务端代传、客户端无需传的 DDPay 字段(供联调对照)**
|
||||
|
||||
以下由服务端在调用 DDPay「入金发起」时自动组装(来自环境/配置),移动端**不要**也无法通过本接口覆盖:
|
||||
|
||||
- `client_id`、`identifier`(若项目环境已配置 `DDPAY_IDENTIFIER`)、`order_id`(即本系统 `order_no`)
|
||||
- `transaction_amount`:取自所选档位的 **`pay_amount`**(法币应付额,类型为 Decimal),币种以商户在 DDPay **onboarding 时约定币种**为准;官方示例表写 **MYR**,若贵司为 THB 等,以 DDPay 后台为准。
|
||||
- `callback_url`、`redirect_url`:由服务端拼接,根地址优先读取环境变量 **`DDPAY_PUBLIC_BASE_URL`**(见 `.env-example`),未配置时按请求 `Host` 推导;官方要求 **HTTPS**(生产务必配置公网 HTTPS 根地址)。
|
||||
|
||||
> **常见 1001 原因(DDPay)**:只传了通用参数,漏传 `payment_type / payer_name / payer_bank_name` 之一;或 `payment_type` 未使用官方 `01/02/03`。
|
||||
|
||||
推荐请求示例(DDPay,MYR + FPX):
|
||||
```json
|
||||
{
|
||||
"tier_id": "t_xxxxxxxx",
|
||||
"channel_code": "ddpay",
|
||||
"idempotency_key": "dp_20260429_xxx",
|
||||
"payment_type": "01",
|
||||
"payer_name": "ZHANG SAN",
|
||||
"payer_bank_name": "Public Bank"
|
||||
}
|
||||
```
|
||||
|
||||
返回参数:
|
||||
- `order_no`:string(含义:充值订单号)
|
||||
@@ -456,31 +494,38 @@
|
||||
- `total_amount`:string(2 位小数,含义:实际入账总额 = amount + bonus_amount)
|
||||
- `pay_channel`:string(含义:支付通道标识,与请求中选择的 `channel_code` 一致,落库在订单上)
|
||||
- `paid`:bool(含义:当前单据是否已到账;`true` 表示钱包已入账、`status=paid`;`false` 表示待玩家在第三方支付页面完成支付)
|
||||
- `pay_url`:string(含义:第三方支付收银台地址;**`paid=false`(待支付)** 时返回**完整 URL**(如 `https://你的域名/api/finance/depositMockPayPage?order_no=...&sign=...`);`paid=true` 时为空串)
|
||||
- `pay_url`:string(含义:DDPay 返回的收银台地址;**`paid=false`(待支付)** 时为三方 **`payment_url`(完整 URL)**;`paid=true` 时为空串)
|
||||
- `status`:string(`pending`/`paid`/`failed`,含义:本接口创建成功时为 `pending`,入账完成后为 `paid`)
|
||||
- `create_time`:int(含义:订单创建时间,秒级时间戳)
|
||||
- `pay_time`:int(含义:订单到账时间,未到账为 0)
|
||||
|
||||
#### 5.4.1 模拟第三方:收银台页与「异步通知」回调(开发/无真网关时使用)
|
||||
#### 5.4.1 DDPay 回调与状态说明(当前实现)
|
||||
|
||||
- **GET** `/api/finance/depositMockPayPage`
|
||||
- **Query**:`order_no`(与 `depositCreate` 返回一致)、`sign`(HMAC,与 `pay_url` 中一致;**不要自行拼接,须完整使用 `depositCreate` 返回的 `pay_url` 或同接口再次查询到的地址**)
|
||||
- 无需 `auth-token` / `user-token`(外跳浏览器使用)。
|
||||
- 返回:HTML 页面,用户点击 **「确认支付(模拟成功)」** 即提交到下方 `depositMockNotify`。
|
||||
- 回调地址:`POST /api/finance/ddpayDepositNotify`(由服务端在 DDPay 发起请求时作为 **`callback_url`** 传给三方;须 **HTTPS**)。
|
||||
- 官方 Webhook 负载字段(文档 §3.5,处理前**必须先验签**):
|
||||
|
||||
- **POST** `/api/finance/depositMockNotify`
|
||||
- **Body**(`application/x-www-form-urlencoded` 或 JSON 均可,字段名一致即可):`order_no`、`sign`(与上页/支付链接一致)
|
||||
- 无需 `user-token`;`auth-token` 可选(当前实现不校验)。
|
||||
- 验签成功后:对 `status=0` 的订单执行入账(`DepositSettlement::settle`,`source=third_party` 语义),并推送 `wallet.changed`。已入账订单**幂等**再调返回当前订单信息。
|
||||
- 成功响应:与 `depositCreate` 成功体相同结构(`code=1` + `data` 为统一充值订单结构)。
|
||||
| 参数 | 说明 |
|
||||
|---|---|
|
||||
| `client_id` | 商户标识 |
|
||||
| `order_id` | 交易引用号(与本系统充值 `order_no` 对应) |
|
||||
| `transaction_status` | 最终状态:`pending` / `completed` / `failed`(含义见官方 §4.1) |
|
||||
| `timestamp` | 通知时间 |
|
||||
| `transaction_amount` | 支付金额(配置币种下) |
|
||||
| `signature` | 通知校验签名 |
|
||||
|
||||
- 我方验签通过后:
|
||||
- `transaction_status=completed`:执行入账结算,订单转 `paid`
|
||||
- `transaction_status=failed`:订单转 `failed`
|
||||
- 官方要求:收到通知后应尽快返回 HTTP **200**,响应体为纯文本 **`{"status":"ok"}`**;未收到确认时平台最多重试 **6** 次(以官方文档为准)。
|
||||
- 若三方暂未提供/未打通回调,`pending` 订单将保持待处理;官方另提供「**入金状态查询**」接口(请求需含 `query_time`,格式 **`YYYY-MM-DD HH:MM:SS`**),当前移动端未单独暴露,由服务端按需扩展。
|
||||
|
||||
错误码约定:
|
||||
- `1001`:缺少必填参数(`tier_id`(或 `tier_key`)、`channel_code`、`idempotency_key` 任一未传或为空字符串)
|
||||
- `1001`(DDPay):缺少 `payment_type` / `payer_name` / `payer_bank_name`,或 `payment_type` 非官方允许取值
|
||||
- `1002`:`idempotency_key` 过长,或与其他玩家的订单冲突
|
||||
- `1003`:模拟回调/链接参数非法(如 `sign` 与 `order_no` 不匹配)——`depositMockNotify` 与无效支付链接
|
||||
- `2000`:订单落库或入账失败(事务回滚后返回原始错误描述)
|
||||
- `2000`:**通用**:订单落库或入账失败(事务回滚等,可能带原始错误描述);**DDPay**:调用「入金发起」失败(网络/HTTP/JSON/验签/`status_code≠0` 等),对外文案为「DDPay 充值发起失败」,**具体原因可查看该笔 `deposit_order.remark`(前缀 `[ddpay]`)或服务端日志**
|
||||
- `2003`:所选 `tier_id` 不存在、已停用或不在启用列表中
|
||||
- `2004`:`channel_code` 未配置或已禁用
|
||||
- `2004`:`channel_code` 非 `ddpay`;或 `ddpay` 未启用;或当前档位币种与该渠道不允许的组合
|
||||
- `2005`:待支付充值单超过上限(`data.max_pending`、`data.pending_count`、`data.expire_seconds`)
|
||||
|
||||
### 5.5 查看充值订单详情
|
||||
@@ -526,13 +571,32 @@
|
||||
|
||||
请求参数:
|
||||
- `withdraw_coin`:string(含义:申请提现金额,必须 > 0)
|
||||
- `receive_account`:string(含义:收款账号)
|
||||
- `receive_type`:string(`bank`/`ewallet`/`crypto`,含义:收款类型)
|
||||
- `receive_account`:string(含义:收款账号;对接 DDPay 出金时对应官方字段 **`receiver_account`**,为收款账户号/手机号,须与银行登记一致)
|
||||
- `receive_type`:string(含义:收款类型;当前版本仅支持 `bank`)
|
||||
- `idempotency_key`:string(含义:防重复提交提现)
|
||||
- `receiver_name`:string(含义:收款账户持有人姓名;`receive_type=bank` 必填,对应官方 **`receiver_name`**,须与银行登记一致)
|
||||
- `bank_code`:string(含义:银行代码;`receive_type=bank` 必填。取值来自收银台配置 `withdraw_banks[].code`;服务端会映射为 DDPay 所需的 **`bank[name]`** 银行全称发起出金)
|
||||
- `bank_branch`:string(含义:银行支行名称;`receive_type=bank` 可选。官方 **`bank_branch`** 为必填项,若客户端不传则服务端按 **`N/A`** 提交,与 DDPay 文档「若缺失则默认值为 `N/A`」一致)
|
||||
|
||||
**DDPay 出金(Payout)官方请求字段对照(文档 §3.2,由服务端在审核通过后组装)**
|
||||
|
||||
| 官方参数 | 说明 |
|
||||
|---|---|
|
||||
| `client_id` | 商户账号标识 |
|
||||
| `bill_number` | 出金唯一引用号(与本系统提现 `order_no` 对应) |
|
||||
| `amount` | 出金金额(Decimal,配置币种下) |
|
||||
| `receiver_name` | 收款账户持有人姓名 |
|
||||
| `receiver_account` | 收款账户号/手机号 |
|
||||
| `bank[name]` | 银行全称(须参考官方附录「出金银行列表」) |
|
||||
| `bank_branch` | 支行名称;缺失为 `N/A` |
|
||||
| `callback_url` | 异步通知地址,须 **HTTPS** |
|
||||
| `signature` | MD5 签名(小写) |
|
||||
|
||||
出金成功响应中,官方还可能返回 `transaction_fee`、`transaction_total`、`transaction_status`、`remark` 等;最终以 DDPay 文档为准。
|
||||
|
||||
返回参数:
|
||||
- `order_no`:string(含义:提现订单号)
|
||||
- `status`:string(`pending_review`/`processing`,含义:提现状态)
|
||||
- `status`:string(`pending_review`/`approved`/`rejected`,含义:提现状态;已打款合并到 `approved`)
|
||||
- `fee_coin`:string(含义:手续费)
|
||||
- `actual_arrival_coin`:string(含义:实到账金额)
|
||||
- `risk_review_required`:bool(含义:是否命中人工审核)
|
||||
@@ -548,7 +612,7 @@
|
||||
- `coin_balance`、`bet_flow_coin`、`total_withdraw_coin`、`ratio`
|
||||
- `max_withdraw_by_flow`:仅按打码量折算的上限(= `max(0, bet_flow_coin / ratio - total_withdraw_coin)`);`ratio=0` 时为 `null`
|
||||
5. 以上全通过后在同一事务内:
|
||||
- `withdraw_order` 写入:`amount` / `fee`(默认 0.5%) / `actual_amount = amount - fee` / `status=0`(待审核) / `channel_id` 取自用户归属渠道快照。
|
||||
- `withdraw_order` 写入:`amount` / `fee`(默认 0.5%) / `actual_amount = amount - fee` / `status=0`(待审核) / `channel_id` 取自用户归属渠道快照;同时写入收款字段(`receive_type/receive_account/receiver_name/bank_code/bank_branch`)。
|
||||
- `user` 表原子更新:`coin -= withdraw_coin` 且 `total_withdraw_coin += withdraw_coin`(WHERE `coin >= withdraw_coin` 防止并发超额扣减)。
|
||||
- `user_wallet_record` 写入 `biz_type=withdraw`、`direction=2`、`amount=withdraw_coin`、`ref_type=withdraw_order`、`idempotency_key=wd_apply_{order_no}`,代表"冻结"动作。
|
||||
|
||||
@@ -556,7 +620,8 @@
|
||||
- 单笔最大可提现 `max_withdrawable = min(coin_balance, max_withdraw_by_flow)`;每笔提现按 `withdraw_coin × ratio` 消耗打码配额,已消耗部分累积在 `total_withdraw_coin`。
|
||||
- `ratio = 0` 时视为"不限打码",单笔上限仅受 `coin_balance` 约束。
|
||||
- 采用"申请即冻结"语义:提现在移动端提交后立即从 `user.coin` 中扣减并写出金流水;后台审核 **拒绝** 时由管理端在同一事务中回冲余额、`total_withdraw_coin` 与流水,不出现"等待审核期间用户还能把这笔钱再下注"的漏洞。
|
||||
- 后台审核 **通过** 时不再额外触碰余额;若管理员调整了 `amount` 或 `fee`,按新旧差额再生成一条 `withdraw` / `withdraw_refund` 流水以保持账务平衡,并同步修正 `total_withdraw_coin`。
|
||||
- 后台审核 **通过** 时会触发三方出金(DDPay Payout);出金 **失败** 会自动回冲余额、`total_withdraw_coin` 与流水(`withdraw_refund`),并将订单标记为 `rejected`(内部 `status=2`)。
|
||||
- 后台审核 **通过** 且出金完成后,订单内部 `status=3`(已打款),移动端对外仍合并展示为 `approved`。
|
||||
- `withdraw_bet_flow_ratio` 由后台「游戏配置」维护,默认 `1.00`,修改后对新请求立即生效。
|
||||
|
||||
### 5.8 查看提现订单详情
|
||||
@@ -728,9 +793,8 @@
|
||||
|
||||
## 8.2 充值到下注到提现闭环
|
||||
1. 拉取档位:`POST /api/finance/depositTierList`(玩家选择一档,并记下该档 `channels[].code`)
|
||||
2. 创建订单:`POST /api/finance/depositCreate`(`tier_id` + `channel_code` + `idempotency_key`,三者为必填;可用 JSON / form-data / x-www-form-urlencoded)
|
||||
- 返回 `paid=false`、`status=pending`、**非空 `pay_url`**:客户端在 WebView/浏览器中打开 `pay_url`(`GET /api/finance/depositMockPayPage`);用户在模拟页点击确认后,由 `POST /api/finance/depositMockNotify` 完成入账,或轮询 `depositDetail` / 等 `wallet.changed` 再刷新余额
|
||||
- 未来接真实第三方:将 `pay_url` 换为真网关,入账仅在支付平台 **异步通知** 中调用 `DepositSettlement::settle`(与当前 `depositMockNotify` 路径一致)
|
||||
2. 创建订单:`POST /api/finance/depositCreate`(`tier_id` + **`channel_code=ddpay`** + `idempotency_key` + DDPay 入金必填字段;可用 JSON / form-data / x-www-form-urlencoded)
|
||||
- 返回 `paid=false`、`status=pending`、**非空 `pay_url`**:客户端在 WebView/浏览器中打开 **`pay_url`(DDPay `payment_url`)** 完成支付;入账由 **`ddpayDepositNotify`** Webhook 驱动,可轮询 `depositDetail` 或等待 `wallet.changed` 刷新余额
|
||||
3. 客户端可选轮询 `POST /api/finance/depositDetail` 兜底确认状态;入账成功后会收到 `wallet.changed`
|
||||
4. 下注:`POST /api/game/placeBet`
|
||||
5. 监听余额:`wallet.changed`(或按订单详情接口核对)
|
||||
|
||||
Reference in New Issue
Block a user