微信公众号支付

公众号支付

      公众号支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付。应用场景有:

  • ◆ 用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付
  • ◆ 用户的好友在朋友圈、聊天窗口等分享商家页面连接,用户点击链接打开商家页面,完成支付
  • ◆ 将商户页面转换成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付

      微信开发者文档链接地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

开发流程  

  第一步:

  你得明白自己需要做的微信支付是什么一个需求,具体的场景规划是什么。

  第二步:

  打开微信公众号支付开发文档,查看一下具体的开发步骤,在上面都有一一的介绍的,不过记得是微信商户平台(pay.weixin.qq.com)

  第三步:

  这个图主要介绍了微信的开发流程,我觉得这个得看懂了,才好开发,对今后的设计什么的,有很大的帮助,脑子里有一个大致支付的概念轮廓。

  
图片描述

  第四步:  

  微信提供的SDK,自己在仔细琢磨一下,结合上面一系列的步骤,很快就知道如何去操作了,关键需要认真去阅读微信提供的SDK源码,了解深入其中。

  多去注意查看一下微信支付文档提供的参数列表看看其中你的一些定义,一些坑就出现在其中。  

  还有我觉得一切支付都在统一下单的地方出现的变化,需要好好设计这里面的逻辑,会让支付系统变得更加的灵活多变,适合以后的扩展。

主要代码

  这里我主要用的是微信的SDK,我用的是最新的SDK,已经修改好7月3号的微信XXE漏洞,其实也就添加一个类,修改一些东西,没啥大改动。

  主要思路:

  根据微信网页授权获取用户的Openid,配置参数,将参数转换为xml格式,通过微信专门接口发给微信,微信在返回给你一系列的东西,可以从其中获取有用的。

  1、微信网页授权(如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。),因为在开发文档中说了,只要trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。可以查看这个文档https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

  原理:经过网页授权获取openid,成功则再重新加载这个地址,获取到openid用session存起来,这个只是个人思路,可以根据个人需求去改造。 

  代码:

public String toWXPublic(String spbill_create_ip, String out_trade_no, int total_fee, Model model, HttpServletResponse response, HttpServletRequest request) throws Exception {
        JSONObject jsonObject = WXPayUtil.getAccessToken(request, response);
        if (jsonObject == null) {
            try {
                String encodeURL = URLEncoder.encode(PayConfigUtil.PROJECT_URL + "/toWXPublic?out_trade_no=" + out_trade_no + "&total_fee=" + total_fee + "&spbill_create_ip=" + spbill_create_ip, "UTF-8");
                String redirectUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + PayConfigUtil.APP_ID + "&redirect_uri=" + encodeURL + "&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
                response.sendRedirect(redirectUrl);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }   
        log.info("openid get Success:" + jsonObject.getString("openid"));
        request.getSession().setAttribute("openid", jsonObject.getString("openid"));
     
        model.addAttribute("out_trade_no", out_trade_no);
        model.addAttribute("total_fee", total_fee);
        model.addAttribute("spbill_create_ip", spbill_create_ip);
        return "/wxPublicPay";
    }

  

  2、吊起统一下单 URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder,主要获取prepay_id=123456789参数。

   原理:通过Ajax请求获取必要的参数,代码中一些类和参数工具可以根据SDK,自行配置一下,主要为了得到prepay_id

   代码:

public Map<String, String> wxPublicPay(Unifiedorder unifiedorder) throws Exception {
        String appid = PayConfigUtil.APP_ID;
        String mch_id = PayConfigUtil.MCH_ID;
        String key = PayConfigUtil.API_KEY;
        //微信统一下单
        HashMap<String, String> data = new HashMap<>();
        data.put("body", "Sales business");
        data.put("out_trade_no", unifiedorder.getOut_trade_no());
        data.put("appid", appid);
        data.put("mch_id", mch_id);
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("total_fee", unifiedorder.getTotal_fee() + "");
        data.put("spbill_create_ip", unifiedorder.getSpbill_create_ip());
        data.put("notify_url", PayConfigUtil.NOTIFY_URL);
        data.put("trade_type", "JSAPI");
        data.put("openid", unifiedorder.getOpenid());
        data.put("sign", WXPayUtil.generateSignature(data, key, WXPayConstants.SignType.HMACSHA256));
        Map<String, String> r = wxPay.unifiedOrder(data);
        HashMap<String, String> packageParams = new HashMap<>();
        packageParams.put("appId", appid);
        packageParams.put("nonceStr", WXPayUtil.generateNonceStr());
        packageParams.put("timeStamp", WXPayUtil.getCurrentTimestamp() + "");
        //用于后续接口调用中使用,该值有效期为2小时
        packageParams.put("package", "prepay_id=" + r.get("prepay_id"));
        packageParams.put("signType", "HMAC-SHA256");
        String sign = WXPayUtil.generateSignature(packageParams, key, WXPayConstants.SignType.HMACSHA256);
        packageParams.put("paySign", sign);
        log.info("| packageParams: " + packageParams);
        return packageParams;
    }

  

  3、通过JS调用微信提供的jweixin-1.2.0.js WeixinJSBridge , 在微信浏览器里面打开H5网页中执行JS调起支付。接口输入输出数据格式为JSON。

   在这里我觉得需要注意一下total_fee这个参数,你不能以前端js这个的total_fee这总金额为标准,需要一起他的方式传输,根据你的需求来设置,你要是只是测试就随便了。

<script>
    var out_trade_no="${out_trade_no}";
    var total_fee="${total_fee}";
    var spbill_create_ip="${spbill_create_ip}";
    $(function(){

        $("#totalFee").text(parseFloat(total_fee).toFixed(2)/100);
        $('#payBtn').click(function(){
            $.ajax({
                url : "/wxPublicPay",
                type : "post",
                data :{
                    "out_trade_no":out_trade_no,
                    "spbill_create_ip":spbill_create_ip
                },
                dataType : 'json',
                success : function(msg) {
                    if (msg.result_code == 0){
                        pay(msg.data)
                    }
                },
                error : function(data) {
                    alert("服务器异常")
                }
            })
        });
        //吊起支付
        function pay(data) {
            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(data);
            }

        }
        //开始支付
        function onBridgeReady(data){
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
                    "appId" : data.appId,     //公众号名称,由商户传入
                    "timeStamp": data.timeStamp+"",         //时间戳,自1970年以来的秒数
                    "nonceStr" : data.nonceStr, //随机串
                    "package" : data.package,
                    "signType" : data.signType,         //微信签名方式:
                    "paySign" : data.paySign    //微信签名
                },
                function(res){
                    if(res.err_msg == "get_brand_wcpay_request:ok" ) {
                        alert("支付成功");  // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
                        window.location.href="/wechatPaySucess?total_fee="+total_fee;
                    }else if (res.err_msg == "get_brand_wcpay_request:cancel")  {
                        alert("已取消支付");
                    }else{
                        //支付失败
                        alert(res.err_msg)
                    }
                }
            );
        }
    })
</script>

  4、流程到这就差不多了,最后剩下一个是异步回调的事情了,支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。

  关键在于我们需要去处理这个信息,并给出应答。

  

@RequestMapping(value = "weChatReturnNotify")
    @ResponseBody
    public void wechatPaySyntony(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            BufferedReader reader = request.getReader();
            String line = "";
            StringBuffer inputString = new StringBuffer();
            PrintWriter writer = response.getWriter();
            while ((line = reader.readLine()) != null) {
                inputString.append(line);
            }
            if (reader != null) {
                reader.close();
            }
            if (!StringUtils.isEmpty(inputString.toString())) {
                WXPayResult wxPayResult = JdomParseXmlUtils.getWXPayResult(inputString.toString());
                if ("SUCCESS".equalsIgnoreCase(wxPayResult.getReturn_code())) {
                    SortedMap<String, String> parameters = new TreeMap<String, String>();
                    parameters.put("appid", wxPayResult.getAppid());
                    parameters.put("attach", wxPayResult.getAttach());
                    parameters.put("bank_type", wxPayResult.getBank_type());
                    parameters.put("cash_fee", wxPayResult.getCash_fee() + "");
                    parameters.put("fee_type", wxPayResult.getFee_type());
                    parameters.put("is_subscribe", wxPayResult.getIs_subscribe());
                    parameters.put("mch_id", wxPayResult.getMch_id());
                    parameters.put("nonce_str", wxPayResult.getNonce_str());
                    parameters.put("openid", wxPayResult.getOpenid());
                    parameters.put("out_trade_no", wxPayResult.getOut_trade_no());
                    parameters.put("result_code", wxPayResult.getResult_code());
                    parameters.put("return_code", wxPayResult.getReturn_code());
                    parameters.put("time_end", wxPayResult.getTime_end());
                    parameters.put("total_fee", wxPayResult.getTotal_fee() + "");
                    parameters.put("trade_type", wxPayResult.getTrade_type());
                    parameters.put("transaction_id", wxPayResult.getTransaction_id());
                    log.info("| out_trade_no: " + wxPayResult.getOut_trade_no() + " | wechat Success Back params:" + parameters);
                    // 反校验签名
                    String sign = WXPayUtil.generateSignature(parameters, PayConfigUtil.API_KEY);
                    log.info("回调得到的sign:"+wxPayResult.getSign());
                    log.info("回调参数的sign:"+sign);
                    //这个是判断你的验签和你之前给微信传的数据,微信服务器组合给你返回的服务器的验签,两者一致则表示成功。执行下一步操作
                    if (sign.equals(wxPayResult.getSign())) {
                        Map<String, String> param = new HashMap<>();
                        param.put("out_trade_no", parameters.get("out_trade_no"));
                        param.put("transaction_id", parameters.get("transaction_id"));
                        param.put("result_code", parameters.get("result_code"));
                        HttpUtil.doPost("请求url地址,通知其支付回调成功", param);
                        writer.write(HttpUtil.backWeixin("SUCCESS", "OK"));
                        log.info("| Successful callback to WeChat :" + param);
                    } else {
                        writer.write(HttpUtil.backWeixin("FAIL", "签名失败"));
                    }
                } else {
                    writer.write(HttpUtil.backWeixin("FAIL", wxPayResult.getReturn_msg()));
                }
                if (writer != null) {
                    writer.close();
                }
            } else {
                writer.write(HttpUtil.backWeixin("FAIL", "未获取到微信返回的结果"));
            }
        } catch (Exception e) {
            System.err.println("微信回调错误!!!");
        }

    }

  到此处你就要去发快递了。

  其实主要好好看一下安全性。根据微信提供的SDK,上网多看看一些坑你就知道啦,商户平台需要配置的东西还蛮多的。

  

  参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

  转载请注明出处:https://www.cnblogs.com/zhouguanglin/p/9283258.html