微信支付V3版本相比V2在安全模型上做了大幅重构,全面采用HTTPS、RSA非对称签名和AES-GCM加密,官方也推出了配套的Java SDK——wechatpay-java。本文将演示如何在Spring Boot项目中整合该SDK,完成从参数配置、统一下单到支付结果回调的完整链路,并重点说明回调验签与解密这两个最容易出错的环节。

一、准备工作与依赖引入
在动手写代码之前,需要先在微信商户平台完成几项准备:申请商户号mchid、在API安全中心申请API v3密钥(32位字符串,用于回调解密)、下载商户API证书并记录证书序列号,同时准备一个HTTPS的回调通知地址用于接收支付结果。这些参数缺一不可,尤其是API v3密钥,一旦设置后SDK会用它来解密回调报文中的敏感字段。
接着在pom.xml中引入官方SDK。推荐使用wechatpay-java而不是已经停止维护的wechatpay-apache-httpclient:
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.14</version>
</dependency>然后在application.yml中集中管理商户配置,敏感信息建议通过环境变量注入而非硬编码进代码仓库:
wxpay: merchant-id: "1900000000" merchant-serial-number: "证书序列号" private-key-path: /data/cert/apiclient_key.pem api-v3-key: "32位APIv3密钥" appid: "wxxxxxxxxxxx" notify-url: https://你的域名/api/pay/notify
二、构建支付配置类与下单接口
SDK提供了RSAAutoCertificateConfig,它会在初始化时自动下载微信支付的平台证书,并在证书临近过期时自动轮换,开发者无需手动管理平台证书的更新,这是V3 SDK最实用的改进之一。我们先编写一个配置类将其注册为Bean:
@Configuration
public class WxPayConfig {
@Value("${wxpay.merchant-id}")
private String merchantId;
@Value("${wxpay.private-key-path}")
private String privateKeyPath;
@Value("${wxpay.merchant-serial-number}")
private String serialNumber;
@Value("${wxpay.api-v3-key}")
private String apiV3Key;
@Bean
public RSAAutoCertificateConfig rsaConfig() {
return new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(serialNumber)
.apiV3Key(apiV3Key)
.build();
}
@Bean
public JsapiServiceExtension jsapiService(RSAAutoCertificateConfig config) {
return new JsapiServiceExtension.Builder().config(config).build();
}
}下单时以JSAPI为例,组装PrepayRequest对象调用prepayWithRequestPayment,该方法会直接返回前端唤起支付所需的appId、timeStamp、nonceStr、package、signType、paySign六要素,省去了自己签名的步骤:
@Service
public class OrderService {
@Autowired
private JsapiServiceExtension jsapiService;
public PrepayWithRequestPaymentResponse createJsapiOrder(OrderInfo order, String openId) {
PrepayRequest request = new PrepayRequest();
request.setAppid("wxxxxxxxxxxx");
request.setMchid("1900000000");
request.setDescription("商品名称");
request.setOutTradeNo(order.getOrderNo());
request.setNotifyUrl("https://你的域名/api/pay/notify");
Amount amount = new Amount();
amount.setTotal(order.getTotalFee()); // 单位为分
request.setAmount(amount);
Payer payer = new Payer();
payer.setOpenid(openId);
request.setPayer(payer);
return jsapiService.prepayWithRequestPayment(request);
}
}Native扫码支付则使用NativePayService的prepay方法,返回code_url后由后端生成二维码展示给用户。无论哪种方式,都要注意金额单位是分而不是元,这是新人最常犯的低级错误之一。下单接口返回的prepay_id有效期约两小时,前端应在用户发起支付时实时请求,而不是提前缓存。
三、支付回调的验签、解密与幂等处理
回调是整个支付链路中最关键也最容易出错的部分。微信服务器会向notify-url以POST方式推送JSON报文,报文中的resource字段是加密的,需要先用请求头中的Wechatpay-Signature完成验签,再用API v3密钥通过AES-256-GCM解密出真正的订单结果。SDK的NotificationParser已经封装了这两步:
@RestController
@RequestMapping("/api/pay")
public class PayNotifyController {
@Autowired
private NotificationParser notificationParser;
@Autowired
private OrderService orderService;
@PostMapping("/notify")
public ResponseEntity<Map<String, String>> notify(HttpServletRequest request) throws IOException {
String serial = request.getHeader("Wechatpay-Serial");
String signature = request.getHeader("Wechatpay-Signature");
String nonce = request.getHeader("Wechatpay-Nonce");
String timestamp = request.getHeader("Wechatpay-Timestamp");
String body = request.getReader().lines().collect(Collectors.joining());
RequestParam params = new RequestParam.Builder()
.serialNumber(serial)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.body(body)
.build();
// 验签并解密,失败会抛出ValidationException
Transaction transaction = notificationParser.parse(params, Transaction.class);
if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
orderService.handlePaySuccess(transaction);
}
// 必须返回200和指定JSON,否则微信会持续重试推送
return ResponseEntity.ok(Map.of("code", "SUCCESS", "message", "成功"));
}
}有两个细节务必注意。第一,读取请求体时必须拿原始字符串,如果先经过Jackson反序列化再转回字符串,可能导致验签时拼出的摘要不一致而失败,因此建议直接用字符流读取。第二,业务处理必须保证幂等:微信在网络抖动时会重复推送同一笔订单的通知,处理前应先根据outTradeNo查询订单状态,若已是已支付则直接返回SUCCESS,避免重复发货。
四、生产环境的补充建议
除了主干流程,还有几点实践经验值得参考。订单状态变更建议放在数据库事务中,同时保存微信支付单号transaction_id方便后续对账与退款;主动查询接口QueryOrderByOutTradeNoRequest可以作为补偿手段,用定时任务扫描长时间未收到回调的订单,主动向微信侧核实支付状态。
金额校验也不要忽略:解密后应比对报文中的amount.total与本地订单金额是否一致,防止极端情况下的数据异常。notify-url必须是HTTPS且外网可访问,本地调试时可以借助内网穿透工具,但要记得上线前切换为正式域名。整个模块完成后,配合完整的对账机制,就能支撑起稳定可靠的线上支付业务了。
Spring Boot微信支付V3支付回调修改时间:2026-08-31 20:30:53