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

9.0 KiB
Raw Permalink Blame History

微信支付集成指南

概述

本项目集成了微信支付功能支持JSAPI支付、支付回调处理、退款等功能。支付网关采用第三方服务简化了支付对接流程。

功能特性

  • JSAPI支付微信公众号支付
  • 支付回调处理
  • 订单状态更新
  • 退款功能
  • 支付日志记录
  • 支付状态查询

配置步骤

1. 微信支付配置

1.1 获取支付配置

联系支付网关服务商获取以下配置:

  • AppId: 微信公众号AppId
  • Secret: 微信公众号Secret
  • BizId: 支付业务ID
  • AppKey: 支付密钥
  • PayUrl: 支付接口地址
  • RefundUrl: 退款接口地址

1.2 系统配置

修改 application-dev.yml 配置文件:

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 支付日志表

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 支付请求

POST /payment/create
Content-Type: application/json

{
  "orderId": "2025030811320001",
  "amount": 99.99,
  "openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o",
  "payType": "JSAPI",
  "subject": "商品订单",
  "body": "商品描述"
}

1.2 支付响应

{
  "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>
  <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>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <return_msg><![CDATA[OK]]></return_msg>
</xml>

3. 退款功能

3.1 退款请求

POST /payment/refund
Content-Type: application/json

{
  "orderNo": "2025030811320001",
  "refundAmount": 99.99,
  "refundReason": "用户申请退款",
  "refundNo": "REF2025030811320001"
}

3.2 退款响应

{
  "code": 200,
  "msg": "success",
  "data": {
    "refundId": "REF2025030811320001",
    "status": "PROCESSING",
    "refundTime": "2025-03-08 11:32:00"
  }
}

代码实现

1. 支付服务

@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. 支付网关

@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. 签名工具

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. 支付创建流程

sequenceDiagram
    participant Client
    participant Backend
    participant PaymentGateway
    participant WeChat

    Client->>Backend: 创建支付订单
    Backend->>PaymentGateway: 预支付请求
    PaymentGateway->>WeChat: 统一下单
    WeChat-->>PaymentGateway: 返回预支付ID
    PaymentGateway-->>Backend: 返回支付参数
    Backend-->>Client: 返回前端支付参数

2. 支付回调流程

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. 异常处理

@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. 单元测试

@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. 数据库优化

  • 支付日志表分表
  • 订单表索引优化
  • 定期清理历史数据