shop-back-end/doc/微信支付集成指南.md

400 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 微信支付集成指南
## 概述
本项目集成了微信支付功能支持JSAPI支付、支付回调处理、退款等功能。支付网关采用第三方服务简化了支付对接流程。
## 功能特性
- ✅ JSAPI支付微信公众号支付
- ✅ 支付回调处理
- ✅ 订单状态更新
- ✅ 退款功能
- ✅ 支付日志记录
- ✅ 支付状态查询
## 配置步骤
### 1. 微信支付配置
#### 1.1 获取支付配置
联系支付网关服务商获取以下配置:
- **AppId**: 微信公众号AppId
- **Secret**: 微信公众号Secret
- **BizId**: 支付业务ID
- **AppKey**: 支付密钥
- **PayUrl**: 支付接口地址
- **RefundUrl**: 退款接口地址
#### 1.2 系统配置
修改 `application-dev.yml` 配置文件:
```yaml
wxshop:
appid: wx9922dfbb0d4cd7bb
secret: 7c7ef0dbb90b6be2abc8c269357f980a
pay:
biz_id: wxshop
old_biz_id: wxshop_old
appkey: wxshop202503081132
pay_url: http://222.218.10.217:7890/open/trade/wx/jsapi/precreate
refund_url: http://222.218.10.217:7890/open/trade/refund
```
### 2. 数据库准备
#### 2.1 支付日志表
```sql
CREATE TABLE `payment_operation_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`order_no` varchar(64) NOT NULL COMMENT '订单号',
`operation_type` varchar(50) NOT NULL COMMENT '操作类型',
`request_data` text COMMENT '请求数据',
`response_data` text COMMENT '响应数据',
`status` tinyint DEFAULT '1' COMMENT '状态',
`error_msg` varchar(500) DEFAULT NULL COMMENT '错误信息',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_order_no` (`order_no`)
);
```
## 核心功能
### 1. 创建支付订单
#### 1.1 支付请求
```bash
POST /payment/create
Content-Type: application/json
{
"orderId": "2025030811320001",
"amount": 99.99,
"openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o",
"payType": "JSAPI",
"subject": "商品订单",
"body": "商品描述"
}
```
#### 1.2 支付响应
```json
{
"code": 200,
"msg": "success",
"data": {
"prepayId": "wx20250308113200abc123",
"paySign": "BDF0099C9FF443C4DC5C5D68C4B97C3F",
"timestamp": "1657794651",
"nonceStr": "1657794651",
"package": "prepay_id=wx20250308113200abc123"
}
}
```
### 2. 支付回调
#### 2.1 回调接口
```
POST /payment/callback
Content-Type: application/xml
```
#### 2.2 回调数据示例
```xml
<xml>
<appid><![CDATA[wx9922dfbb0d4cd7bb]]></appid>
<bank_type><![CDATA[CFT]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[1600000000]]></mch_id>
<nonce_str><![CDATA[1657794651]]></nonce_str>
<openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
<out_trade_no><![CDATA[2025030811320001]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[BDF0099C9FF443C4DC5C5D68C4B97C3F]]></sign>
<time_end><![CDATA[20140903131540]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[JSAPI]]></trade_type>
<transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml>
```
#### 2.3 回调响应
```xml
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
```
### 3. 退款功能
#### 3.1 退款请求
```bash
POST /payment/refund
Content-Type: application/json
{
"orderNo": "2025030811320001",
"refundAmount": 99.99,
"refundReason": "用户申请退款",
"refundNo": "REF2025030811320001"
}
```
#### 3.2 退款响应
```json
{
"code": 200,
"msg": "success",
"data": {
"refundId": "REF2025030811320001",
"status": "PROCESSING",
"refundTime": "2025-03-08 11:32:00"
}
}
```
## 代码实现
### 1. 支付服务
```java
@Service
@RequiredArgsConstructor
public class PaymentService {
private final PaymentGateway paymentGateway;
private final PaymentOperationLogService logService;
public WxJsApiPreCreateResponse createPayment(WxJsApiPreCreateRequest request) {
// 记录请求日志
PaymentOperationLog log = createRequestLog(request);
try {
// 调用支付网关
WxJsApiPreCreateResponse response = paymentGateway.preCreate(request);
// 记录响应日志
updateResponseLog(log, response, true);
return response;
} catch (Exception e) {
// 记录错误日志
updateErrorLog(log, e.getMessage());
throw new ApiException("支付创建失败: " + e.getMessage());
}
}
}
```
### 2. 支付网关
```java
@Component
public class PaymentGateway {
@Value("${wxshop.pay.pay-url}")
private String payUrl;
@Value("${wxshop.pay.appkey}")
private String appKey;
public WxJsApiPreCreateResponse preCreate(WxJsApiPreCreateRequest request) {
// 构建请求参数
Map<String, String> params = buildParams(request);
// 生成签名
String sign = SignUtils.generateSign(params, appKey);
params.put("sign", sign);
// 发送请求
String response = HttpUtil.post(payUrl, params);
// 解析响应
return parseResponse(response);
}
}
```
### 3. 签名工具
```java
public class SignUtils {
public static String generateSign(Map<String, String> params, String appKey) {
// 参数排序
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
// 拼接参数
StringBuilder sb = new StringBuilder();
for (String key : keys) {
String value = params.get(key);
if (value != null && !value.isEmpty()) {
sb.append(key).append("=").append(value).append("&");
}
}
sb.append("key=").append(appKey);
// MD5加密
return DigestUtils.md5Hex(sb.toString()).toUpperCase();
}
}
```
## 支付流程
### 1. 支付创建流程
```mermaid
sequenceDiagram
participant Client
participant Backend
participant PaymentGateway
participant WeChat
Client->>Backend: 创建支付订单
Backend->>PaymentGateway: 预支付请求
PaymentGateway->>WeChat: 统一下单
WeChat-->>PaymentGateway: 返回预支付ID
PaymentGateway-->>Backend: 返回支付参数
Backend-->>Client: 返回前端支付参数
```
### 2. 支付回调流程
```mermaid
sequenceDiagram
participant WeChat
participant Backend
participant Database
WeChat->>Backend: 支付结果通知
Backend->>Backend: 验证签名
Backend->>Database: 更新订单状态
Backend-->>WeChat: 返回成功响应
```
## 错误处理
### 1. 支付错误码
| 错误码 | 说明 | 处理建议 |
|-------|------|----------|
| `SYSTEMERROR` | 系统错误 | 重试操作 |
| `ORDERPAID` | 订单已支付 | 检查订单状态 |
| `OUT_TRADE_NO_USED` | 商户订单号重复 | 使用新订单号 |
| `NOTENOUGH` | 余额不足 | 提示用户 |
| `APPID_NOT_EXIST` | AppID不存在 | 检查配置 |
### 2. 异常处理
```java
@RestControllerAdvice
public class PaymentExceptionHandler {
@ExceptionHandler(PaymentException.class)
public ResponseDTO<Void> handlePaymentException(PaymentException e) {
log.error("支付异常: {}", e.getMessage(), e);
return ResponseDTO.fail(e.getCode(), e.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseDTO<Void> handleException(Exception e) {
log.error("系统异常: {}", e.getMessage(), e);
return ResponseDTO.fail(500, "系统繁忙,请稍后重试");
}
}
```
## 安全注意事项
### 1. 数据安全
- 支付密钥妥善保管
- 敏感数据传输使用HTTPS
- 支付回调验证签名
- 防止重复支付
### 2. 业务安全
- 订单金额校验
- 用户身份验证
- 防止SQL注入
- 日志记录完整
## 监控和日志
### 1. 监控指标
- 支付成功率
- 支付平均耗时
- 退款成功率
- 回调处理成功率
### 2. 日志记录
- 支付请求/响应日志
- 回调处理日志
- 错误日志
- 性能日志
## 测试建议
### 1. 单元测试
```java
@SpringBootTest
class PaymentServiceTest {
@Autowired
private PaymentService paymentService;
@Test
void testCreatePayment() {
WxJsApiPreCreateRequest request = new WxJsApiPreCreateRequest();
request.setOrderNo("TEST001");
request.setAmount(new BigDecimal("0.01"));
request.setOpenid("test_openid");
WxJsApiPreCreateResponse response = paymentService.createPayment(request);
assertNotNull(response);
assertNotNull(response.getPrepayId());
}
}
```
### 2. 集成测试
- 支付流程完整测试
- 回调处理测试
- 退款流程测试
- 异常场景测试
## 性能优化
### 1. 缓存优化
- 支付配置缓存
- 订单状态缓存
- 用户信息缓存
### 2. 异步处理
- 支付日志异步记录
- 通知消息异步发送
- 统计报表异步生成
### 3. 数据库优化
- 支付日志表分表
- 订单表索引优化
- 定期清理历史数据