微信小程序支付 相关工具类和回调

支付和退款公共接口

 @GetMapping("/back")
    public ResultData back(Integer id,HttpServletRequest request){
        Integer userId = userService.getUserId(request);
        String uuid = WXPayUtil.getUUID();
        //构造退款的请求body 参数为 微信订单号或自己生成的订单号,退款单号(自己生成) ,退款金额,总金额
        String backJson = WXPayUtil.buildBackJson(order.getOrderId(), uuid, Integer.valueOf(order.getAmount()), Integer.valueOf(order.getAmount()));
        //封装的退款工具类 调用即发起退款
        JSONObject back = WXPayUtil.back(backJson);
        //自己封装的通用返回  
        if (ObjectUtils.isNotEmpty(back)) return ResultData.success();
        return ResultData.fail();
    }

    @PostMapping("/pay")
    public ResultData pay(@RequestBody WxPay wxPay,HttpServletRequest request) throws Exception {
        String uuid = WXPayUtil.getUUID();
        String openid = request.getHeader("openid");
        Integer userId = userService.getUserId(request);
        // 穿插自己的业务和获取需要支付的金额
        // 构造付款的body  参数为 商品描述,自己生成的订单号,金额,付款人的openid
        JSONObject payJson = WXPayUtil.buildPayJson(wxPay.getDescription(), uuid, Integer.valueOf(payAmount), openid);
        String prepayId = WXPayUtil.pay(payJson.toJSONString());
        if (StringUtils.isNotBlank(prepayId)){
            Long time = WXPayUtil.getTime();
            //生成签名的工具类
            String sign = WXPayUtil.getSign(time,uuid,"prepay_id=" + prepayId);
            //封装的给前端用于唤起小程序支付的参数
            AppletsPay appletsPay = new AppletsPay();
            appletsPay.setTimestamp(time);
            appletsPay.setNonceStr(uuid);
            appletsPay.setPrepayId(prepayId);
            appletsPay.setPaySign(sign);
            if (ObjectUtils.isNotEmpty(appletsPay)) return 
 ResultData.success(appletsPay);
        }
        return ResultData.fail();
    }

  支付回调(支付和退款写在一起)

    @ResponseBody
    @PostMapping(value = "/callBack")
    public WxResponse wxPayCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            Gson gson =new Gson();
            String body = WXPayUtil.readData(request);
            String serialNumber = request.getHeader("Wechatpay-Serial");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String signature = request.getHeader("Wechatpay-Signature");
            Map<String,String> map =new HashMap<>();
            // 构建request,传入必要参数
            NotificationRequest Nrequest = new NotificationRequest.Builder().withSerialNumber(serialNumber)
                    .withNonce(nonce)
                    .withTimestamp(timestamp)
                    .withSignature(signature)
                    .withBody(body)
                    .build();
//WechatPayUtils.getVerifier()就是获取上面的验签器,这只是我定义的一个方法来获取而已,你要怎样获取都行。构造验签器方法跟上面还是一模一样.
            NotificationHandler handler = new NotificationHandler(WXPayUtil.getVerifier(),
                    WXPayUtil.v3Key.getBytes(StandardCharsets.UTF_8));
//WechatPayConfig.v3Key  也就是APIV3key
//JSON.parseObject,是将Json字符串转化为相应的对象;JSON.toJSONString则是将对象转化为Json字符串.用 Gson.toJson也行
            try {
                // 验签和解析请求体
                Notification notification = handler.parse(Nrequest);
                // 从notification中获取解密报文。
                String plainText = notification.getDecryptData();
//将密文转为map ,之后处理业务逻辑
                Map resultMap =gson.fromJson(plainText,HashMap.class);
//支付成功的处理
                if (ObjectUtils.isNotEmpty(resultMap.get("trade_state")) && "SUCCESS".equals(resultMap.get("trade_state").toString())){ 
                   //支付成功的业务逻辑
                }
//退款成功的处理
                if (ObjectUtils.isNotEmpty(resultMap.get("refund_status")) && "SUCCESS".equals(resultMap.get("refund_status").toString())){
                   //退款成功的业务逻辑
                }
                //成功应答
                WxResponse wxResponse = new WxResponse();
                wxResponse.setCode("SUCCESS");
                wxResponse.setMessage("成功");
                return wxResponse;
            } catch (Exception e) {
                e.printStackTrace();
                //应答失败
                WxResponse wxResponse = new WxResponse();
                wxResponse.setCode("FAIL");
                wxResponse.setMessage("出错了");
                return wxResponse;
            }
        }catch (Exception e){
            WxResponse wxResponse = new WxResponse();
            wxResponse.setCode("SUCCESS");
            wxResponse.setMessage("成功");
            return wxResponse;
        }
    }

    @Data
    class WxResponse{
        private String code;

        private String message;
    }

  微信支付工具类

public class WXPayUtil {
    // 商户号 后台获取
    private static final String mchId = "";
    //appid  后台获取
    public static final String appid = "";
    // v3Key 后台自己设定
    public static final String v3Key = "";
    // 商户证书序列号 后台获取
    private static final String mchSerialNo = "";
    // 支付链接
    private static final String pay_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
    // 退款链接
    private static final String back_url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    // 你的商户私钥 去掉begin头和end结尾 还有\n换行符
    private static final String privateKey = "";
    // 你的微信支付平台证书 可不去掉多余信息
    private static final String certificate = "";

    private static CloseableHttpClient httpClient;

    public static void setup() {
        PrivateKey key = PemUtil.loadPrivateKey(privateKey);
        X509Certificate wechatPayCertificate = PemUtil.loadCertificate(
                new ByteArrayInputStream(certificate.getBytes(StandardCharsets.UTF_8)));

        ArrayList<X509Certificate> listCertificates = new ArrayList<>();
        listCertificates.add(wechatPayCertificate);

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId,mchSerialNo, key)
                .withWechatPay(listCertificates);
        httpClient = builder.build();
    }
    /**
     *wxMchid商户号
     *wxCertno证书编号
     *wxCertPath证书地址
     *wxPaternerKey   v3秘钥
     *pay_url jsapi下单地址
     *body buildWxJsApiPayJson返回的json.toString
     */
    public static String pay(String body) {
        if (httpClient == null) {
            setup();
        }
        HttpPost httpPost  = new HttpPost(pay_url);
        httpPost.addHeader("Content-Type","application/json;chartset=utf-8");
        httpPost.addHeader("Accept", "application/json");
        try{
            if(StringUtils.isBlank(body)){
                throw  new IllegalArgumentException("data参数不能为空");
            }
            StringEntity stringEntity = new StringEntity(body,"utf-8");
            httpPost.setEntity(stringEntity);
            // 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();

            if(httpResponse.getStatusLine().getStatusCode() == 200){
                String jsonResult = EntityUtils.toString(httpEntity);
                JSONObject jsonObject = JSONObject.parseObject(jsonResult);
                return jsonObject.getString("prepay_id");
            }else{
                System.err.println("微信支付错误信息"+EntityUtils.toString(httpEntity));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 构造微信JsApi付款的json
     * @param description
     * @param out_trade_no
     * @param amount
     * @return
     */
    public static JSONObject buildPayJson(String description ,
                                          String out_trade_no ,
                                          Integer amount,
                                          String openId){
        //订单金额json
        JSONObject amountJson = new JSONObject();
        amountJson.put("total",amount);
        amountJson.put("currency","CNY");

        //支付者json
        JSONObject payerJson = new JSONObject();
        payerJson.put("openid",openId);

        //基础信息json
        JSONObject json = new JSONObject();
        json.put("appid",appid);
        json.put("mchid",mchId);
        json.put("description",description);
        json.put("out_trade_no",out_trade_no);
        json.put("notify_url","https://域名/callBack");
        json.put("amount",amountJson);
        json.put("payer",payerJson);
        return json;
    }

    public static String buildBackJson(String orderId,String refundId ,Integer total , Integer refund){
        Map<String, Object> map = new HashMap<>();
        map.put("out_trade_no",orderId);
        map.put("out_refund_no",refundId);
        map.put("reason","退款理由 可参数参入");
        map.put("notify_url","https://域名/callBack");
        map.put("funds_account","AVAILABLE");
        Map<String, Object> amounts = new HashMap<>();
        amounts.put("refund",refund);
        amounts.put("total",total);
        amounts.put("currency","CNY");
        map.put("amount",amounts);
        Gson gson = new Gson();
        String json = gson.toJson(map);
        return json;
    }

    public static JSONObject back(String body) {
        if (httpClient == null) {
            setup();
        }
        HttpPost httpPost  = new HttpPost(back_url);
        httpPost.addHeader("Content-Type","application/json;chartset=utf-8");
        httpPost.addHeader("Accept", "application/json");
        try{
            if(StringUtils.isBlank(body)){
                throw  new IllegalArgumentException("data参数不能为空");
            }
            StringEntity stringEntity = new StringEntity(body,"utf-8");
            httpPost.setEntity(stringEntity);
            // 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();

            if(httpResponse.getStatusLine().getStatusCode() == 200){
                String jsonResult = EntityUtils.toString(httpEntity);
                JSONObject jsonObject = JSONObject.parseObject(jsonResult);
                return jsonObject;
            }else{
                System.err.println("微信支付错误信息"+EntityUtils.toString(httpEntity));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }


 // 获取签名 =========================================================

    // 签名
    private static String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        PrivateKey key = PemUtil.loadPrivateKey(privateKey);
        sign.initSign(key);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    public static String getUUID(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    public static Long getTime(){
        return System.currentTimeMillis() / 1000;
    }

    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static Verifier getVerifier() throws Exception {
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
                new PrivateKeySigner(mchSerialNo, PemUtil.loadPrivateKey(privateKey)))
                , v3Key.getBytes(StandardCharsets.UTF_8));
        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(mchId);
        return verifier;
    }

    /**
     * 作用:使用字段appId、timeStamp、nonceStr、package计算得出的签名值
     * 场景:根据微信统一下单接口返回的 prepay_id 生成调启支付所需的签名值
     * @param timestamp
     * @param nonceStr
     * @param pack package
     * @return
     * @throws Exception
     */
    public static String getSign(long timestamp, String nonceStr, String pack){
        String message = buildMessage(timestamp, nonceStr, pack);
        String paySign= null;
        try {
            paySign = sign(message.getBytes("utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return paySign;
    }
    private static String buildMessage(long timestamp, String nonceStr, String pack) {
        return appid + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + pack + "\n";
    }
}

  tips!!!!!!!!!!!!!!!!

certificate 获取方式按下列步骤

(1)https://github.com/EliasZzz/CertificateDownloader/releases这里下载那个jar包, 2022-7-22版本为1.2.0

(2)执行:“java -jar CertificateDownloader完整包.jar -f 商户私钥文件路径 -k v3密钥 -m 商户号 -o 证书保存路径 -s 商户证书序列号”就行了。
商户私钥文件路径”是账号中心->API安全->API证书中设置并下载的证书(就是其中的apiclient_key.pem,下载还会获得apiclient_cert.pem,我之前把这个当做支付证书了,其实不是,apiclient_cert.pem这用不着)
执行完了是个类似wechatpay_****************.pem的文件。

最后补上相关版本信息

<dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.7</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.15.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.10.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.9.0</version>
        </dependency>

  

posted @ 2022-07-22 11:47  DarkerbeS  阅读(821)  评论(0编辑  收藏  举报