微信支付V3版本集成详解【避坑指南】
创始人
2025-07-06 19:51:04
0

最近对项目中的微信支付功能做了升级,之前使用的是V2版本。V2版本目前还可以使用,但已暂停更新。V3版本的集成,官方文档还是比较清晰的,但各类的配置,一个不小心就掉坑里半天爬不出来。趁着思路清晰,特此记录一下。

V2版本参数格式是xml格式,不太好维护,V3版本已改成json格式。

V2版本的签名是拼在参数里面的,V3版本校验都放在配置类里面了,更加方便灵活。

前置条件

官方文档:
https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml

1、微信开放平台 – APP支付

  • 注册APP,获取appId appSecret等信息

2、微信公众平台 – (微信公众号 小程序) 微信内支付

  • 开通账号,申请支付功能,绑定商户平台
  • 配置域名等

3、浏览器H5支付

  • 申请权限:微信支付商户平台—>产品中心—>H5支付—>申请开通
  • 配置:产品中心—>开发配置—>H5支付

4、微信商户平台

  •  商户号
  • API证书密钥及证书序列号

  • API v3密钥

代码集成

微信提供两种集成方式:wechatpay-java(推荐);wechatpay-apache-httpclient,以推荐的方式为例:


  com.github.wechatpay-apiv3
  wechatpay-java
  0.2.5

配置初始化 – 加载微信支付平台证书

使用自动更新平台证书的配置类 RSAAutoCertificateConfig。注:每个商户号只能创建一个 RSAAutoCertificateConfig。

代码实现,将配置交由Spring统一管理,单例模式保证初始化一次。

@Configuration
public class WXPayConfig {

    private Config config;

    @PostConstruct
    public void init(){
        config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(WXPayConstants.MCHID)
                        .privateKey(WXPayConstants.PRIVATE_KEY)
                        .merchantSerialNumber(WXPayConstants.MERCHANT_SERIAL_NUMBER)
                        .apiV3Key(WXPayConstants.API_V3_KEY)
                        .build();
    }

    @Bean("h5Service")
    public H5Service getH5Service(){
        // H5支付
        return new H5Service.Builder().config(config).build();
    }

    @Bean("jsService")
    public JsapiServiceExtension getJsService(){

        // 微信js支付
        return new JsapiServiceExtension.Builder()
                        .config(config)
                        .signType("RSA") // 不填则默认为RSA
                        .build();
    }

    @Bean("appService")
    public AppServiceExtension getAppService() {
        // App支付
        return new AppServiceExtension.Builder().config(config).build();
    }

    @Bean("NotificationParser")
    public NotificationParser getNotificationParser(){
        // 支付回调的解析器
        return new NotificationParser((NotificationConfig)config);
    }
}

获取支付请求信息

APP下单

/**
 * 获取微信支付参数(APP)
 */
public WechatPayDTO getWechatAppPayParam(BigDecimal money, String orderNumber, String notifyUrl) throws Exception {
    // 下单
    com.wechat.pay.java.service.payments.app.model.PrepayRequest request = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
    com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
    amount.setTotal(Integer.parseInt(totalFee(money)));
    amount.setCurrency("CNY");
    request.setAmount(amount);
    request.setAppid(WXPayConstants.APPID);
    request.setMchid(WXPayConstants.MCHID);
    request.setDescription("");
    request.setNotifyUrl(notifyUrl);
    request.setOutTradeNo(orderNumber);
    com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse response = appService.prepayWithRequestPayment(request);
    return WechatPayDTO.builder()
            .appid(response.getAppid())
            .partnerid(response.getPartnerId())
            .prepayid(response.getPrepayId())
            .packageVal(response.getPackageVal())
            .timestamp(response.getTimestamp())
            .noncestr(response.getNonceStr())
            .sign(response.getSign())
            .build();
}

公众号 小程序下单

/**
 * 获取微信支付参数(公众号 小程序)
 */
public WechatPayDTO getWechatJSAPIPayParam(String openid, BigDecimal money, String orderNumber, String notifyUrl) throws Exception {
    // 下单
    com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest request = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
    com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
    amount.setTotal(Integer.parseInt(totalFee(money)));
    amount.setCurrency("CNY");
    request.setAmount(amount);
    request.setAppid(WXPayConstants.PUBLIC_APPID);
    request.setMchid(WXPayConstants.MCHID);
    request.setDescription("");
    request.setNotifyUrl(notifyUrl);
    request.setOutTradeNo(orderNumber);
    Payer payer = new Payer();
    payer.setOpenid(openid);
    request.setPayer(payer);
    PrepayWithRequestPaymentResponse response = jsService.prepayWithRequestPayment(request);
    logger.info("JS支付参数:{}", response.toString());
    return WechatPayDTO.builder()
            .appid(response.getAppId())
            .packageVal(response.getPackageVal())
            .timestamp(response.getTimeStamp())
            .noncestr(response.getNonceStr())
            .signType(response.getSignType())
            .paySign(response.getPaySign())
            .build();
}

H5下单

/**
  * 获取微信H5支付连接
	*/
public String getWechatH5PayUrl(BigDecimal money, String orderNumber, String notifyUrl) {
      // 下单
      PrepayRequest request = new PrepayRequest();
      Amount amount = new Amount();
      amount.setTotal(Integer.parseInt(totalFee(money)));
      amount.setCurrency("CNY");
      request.setAmount(amount);
      SceneInfo sceneInfo = new SceneInfo();
      sceneInfo.setPayerClientIp("");
      request.setSceneInfo(sceneInfo);
      request.setAppid(WXPayConstants.PUBLIC_APPID);
      request.setMchid(WXPayConstants.MCHID);
      request.setDescription("");
      request.setNotifyUrl(notifyUrl);
      request.setOutTradeNo(orderNumber);
      // 调用接口
      PrepayResponse response = h5Service.prepay(request);
      return response.getH5Url();
}

支付回调

获取 HTTP 请求头中的以下值,构建 RequestParam 。

  • Wechatpay-Signature
  • Wechatpay-Nonce
  • Wechatpay-Timestamp
  • Wechatpay-Serial
  • Wechatpay-Signature-Type

获取 HTTP 请求体 body。切记不要用 JSON 对象序列化后的字符串,避免验签的 body 和原文不一致。

根据解密后的通知数据数据结构,构造解密对象类 DecryptObject 。支付结果通知解密对象类为 Transaction,退款结果通知解密对象类为 RefundNotification。

初始化 RSAAutoCertificateConfig(已在前文统一初始化)。

初始化 NotificationParser(已在前文统一初始化)。

使用请求参数 requestParam 和 DecryptObject.class ,调用 parser.parse 验签并解密报文。

RequestParam requestParam = new RequestParam.Builder()
      .serialNumber(request.getHeader("Wechatpay-Serial"))
      .nonce(request.getHeader("Wechatpay-Nonce"))
      .signature(request.getHeader("Wechatpay-Signature"))
      .timestamp(request.getHeader("Wechatpay-Timestamp"))
      .signType(request.getHeader("Wechatpay-Signature-Type"))
      .body(body)
      .build();

Transaction transaction = notificationParser.parse(requestParam, Transaction.class);
if (Objects.equals(transaction.getTradeState(), Transaction.TradeStateEnum.SUCCESS)){
  //处理业务逻辑
  //通知微信支付成功
  wechatPayUtil.paySuccessful(response);
}

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...
《非诚勿扰》红人闫凤娇被曝厕所... 【51CTO.com 综合消息360安全专家提醒说,“闫凤娇”、“非诚勿扰”已经被黑客盯上成为了“木...
2012年第四季度互联网状况报... [[71653]]  北京时间4月25日消息,据国外媒体报道,全球知名的云平台公司Akamai Te...