Spring Boot项目实战:用wechatpay-java 0.2.12搞定小程序支付与退款(附完整回调处理)
·
Spring Boot实战:基于wechatpay-java 0.2.12构建企业级支付中台
在移动支付成为标配的今天,小程序支付接入的质量直接影响用户体验和资金安全。作为Java开发者,我们既需要快速实现功能闭环,又要确保生产环境下的稳定性和安全性。wechatpay-java SDK 0.2.12版本通过自动证书管理、链式API设计等特性,为Spring Boot项目提供了开箱即用的支付解决方案。
1. 环境配置与SDK初始化
支付模块作为系统核心组件,其配置管理需要兼顾安全性和可维护性。建议采用环境变量+配置文件的双重加密方案:
# application-prod.yml
wechat:
pay:
app-id: ${WX_APP_ID}
mch-id: ${WX_MCH_ID}
api-v3-key: ${WX_API_V3_KEY}
cert-serial: ${WX_CERT_SERIAL}
private-key: |
-----BEGIN PRIVATE KEY-----
${WX_PRIVATE_KEY}
-----END PRIVATE KEY-----
notify-host: https://yourdomain.com
对应的配置类需要处理密钥的多种加载方式,并初始化核心服务:
@Getter @Setter
@ConfigurationProperties(prefix = "wechat.pay")
public class WechatPayConfig {
private String appId;
private String mchId;
private String apiV3Key;
private String certSerial;
private String privateKey;
private String notifyHost;
@Bean
public RSAAutoCertificateConfig certificateConfig() {
return new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.merchantSerialNumber(certSerial)
.apiV3Key(apiV3Key)
.privateKey(privateKey) // 支持直接传入密钥内容
.build();
}
@Bean
public JsapiService jsapiService(RSAAutoCertificateConfig config) {
return new JsapiService.Builder()
.config(config)
.signType("RSA") // 明确指定签名算法
.build();
}
}
关键安全实践:
- 私钥内容通过Vault或KMS系统注入
- 不同环境使用独立商户号
- APIv3密钥定期轮换策略
2. 支付全流程实现
2.1 预支付订单创建
支付流程始于服务端创建预支付订单,需要注意金额单位的精确处理:
public PrepayResponse createPrepay(String orderId, int amount, String openId) {
Amount orderAmount = new Amount()
.setTotal(amount)
.setCurrency("CNY");
Payer payer = new Payer().setOpenid(openId);
PrepayRequest request = new PrepayRequest()
.setAppid(config.getAppId())
.setMchid(config.getMchId())
.setDescription("订单支付")
.setOutTradeNo(orderId)
.setNotifyUrl(config.getNotifyHost() + "/api/pay/notify")
.setAmount(orderAmount)
.setPayer(payer);
return jsapiService.prepay(request);
}
金额处理要点:
- 金额单位始终为分(最小货币单位)
- 前端传参需做二次校验
- 货币类型硬编码为CNY
2.2 支付结果通知处理
支付回调是支付系统最关键的环节,需要实现:
- 请求体验签
- 数据解密
- 业务处理
- 幂等控制
@PostMapping("/api/pay/notify")
public String handlePayNotify(@RequestBody String jsonBody,
@RequestHeader("Wechatpay-Signature") String signature,
@RequestHeader("Wechatpay-Serial") String serial,
@RequestHeader("Wechatpay-Nonce") String nonce,
@RequestHeader("Wechatpay-Timestamp") String timestamp) {
// 1. 验证签名
if (!signatureVerifier.verify(serial, jsonBody, signature, nonce, timestamp)) {
throw new SecurityException("签名验证失败");
}
// 2. 解析并解密数据
Notification notification = JSON.parseObject(jsonBody, Notification.class);
String plainText = decryptor.decrypt(notification.getResource());
// 3. 处理业务逻辑
PaymentResult result = processPayment(plainText);
// 4. 返回成功响应
return "{\"code\": \"SUCCESS\", \"message\": \"成功\"}";
}
安全增强措施:
- 使用Redis实现分布式锁防重
- 失败场景重试机制
- 通知日志全量审计
3. 退款与订单管理
3.1 全额/部分退款实现
退款流程需要特别注意金额的逆向校验:
public Refund applyRefund(String orderId, String refundId, int refundAmount, String reason) {
// 查询原订单金额
Order order = orderService.get(orderId);
if (order == null) {
throw new BusinessException("订单不存在");
}
// 金额校验
if (refundAmount > order.getAmount()) {
throw new BusinessException("退款金额超过订单总额");
}
CreateRequest request = new CreateRequest()
.setOutTradeNo(orderId)
.setOutRefundNo(refundId)
.setReason(reason)
.setNotifyUrl(config.getNotifyHost() + "/api/refund/notify");
AmountReq amount = new AmountReq()
.setRefund(refundAmount)
.setTotal(order.getAmount())
.setCurrency("CNY");
request.setAmount(amount);
return refundService.create(request);
}
3.2 订单状态同步策略
支付系统需要实现主动查询和被动通知的双重保障:
| 场景 | 策略 | 频率 | 备注 |
|---|---|---|---|
| 支付超时 | 主动查询 | 每5分钟 | 最多重试3次 |
| 退款处理 | 被动等待 | - | 超时2小时转主动查询 |
| 对账差异 | 批量查询 | 每日凌晨 | 补单处理 |
@Scheduled(fixedRate = 5 * 60 * 1000)
public void checkPendingOrders() {
List<Order> pendingOrders = orderRepository.findByStatus(OrderStatus.PENDING);
pendingOrders.forEach(order -> {
try {
Transaction transaction = jsapiService.queryOrderByOutTradeNo(
new QueryOrderByOutTradeNoRequest()
.setMchid(config.getMchId())
.setOutTradeNo(order.getId())
);
updateOrderStatus(order, transaction);
} catch (Exception e) {
log.error("订单查询失败: {}", order.getId(), e);
}
});
}
4. 生产环境最佳实践
4.1 证书自动化管理
wechatpay-java的自动证书管理需要关注:
@Bean
public RSAAutoCertificateConfig certificateConfig() {
return new RSAAutoCertificateConfig.Builder()
.merchantId(config.getMchId())
.merchantSerialNumber(config.getCertSerial())
.apiV3Key(config.getApiV3Key())
.privateKey(config.getPrivateKey())
.certificateDownloader(new CustomCertificateDownloader()) // 自定义下载器
.certificateVerifier(new CustomCertificateVerifier()) // 自定义验证器
.build();
}
关键配置项:
- 证书更新阈值(默认提前1小时)
- 下载失败重试策略
- 证书验证白名单
4.2 监控与告警体系
支付系统需要建立完善的监控指标:
-
基础指标
- API成功率 ≥ 99.9%
- 平均响应时间 < 500ms
- 证书有效期 > 24h
-
业务指标
- 支付成功率 ≥ 98%
- 退款处理时效 < 30分钟
- 对账差异率 < 0.1%
# Prometheus监控示例
wx_pay_api_duration_seconds{api="jsapi/prepay"} 0.45
wx_pay_cert_expire_hours 36
wx_pay_notify_failure_total 2
4.3 压力测试方案
支付系统上线前需要验证:
-
基准测试
wrk -t4 -c100 -d60s --latency \ -s post.lua \ https://api.example.com/pay/prepay -
峰值测试
- 模拟秒杀场景突发流量
- 验证分布式锁有效性
-
故障注入
- 证书突然失效
- 通知接口超时
- 数据库连接中断
支付模块作为电商系统的核心组件,其稳定性和安全性需要持续优化。在实际项目中,我们通过引入熔断机制和分级降级策略,将支付失败率从最初的2%降低到0.3%以下。特别是在大促期间,动态证书加载机制成功避免了因证书过期导致的支付中断。
更多推荐


所有评论(0)