400 lines
9.0 KiB
Markdown
400 lines
9.0 KiB
Markdown
|
|
# 微信支付集成指南
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本项目集成了微信支付功能,支持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. 数据库优化
|
|||
|
|
- 支付日志表分表
|
|||
|
|
- 订单表索引优化
|
|||
|
|
- 定期清理历史数据
|