java后端AppV3版本微信支付CV大法2021-11-12

本人刚入这行最近项目要对接app版支付宝和微信支付,网上教程不是要钱,就是乱废话不多说开始上代码!
## 前期准备
	这方面我不会细说大概过一下,不知道的自行百度**你要对接支付必须要了解整个流程的**!如果文章反响大的话我会后面出教程
	1.首先你要有微信的商户号这个必须要有相关的营业执照具体申请地址:	https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F
	设置V3密钥参数用![在这里插入图片描述](https://img-blog.csdnimg.cn/d0fde85abd8d41998f24ea08845c1364.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5pGp5p-v5peg6YeP,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)

	2.登录开放平台申请相关APP应用需要300大洋
	话不多说上代码
	本项目采用的是Springboot多模块搭建
Maven配置
  <!--微信支付需要的依赖[BEGIN]-->
    <dependency>
        <groupId>com.github.wechatpay-apiv3</groupId>
        <artifactId>wechatpay-apache-httpclient</artifactId>
        <version>0.2.2</version>
    </dependency>
       <!--json和对象的互转-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
    </dependency>
常量工具类
package com.xqt.pay_wechat.utils;

/**
 * @Description :  通用支付常量类
 * @Author : HaoZheng
 * @Date: 2021/9/28 17:59
 */
public interface PayConstants {
    String NOTIFY_URL=" ";   //支付成功回调地址
    String MCH_ID=" ";       //商户号
    String MCH_SERIAL_NO=" ";    //商户证书序列号
    String API_V3KEY=" ";    //api密钥
    String APP_ID=" ";   //appid
    String PACKAGE="Sign=WXPay";  //签名固定字符串(微信要求的)
    //商户私钥
    String  PRIVATE_KEY="  ";
#### 验签及密钥解析类
package com.xqt.pay_wechat.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
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.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.xqt.base.config.ResultJson;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;

public class callback {

    //属于回调 验证签名是否正确 请求头:serial 验签的报文内容:message  自动更新验证器:signature
    public static boolean signVerify(String serial,String message,String signature) {
        AutoUpdateCertificatesVerifier verifier=null;
        try {
            // 加载商户私钥(privateKey:私钥字符串)
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                    new ByteArrayInputStream(PayConstants.PRIVATE_KEY.getBytes("utf-8")));//传入商户私钥

            //使用自动更新的签名验证器,不需要传入证书 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
            verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
                    PayConstants.API_V3KEY.getBytes("utf-8"));
            return verifier.verify(serial, message.getBytes("utf-8"), signature);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return false;
    }

    //回调解密详情看开发文档"通知数据"https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_5.shtml
    public static String decryptOrder(String body){
        try {
            //通过调用微信工具类进行解密
            AesUtil aesUtil = new AesUtil(PayConstants.API_V3KEY.getBytes("utf-8"));
            //将相应的数据转化为JsonNode
            ObjectMapper objectMapper=new ObjectMapper();
            JsonNode node=objectMapper.readTree(body);
            //从JsonNode拿到通知数据resource
            JsonNode resource=node.get("resource");
            //取出微信返回的各项数据,数据密文并进行转换为String
            String ciphertext=resource.get("ciphertext").textValue();
            //附加数据
            String associated_data=resource.get("associated_data").textValue();
            //随机串
            String nonce=resource.get("nonce").textValue();

            return aesUtil.decryptToString(associated_data.getBytes("utf-8"),
                    nonce.getBytes("utf-8"),ciphertext);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }


    //关单 如果用户长时间不支付就需要关闭预支付订单  商户订单号outTradeNo
    public static ResultJson closeOrder(String outTradeNo){
        // 加载商户私钥(privateKey:私钥字符串)
        PrivateKey merchantPrivateKey = null;//传入商户私钥
        //使用自动更新的签名验证器,不需要传入证书 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
        AutoUpdateCertificatesVerifier verifier = null;
        try {
            merchantPrivateKey = PemUtil.loadPrivateKey(
                    new ByteArrayInputStream(PayConstants.PRIVATE_KEY.getBytes("utf-8")));

            verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
                    PayConstants.API_V3KEY.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
        
        // 初始化httpClient
        CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(PayConstants.MCH_ID, PayConstants.MCH_SERIAL_NO, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier))
                .build();
        //关单url地址直接发送动态订单号
        HttpPost httpPost=new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/"+outTradeNo+"/close");
        httpPost.addHeader("Accept","application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");

        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectMapper objectMapper=new ObjectMapper();

        ObjectNode rootNode=objectMapper.createObjectNode();
        rootNode.put("mchid",PayConstants.MCH_ID);

        try {
            objectMapper.writeValue(bos,rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"),"UTF-8"));
            CloseableHttpResponse response=httpClient.execute(httpPost);
            //读取微信返回的状态码
            System.out.println(response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode()==204){
                return  ResultJson.ok("关闭订单成功!",response.getStatusLine().getStatusCode());
            }
            return ResultJson.err(response.getStatusLine().getStatusCode(),"关闭订单失败!");
//          String bodyAsString=EntityUtils.toString(response.getEntity());
//          System.out.println(bodyAsString);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ResultJson.err(500,"系统错误!请联系开发人员!");
    }
}

注意!注意!注意!
##这个类就是你需要进行进行支付调用的方法支付的话直接调用下边的方法一个是微信预支的一个回调的方法只需要调用方法传入

package com.xqt.pay_wechat.api;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
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.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import com.xqt.base.config.ResultJson;
import com.xqt.base.entity.Order;
import com.xqt.pay_wechat.utils.PayConstants;
import com.xqt.pay_wechat.utils.callback;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
/**
 * @Description :  预支付下单
 * @Author : HaoZheng
 * @Date: 2021/9/29 13:57
 */
@Slf4j
public class WeChatPayAPI {

  //预支付下单(订单id(id),金额(amount),描述(desc),回调地址(PayConstants.NOTIFY_URL))
    public static ResultJson wechatPay(String id,String ticketType,Integer amount) throws Exception{
      System.out.println("订单号:"+id);
      // 加载商户私钥(privateKey:私钥字符串)
      PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
              new ByteArrayInputStream(PayConstants.PRIVATE_KEY.getBytes("utf-8")));//传入商户私钥

      //使用自动更新的签名验证器,不需要传入证书 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
      AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
              new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
              PayConstants.API_V3KEY.getBytes("utf-8"));
      // 初始化httpClient
      CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
              .withMerchant(PayConstants.MCH_ID, PayConstants.MCH_SERIAL_NO, merchantPrivateKey)
              .withValidator(new WechatPay2Validator(verifier))
              .build();

        //定义预支付请求地址为post及支付的数据格式
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");  //统一下单接口
        httpPost.addHeader("Accept", "application/json");   //json格式数据传输
        httpPost.addHeader("Content-type","application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        //调用此方法生成json
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", PayConstants.MCH_ID)      //商户订单号
                .put("appid", PayConstants.APP_ID)    //appid
                .put("notify_url",PayConstants.NOTIFY_URL)   //回调地址
                .put("description", ticketType)    //商品描述
                .put("out_trade_no", id);  //商品订单号
        //金额设置为分
        rootNode.putObject("amount")
                .put("total", amount);
        //直接将传入的对象序列化为json,并且返回给客户端
        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        //接收返回参数
        CloseableHttpResponse response = httpClient.execute(httpPost);

        //取出prepay_id预支付订单id
        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println(bodyAsString+"接口里的预支付id!!!");
        JSONObject json = new JSONObject(bodyAsString);
        String prepay_id= (String) json.get("prepay_id");
        System.out.println(prepay_id);


      //根据微信文档必填参数进行加签
        //生成时间戳
        String timestamp=System.currentTimeMillis()+"";
        //调用hutool工具类生成随机字符串为不低于32位
        String nonce= RandomUtil.randomString(32);

       //拼接微信需要的参数使用进行加密.append方法拼接参数appid,时间戳,随机字符串,预支付id
        StringBuffer builder=new StringBuffer();
        //应用id
        builder.append(PayConstants.APP_ID).append("\n");
        //时间戳
        builder.append(timestamp).append("\n");
        //随机字符串后台支付id
        builder.append(nonce).append("\n");
        //预支付会话id通过调用此方法转换为数字
        JsonNode node=objectMapper.readTree(bodyAsString);
        builder.append(node.get("prepay_id")).append("\n");
        //通过微信pom依赖引入RsaCryptoUtil.encryptOAEP()方法进行加密   .decryptOAEP()解密
        String ciphertext = RsaCryptoUtil.encryptOAEP(builder.toString(), verifier.getValidCertificate());
        System.out.println("加密签名是:"+ciphertext);

        //封装为map返回
        Map map=new HashMap();
        map.put("appid",PayConstants.APP_ID);
        map.put("noncestr",nonce);
        map.put("package",PayConstants.PACKAGE);
        map.put("mch_id",PayConstants.MCH_ID);
        map.put("timestamp",timestamp);
        map.put("sign",ciphertext);
        map.put("prepay_id",prepay_id);
        map.put("money",amount);
        map.put("orderId",id);
        return ResultJson.ok("微信预支付成功!",map);
    }

    //微信回调请求解析
    public static Map callbackFunction(HttpServletRequest request) {

      //微信相关文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml
      //取出请求头部时间戳
      System.out.println("Wechatpay-Timestamp:" + request.getHeader("Wechatpay-Timestamp"));
      //HTTP头Wechatpay-Nonce 中的应答随机串
      System.out.println("Wechatpay-Nonce:" + request.getHeader("Wechatpay-Nonce"));
      //获取应答签名
      //微信支付的应答签名通过HTTP头Wechatpay-Signature传递。
      System.out.println("Wechatpay-Signature:" + request.getHeader("Wechatpay-Signature"));
      //因为微信支付后台会存一个最新的微信证书序列号用此来判断本地证书是否是最新的来验证本地的序列号(签名是由最新证书生成每次的生成都不一样)
      System.out.println("Wechatpay-Serial:" + request.getHeader("Wechatpay-Serial"));

      //这是返回微信的数据
      Map result = new HashMap();
      //验证签名主要是否是,微信回调的
      try {
        //首先验证签名是否正确
        StringBuilder signStr = new StringBuilder();
        //应答时间戳\n 添加signStr
        signStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
        //应答随机串\n 添加signStr
        signStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");

        /**
         *  应答主体报文(response Body),需要按照接口返回的顺序进行验签,错误的顺序将导致验签失败。
         *  然后,请按照以下规则构造应答的验签名串。签名串共有三行,行尾以\n 结束,包括最后一行。\n为换行符
         *  (ASCII编码值为0x0A)。若应答报文主体为空(如HTTP状态码为204 No Content),最后一行仅为一个\n换行符。
         */
        //request.getReader()方法来获取请求内容值
        BufferedReader br = request.getReader();
        String str = null;
        StringBuilder builder = new StringBuilder();
        //一次读一行(\n),读入null时文件结束
        while ((str = br.readLine()) != null) {
          builder.append(str);
        }
        System.out.println(builder + "正在调用");
        //应答主体报文\n 添加signStr
        signStr.append(builder.toString()).append("\n");
        //调用PayUtil类里的方法进行验签
        if (!callback.signVerify(request.getHeader("Wechatpay-Serial"),
                signStr.toString(), request.getHeader("Wechatpay-Signature"))) {
          //如果验签失败返回给微信接口错误码
          result.put("msg", "sign error");
          return result;
        }

        //解密密文
        String wxOrder = callback.decryptOrder(builder.toString());
        System.out.println(callback.decryptOrder(builder.toString()));
        //验证订单
        //调用数据库验证订单号是否存在out_trade_no
        JSONObject json = new JSONObject(wxOrder);
        String out_trade_no = (String) json.get("out_trade_no");
        String transaction_id = (String) json.get("transaction_id");
        //取出微信回调的订单号
        result.put("out_trade_no",out_trade_no);
        //取出微信生成的订单号
        result.put("transaction_id",transaction_id);
        result.put("msg","验签成功");
        return result;
      } catch (IOException e) {
        e.printStackTrace();
      }
      result.put("msg","验签失败请联系管理员!");
      return result;
    }
}

如有不足还请大佬多多指教!回调牵扯内网穿透明天在更新!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值