文章目录
Spring Boot实现微信公众号/小程序微信支付实战指南
本文将从零开始讲解如何在Spring Boot中实现微信支付功能,涵盖开通流程、版本差异、接口详解、回调处理及企业级注意事项,并附带可直接集成到项目中的代码示例。
一、微信支付开通与材料准备
1.1 开通流程
-
注册公众号/小程序
- 完成企业认证(需营业执照)
- 开通微信支付功能
-
申请微信支付商户号
- 登录微信支付商户平台
- 提交企业资质文件:
- 营业执照扫描件
- 法人身份证正反面
- 银行账户信息
-
获取关键凭证
- 商户号(mch_id)
- API密钥(登录商户平台设置)
- AppID(公众号/小程序后台获取)
-
下载证书文件
- V2版本:
apiclient_cert.p12
- V3版本:
apiclient_cert.pem
和apiclient_key.pem
- V2版本:
二、微信支付版本对比
2.1 V2与V3核心差异
对比项 | V2版本 | V3版本 |
---|---|---|
通信协议 | XML | JSON |
签名算法 | MD5/HMAC-SHA256 | SHA256-RSA |
证书类型 | PKCS#12格式 | PEM格式 |
回调验证 | 本地签名验证 | 需下载平台证书验证 |
接口风格 | URL参数拼接 | RESTful风格 |
安全性 | 较低 | 支持报文加密 |
2.2 交互流程对比
V2流程:
生成预支付订单 → 客户端调起支付 → 同步返回 → 异步回调
V3流程:
生成支付凭证 → 客户端支付 → 异步通知 → 订单状态查询
三、微信支付核心接口
3.1 基础接口
-
统一下单接口
- V2:
/pay/unifiedorder
- V3:
/v3/pay/transactions/jsapi
- 作用:生成预支付交易单
- V2:
-
支付结果通知
- 路径:由开发者自定义(需公网可访问)
- 作用:接收微信支付结果推送
-
订单查询接口
- V2:
/pay/orderquery
- V3:
/v3/pay/transactions/out-trade-no/{out_trade_no}
- 作用:主动查询订单状态
- V2:
-
申请退款接口
- V2:
/secapi/pay/refund
- V3:
/v3/refund/domestic/refunds
- 作用:发起退款请求
- V2:
四、支付回调处理实战
4.1 V2回调处理
@PostMapping("/v2/notify")
public String handleV2Notify(HttpServletRequest request) {
try {
String xmlData = IOUtils.toString(request.getInputStream(), "UTF-8");
Map<String, String> params = XmlUtils.xmlToMap(xmlData);
// 验证签名
if (WxPayV2Util.isValidSign(params, apiKey)) {
String outTradeNo = params.get("out_trade_no");
String transactionId = params.get("transaction_id");
// 更新订单状态逻辑
return "<xml><return_code>SUCCESS</return_code></xml>";
}
return "<xml><return_code>FAIL</return_code></xml>";
} catch (Exception e) {
logger.error("V2回调处理异常", e);
return "<xml><return_code>FAIL</return_code></xml>";
}
}
4.2 V3回调处理
@PostMapping("/v3/notify")
public ResponseEntity<String> handleV3Notify(
@RequestBody String body,
@RequestHeader("Wechatpay-Signature") String signature,
@RequestHeader("Wechatpay-Nonce") String nonce,
@RequestHeader("Wechatpay-Timestamp") String timestamp,
@RequestHeader("Wechatpay-Serial") String serial) {
try {
// 1. 获取平台证书
X509Certificate certificate = certManager.getCertificate(serial);
// 2. 构造验签串
String verifyStr = timestamp + "\n" + nonce + "\n" + body + "\n";
// 3. 验证签名
boolean valid = RsaUtil.verify(
verifyStr.getBytes(StandardCharsets.UTF_8),
signature,
certificate.getPublicKey()
);
if (valid) {
// 4. 解密资源数据
JsonNode rootNode = JsonUtils.parse(body);
JsonNode resource = rootNode.get("resource");
String cipherText = resource.get("ciphertext").asText();
String associatedData = resource.get("associated_data").asText();
String nonceValue = resource.get("nonce").asText();
String plainText = AesUtil.decryptGcm(
cipherText,
apiV3Key.getBytes(),
associatedData.getBytes(),
nonceValue.getBytes()
);
// 5. 处理业务逻辑
processPaymentResult(plainText);
return ResponseEntity.ok("{\"code\":\"SUCCESS\"}");
}
return ResponseEntity.status(400).body("{\"code\":\"FAIL\"}");
} catch (Exception e) {
logger.error("V3回调处理异常", e);
return ResponseEntity