JAVAWEB开发的微信公众号H5支付

一切需求都是来源于业务需要,前一阵子做了微信扫码支付,的确相对PC用户来说方便了很多。但是如果手机下单,你总不能让用户自己扫自己吧?查看了一下文档,微信还是支持公众号内网页端调起支付(前提你必须有微信服务号并且申请了微信支付功能)。

由于公司目前使用的支付项目是由JAVA代码开发的,但是微信官方给出的demo中是没有JAVA版本的,只有PHP版本(PHP果然是世界上最好的语言)。

开场白可以略过,我们来看一下微信给出的业务流程时序图:
chapter7_4_1.png

咋一看,是不是很吓人,其实做过扫码支付,逻辑还是很简单的,我们需要开发的为红色标记出的。

虽然微信官网配图还是很详细的,这里我还要再理一遍思路。
用户选择商品-->{一大堆流程,后面细写}-->重定向到一个用户确认界面,包含商品信息、金额等一些信息-->用户输入密码后出来一个支付成功的页面-->用户点击完成回调通知。

请求生成订单,生成商户订单,获取用于openID,统一下单获取prepay_id参数,生成JSAPI页面调用的支付参数并签名。

一、请求生成订单
这里下单跟扫码支付调用的API是一样的,只是参数有所不同。
1)交易类型trade_type值必须为JSAPI(JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口)

2)trade_type=JSAPI时(即公众号支付)用户标识openId是必填参数,这里就涉及到获取的问题了,有些文章说不获取也可以下单。亲测,最起码我这里是不可以的。

首先前台调用微信http://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxx&redirect_uri=http://weixin.52itstyle.com/pay/weixinMobile/dopay&response_type=code&scope=snsapi_base&state="+xxxxx+"#wechat_redirect";
这时候后台你的dopay方法中会接受到一个code参数。
String code = request.getParameter("code");
然后根据这个code获取用户openid,具体方法请参考。

3)生成订单后获取prepay_id,这个是重点,然后根据https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 所给的参数进行加密。
最终将参数返回到前台,也就是url所给的方式进行支付操作。

流图不留种,菊花万人捅,所以最后附上一段代码:

/**
     * 预下单(对于已经产生的订单)
     * @Author    张志朋
     * @param request
     * @param response
     * @return
     * @throws Exception  String
     * @Date    2017年1月17日
     * 更新日志
     * 2017年1月17日 张志朋  首次创建
     *
     */
    @SuppressWarnings("rawtypes")
    @RequestMapping(value = "dopay")
    public String dopay(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //其实这里只要传递一个订单编号即可
        String state = request.getParameter("state");
        String[] split = state.split("CUT");
        String totalFee = split[0];
        String orderNo = split[1];
        //获取code 这个在微信支付调用时会自动加上这个参数 无须设置
        String code = request.getParameter("code");
        //获取用户openID(JSAPI支付必须传openid)
        String openId = MobileUtil.getOpenId(code);
        String notify_url = "http://weixin.52itstyle.com/pay/weixinMobile/WXPayBack";//回调接口
        String trade_type = "JSAPI";// 交易类型H5支付
        SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
        ConfigUtil.commonParams(packageParams);
        packageParams.put("body","测试微信H5支付");// 商品描述
        packageParams.put("out_trade_no", orderNo);// 商户订单号
        packageParams.put("total_fee", totalFee);// 总金额
        packageParams.put("spbill_create_ip", "127.0.0.1");// 发起人IP地址
        packageParams.put("notify_url", notify_url);// 回调地址
        packageParams.put("trade_type", trade_type);// 交易类型
        packageParams.put("openid", openId);//用户openID
        String sign = PayCommonUtil.createSign("UTF-8", packageParams,ConfigUtil.API_KEY);
        packageParams.put("sign", sign);// 签名
        String requestXML = PayCommonUtil.getRequestXml(packageParams);
        String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, requestXML);
        Map map = XMLUtil.doXMLParse(resXml);
        String returnCode = (String) map.get("return_code");
        String returnMsg = (String) map.get("return_msg");
        String url ="";
        if("SUCCESS".equals(returnCode)){
            String resultCode = (String) map.get("result_code");
            String errCodeDes = (String) map.get("err_code_des");
            if("SUCCESS".equals(resultCode)){
                //获取预支付交易会话标识
                String prepay_id = (String) map.get("prepay_id");
                String prepay_id2 = "prepay_id=" + prepay_id;
                String packages = prepay_id2;
                SortedMap<Object, Object> finalpackage = new TreeMap<Object, Object>();
                String timestamp = DateUtil.getTimestamp();
                String nonceStr = packageParams.get("nonce_str").toString();
                finalpackage.put("appId",  ConfigUtil.APP_ID);
                finalpackage.put("timeStamp", timestamp);
                finalpackage.put("nonceStr", nonceStr);
                finalpackage.put("package", packages);  
                finalpackage.put("signType", "MD5");
                //这里很重要  参数一定要正确 狗日的腾讯 参数到这里就成大写了
                //可能报错信息(支付验证签名失败 get_brand_wcpay_request:fail)
                sign = PayCommonUtil.createSign("UTF-8", finalpackage,ConfigUtil.API_KEY);
                url = "redirect:/weixinMobile/pay?timeStamp="
                        + timestamp
                        + "&nonceStr=" + nonceStr + "&package=" + packages
                        + "&signType=MD5" + "&paySign=" + sign
                        +"&appid="+ ConfigUtil.APP_ID;
            }else{
                LogUtil.info("订单号:"+orderNo+"错误信息:"+errCodeDes);
                url = "redirect:/weixinMobile/error?code=0";//该订单已支付
            }
        }else{
            LogUtil.info("订单号:"+orderNo+"错误信息:"+returnMsg);
            url = "redirect:/weixinMobile/error?code=1";//系统系统
        }
        return url;
    }

pay.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

String appId = request.getParameter("appid");
String timeStamp = request.getParameter("timeStamp");
String nonceStr = request.getParameter("nonceStr");
String packageValue = request.getParameter("package");
String paySign = request.getParameter("paySign");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>微信支付</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <link   rel="stylesheet" type="text/css" href="<%=basePath%>static/css/wxzf.css"/>
    <script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
  </head>
  <body>
    <div class="wenx_xx">
        <div class="mz">(这是一个最终付款确认页)</div>
        <div class="wxzf_price">¥11.90</div>
    </div>
    <div class="skf_xinf">
        <div class="all_w">
            <span class="bt">收款方</span> <span class="fr">科帮网</span>
        </div>
    </div>
    <a id="wx-pay" href="javascript:void(0);" onclick="callpay()" class="ljzf_but all_w">立即支付</a>
</body>
  <script type="text/javascript">
      function onBridgeReady(){
         WeixinJSBridge.invoke('getBrandWCPayRequest',{
                   "appId" : "<%=appId%>",
                   "timeStamp" : "<%=timeStamp%>",
                   "nonceStr" : "<%=nonceStr%>", 
                   "package" : "<%=packageValue%>",
                   "signType" : "MD5",
                   "paySign" : "<%=paySign%>" 
               },function(res){
                if(res.err_msg == "get_brand_wcpay_request:ok"){
                    alert("微信支付成功!");
                    //重定向跳转
                }else if(res.err_msg == "get_brand_wcpay_request:cancel"){  
                    alert("用户取消支付!");  
                }else{  
                    alert("支付失败!");  
                }  
            })
        }
          function callpay(){  
            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();
             }
         }
  </script>
</html>
爪哇笔记

作者: 小柒

出处: https://blog.52itstyle.vip

分享是快乐的,也见证了个人成长历程,文章大多都是工作经验总结以及平时学习积累,基于自身认知不足之处在所难免,也请大家指正,共同进步。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 如有问题, 可邮件(345849402@qq.com)咨询。