说明
所有的业务系统中。
都需要哦对交易中的用户的交易金额+交易次数进行限制。
本质:一定的时间维度,对指定的用户的指定类别的信息进行统计。如果超过一定的次数,则进行拦截(处罚)。
基本的交易限额限次
基本流程
所有的交易分为事前+事后。
发起交易之前,首先调用风控系统判断是否存在风险。
交易完成之后,把交易最后的结果要通知风控。(需要统计成功的交易时,需要状态)
事前请求响应的状态
调用风控的时候,会有 3 个结果:
-
成功
-
失败
-
超时
接口设计
事前
- beforeRisk
[plaintext]
1
2
3
4
5
6
7
8
9
10
11
12appId: 唯一应用标识
traceId: 唯一跟踪号
requestTime: 请求时间
checksum: 签名 (每一个应用,都有 appSecret。通过这个统一处理)
orderId: 业务订单号
transAmt: 交易金额
transType: 交易类别
prodId: 产品号
merId: 商户号
transTime: 交易时间 yyyyMMddHHmmssSSS
transDate: 交易日期 yyyyMMdd
要求:orderId + appId 唯一。
事后
- afterRisk
[plaintext]
1
2
3
4
5
6
7appId: 唯一应用标识
traceId: 唯一跟踪号
requestTime: 请求时间
checksum: 签名 (每一个应用,都有 appSecret。通过这个统一处理)
orderId: 业务订单号
transStatus: 交易状态
chat
接口设计
针对交易限额限次需求,设计以下HTTP接口方案,包含事前请求和事后通知接口,确保安全性、幂等性和唯一性约束:
1. 事前请求接口
功能:提交交易信息,进行额度/次数校验
URL:POST /api/transaction/pre-event
Content-Type:application/json
请求体:
[json]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{
"appId": "APP123456",
"traceId": "TRACE-20250602120000-001",
"requestTime": 1717315200000, // Unix毫秒时间戳
"checksum": "a1b2c3d4e5f6...", // 签名值
"orderId": "ORDER-20250602-001",
"transAmt": 150.00,
"transType": "PAYMENT",
"prodId": "PROD-1001",
"merId": "MERCHANT-01",
"transTime": "20250602120000123", // yyyyMMddHHmmssSSS
"transDate": "20250602", // yyyyMMdd
"extraMap": {
"userIp": "192.168.1.1",
"deviceId": "DEV-001"
}
}
签名生成规则:
- 排除
checksum
字段,将其他字段按字典序排序 - 拼接键值对:
key1=value1&key2=value2...
- 末尾追加
appSecret
:拼接串 + appSecret
- 计算SHA256:
checksum = SHA256(拼接串).toLowerCase()
响应:
[json]
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 成功 (HTTP 200)
{
"code": 0,
"msg": "SUCCESS",
"data": {
"limitCheckPass": true // 是否通过限额检查
}
}
// 失败示例 (HTTP 200)
{
"code": 1001, // 错误码
"msg": "DUPLICATE_ORDER" // 重复订单/额度不足/签名无效等
}
2. 事后通知接口
功能:上报交易最终状态
URL:POST /api/transaction/post-event
Content-Type:application/json
请求体:
[json]
1
2
3
4
5
6
7
8{
"appId": "APP123456",
"traceId": "TRACE-20250602120500-001",
"requestTime": 1717315500000,
"checksum": "d5e6f7a8b9c0...",
"orderId": "ORDER-20250602-001",
"transStatus": "SUCCESS" // SUCCESS/FAIL
}
签名生成:同事前接口规则(排除checksum
后排序签名)
响应:
[json]
1
2
3
4
5
6
7
8
9
10
11// 成功 (HTTP 200)
{
"code": 0,
"msg": "STATUS_UPDATED"
}
// 失败示例 (HTTP 200)
{
"code": 2001,
"msg": "ORDER_NOT_FOUND" // 订单不存在/状态已更新
}
关键设计说明
- 唯一性约束
- 数据库对
(appId + orderId)
建立唯一索引,确保事前请求幂等 - 重复请求返回
DUPLICATE_ORDER
错误码
- 数据库对
- 安全机制
- 签名校验:服务端按相同规则生成签名并比对
- 时效控制:校验
requestTime
与服务器时间差(建议±5分钟) - 防重放攻击:缓存
traceId
短期有效(如10分钟)
- 数据存储设计
[sql]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15CREATE TABLE transaction_records ( id BIGINT AUTO_INCREMENT PRIMARY KEY, app_id VARCHAR(32) NOT NULL, order_id VARCHAR(64) NOT NULL, trace_id VARCHAR(64) NOT NULL, trans_amt DECIMAL(18,2) NOT NULL, trans_type VARCHAR(20) NOT NULL, prod_id VARCHAR(32), mer_id VARCHAR(32) NOT NULL, trans_time CHAR(17) NOT NULL, /* yyyyMMddHHmmssSSS */ trans_date CHAR(8) NOT NULL, /* yyyyMMdd */ extra_map JSON, trans_status ENUM('PENDING','SUCCESS','FAIL') DEFAULT 'PENDING', UNIQUE KEY udx_app_order (app_id, order_id) );
- 状态机流转
- 错误码规范 | 错误码 | 含义 | |——–|———————–| | 0 | 成功 | | 1001 | 重复订单 | | 1002 | 限额不足 | | 1003 | 签名验证失败 | | 1004 | 请求过期 | | 2001 | 订单不存在 | | 2002 | 状态已更新(无需重复)|
调用示例流程
- 事前请求
- 商户系统生成唯一
orderId
,计算签名 - 调用
/pre-event
获取额度检查结果 - 若通过则执行实际交易
- 商户系统生成唯一
- 事后通知
- 交易完成后(无论成功/失败)
- 携带最终状态调用
/post-event
- 服务端更新状态并释放/占用额度
此设计保证接口的幂等性、安全性,并通过唯一约束和状态机确保数据一致性。
表结构的拓展
如果让你来设计限额限次,你会设计哪些表?为什么