JAVA_微信小程序支付

352 阅读5分钟
  1. 准备工作
  • 已认证的微信小程序
  • 已开通微信支付商户号
  • 小程序后台已关联商户号
  • 配置支付回调域名
  1. 后端实现流程

a) 统一下单接口

@RestController
@RequestMapping("/api/pay")
public class WxPayController {
    
    @Autowired
    private WxPayService wxPayService;
    
    @PostMapping("/create")
    public Result createOrder(@RequestBody OrderDTO orderDTO) {
        try {
            // 1. 创建统一下单请求对象
            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
            request.setBody(orderDTO.getBody());
            request.setOutTradeNo(orderDTO.getOrderNo());
            request.setTotalFee(orderDTO.getTotalFee());
            request.setSpbillCreateIp(orderDTO.getClientIp());
            request.setNotifyUrl("https://你的域名/api/pay/notify");
            request.setTradeType("JSAPI");
            request.setOpenid(orderDTO.getOpenid()); // 小程序的openid

            // 2. 调用统一下单接口
            WxPayUnifiedOrderResult result = wxPayService.unifiedOrder(request);
            
            // 3. 生成支付参数
            Map<String, String> payInfo = new HashMap<>();
            payInfo.put("appId", wxPayService.getConfig().getAppId());
            payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
            payInfo.put("nonceStr", WxPayUtil.generateNonceStr());
            payInfo.put("package", "prepay_id=" + result.getPrepayId());
            payInfo.put("signType", "RSA");
            payInfo.put("paySign", WxPayUtil.generateSignature(payInfo, wxPayService.getConfig().getPrivateKey()));

            return Result.success(payInfo);
        } catch (WxPayException e) {
            return Result.error("创建支付订单失败");
        }
    }
}

b) 支付回调处理

@PostMapping("/notify")
public String payNotify(HttpServletRequest request, HttpServletResponse response) {
    try {
        // 1. 解析支付结果通知
        WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(request.getInputStream());
        
        // 2. 验证签名
        if (notifyResult.getResultCode().equals("SUCCESS")) {
            // 3. 处理业务逻辑
            String orderNo = notifyResult.getOutTradeNo();
            // 更新订单状态
            orderService.updateOrderStatus(orderNo, OrderStatus.PAID);
            
            // 4. 返回成功响应
            return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
        }
    } catch (WxPayException e) {
        return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";
    }
}

  1. 小程序端实现
// pages/pay/pay.js
Page({
  data: {
    orderNo: '',
    totalFee: 0
  },
  
  // 发起支付
  handlePay() {
    wx.request({
      url: 'https://你的域名/api/pay/create',
      method: 'POST',
      data: {
        orderNo: this.data.orderNo,
        totalFee: this.data.totalFee,
        body: '商品描述',
        clientIp: '127.0.0.1' // 实际应该是用户真实IP
      },
      success: (res) => {
        if (res.data.code === 0) {
          const payParams = res.data.data;
          // 调起支付
          wx.requestPayment({
            timeStamp: payParams.timeStamp,
            nonceStr: payParams.nonceStr,
            package: payParams.package,
            signType: payParams.signType,
            paySign: payParams.paySign,
            success: (res) => {
              // 支付成功
              wx.showToast({
                title: '支付成功',
                icon: 'success'
              });
              // 跳转到订单详情页
              wx.redirectTo({
                url: '/pages/order/detail?orderNo=' + this.data.orderNo
              });
            },
            fail: (err) => {
              // 支付失败
              wx.showToast({
                title: '支付失败',
                icon: 'none'
              });
            }
          });
        }
      }
    });
  }
});

  1. 配置文件示例
wx:
  pay:
    appId: 小程序appId
    mchId: 商户号
    mchKey: 商户密钥
    keyPath: 商户证书路径
    notifyUrl: https://你的域名/api/pay/notify

  1. 关键注意事项
  • 签名验证:必须验证签名,确保支付安全
  • 金额单位:金额单位为分,例如:1元 = 100分
  • openid获取:确保正确获取用户的openid
  • 订单号唯一性:商户订单号必须唯一
  • 超时处理:设置合理的支付超时时间
  • 异常处理:做好各种异常情况的处理
  • 并发处理:注意并发支付的情况
  1. 安全建议
  • 使用HTTPS进行通信
  • 对敏感信息进行加密
  • 实现防重放攻击机制
  • 做好日志记录
  • 定期更新证书
  1. 测试流程

  2. 使用微信支付沙箱环境进行测试

  3. 测试支付成功场景

  4. 测试支付失败场景

  5. 测试取消支付场景

  6. 测试网络异常场景

  7. 测试并发支付场景

  8. 常见问题处理

  • 签名错误:检查签名算法和密钥配置
  • 支付超时:检查网络和超时设置
  • 重复支付:做好订单状态管理
  • 回调失败:检查回调地址配置和网络
  1. 开发建议

  2. 先使用沙箱环境开发

  3. 实现完整的日志记录

  4. 做好异常处理

  5. 实现订单状态管理

  6. 添加支付结果查询接口

  7. 实现订单退款功能

  8. 添加支付统计功能

  9. 代码优化建议

  • 使用统一的异常处理
  • 实现支付结果查询接口
  • 添加支付超时处理
  • 实现订单状态管理
  • 添加支付日志记录
  • 实现防重复支付机制

我来详细介绍如何配置微信支付回调域名的详细步骤:

  1. 登录微信支付商户平台
  1. 进入配置页面
  • 点击左侧菜单 “产品中心”
  • 选择 “开发配置”
  • 找到 “支付回调配置” 部分
  1. 配置回调域名
  • 点击 “添加” 按钮

  • 填写你的服务器域名(不需要带协议头和路径)

    • 例如:api.yourdomain.com
    • 注意:不能填写 IP 地址
    • 不能带 http:// 或 https://
    • 不能带端口号
    • 不能带路径
  1. 域名要求
  • 必须是已备案的域名
  • 必须配置了 SSL 证书(必须使用 HTTPS)
  • 域名必须可以正常访问
  • 建议使用专门的支付回调域名
  1. 验证配置
  • 配置完成后,系统会自动验证域名

  • 验证内容包括:

    • 域名是否可访问
    • 是否支持 HTTPS
    • 证书是否有效
  1. 注意事项
  • 一个商户号最多可以配置 5 个回调域名
  • 域名配置后需要等待审核(一般 1-2 个工作日)
  • 建议使用专门的子域名用于支付回调
  • 确保服务器防火墙允许微信服务器的访问
  1. 测试验证
  • 配置完成后,可以使用微信支付提供的测试工具进行验证
  • 在商户平台 -> 开发配置 -> 支付回调配置
  • 点击 “测试” 按钮
  • 系统会发送测试通知到你的服务器
  1. 常见问题处理
  • 如果验证失败,检查:

    • 域名是否正确
    • SSL 证书是否有效
    • 服务器是否可访问
    • 防火墙是否允许访问
    • 域名是否已备案
  1. 配置示例
正确配置:
api.yourdomain.com

错误配置:
http://api.yourdomain.com
https://api.yourdomain.com
api.yourdomain.com:8080
api.yourdomain.com/notify

  1. 安全建议
  • 使用专门的子域名
  • 配置 SSL 证书
  • 设置访问白名单
  • 做好日志记录
  • 定期检查证书有效期
  1. 验证回调配置
@RestController
@RequestMapping("/api/pay")
public class WxPayController {
    
    @PostMapping("/notify")
    public String payNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 1. 获取通知数据
            String xmlData = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
            
            // 2. 记录日志
            log.info("收到支付回调通知:{}", xmlData);
            
            // 3. 解析通知数据
            WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
            
            // 4. 验证签名
            if (notifyResult.getResultCode().equals("SUCCESS")) {
                // 5. 处理业务逻辑
                String orderNo = notifyResult.getOutTradeNo();
                // 更新订单状态
                orderService.updateOrderStatus(orderNo, OrderStatus.PAID);
                
                // 6. 返回成功响应
                return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
            }
        } catch (Exception e) {
            log.error("处理支付回调异常", e);
            return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";
        }
    }
}

  1. 配置检查清单
  •  域名已备案
  •  配置了 SSL 证书
  •  域名可以正常访问
  •  服务器防火墙配置正确
  •  回调接口可以正常响应
  •  日志记录完善
  •  异常处理完善
  •  签名验证正确
  •  响应格式正确
  •  测试验证通过
  1. 监控建议
  • 监控回调接口的可用性
  • 监控 SSL 证书有效期
  • 监控服务器负载
  • 监控异常情况
  • 设置告警机制
  1. 维护建议
  • 定期检查证书有效期
  • 定期检查域名配置
  • 定期检查服务器状态
  • 定期检查日志记录
  • 定期更新安全配置