评论

微信支付v3接口 回调+回调验签 java示例代码

微信支付v3接口回调接口java示例代码

这里使用的是springboot框架进行测试,比较方便简单,代码仅供参考

原理:java 的前后端通信

注意:1、域名一般需要备案:如果没有备案可能会被拦截,无法正常应答验签(申请域名的平台都会提供备案的攻略,按照攻略正常备案即可)

2、一般无法本地测试(也是就是个人pc电脑),因为没有固定的ip地址,需要申请购买一个服务器后,有固定的对外ip(记得把对应默认端口后80打开,不然会被拦截),把对应的ip映射到对应的域名后(映射其实就是绑定的意思,在申请到域名后设置一下,比较简单),看看外网能不能ping通,如果无法ping通就说明没有映射成功,或者映射的ip不是外网可以访问

pox文件:

4.0.0org.springframework.bootspring-boot-starter-parent2.4.3 cn.hzldemo0.0.1-SNAPSHOTjardemoDemo project for Spring Boot1.82.9.2org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-starter-testtest

        org.springframework.bootspring-boot-starter-web

        com.github.wechatpay-apiv3wechatpay-apache-httpclient0.2.3org.projectlomboklombokcom.squareup.okhttp3okhttp3.14.9org.springframeworkspring-tx5.3.1io.springfoxspringfox-swagger2${swagger2.version}io.springfoxspringfox-swagger-ui${swagger2.version}com.alibabafastjson1.2.60

        com.github.wechatpay-apiv3wechatpay-apache-httpclient0.2.3

    org.springframework.bootspring-boot-maven-plugin

启动类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}


配置文件:(application.yml)只配置了默认的端口号

server:
  port: 80



回调主要代码:

import com.alibaba.fastjson.JSON;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.cert.X509Certificate;
import java.security.*;
import java.util.*;


@RestController
@RequestMapping("/weixin/pay")
public class WeiXinPayController {
     
      //商户api证书私钥文件的私钥串,apiclient_key.pem文本形式打开即可获取,不需要前后缀
      static String s ="商户api证书私钥文件的私钥串";

      //验签方法,非微信官方的,微信支付官方的基本也查不到,封装的方式不一样,下面也有
      public boolean verify(String srcData, X509Certificate certificate, String sign, String Serial) throws Exception {
          //先验证证书序列号是否正确
          if (certificate.getSerialNumber().toString(16).toUpperCase().equals(Serial)){
              Signature sha256withRSA = Signature.getInstance("SHA256withRSA");
              sha256withRSA.initVerify(certificate.getPublicKey());
              sha256withRSA.update(srcData.getBytes());
              return  sha256withRSA.verify(Base64Utils.decodeFromString(sign));
          }else {
              return false;
          }
      }
  
  
      /**
       * 微信支付回调地址
       */
      @ApiOperation(value = "微信支付回调地址")
      @PostMapping(value = "/callback/test")//域名后面的路径,可以根据自己的喜好或者业务需求设置,前面是域名,不能携带端口号,比如https://www.baidu.com/weixin/pay/callback/test,  https://www.baidu.com找到你的服务器,/weixin/pay/callback/test服务器的哪个方法进行处理
      /**
       * request 请求体
       * response 响应体
       */
      public String   callBack(HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException {
          String characterEncoding = request.getCharacterEncoding();
          System.out.println("characterEncoding=" + characterEncoding);
          //从请求头获取验签字段
          String Timestamp = request.getHeader("Wechatpay-Timestamp");
          String Nonce = request.getHeader("Wechatpay-Nonce");
          String Signature = request.getHeader("Wechatpay-Signature");
          String Serial = request.getHeader("Wechatpay-Serial");
  
  
          System.out.println("开始读取请求头的信息");
          //请求头
          System.out.println("Wechatpay-Timestamp=" + Timestamp);
          System.out.println("Wechatpay-Nonce=" + Nonce);
          System.out.println("Wechatpay-Signature=" + Signature);
          System.out.println("Wechatpay-Serial=" + Serial);
  
          System.out.println("=================");
  
          //加载平台证书,官方的sdk,s为商户api证书私钥
          PrivateKey merchantPrivateKey = PemUtil
                  .loadPrivateKey(new ByteArrayInputStream(s.getBytes("utf-8")));
  
          //加载官方自动更新证书
          AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                                                                            //商户平台查看                            //不是API密钥                                                  
                  new WechatPay2Credentials("商户号", new PrivateKeySigner("商户api证书序列号", merchantPrivateKey)), "APIv3密钥".getBytes("utf-8"));
  
  
          //读取请求体的信息
          System.out.println("开始读取请求体的信息");
          ServletInputStream inputStream = request.getInputStream();
          StringBuffer stringBuffer = new StringBuffer();
          BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
          String s;
          //读取回调请求体
          while ((s = bufferedReader.readLine()) != null) {
              stringBuffer.append(s);
          }
  
  
          String s1 = stringBuffer.toString();
          System.out.println("请求体" + s1);
          Map requestMap = (Map) JSON.parse(s1);
  
  
          //开始按照验签进行拼接
          String id = requestMap.get("id");
          System.out.println("id=" + id);
  
  
          String resource = String.valueOf(requestMap.get("resource"));
          System.out.println("resource=" + resource);
          Map requestMap2 = (Map) JSON.parse(resource);
  
          String associated_data = requestMap2.get("associated_data");
          String nonce = requestMap2.get("nonce");
          String ciphertext = requestMap2.get("ciphertext");
  
  
          //按照文档要求拼接验签串
          String VerifySignature = Timestamp + "\n" + Nonce + "\n" + s1 + "\n";
          System.out.println("拼接后的验签串=" + VerifySignature);
  
          //使用官方验签工具进行验签
          boolean verify1 = verifier.verify(Serial, VerifySignature.getBytes(), Signature);
          System.out.println("官方工具验签=" + verify1);


        //使用自己写的验签方法进行验签
        //赋一个默认值
        boolean verify = false;
        try {
            verify = verify(VerifySignature, verifier.getValidCertificate(), Signature, Serial);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("验签方法进行验签=" + verify);

        //判断验签的结果
        System.out.println("=======判断验签结果=======");
        if (verify == false) {
            System.out.println("验签失败,应答接口");
            Map map = new HashMap<>();
            //响应接口

            //设置状态码
            response.setStatus(500);
            return "{"+
                    '"'+ "code"+'"'+":"+'"'+"FAIL"+'"'+" "+
                    '"'+ "message"+'"'+":"+'"'+"失败"+'"'+
                    "}";
        }
        System.out.println("验签成功后,开始进行解密");
        //解密,如果这里报错,就一定是APIv3密钥错误
        AesUtil aesUtil = new AesUtil("APIv3密钥".getBytes());
        String aes = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);

        System.out.println("解密后=" + aes);

        Map map = new HashMap<>();
        //响应接口
        map.put("code", "SUCCESS");
        map.put("message", "成功");

        //设置状态码
        response.setStatus(200);
            return "{"+
                   '"'+ "code"+'"'+":"+'"'+"SUCCESS"+'"'+" "+
                   '"'+ "message"+'"'+":"+'"'+"成功"+'"'+
                   "}";
    }
}


最后一次编辑于  2022-05-27  
点赞 1
收藏
评论

15 个评论

  • Starkwang
    Starkwang
    置顶评论2023-08-29

    可以试试云开发工作流来接收微信支付回调:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/wechatpay/workflow.html

    2023-08-29
    赞同
    回复
  • momo
    momo
    2023-06-25

    微信支付的文档跟屎一样 通知也没找到示例代码 蚂蚁支付都有响应的示例

    2023-06-25
    赞同 16
    回复 2
    • 名字是乱打的
      名字是乱打的
      2023-10-16
      微信的文档真的像上古世纪的开发者写的
      2023-10-16
      1
      回复
    • Qiang
      Qiang
      2023-11-18
      同意,微信文档写的是什么玩意
      2023-11-18
      1
      回复
  • Jorey
    Jorey
    2023-09-28
    PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(properties.getApiv3Key().getBytes("utf-8")));
    //加载官方自动更新证书
    AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
            //商户平台查看                            //不是API密钥
            new WechatPay2Credentials("商户号", new PrivateKeySigner("商户api证书序列号", merchantPrivateKey)), "APIv3密钥".getBytes("utf-8"));
    
    这些是哪个包中的,PemUtil  AutoUpdateCertificatesVerifier  无法引入
    
    2023-09-28
    赞同 4
    回复
  • 万游
    万游
    04-25

    微信文档太垃圾了,没有一个是全面的

    04-25
    赞同 1
    回复
  • 。
    01-30

    本地调试的兄弟,你们参数是咋整的啊

    01-30
    赞同
    回复
  • lv
    lv
    2023-12-23

    哥们 用的就是你的实例代码 支付回调验签过了 但是退款的回调为啥一直失败啊,同样的验签代码

    还有这个问题 "java.net.SocketException: Socket Closed"

    2023-12-23
    赞同
    回复
  • 1
    1
    2023-08-31

    哥么 问一下 微信支付的native下单那里 需要给请求头生成auth签名的东西吗 他那个文档上没写要 但我看了很多demo都要那个签名 我们现在在替换公司名所以appid掉了没法用 我自己测不了....

    2023-08-31
    赞同
    回复
  • 沐千熏
    沐千熏
    2023-08-10

    我用微信maven包中的wxPayService.parseOrderNotifyV3Result方法来进行解析传来的参数,一直在包null但是看源码看他报错的地方是360 但是360又是一个方法结束“}”号

    2023-08-10
    赞同
    回复
  • 星辰
    星辰
    2023-06-19

    javax.crypto.AEADBadTagException: Tag mismatch!

    这个什么原因呢

    2023-06-19
    赞同
    回复 1
    • 星辰
      星辰
      2023-06-19
      ApiV3Key 是正确的
      2023-06-19
      回复
  • iiskam
    iiskam
    2023-04-14

    请求头里的四个参数为空是为什么呀

    

    2023-04-14
    赞同
    回复

正在加载...

登录 后发表内容