mirror of
https://github.com/codewendao/PayPro.git
synced 2026-09-03 06:13:46 +08:00
223 lines
6.5 KiB
Markdown
223 lines
6.5 KiB
Markdown
# 外部订单接口文档
|
||
|
||
## 接口概述
|
||
|
||
该接口用于外部系统向支付系统添加订单,支持MD5签名验证、时间戳校验、事务处理等功能。
|
||
|
||
## 接口信息
|
||
|
||
- **接口路径**: `POST /api/openapi/add`
|
||
- **请求方法**: POST
|
||
- **Content-Type**: application/json
|
||
|
||
## 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 |
|
||
|--------|------|------|------------------------------------------|
|
||
| orderNo | String | 是 | 订单号,唯一标识 |
|
||
| amount | BigDecimal | 是 | 订单金额,必须大于0且小于等于100000 |
|
||
| payType | String | 是 | 支付方式(alipay/wechat/alipay_dmf/wechat_zs) |
|
||
| nickName | String | 否 | 用户昵称 |
|
||
| description | String | 否 | 订单描述 |
|
||
| email | String | 否 | 用户邮箱 |
|
||
| notifyUrl | String | 否 | 异步通知地址 |
|
||
| userId | String | 否 | 用户ID |
|
||
| productId | Long | 否 | 产品ID |
|
||
| timestamp | Long | 是 | 请求时间戳(毫秒),有效期5分钟 |
|
||
| expireSeconds | Integer | 否 | 过期时间(秒),订单将在该时间后过期 |
|
||
| sign | String | 是 | MD5签名 |
|
||
|
||
## 签名算法
|
||
|
||
1. 将所有参数(除sign外)按字母顺序排序
|
||
2. 拼接成 `key1=value1&key2=value2&...&key=secretKey` 格式
|
||
3. 对拼接后的字符串进行MD5加密,并转为大写
|
||
|
||
### 签名示例
|
||
|
||
假设配置的密钥为:`your_openapi_secret_key_here`
|
||
|
||
参数:
|
||
```json
|
||
{
|
||
"orderNo": "EXT20250303001",
|
||
"amount": 10.00,
|
||
"payType": "alipay",
|
||
"timestamp": 1733232000000
|
||
}
|
||
```
|
||
|
||
排序后的参数:
|
||
```
|
||
amount=10.00&orderNo=EXT20250303001&payType=alipay×tamp=1733232000000&key=your_openapi_secret_key_here
|
||
```
|
||
|
||
MD5签名:`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`
|
||
|
||
## 响应格式
|
||
|
||
### 成功响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"orderNo": "EXT20250303001",
|
||
"amount": 10.00,
|
||
"payType": "alipay",
|
||
"payNum": "随机支付标识",
|
||
"state": 0,
|
||
"message": "订单创建成功",
|
||
"timestamp": 1733232000000,
|
||
"qrCodeUrl": "支付二维码URL",
|
||
"returnUrl": "支付完成返回URL"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 返回值说明
|
||
|
||
| 字段名 | 类型 | 说明 |
|
||
|--------|------|------|
|
||
| orderNo | String | 外部订单号 |
|
||
| amount | BigDecimal | 订单金额 |
|
||
| payType | String | 支付方式(alipay/wechat/alipay_dmf/wechat_zs) |
|
||
| payNum | String | 支付标识,用于后续查询订单 |
|
||
| state | Integer | 订单状态(0-待支付,1-已支付,2-已失败,3-已支付,4-已扫码) |
|
||
| message | String | 处理结果消息 |
|
||
| timestamp | Long | 系统处理完成时间戳(毫秒) |
|
||
| qrCodeUrl | String | 支付二维码URL,用于展示支付二维码 |
|
||
| returnUrl | String | 跳转url,可以跳转至改地址进行后续处理 |
|
||
|
||
### 失败响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"msg": "错误信息描述",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
## 错误码说明
|
||
|
||
| 错误码 | 说明 |
|
||
|--------|------|
|
||
| 200 | 成功 |
|
||
| 400 | 参数错误 |
|
||
| 401 | 签名验证失败 |
|
||
| 402 | 时间戳无效或已过期 |
|
||
| 403 | 订单号已存在 |
|
||
| 404 | 金额错误 |
|
||
| 500 | 系统内部异常 |
|
||
|
||
## 请求示例
|
||
|
||
### cURL 示例
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8892/api/openapi/add \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"orderNo": "EXT20250303001",
|
||
"amount": 10.00,
|
||
"payType": "alipay",
|
||
"nickName": "测试用户",
|
||
"description": "测试订单",
|
||
"email": "test@example.com",
|
||
"userId": "USER001",
|
||
"productId": 1,
|
||
"notifyUrl": "http://example.com/notify",
|
||
"timestamp": 1733232000000,
|
||
"expireSeconds": 3600,
|
||
"sign": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||
}'
|
||
```
|
||
|
||
### Java 示例
|
||
|
||
```java
|
||
import cn.hutool.http.HttpRequest;
|
||
import cn.hutool.json.JSONObject;
|
||
import cn.hutool.crypto.SecureUtil;
|
||
|
||
public class ExternalOrderClient {
|
||
|
||
private static final String API_URL = "http://localhost:8892/api/openapi/add";
|
||
private static final String SECRET_KEY = "your_openapi_secret_key_here";
|
||
|
||
public static void main(String[] args) {
|
||
JSONObject params = new JSONObject();
|
||
params.put("orderNo", "EXT20250303001");
|
||
params.put("amount", 10.00);
|
||
params.put("payType", "alipay");
|
||
params.put("nickName", "测试用户");
|
||
params.put("description", "测试订单");
|
||
params.put("email", "test@example.com");
|
||
params.put("userId", "USER001");
|
||
params.put("productId", 1);
|
||
params.put("notifyUrl", "http://example.com/notify");
|
||
params.put("timestamp", System.currentTimeMillis());
|
||
params.put("expireSeconds", 3600); // 过期时间为1小时
|
||
|
||
String sign = generateSign(params);
|
||
params.put("sign", sign);
|
||
|
||
String response = HttpRequest.post(API_URL)
|
||
.header("Content-Type", "application/json")
|
||
.body(params.toString())
|
||
.execute()
|
||
.body();
|
||
|
||
System.out.println(response);
|
||
}
|
||
|
||
private static String generateSign(JSONObject params) {
|
||
List<String> keys = new ArrayList<>(params.keySet());
|
||
Collections.sort(keys);
|
||
|
||
StringBuilder sb = new StringBuilder();
|
||
for (String key : keys) {
|
||
Object value = params.get(key);
|
||
if (value != null && !"".equals(value) && !"sign".equals(key)) {
|
||
sb.append(key).append("=").append(value).append("&");
|
||
}
|
||
}
|
||
sb.append("key=").append(SECRET_KEY);
|
||
|
||
return SecureUtil.md5(sb.toString()).toUpperCase();
|
||
}
|
||
}
|
||
```
|
||
|
||
## 配置说明
|
||
|
||
在 `application.yml` 中配置外部接口密钥:
|
||
|
||
```yaml
|
||
paypro:
|
||
openapi:
|
||
secret: your_openapi_secret_key_here
|
||
```
|
||
|
||
## 安全特性
|
||
|
||
1. **MD5签名验证**:确保请求参数未被篡改
|
||
2. **时间戳校验**:防止重放攻击,时间戳有效期5分钟
|
||
3. **订单号唯一性**:防止重复订单
|
||
4. **金额限制**:单笔订单金额不超过100000元
|
||
5. **事务处理**:确保订单创建的原子性
|
||
|
||
## 注意事项
|
||
|
||
1. 请妥善保管密钥,不要泄露给第三方
|
||
2. 时间戳必须使用毫秒级时间戳
|
||
3. 订单号必须唯一,重复的订单号会返回错误
|
||
4. 建议在生产环境中使用HTTPS协议
|
||
5. 建议实现IP白名单等额外的安全措施
|
||
|
||
## 测试页面
|
||
|
||
访问 `http://localhost:8889/open-api-test.html` 可以使用测试页面进行接口测试。
|