从微信授权到微信支付

一、微信授权
1、在对接微信之前,首先从微信官网(https://mp.weixin.qq.com)去注册微信公众公众账号,提交资料等待验证通过。

2、验证通过后就可以 微信公众平台 获取到 AppID 和 AppScret 两个参数值,如图:
基本配置
3、网页授权域名
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
在 “开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名” 进行网页域名授权,需要下载校验文件并放在项目根目录下,注意能被访问到,避免拦截器,过滤器等拦截了,如图:
授权域名配置
4、此时,按照微信官方文档,需要进行 填写服务器配置,参考微信接入指南 (https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319),如图:
服务器域名校验配置
注意:
URL 参数是一个接口地址,需要写一个接口(http://xx.xx.com/x/xSignature类似的)供微信端调用校验。
Token 自己填写。
如图:
校验接口
只要能返回 随机字符串 就可以通过。
调用成功后如图:
服务器域名校验成功
注意:只要验证通过,微信自定义菜单就会失效,需要调用接口生成自定义菜单,生成方式查看微信开发文档,可以在微信公众平台线接口调试生成。

5、因为需要调用接口生成菜单,所以需要获取 调用接口的 access_token。
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?
grant_type=client_credential&appid=APPID&secret=APPSECRET
创建菜单接口 POST
https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

6、在项目中进行拦截,微信端嵌套H5页面。
代码1

// 判断是否微信端跳转
if(userAgent.contains("micromessenger")){
    String version = userAgent.substring(userAgent.indexOf("micromessenger"));
    System.out.println("微信请求...去授权");
    weChatAuthorityUtil.weChatRedirect(request,response);
}

然后跳转到微信端进行授权,并且传入回调接口地址,用于获取code 和 state 两个参数值,code 和 state 两个参数用于获取微信用户信息,在微信端校验。

代码2

@RequestMapping(value = "/ucode", method = RequestMethod.GET)
public void weChatCode(HttpServletRequest request, HttpServletResponse response, String code, String state){
    if(code != null && code.length() > 0 && propertiesUtil.getState().equals(state)){
        //通过 code  获取 access_token,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期
        Map<String,String> param = new HashMap<String,String>();
        param.put("appid",propertiesUtil.getAppId());
        param.put("secret",propertiesUtil.getAppSecret());
        param.put("code",code);
        param.put("grant_type","authorization_code");
        //获取用户token openid
        String resStr = HttpUtil.doPostMap(WXPayConstants.WECHAT_ACCESS_TOKEN,param);
        if(resStr != null && resStr.length() > 0){
            System.out.println("获取token resStr = " + resStr);
            Map<String,Object> res = JSON.parseObject(resStr,Map.class);
            String errcode = (String) res.get("errcode");
            if(errcode == null){ //errcode 错误码
                String accessToken = (String) res.get("access_token");
                int expiresIn = (int) res.get("expires_in");
                String openId = (String) res.get("openid");
                String scope = (String) res.get("scope");
                //首先从数据库中获取用户信息,没有从微信端获取
                UserIdentity userIdentity = userService.getUserIdentityByOpenId(openId);
                if(userIdentity != null){ //数据库中没有获取到,第一次登录
                    //数据库里面有,放入session
                    //业务逻辑
                }else{
                    //获取用户信息 存库  加入session
                    userIdentity = weChatAuthorityUtil.getUserInfoBy(openId,accessToken);
                    //业务逻辑
                }
            }else{
                String errmsg = (String) res.get("errmsg");
                //此处,说明微信获取token失败
            }
        }
    }
}

二:微信支付
1、在对接微信支付之前,同上,也是需要去 “微信支付商务” 平台去开通;
选择支付方式 JSAPI,NATIVE,APP,MWEB–H5支付
在 微信商户平台 ——> 账户中心 配置个人信息; 在 API安全 中配置 API密匙(后面调用下单接口需要用到,32为码)
在 微信商户平台 ——> 产品中心——>开发配置 添加域名配置,授权等

2、此项目首先选择 JSAPI支付,首先需要调用 统一下单(URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder)接口在微信端下单。
注意:在 统一下单 接口里面,需要对接口参数(sign)进行一个签名认证,签名算法看官方文档(接口规则 --> 安全规范)
可以校验签名,在线
为了校验签名是否正确,用 签名校验工具 验证
在线校验签名
此处 商户Key 为 商户号
获取商户号Mch_id
把生成的xml结构的参数粘贴进去,参数中需要有 sign 已生成好的签名进行校验,代码3 类似这样:

<?xml version="1.0" encoding="UTF-8"?>
<xml>
	<appid>wx69767be593fa4866</appid>
	<body>水费-缴费</body>
	<mch_id>1543011521</mch_id>
	<nonce_str>20ef119e812exxxxxxxxxxa448b57ebc</nonce_str>
	<notify_url>http://xx.xxx.com/xx/callback</notify_url>
	<out_trade_no>82bae3axxxxxxxxxxxa17aff7310097b</out_trade_no>
	<spbill_create_ip>123.151.0.0</spbill_create_ip>
	<total_fee>50</total_fee>
	<trade_type>JSAPI</trade_type>
	<sign>758394EB3F6A59E60BE3BCB21D1DD166</sign>
	<openid>oxrDv57zsMrMtnPvLoDZZmnAkWDE</openid>
</xml>

3、此时需要进行调用微信 统一下单(https://api.mch.weixin.qq.com/pay/unifiedorder) 接口生成微信端订单。
此接口 为 post提交,xml的形式传参数。类似上面提到的xml格式的数据 代码3。
统一下单接口中 签名校验 和 页面JS 发起支付需要的签名算法一样。
代码4

@RequestMapping(value = "/payment",method = RequestMethod.GET)
public ModelAndView wxPayment(HttpServletRequest request, HttpServletResponse response, Long id){
    ModelAndView modelAndView = new ModelAndView("view/");
    PayBill payBill = new PayBill();
    payBill.setId(BigInteger.valueOf(id));
    RsFeesMessage rsFeesMessage = payBillService.getFeesMessage(payBill);
    UserIdentity userIdentity = (UserIdentity) request.getSession().getAttribute(SessionConstant.ACCOUNT_SESSION_KEY);
    WxPayEntity result = new WxPayEntity();
    try {
        //查询账单信息,使用账单中的金额
        PayBill bill = payBillService.getBillMessage(id);
        //商户订单号
        String outTradeNo = WXPayUtil.getOut_trade_no();
        //产品价格,单位:分
        BigDecimal multiply = bill.getPaymentAmount().multiply(new BigDecimal(100));
        Integer totalFee = multiply.intValue();
        //客户端ip
        String ip = request.getRemoteAddr();
        //支付成功后回调的url地址
        String notifyUrl ="http//xx.xx.com/pay/callback";

        //String nonceStr = WXPayUtil.getNonceStr();//随机数据
        WxPayEntity payEntity = new WxPayEntity();
        payEntity.setBody("水费-缴费");
        payEntity.setOutTradeNo(outTradeNo);
        payEntity.setTotalFee(totalFee);
        payEntity.setNotifyUrl(notifyUrl);
        payEntity.setOpenId(userIdentity.getOpenId());
        payEntity.setAppId(propertiesUtil.getAppId());
        payEntity.setMchId(propertiesUtil.getMchId());
        payEntity.setAppKey(propertiesUtil.getAppKey());
        /*SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        long time = 30*60*1000;//30分钟
        Date afterDate = new Date(new Date().getTime() + time);//30分钟后的时间
        String timeExpire = sdf.format(afterDate);*/
        //统一下单
        String strResult = WXPayUtil.unifiedorder("测试", outTradeNo, totalFee,ip, notifyUrl,userIdentity.getOpenId(),propertiesUtil.getAppId(),propertiesUtil.getMchId(),propertiesUtil.getAppKey());
        //将解析结果存储在HashMap中
        Map map = WXPayUtil.doXMLParse(strResult);
        //返回状态码
        String returnCode = (String) map.get("return_code");
        String resultCode = (String) map.get("result_code");
        System.out.println("resultCode = " + resultCode);
        System.out.println("----------------------------------------------");
        //两者都为SUCCESS才能获取prepay_id
        if( returnCode.equals(WXPayConstants.SUCCESS) && resultCode.equals(WXPayConstants.SUCCESS) ){
            System.out.println("统一下单成功...");
            //业务逻辑,写入订单日志(你自己的业务) .....
            payBill.setUserId(userIdentity.getId());
            //修改账单表 支付中
            payBillService.updatePayBillById(payBill);

            String timeStamp = WXPayUtil.getTimeStamp();//时间戳
            String nonceStr = WXPayUtil.getNonceStr();//随机字符串
            //注:上面这两个参数,一定要拿出来作为后续的value,不能每步都创建新的时间戳跟随机字符串,不然H5调支付API,会报签名参数错误
            String prepayId = (String) map.get("prepay_id");
            System.out.println("prepayId = " + prepayId);
            result.setResultCode(resultCode);
            result.setAppId(propertiesUtil.getAppId());
            result.setTimeStamp(timeStamp);
            result.setNonceStr(nonceStr);
            result.setPackageStr("prepay_id="+prepayId);
            result.setSignType(WXPayConstants.MD5);
            /*redisUtil.set(userIdentity.getId()+"_prepay_id",prepayId);
            redisUtil.setObject(RedisConstant.PREPAY_ID_KEY,userIdentity,RedisConstant.KEY_EXPIRE_30m);*/
            //WXPayUtil.unifiedorder(.....) 下单操作中,也有签名操作,那个只针对统一下单,要区别于下面的paySign
            //第二次签名,将微信返回的数据再进行签名
            SortedMap<Object,Object> signMap = new TreeMap<Object,Object>();
            signMap.put("appId",propertiesUtil.getAppId());
            signMap.put("timeStamp", timeStamp);
            signMap.put("nonceStr", nonceStr);
            signMap.put("package", "prepay_id="+prepayId);  //注:看清楚,值为:prepay_id=xxx,别直接放成了wxReturnData.getPrepay_id()
            signMap.put("signType", WXPayConstants.MD5);
            String paySign = WxSign.createSign(signMap,  propertiesUtil.getAppKey());//支付签名
            result.setSign(paySign);
        }else{
            result.setResultCode(WXPayConstants.FAIL);
            if((map.get("return_code")).equals(WXPayConstants.FAIL)){
                saveBillLog(payBill,(String) map.get("return_msg"));
            }else if((map.get("result_code")).equals(WXPayConstants.FAIL)){
                saveBillLog(payBill,(String) map.get("err_code_des"));
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    modelAndView.addObject("rsFeesMessage",rsFeesMessage);
    modelAndView.addObject("result",result);
    return modelAndView;
}

生成签名算法:
代码5

public static String createSign(SortedMap<Object,Object> parameters, String key){
    StringBuffer sb = new StringBuffer();
    Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)
    Iterator it = es.iterator();
    while(it.hasNext()) {
        Map.Entry entry = (Map.Entry)it.next();
        String k = (String)entry.getKey();
        Object v = entry.getValue();
        if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
            sb.append(k + "=" + v + "&");
        }
    }
    sb.append("key=" + key);
    String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
    return sign;
}

4、下单成功后,将获取到的订单号 prepay_id 参数传递到页面进行调用JS支付此订单。
注意:传输到页面的package参数是订单号,参数格式为 prepay_id=11111111111
代码6

function onBridgeReady(){
         WeixinJSBridge.invoke(
             'getBrandWCPayRequest', {
                 "appId":appId,           //公众号名称,由商户传入
                  "timeStamp":timeStamp,  //时间戳,自1970年以来的秒数
                 "nonceStr":nonceStr,  //随机串
                 "package":package,
                 "signType":signType,    //微信签名方式:
                 "paySign":paySign        //微信签名
             },
             function(res){
                 if(res.err_msg == "get_brand_wcpay_request:ok" ){
                     //您已付款成功
                     // 使用以上方式判断前端返回,微信团队郑重提示:
                     //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                 }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
                     //您已取消支付
      }else if(res.err_msg == "get_brand_wcpay_request:fail"){
                     //支付遇到问题
                 }
             });
     }
     var fn = function() {
         alert($("input[name='checkbox']").is('checked'));
         if(resultCode == "SUCCESS"){
             if (typeof WeixinJSBridge == "undefined"){
                 if( document.addEventListener ){
                     document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
                 }else if (document.attachEvent){
                     document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
                     document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
                 }
             }else{
                 onBridgeReady();
             }
        }
     }

5、在前段用js发起支付,微信就可以支付了
6、支付成功,微信回调你的回调接口,放回支付成功信息,咋流中获取xml数据:

@RequestMapping("/callback")
@ResponseBody
public String callback(HttpServletRequest request, HttpServletResponse response){
    System.out.println("支付回调...");
    String resXml = "";
    InputStream inStream;
    Map resultMap = null;
    PayBill payBill = new PayBill();
    try {
        inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }

        // 获取微信调用我们notify_url的返回信息
        String result = new String(outSteam.toByteArray(), "utf-8");
        // 关闭流
        outSteam.close();
        inStream.close();

        resultMap = WXPayUtil.doXMLParse(result); //解析为Map
        
        //返回状态码
        String returnCode = (String) resultMap.get("return_code");
        String resultCode = (String) resultMap.get("result_code");
        //String sign = (String) resultMap.get("sign");
        if (resultCode.equals(WXPayConstants.SUCCESS)) {
            logger.info("wxnotify:微信支付----返回成功");
            String xmlStr = WXPayUtil.mapToXml(resultMap);
            //验证签名 和之前一样方式验证
            boolean flag = WXPayUtil.isSignatureValid(xmlStr,propertiesUtil.getAppKey());
            // 保存数据或者 处理业务
            }
        } else {
            logger.error("wxnotify:支付失败,错误信息:" + resultMap.get("return_msg"));
            resXml = resFailXml;
        }
    }catch (Exception e) {
        logger.error("wxnotify:支付回调发布异常:", e);
    } finally {
        //处理数据
        saveBillLog(payBill,(String)resultMap.get("return_msg"));
    }
    // <xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>
    //需要给微信端返回结果,要不一直调好几次
    return resXml;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值