微信统一下单接口

 

 项目目前使用了微信公众平台的支付接口,遇到不少坑。其实仍是以为微信接口的API太散乱,作完了一套以后再看下,发现基本都有说明。没有java版本的demo。下面是本身的一些步骤思路;前端

  1. 步骤大致讲解:项目作的时候,是用户点击付款,获取预支付ID,在返回前台页面进行调用JSAPI,付款完成。因此第7-9步我先作了。若是按照图中步骤作也许能成功。

        

  • 付款详情页面 (有金额、付款对象、商品等信息)
  • 点击付款按钮,进入到后台
  • 将付款的参数、key、appid、mchid 等进行签名,以后组装为xml格式的字符串
  • 调用支付API并传递字符串。获得返回的预支付xml字符串,验证code
  • 将预支付ID以及必要的参数 返回到前台调用JSAPI进行付款。
  • 用户输入密码,提示付款完成
  • 微信调用回调通知地址
  • 通知地址返回成功信息
  • 结束

2.准备工做:java

  • appid:在微信公众平台获取
  • key:微信商户key(第一次的时候会出现签名无效,重置下key)
  • mch_id:商户ID

3.步骤详解git

  • 付款详情页面:
    $("#wxsubmit").on("click",function(){
    	
    	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();
    	}
    				
    	function onBridgeReady(){
    		$$.ajax({
    			type: "get",
    			async:"false",
    			url:  "pay?total_fee=" + pay_total_fee, //请求后台支付方法
    			dataType: "json",
    			success:function(result){
    			     var data = result.data;
    			     if(null == data.appId || data.code == "-100"){
    			          alert(data.msg);
    			     }
                     //获得预支付ID、其余必须值传递给微信
    			     WeixinJSBridge.invoke('getBrandWCPayRequest',{
    					"appId" : data.appId.trim(),
    					"timeStamp" : data.timeStamp.trim(),
    					"nonceStr" : data.nonceStr.trim(),
    					"package" : data.package.trim(),
    					"signType" :"MD5".trim(),
    					"paySign" : data.sign.trim()
    				},function(res){
    					 // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
    					if(res.err_msg == "get_brand_wcpay_request:ok" ) {
    					    view.router.back();
    					 }
    				})
    			}
    		});
    	}
    });

    支付方法:ajax

  • /**
         * 微信支付接口
         *
         * @param request
         * @param total_fee 支付金额
         * @return
         * @throws Exception
         */
        @ResponseBody
        @RequestMapping(value = "pay")
        public Map<String, Object> pay(HttpServletRequest request,
                                       @RequestParam(required = true) String total_fee) throws Exception {
            
            String appid =  "********"; 
            String key =  "********"; //商户号key
            String mch_id =  "********"; //商户号
            String openid = "********";// 用户在商户appid下的惟一标识
            String uuid = UUIDUtil.create32UUID(); //做为随机值串、商户订单号使用
            String product_name = "付款";
            String totalFee = 1;//获取提交的商品价格(分)
            Map<String, Object> sign; //用于两次MD5签名结果
    
            //参与签名的map
            Map<String, Object> signParams = new HashMap<>();
            signParams.put("attach", compId); //微信附带参数,给什么返回什么
            signParams.put("appid", appid);
            signParams.put("mch_id", mch_id);
            signParams.put("nonce_str", uuid);
            signParams.put("body", product_name); //商品描述
            signParams.put("out_trade_no", uuid); //商户订单号
            signParams.put("total_fee", totalFee); //商品总金额,以分为单位
            signParams.put("spbill_create_ip", request.getRemoteAddr()); //订单生成的机器IP,指用户浏览器端IP
            signParams.put("notify_url", prop.get("wx.NOTIFY_URL")); //通知地址
            signParams.put("trade_type", prop.get("wx.TRADE_TYPE")); //交易类型
            signParams.put("openid", openid);
    
            //MD5签名
            sign = createSign(signParams, key);
    
            //组装xml字符串
            StringBuffer xmlStr = new StringBuffer();
            xmlStr.append("<xml>");
            for (String str : new ArrayList<>(sign.keySet())) {
                xmlStr.append("<" + str + "><![CDATA[" + sign.get(str) + "]]></" + str + ">");
            }
            xmlStr.append("</xml>");
    
            try {
                //与微信支付创建链接,获取预支付ID(prepay_id),以便支付使用
                HttpURLConnection conn = (HttpURLConnection) new URL((String) prop.get("wx.URL")).openConnection();
                conn.setRequestMethod("POST");
                conn.setDoOutput(true); //是否进行输入输出 默认为false
                BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
    
                //向输出流中添加数据
                buffOutStr.write(xmlStr.toString().getBytes("UTF8"));
                buffOutStr.flush();
                buffOutStr.close();
    
                //获取微信返回xml字符串
                String sa = getStrFormInStream(conn.getInputStream());
    
                //将xml转换为Map
                Map<String, Object> map = XmlToMap.getMap(sa);
                //预支付ID,返回的code正确(根据微信文档return_code和result_code都为SUCCESS的时候才会返回code_url、prepay_id)
                if (!CollectionUtils.isEmpty(map)
                        && "SUCCESS".equals(map.get("return_code")) && "SUCCESS".equals(map.get("result_code"))) {
                    //设置支付参数
                    Map<String, Object> params = new TreeMap<>();
                    params.put("appId", appid);
                    params.put("nonceStr", map.get("nonce_str").toString().trim());
                    params.put("package", "prepay_id=" + map.get("prepay_id").toString().trim());
                    params.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000).trim());
                    params.put("signType", "MD5");
    
                    //第二次签名
                    sign.clear();
                    sign = createSign(params, key);
    
                    //返回结果到前台,并调用微信支付页面
                    return Response.success(sign);
                } else {
                    return Response.failure("获取预支付ID失败");
                }
            } catch (Exception e) {
               
            }
        }

    公用方法(支付方法中用到的通用方法):spring

    /**
         * 微信支付结果通知
         * @param request
         * @param response
         * @throws Exception
         */
        @SystemLog(module = "微信支付结果通知", methods = "/wx/notifyUrl.json", description = "", level = "1", type = "1")
        @RequestMapping(value = "/wx/notifyUrl.json", method = {RequestMethod.GET, RequestMethod.POST})
        public void notifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
            //获取微信返回结果xml字符串,并转换为map
            String result = getStrFormInStream(request.getInputStream());
            Map<String, Object> map = XmlToMap.getMap(result);
            saleService.pay(payRecordService, (String) map.get("attach"), (String) map.get("out_trade_no"));
            //TODO 测试是否有8次 通知 --  像一张表中加入一条数据 8条数据
            response.getWriter().print("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
        }
    
        /**
         * 将输入流中的字符转换为utf8编码的字符串
         *
         * @param inStream 输入流
         * @return uft8编码字符串
         * @throws IOException
         */
        private String getStrFormInStream(InputStream inStream) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
            String line;
            StringBuffer sb = new StringBuffer();
    
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            inStream.close();
            return new String(sb.toString().getBytes("gbk"), "utf-8");
        }
    
        /**
         * 建立md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
         *
         * @param params
         * @param key
         * @return
         */
        private Map<String, Object> createSign(Map<String, Object> params, String key) {
            List<String> list = new ArrayList<String>(params.keySet());
            Collections.sort(list);
            StringBuffer rs = new StringBuffer();
            for (String str : list) {
                String value = (String) params.get(str);
                if (null != value && !"".equals(value) && !"sign".equals(value)
                        && !"key".equals(value)) {
                    rs.append("&" + str + "=" + value);
                    logger.info("  " + str + " : " + value);
                }
            }
            params.put("sign", MD5Util.MD5Encode((rs.append("&key=") + key).toString().replaceFirst("&", ""), "UTF8").toUpperCase());
           
            return params;
        }

    基础类(公共方法中用的的类):json

  • MD5加密类:浏览器

    package ******;
    
    import java.security.MessageDigest;
    
    /**
     * @author JGS
     * @description MD5加密
     * @created 2016-09-08 15:49
     */
    public class MD5Util {
    
        private static String byteArrayToHexString(byte b[]) {
            StringBuffer resultSb = new StringBuffer();
            for (int i = 0; i < b.length; i++)
                resultSb.append(byteToHexString(b[i]));
    
            return resultSb.toString();
        }
    
        private static String byteToHexString(byte b) {
            int n = b;
            if (n < 0)
                n += 256;
            int d1 = n / 16;
            int d2 = n % 16;
            return hexDigits[d1] + hexDigits[d2];
        }
    
        public static String MD5Encode(String origin, String charsetname) {
            String resultString = null;
            try {
                resultString = new String(origin);
                MessageDigest md = MessageDigest.getInstance("MD5");
                if (charsetname == null || "".equals(charsetname))
                    resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
                else {
                    resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
                }
            } catch (Exception exception) {
            }
            return resultString;
        }
    
        private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
                "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
    
    }

    解析xml微信

  • package *****;
    
    import com.suncd.dev.util.BeanUtil;
    import org.springframework.util.StringUtils;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.SAXParserFactory;
    import java.io.IOException;
    import java.io.StringReader;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author JGS
     * @description 解析xml(key=xml的不解析)
     * @created 2016-09-12 11:12
     */
    
    public class XmlToMap extends DefaultHandler {
    
        private StringBuffer buf;
        private String str;
        private static Map<String, Object> map = new HashMap<>();
    
        public XmlToMap() {
            super();
        }
    
        public void startDocument() throws SAXException {
            buf = new StringBuffer();
            System.out.println("*******开始解析XML*******");
        }
    
        public void endDocument() throws SAXException {
            map.remove("xml");
            System.out.println("*******XML解析结束*******");
        }
    
        public void endElement(String namespaceURI, String localName, String fullName) throws SAXException {
            str = buf.toString();
            String value = str;
            if (!StringUtils.isEmpty(buf) && !fullName.equals("xml")) {
                map.put(fullName, value);
            }
            buf.delete(0, buf.length());
        }
    
        public void characters(char[] chars, int start, int length) throws SAXException {
            //将元素内容累加到StringBuffer中
            buf.append(chars, start, length);
            System.out.println(buf.toString());
        }
    
        /**
         * 将xml字符串转换为对象
         *
         * @param str       转换的XML字符串
         * @param beanClass 对象Class
         * @return
         * @throws javax.xml.parsers.ParserConfigurationException
         * @throws org.xml.sax.SAXException
         * @throws java.io.IOException
         */
        public static <T> T getObject(String str, Class<T> beanClass) throws IOException, SAXException, ParserConfigurationException {
            return (T) BeanUtil.map2Bean(getMap(str), beanClass);
        }
    
        public static Map getMap(String str) throws ParserConfigurationException, SAXException, IOException {
            XmlToMap xmlMap = new XmlToMap();
            SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(new StringReader(str)), xmlMap);
            return map;
        }
    }
相关文章
相关标签/搜索