java微信支付

helei 2022-11-27 702 11/27

1.初始化jsapi

后台

/**
     * 获取微信JSAPI初始化配置
     * @return
     */
    @GetMapping("/getWxconfig")
    public  Result getWxconfig(@RequestParam String url) throws NoSuchAlgorithmException {
        log.info("前端url:"+url);
        String appid = "";
        String timestamp = String.valueOf(new Date().getTime());
        String nonceStr = "";
        String signature = "";
        List   jsApiList = new ArrayList();
        //获取随机字符串
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<32;i++){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        nonceStr = sb.toString();
        //获取支付access_token
        String access_tokenurl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=&secret=&code";
        RestTemplate restTemplate = new RestTemplate();
        Gson gson = new Gson();
        Map<String,String> response =  gson.fromJson(restTemplate.getForObject(access_tokenurl, String.class),new TypeToken<Map<String,String>>(){}.getType());
        log.info(response.toString());
        redisUtil.set("yygh_pay_access_token",response.get("access_token"),7000);
        //获取jsapi_ticket
        String jsapi_ticket = "";
        String access_token = (String) redisUtil.get("yygh_pay_access_token");
        String ticeturl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+access_token+"&type=jsapi";
        log.info(access_token);
        restTemplate = new RestTemplate();
        gson = new Gson();
        response =  gson.fromJson(restTemplate.getForObject(ticeturl, String.class),new TypeToken<Map<String,String>>(){}.getType());
        log.info(response.toString());
        jsapi_ticket = response.get("ticket");
        redisUtil.set("yygh_jsapi_ticket",response.get("ticket"),7000);
        //拼接签名字符串
        String signatureStr = "jsapi_ticket="+jsapi_ticket+"&noncestr="+nonceStr+"&timestamp"+timestamp+"=&url="+url;
        signature = ShaUtils.getSHA1(signatureStr,false);
        //设置需要使用的接口
        jsApiList.add("chooseWXPay");

        List openTagList = new ArrayList();
        openTagList.add("wx-open-subscribe");

        Map<String,Object> m = new HashMap<>();
        m.put("debug",false);
        m.put("appId",appid);
        m.put("timestamp",timestamp);
        m.put("nonceStr",nonceStr);
        m.put("signature",signature);
        m.put("jsApiList",jsApiList);
        m.put("openTagList",openTagList);

        return Result.OK(m);
    }

前台

getWxconfig() {
                console.log("haha");
                var url = this.apiurl
                uni.request({
                    method: "GET",
                    url: url + "/getWxconfig",
                    data: {
                        url: window.location.href
                    },
                    success(res) {
                        console.log(res);
                        var config = {}
                        config = res.data.result
                        jWeixin.config(config);
                        jWeixin.error(function(res) {
                            console.log(res);
                        });
                        jWeixin.ready(function(res) {
                            console.log("成功了", res);
                        })
                    }
                })
            },

2.支付签名

/**
     * 获取微信支付的签名参数
     */
    @GetMapping("/getChooseWXPay")
    public  Result getChooseWXPay(@RequestParam String  prepayId){
        String privateKey = WxConfig.getPrivateKey().replace("-----END PRIVATE KEY-----\n","").replace("-----BEGIN PRIVATE KEY-----\n","");
        String timestamp = String.valueOf(new Date().getTime()/1000);
        String nonceStr = "";
        String packageStr = "prepay_id="+prepayId;
        String signType = "RSA";
        String paySign = "";
        String appid = "";

        //获取随机字符串
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<32;i++){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        nonceStr = sb.toString();

        String paySignstr =  appid+"\n"+timestamp+"\n"+nonceStr+"\n"+packageStr+"\n";
        String chatSet = "UTF-8";
        paySign = ShaUtils.signBySHA256WithRSA(paySignstr,privateKey,chatSet);

        Map map = new HashMap();
        map.put("timestamp",timestamp);
        map.put("nonceStr",nonceStr);
        map.put("package",packageStr);
        map.put("signType",signType);
        map.put("paySign",paySign);

        return  Result.OK(map);
    }

3.微信支付下单

/**
     * 微信支付下单
     * @return
     */
    @PostMapping("/getPrepayId")
    @Transactional
    public Result  getPrepayId(@RequestBody Yuyue yuyue , HttpServletRequest request) throws Exception {
        String privateKey = WxConfig.getPrivateKey();
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                new ByteArrayInputStream(privateKey.getBytes("utf-8")));
        String apiV3Key = "";
        String merchantId = "";
        String merchantSerialNumber = "";
        String appid = "";

        Verifier verifier = this.getVerifier();
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient

        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        String out_trade_no = "";
        String time = String.valueOf(new Date().getTime());
        String randn = String.valueOf(new Random().nextInt(90001)+1000);
        String randntwo = String.valueOf(new Random().nextInt(9001)+1000);
        String notifyUrl = WxConfig.getNotifyUrl();
        String ip = request.getHeader("x-forwarded-for");

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("http_client_ip");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip != null && ip.indexOf(",") != -1) {
            ip = ip.substring(ip.lastIndexOf(",") + 1, ip.length()).trim();
        }
        log.info("当前用户ip:"+ip);
        String resip = ip.replace(".","");
        //获取预约单号
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        int num = iYuyueService.getMaxDayYuyueNum(simpleDateFormat.format(new Date()));
        String yuyueNum = String.valueOf(num);

        out_trade_no = time +""+yuyueNum+""+resip+""+randn;

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid",merchantId)
                .put("appid", appid)
                .put("description", "挂号费用")
                .put("notify_url", notifyUrl)
                .put("out_trade_no", out_trade_no);
        rootNode.putObject("amount")
                .put("total", (int)(Double.parseDouble(yuyue.getPayMoney())*100));
        rootNode.putObject("payer")
                .put("openid", yuyue.getWxId());

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        log.info(bodyAsString);
        Gson gson = new Gson();
        Map<String,String> map = gson.fromJson(bodyAsString,new TypeToken<Map<String,String>>(){}.getType());
        System.out.println(map.toString());

        //获取到微信支付信息后保存预约单信息
        yuyue.setOrderNum(out_trade_no);
        yuyue.setPrepayId(map.get("prepay_id"));
        //yuyue.setYuyueNum(yuyueNum);
        iYuyueService.save(yuyue);

        //获取刚刚新增的订单返回到页面
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("jz_people",yuyue.getJzPeople());
        queryWrapper.last("limit 1");
        queryWrapper.orderByDesc("create_time");
        Yuyue yuyue1 = (Yuyue) iYuyueService.list(queryWrapper).get(0);

        return Result.OK(yuyue1);
    }

前台

uni.request({
                    method: "POST",
                    url: url + "/getPrepayId",
                    data: data,
                    success(res) {
                        console.log(res);
                        uni.showLoading({
                            title: "预约中..."
                        })
                        that.clicked = false
                        if (res.data.success) {
                            var orderRes = res.data.result
                            // uni.redirectTo({
                            //  url: "../success/success?data=" + JSON.stringify(res.data.result)
                            // })
                            data = {
                                prepayId: res.data.result.prepayId
                            }
                            uni.request({
                                method: "GET",
                                url: url + "/getChooseWXPay",
                                data: data,
                                success(res) {
                                    console.log(res);
                                    var resdata = res.data.result
                                    jWeixin.chooseWXPay({
                                        timestamp: resdata
                                            .timestamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
                                        nonceStr: resdata.nonceStr, // 支付签名随机串,不长于 32 位
                                        package: resdata
                                            .package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
                                        signType: resdata
                                            .signType, // 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致
                                        paySign: resdata.paySign, // 支付签名
                                        success: function(res) {
                                            uni.redirectTo({
                                                url: "../success/success?orderNum=" +
                                                    orderRes.orderNum
                                            })
                                            // 支付成功后的回调函数
                                            // console.log(res);
                                            // uni.showLoading({
                                            //  title:"预约中..."
                                            // })
                                            // data = {
                                            //  orderNum: orderRes.orderNum
                                            // }
                                            // uni.request({
                                            //  method: "GET",
                                            //  url: url +
                                            //      "/updatePayStatus",
                                            //  data: data,
                                            //  success(res) {
                                            //      console.log(res);
                                            //      orderRes.payStatus = "已支付"
                                            //      if (res.data.success) {
                                            //          uni.hideLoading()
                                            //          uni.redirectTo({
                                            //              url: "../success/success?orderNum=" +orderRes.orderNum
                                            //          })
                                            //      }
                                            //  }
                                            // })
                                        },
                                        cancel: function(res) {
                                            uni.showModal({
                                                content: "取消支付",
                                                showCancel: false
                                            });
                                        },
                                        fail: function(res) {
                                            uni.showModal({
                                                content: "支付失败",
                                                showCancel: false
                                            });
                                        }
                                    });
                                },
                                fail(res) {
                                    uni.showModal({
                                        content: "预约失败,请重试或到窗口挂号"
                                    })
                                },
                                complete() {
                                    uni.hideLoading()
                                    that.clicked = false
                                }
                            })
                        } else {
                            uni.showModal({
                                content: "预约失败,请重试或到窗口挂号",
                                showCancel: false
                            })
                        }
                    },
                    fail(res) {
                        uni.showModal({
                            content: "预约失败,请重试或到窗口挂号"
                        })
                    },
                    complete() {
                        uni.hideLoading()
                        that.clicked = false
                    }
                })

4.接受微信支付结果

/**
     * 获取微信支付结果通知
     * @return
     */
    @PostMapping("/getPayNotify")
    public  Result getPayNotify(HttpServletRequest request,@RequestBody JSONObject jsonObject ) throws Exception {
        String signature = request.getHeader("Wechatpay-Signature");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");
        log.info("签名信息:"+signature+"--"+timestamp+"--"+nonce+"--"+wechatPaySerial);

        // 从证书管理器中获取verifier
        Verifier verifier = this.getVerifier();
        String apiV3Key = WxConfig.getApiV3Key();

        String body = jsonObject.toJSONString();
        Gson gson = new Gson();
        Map<String,Object> mapMap = gson.fromJson(body,new TypeToken<Map<String,Object>>(){}.getType());
        log.info("我通知了:"+body);

        String associatedData = ((Map<String,String>)mapMap.get("resource")).get("associated_data");
        String nonceres = ((Map<String,String>)mapMap.get("resource")).get("nonce");
        String ciphertext = ((Map<String,String>)mapMap.get("resource")).get("ciphertext");

        AesUtil aesUtil = new AesUtil(WxConfig.getApiV3Key().getBytes());
        String res = aesUtil.decryptToString(associatedData.getBytes(),nonceres.getBytes(),ciphertext);
        log.info("訂單信息:"+res);
        Map<String,Object> resmap = gson.fromJson(res,new TypeToken<Map<String,Object>>(){}.getType());
        String ordernum = (String) resmap.get("out_trade_no");
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("order_num",ordernum);
        Yuyue yuyue = iYuyueService.getOne(queryWrapper);
        yuyue.setPayStatus("已支付");
        //获取预约单号
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        int num = iYuyueService.getMaxDayYuyueNum(simpleDateFormat.format(new Date()));
        String yuyueNum = String.valueOf(num);
        yuyue.setYuyueNum(yuyueNum);
        iYuyueService.updateById(yuyue);
        this.sendPaySuccessMsg(yuyue);
        List<SysUser> sysUserList = iUserService.list();
        log.info(sysUserList.toString());

        List<String> stringList = new ArrayList<>();
        for (SysUser sysUser:sysUserList) {
            stringList.add(sysUser.getId());
        }
        sysBaseAPI.sendWebSocketMsg(stringList.toArray(new String[stringList.size()]), "新订单");
        return  Result.OK();
    }

5.退款

@GetMapping("/payRefund")
    public  Result payRefund(@RequestParam String orderNum) throws Exception {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("order_num",orderNum);
        Yuyue yuyue = iYuyueService.getOne(queryWrapper);
        if(!yuyue.getStatus().equals("未录入")){
            return  Result.error("当前订单无法退款");
        }
        if(yuyue.getStatus().equals("已录入")){
            return  Result.error("当前订单已完成不支持退款");
        }
        if(yuyue.getStatus().equals("已退款")){
            return  Result.error("非法操作");
        }
        String privateKey = WxConfig.getPrivateKey();
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                new ByteArrayInputStream(privateKey.getBytes("utf-8")));
        String apiV3Key = "";
        String merchantId = "";
        String merchantSerialNumber = "";
        String appid = "";

        Verifier verifier = this.getVerifier();
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient

        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();

        String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
        HttpPost httpPost = new HttpPost(url);
        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();
        int money = (int)(Double.parseDouble(yuyue.getPayMoney())*100);
        rootNode.put("out_trade_no",yuyue.getOrderNum())
                .put("out_refund_no", yuyue.getOrderNum())
                .put("reason", "用户申请退款");
        rootNode.putObject("amount")
                .put("refund", money)
                .put("total", money)
                .put("currency", "CNY");
        objectMapper.writeValue(bos, rootNode);
        log.info(String.valueOf(rootNode));
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        log.info(String.valueOf( response.getStatusLine().getStatusCode()));
        String bodyAsString = EntityUtils.toString(response.getEntity());
        log.info(bodyAsString);
        String code = String.valueOf(response.getStatusLine().getStatusCode());
        if(code.equals("200")){
            yuyue.setPayStatus("已退款");
            iYuyueService.updateById(yuyue);
            this.sendPayCancelMsg(yuyue);
            return  Result.OK();
        }else{
            return  Result.error("退款失败");
        }
    }
- THE END -

helei

11月27日00:38

最后修改:2022年11月27日
0

非特殊说明,本博所有文章均为博主原创。