微信公众号支付(服务商模式)

来源:互联网 发布:颜值评分软件 编辑:程序博客网 时间:2024/05/22 09:49

微信公众号支付(服务商模式)

  • 公众号支付官方文档地址

业务流程时序图:

这里写图片描述

第一步:

在H5页面点击支付后,生成订单,然后调用统一下单API后台生成预支付交易单

/**     * 生成预支付交易单     * @author      *  @version 创建时间:2017年3月6日  下午5:20:23     * @return     * @throws JDOMException     * @throws IOException     */    @SuppressWarnings("unchecked")    public String weixinPay() throws JDOMException, IOException {        if (state.indexOf(ServletActionContext.getRequest().getSession().getId()) == -1) {            return ERROR;        } else {            //获取用户IP            HttpServletRequest requests = ServletActionContext.getRequest();            String ip = requests.getHeader("X-Forwarded-For");            if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){                int index = ip.indexOf(",");                if(index != -1){                    ip= ip.substring(0,index);                }            }            //用code来获取用户的openid,替换为自己的获取用户openid的方法            JSONObject object = new JSONObject();            object = GetWxOpenId.codeGetWxOpenId(code, "gzh");            SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();            String mch_id = mchId;//服务商的商户号            String sub_mch_id="绑定的子商户号";//绑定的子商户号(二者必须有匹配关系)            String nonce_str = String.valueOf(System.currentTimeMillis());            String body = "商品描述";// body==商品描述==商品或支付单简要描述            String openid = object.getString("openid").toString();//替换为自己获取openid的方法            String code = PayUtil.createCode(5);//生成五位随机数            String out_trade_no =Long.toString(new Date().getTime())+code;// 商户订单号==商户系统内部的订单号,32个字符内、可包含字母            number=100;//替换为H5页面传来的支付金额            String total_fee = String.valueOf( (int)(Double.parseDouble(number)*100));// total_fee==总金额==订单总金额            String spbill_create_ip = ip;// APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。            String notify_url = "替换为自己项目的支付成功回调地址";// 通知地址,接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。            String trade_type = "JSAPI";// trade_type,交易类型取值如下:JSAPI,NATIVE,APP。我们这里使用的JSAPI。标题已经说了,是微信公众号支付。            String key = paykey;//服务商的key(服务商商户平台配置的key)            parameters.put("appid", appid);            parameters.put("sub_mch_id", sub_mch_id);            parameters.put("mch_id", mch_id);            parameters.put("nonce_str", nonce_str);            parameters.put("body", body);            parameters.put("out_trade_no", out_trade_no);            parameters.put("total_fee", total_fee);            parameters.put("spbill_create_ip", spbill_create_ip);            parameters.put("notify_url", notify_url);            parameters.put("trade_type", trade_type);            parameters.put("openid", openid);            String sign = PayCommonUtil.createSign("UTF-8", parameters,key);            parameters.put("sign", sign);            String requestXML = PayCommonUtil.getRequestXml(parameters);            String result =  CommonUtils.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST",requestXML);            Map<String, String> map = XMLUtil.doXMLParse(result);// 解析微信返回的预支付交易会话标识等信息,以Map形式存储便于取值            SortedMap<String, String> params = new TreeMap<String, String>();            params.put("appId", appid);            params.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));            params.put("nonceStr", nonce_str);            params.put("package", "prepay_id=" + map.get("prepay_id"));            params.put("signType", "MD5");            Map<String, String> sPara = PayUtil.paraFilter(params);            String prestr = PayUtil.createLinkString(sPara); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串            String mykey = "&key=" + key; // 商户支付密钥            String paySign = PayUtil.sign(prestr, mykey, "utf-8").toUpperCase();            params.put("paySign", paySign);             request.put("appId", appid);            request.put("timeStamp", params.get("timeStamp"));            request.put("nonceStr", nonce_str);            request.put("signType", "MD5");            request.put("packageValue", "prepay_id=" + map.get("prepay_id"));             request.put("paySign", paySign); // paySign的生成规则和Sign的生成规则一致            request.put("sendUrl", "付款成功后跳转的页面,我在这里没有用到"); // 付款成功后跳转的页面            request.put("detail", params);            request.put("paySn", paySn);            return SUCCESS;        }    }

第二步:

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head>        <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>        <script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>        <script type="text/javascript">        function onBridgeReady(){               WeixinJSBridge.invoke(                   'getBrandWCPayRequest', {                       "appId" : "${request.appId}",                         "timeStamp" : "${request.timeStamp}",//时间戳,自1970年以来的秒数                            "nonceStr" : "${request.nonceStr}", //随机串                            "package" : "${request.packageValue}",                            "signType" : "${request.signType}",//微信签名方式                            "paySign" : "${request.paySign}" //微信签名                    },                   function(res){                            // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。                       if(res.err_msg == "get_brand_wcpay_request:ok") {                           //替换为自己支付成功后的处理代码                       }                       else if(res.err_msg == "get_brand_wcpay_request:cancel"){                            //替换为自己支付取消的处理代码                       }                       else if(res.err_msg == "get_brand_wcpay_request:fail"){                           //替换为自己支付失败的处理代码                       }                   }               );             }            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><title>微信支付</title> </head>    <body>    </body></html>

后台处理涉及到的工具类

public class PayCommonUtil {    private static Logger log = LoggerFactory.getLogger(PayCommonUtil.class);    public static String CreateNoncestr(int length) {        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";        String res = "";        for (int i = 0; i < length; i++) {            Random rd = new Random();            res += chars.indexOf(rd.nextInt(chars.length() - 1));        }        return res;    }    public static String CreateNoncestr() {        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";        String res = "";        for (int i = 0; i < 16; i++) {            Random rd = new Random();            res += chars.charAt(rd.nextInt(chars.length() - 1));        }        return res;    }    /**     * @author      * @date      * @Description:sign签名     * @param characterEncoding 编码格式     * @param parameters 请求参数     * @return     */    public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters,String key){        StringBuffer sb = new StringBuffer();        Set es = parameters.entrySet();        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(), characterEncoding).toUpperCase();        return sign;    }    /**     * @author      * @date      * @Description:将请求参数转换为xml格式的string     * @param parameters  请求参数     * @return     */    public static String getRequestXml(SortedMap<Object,Object> parameters){        StringBuffer sb = new StringBuffer();        sb.append("<xml>");        Set es = parameters.entrySet();        Iterator it = es.iterator();        while(it.hasNext()) {            Map.Entry entry = (Map.Entry)it.next();            String k = (String)entry.getKey();            String v = (String)entry.getValue();            if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {                sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");            }else {                sb.append("<"+k+">"+v+"</"+k+">");            }        }        sb.append("</xml>");        return sb.toString();    }    /**     * @author     * @date      * @Description:返回给微信的参数     * @param return_code 返回编码     * @param return_msg  返回信息     * @return     */    public static String setXML(String return_code, String return_msg) {        return "<xml><return_code><![CDATA[" + return_code                + "]]></return_code><return_msg><![CDATA[" + return_msg                + "]]></return_msg></xml>";    }}public class CommonUtils{    /**     *      * @param requestUrl请求地址     * @param requestMethod请求方法     * @param outputStr参数     */    public static String httpRequest(String requestUrl,String requestMethod,String outputStr){        // 创建SSLContext        StringBuffer buffer=null;        try{        URL url = new URL(requestUrl);        HttpURLConnection conn = (HttpURLConnection) url.openConnection();        conn.setRequestMethod(requestMethod);        conn.setDoOutput(true);        conn.setDoInput(true);        conn.connect();        //往服务器端写内容        if(null !=outputStr){            OutputStream os=conn.getOutputStream();            os.write(outputStr.getBytes("utf-8"));            os.close();        }        // 读取服务器端返回的内容        InputStream is = conn.getInputStream();        InputStreamReader isr = new InputStreamReader(is, "utf-8");        BufferedReader br = new BufferedReader(isr);        buffer = new StringBuffer();        String line = null;        while ((line = br.readLine()) != null) {                      buffer.append(line);        }        }catch(Exception e){            e.printStackTrace();        }        return buffer.toString();        }       /**     *      * @param requestUrl请求地址     * @param requestMethod请求方法     * @param outputStr参数     */    public static String httpsRequest(String requestUrl,String requestMethod,String outputStr){        // 创建SSLContext        StringBuffer buffer=null;        try{        SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");        TrustManager[] tm = { new MyX509TrustManager() };        // 初始化        sslContext.init(null, tm, new java.security.SecureRandom());        // 获取SSLSocketFactory对象        SSLSocketFactory ssf = sslContext.getSocketFactory();        // TODO Auto-generated method stub        URL url = new URL(requestUrl);        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();        conn.setRequestMethod(requestMethod);        conn.setSSLSocketFactory(ssf);        conn.setDoOutput(true);        conn.setDoInput(true);        conn.connect();        //往服务器端写内容        if(null !=outputStr){            OutputStream os=conn.getOutputStream();            os.write(outputStr.getBytes("utf-8"));            os.close();        }        // 读取服务器端返回的内容        InputStream is = conn.getInputStream();        InputStreamReader isr = new InputStreamReader(is, "utf-8");        BufferedReader br = new BufferedReader(isr);        buffer = new StringBuffer();        String line = null;        while ((line = br.readLine()) != null) {                      buffer.append(line);        }        }catch(Exception e){            e.printStackTrace();        }        return buffer.toString();        }       //获取access_token 的请求地址        public final static String token_url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";        /**         * 获取access_token 公众号的唯一凭证         * @param appid         * @param appsecret         * @return         */        public static Token getAccessToken(String appid,String appsecret){            Token token=null;            //拼接请求地址            String requestUrl=token_url.replace("APPID", appid).replaceAll("APPSECRET", appsecret);            //调用接口            String jsonString=CommonUtil.httpsRequest(requestUrl, "GET", null);            try{            //将json字符串转换成java对象            JSONObject jsonObject=JSONObject.fromObject(jsonString);            String accessToken=jsonObject.getString("access_token");            int expiresIn=jsonObject.getInt("expires_in");            token=new Token();            token.setAccess_token(accessToken);            token.setExpires_in(expiresIn);            }catch(Exception e){                e.printStackTrace();            }            return token;        }    public static String urlEncodeUTF8(String source){        String result=source;        try {            result=java.net.URLEncoder.encode(source, "UTF-8");        } catch (UnsupportedEncodingException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return result;    }}

第三步:

支付成功后,微信服务器会向开发者设置的回调地址发送请求

/**     * 微信支付成功回调处理     * @author     *  @version 创建时间:2017年3月8日  下午6:19:26     * @throws IOException     * @throws DocumentException     * @throws ParseException     */    @SuppressWarnings("unchecked")    public void acquire() throws IOException, DocumentException, ParseException {        HttpServletRequest requests = ServletActionContext.getRequest();        // 解析结果存储在HashMap        Map<String, String> map = new HashMap<String, String>();        InputStream inputStream = requests.getInputStream();        // 读取输入流        SAXReader reader = new SAXReader();        Document document = reader.read(inputStream);        // 得到xml根元素        Element root = document.getRootElement();        // 得到根元素的所有子节点        List<Element> elementList = root.elements();        // 遍历所有子节点        for (Element e : elementList){            map.put(e.getName(), e.getText());        }        // 释放资源        inputStream.close();        inputStream = null;        // 返回状态码        String return_code = map.get("return_code");        // 返回信息        String return_msg = map.get("return_msg");        // 业务结果,判断交易是否成功        String result_code = map.get("result_code");        if(return_code=="SUCCESS"||return_code.equals("SUCCESS")){            if(result_code.equals("SUCCESS")){                //支付成功后的处理代码            }    }

以上是微信支付服务商模式下的支付,其中涉及到的一些参数可以去微信商户平台找到
商户平台可看到特约商户的商户号即sub_mch_id

代码如有错误,欢迎大家批评指正,多多交流


0 0
原创粉丝点击