精华内容
下载资源
问答
  • 2017年微信最新用于eclipse java开发调用微信统一接口。jar包齐全可以立马运行。只要参数正确就可以了
  • 这是我个人写的微信支付的代码,踩过不少坑啊!!!里面有两个微信支付接口实现的代码。分别是jsapi和native。代码里几乎每步都有详细的注释(我的注释写的很详细),...至于其它接口都可参照这两个接口来实现.......
  • 本篇文章主要给大家介绍调用微信公众支付的统一下单API,通过参数封装为xml格式并发送到微信给的接口地址就可以获得返回内容,需要的朋友可以参考下本
  • 因该接口需要商户系统中自己的订单编号,笔者先在后台生成订单后将订单编号传到了小程序端,通过wx.request传到后台并触发调用统一支付接口 调用接口流程: 1. 签名 a. 将官方文档中的必选项参数以键值对的形式放入...

    微信统一支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1

    因该接口需要商户系统中自己的订单编号,笔者先在后台生成订单后将订单编号传到了小程序端,通过wx.request传到后台并触发调用统一支付接口

    目录:

    微信小程序接入微信支付(一):大概流程与准备需知
    微信小程序接入微信支付(二):后台调用统一下单接口
    微信小程序接入微信支付(三):小程序端调用支付接口
    微信小程序接入微信支付(四):接收支付结果通知与沙箱测试

    调用接口流程:

    1. 签名
    a. 将官方文档中的必选项参数以键值对的形式放入Map中(”sign“值为空或先不放入)
    b. 将除sign的键值对按照属性名排序并拼接成字符串,并接上”key“的值
    c. 把该字符串进行MD5运算并转为大写,生成的值即为”sign“的值
    d. 把sign值对放入Map中

    2. 生成参数
    将整个Map转义为xml格式的字符串,该字符串为调用接口的参数

    3. 发送请求
    后台发送post请求到URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder,并传递上一步参数

    4. 处理回调结果
    处理微信返回的结果,如果各项无误的话我们会接收到"prepay_id"(注意微信返回的结果也是xml格式的字符串,需要我们处理成自己喜欢的形式,如Map)

    5. 返回支付接口所需参数到小程序端
    支付接口所需参数,以及生成签名所需参数请参考https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
    a. 将”prepay_id“拼接为”package“,与其他所需参数按照上述第一步中的签名方式生成签名,赋值给”paySign“
    b. 将参数传递到前台

    直接上代码

    java后端

    /**
    	 * 调用微信支付统一下单接口,获取prepay_id等信息
    	 * 获取到prepay_id后,向前台发送wx.requestPayment所需参数
    	 * @param order_code  订单编号
    	 * @param pay_money  订单金额
    	 * @return
    	 * @throws Exception 
    	 */
    	@GetMapping("/uniorder")
        @ResponseBody
    	public JSONObject uniOrder(@Param("order_code") String order_code, @Param("pay_money") Double pay_money,
    			@Param("openid") String openid) throws Exception {
    			
    		//微信支付统一下单接口用的参数先存放在Map中
    		Map<String, String> map = new HashMap<>();
    		map.put("appid", appId);
    		map.put("mch_id", mchId);
    		//用户标识 openid
    		map.put("openid", openid);
    		//订单编号
    		map.put("out_trade_no", order_code);
    		//订单支付金额,单位为分 String.valueOf((int)(pay_money * 100))
    		map.put("total_fee", String.valueOf((int)(pay_money * 100)));
    		//32位随机字符串 
    		map.put("nonce_str", getRandomString());
    		//支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP,写服务器IP即可
    		map.put("spbill_create_ip", spbillCreateIp);
    		//商品描述
    		map.put("body", new String(("RPH-ORDER").getBytes("utf-8")));
    		//异步接收微信支付结果通知的回调地址
    		map.put("notify_url", notifyUrl);
    		//支付类型 小程序取值如下:JSAPI
    		map.put("trade_type", tradeType);
    		
    		//生成符合规格的签名串
    		String stringSignTemp = getParamStr(map,realKey);  //realKey == key
    		//进行MD5运算,并转为大写,获得sign参数的值
    		String signValue = MD5(stringSignTemp.toString()).toUpperCase();
    		//把sign放入map中
    		map.put("sign",signValue);
    		
    		//当sign参数有值时
    		if (map.get("sign").trim().length()>0) {
    			//map转义为xml格式 
    			String requestParam = new String(mapToXml(map).getBytes(),"utf-8");
    			LOG.info("----uniorder requestParam: " + requestParam);
    			//发送请求
    			String result = sendPostParam(orderApplyUrl, requestParam);  //统一下单接口URL地址
    			LOG.info("----uniorder result: " + result);
    			//将返回的结果从xml字符串转义为map
    			Map<String, String> resultMap = xmlToMap(result);
    			//初步处理验证返回的结果
    			String return_code;
    	        if (resultMap.containsKey("return_code")) {
    	            return_code = resultMap.get("return_code");
    	        }
    	        else {
    	            throw new Exception(String.format("No `return_code` in XML: %s", result));
    	        }
    
    	        if (return_code.equals("FAIL")) {
    	        	throw new Exception("return_code : fail, the result xml string is :" + result);
    	        }
    	        else if (return_code.equals("SUCCESS")) {
    	        	//return_code和result_code都为SUCCESS时返回prepay_id
    	        	if (resultMap.get("result_code").equals("SUCCESS")) {
    		        	//验证签名。
    		    		//将返回结果生成符合规格的签名串
    		    		String stringResultSignTemp = getParamStr(resultMap,realKey);
    		    		//进行MD5运算,并转为大写,获得sign参数的值
    		    		String signResultValue = MD5(stringResultSignTemp.toString()).toUpperCase();
    		           if (signResultValue.equals(resultMap.get("sign"))) {
    		        	   //签名验证成功后向前台传送wx.requestPayment所需参数
    		        	   //所需参数位timeStamp, nonceStr, package, signType, paySign
    		        	   String timeStamp = String.valueOf(System.currentTimeMillis());
    		        	   String nonceStr = getRandomString();
    		        	   String packageStr = "prepay_id="+resultMap.get("prepay_id");
    		        	   
    		        	   //准备paySign签名
    		        	   Map<String, String> paymentMap = new HashMap<>();
    		        	   paymentMap.put("appId", appId);
    		        	   paymentMap.put("timeStamp", timeStamp);
    		        	   paymentMap.put("nonceStr", nonceStr);
    		        	   paymentMap.put("package", packageStr);
    		        	   paymentMap.put("signType", "MD5");
    		        	   //生成符合规格的签名串
    		        	   String stringPaySignTemp = getParamStr(paymentMap,realKey);
    		        	   //进行MD5运算,并转为大写,获得paySign参数的值
    			       	   String paySignValue = MD5(stringPaySignTemp.toString()).toUpperCase();
    			       	   
    			       	   //把值放入json对象传给前台
    			       	   JSONObject payInfo = new JSONObject();
    			       	   payInfo.put("timeStamp", timeStamp);
    			       	   payInfo.put("nonceStr", nonceStr);
    			       	   payInfo.put("package", packageStr);
    			       	   payInfo.put("paySign", paySignValue);
    		        	   
    		               return payInfo;
    		           }
    		           else {
    		               throw new Exception(String.format("Invalid sign value in XML: %s", result));
    		           }
    	        		
    	        	}else {
    	        		return null;
    	        	}
    	        }
    	        else {
    	            throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, result));
    	        }
    		}
    			
    		return null;
    	}
    

    上述代码中用到的方法,在后续会一直用到

    /**
    	 * 生成32位随机字符串
    	 * @return
    	 */
    	public static String getRandomString() {
    		String temp = "123456789qazwsxedcrfvtgbyhnujmikolp";
    		Random random=new Random(); 
    		StringBuffer sb=new StringBuffer();
    		for(int i = 0; i< 32; i++) {
    		  int number=random.nextInt(32);
    		  sb.append(temp.charAt(number));
    		}
    		return sb.toString().toUpperCase();		
    	}
    	
    	/**
    	 * 将Map转换为签名字符串
    	 * @param map
    	 * @return
    	 */
        private static String getParamStr(Map<String, String> map, String key) {
            Set<String> keySet = map.keySet();
            String[] keyArray = keySet.toArray(new String[keySet.size()]);
            Arrays.sort(keyArray);
            StringBuilder sb = new StringBuilder();
            //将除sign的其他非空参数按照ASCII码排序,并拼接位URL键值对形式的字符串
            for (String k : keyArray) {
            	//遇到sign参数跳过不处理
            	if (k.equals("sign")) {
            		continue;
            	}
                if (map.get(k).trim().length() > 0) // 参数值为空,则不参与签名
                    sb.append(k).append("=").append(map.get(k).trim()).append("&");
            }
            //结尾拼接key
            sb.append("key=").append(key);
            return sb.toString();
        }
        
        /**
         * 生成 MD5
         *
         * @param data 待处理数据
         * @return MD5结果
         */
        public static String MD5(String data) throws Exception {
            java.security.MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString().toUpperCase();
        }
    	
        /**
         * 将Map转换为XML格式的字符串
         *
         * @param data Map类型数据
         * @return XML格式的字符串
         * @throws Exception
         */
        public static String mapToXml(Map<String, String> data) throws Exception {
        	DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            org.w3c.dom.Document document = documentBuilderFactory.newDocumentBuilder().newDocument();
            //document.setXmlStandalone(true);
            org.w3c.dom.Element root = document.createElement("xml");
            document.appendChild(root);
            for (String key: data.keySet()) {
                String value = data.get(key);
                if (value == null) {
                    value = "";
                }
                value = value.trim();
                org.w3c.dom.Element filed = document.createElement(key);
                filed.appendChild(document.createTextNode(value));
                root.appendChild(filed);
            }
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            DOMSource source = new DOMSource(document);
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        	//transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
            StringWriter writer = new StringWriter();
            StreamResult result = new StreamResult(writer);
            transformer.transform(source, result);
            String output = writer.getBuffer().toString(); 
            try {
                writer.close();
            }
            catch (Exception ex) {
            }
            return output;
        	
        }
        
        /**
         * 发送post请求
         *
         * @param url   地址
         * @param param 参数 name1=value1&name2=value2
         * @return
         */
        public static String sendPostParam(String url, String param) {
            String result = "";
            try {
                URL realUrl = new URL(url);
                //打开url连接
                HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
                //设置通用请求属性
                connection.setRequestMethod("POST");
                connection.setConnectTimeout(3000);
                connection.setRequestProperty("accept", "*/*");
                connection.setRequestProperty("connection", "Keep-Alive");
                connection.setRequestProperty("user-agent",
                        "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36");
                // 发送POST请求必须设置如下两行
                connection.setDoOutput(true);
                connection.setDoInput(true);
                // 获取URLConnection对象对应的输出流
                PrintWriter pw = new PrintWriter(connection.getOutputStream());
                // 发送请求参数
                pw.print(param);
                // flush输出流的缓冲
                pw.flush();
                // 定义BufferedReader输入流来读取URL的响应
                BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String s;
                StringBuffer stringBuffer = new StringBuffer();
                while ((s = br.readLine()) != null) {
                    stringBuffer.append(s);
                }
                result = stringBuffer.toString();
                //关闭输入输出流
                br.close();
                pw.close();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        }
        
        /**
         * 将XML格式的字符串转化为Map
         * 
         * @param xml格式字符串
         * @return map
         */
        public static Map<String, String> xmlToMap(String strXML) throws Exception {
            try {
                Map<String, String> data = new HashMap<String, String>();
            	DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
                org.w3c.dom.Document doc = documentBuilder.parse(stream);
                doc.getDocumentElement().normalize();
                NodeList nodeList = doc.getDocumentElement().getChildNodes();
                for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                    Node node = nodeList.item(idx);
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                        data.put(element.getNodeName(), element.getTextContent());
                    }
                }
                try {
                    stream.close();
                } catch (Exception ex) {
                    // do nothing
                }
                return data;
            } catch (Exception ex) {
            	Logger logger = LoggerFactory.getLogger("wxpay java");
            	logger.warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
                throw ex;
            }
    
        }
    

    小程序端

    //触发后台调用微信支付统一下单接口
    wx.request({
        url: app.globalData.url+'/neigou/pay/uniorder',
        method: 'get',
        data: {
          order_code: res.data.order_code,
          pay_money: that.data.order.pay_money,
          openid: that.data.userInfo.openid
        },
        success: function(r){
          console.log(r.data)
          //返回结果如果不为空
          if(r.data != null && r.data.paySign != null){
              //调用微信支付
          }else{
              wx.showToast({
                title: '统一下单出现异常',
                icon: 'none'
              })
          }
        }
      })
    
    展开全文
  • java调用微信统一下单接口 今天项目中对接微信支付这一块,记录一下 这是对接微信支付的开发文档地址 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3 流程分析: 我们可以看到...

    java调用微信小程序统一下单接口

    今天项目中对接微信支付这一块,记录一下

    这是对接微信支付的开发文档地址

    https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3

    微信支付流程


    流程分析:

    我们可以看到,前端人员只用给我们一个code,我们通过code去获取用户openId,然后生成订单,再调用微信支付统一下单API,微信返回给我们一个prepay_id,我们封装起来一起返给前端即可,前端支付成功了,微信会发送通知给我们的回调地址,我们更新订单状态即可,大致流程就是这样,这篇文章主要讲的就是调用统一下单API返回prepay_id的过程

    代码部分:

        /**
         * 支付接口
         *
         * @param code     前端传的只能用一次的code
         * @param oderInfo 订单信息接口
         * @param response 响应
         * @return ResultInfo 统一结果集
         */
        @RequestMapping(value = "/weChat/getPrePayId/{code}")
        @ResponseBody
        @ApiOperation(value = "调用微信支付统一下单接口", notes = "调用微信支付统一下单接口", httpMethod = "POST")
        public ResultInfo getPrePayId(@PathVariable(value = "code") String code,
                                              @ApiParam(name = "oderInfo", value = "订单详细信息", required = true)
                                              @RequestBody OrderBO oderInfo,
                                              HttpServletResponse response) {
            response.addHeader("Access-Control-Allow-Origin", "*");
    
            ResultInfo resultInfo = new ResultInfo();
    
            //通过code获取openId
            JSONObject openIdResult;
            try {
    
                openIdResult = (JSONObject) WechatUtil.getMpAccessToken(code);
                if (null == openIdResult) {
    
                    resultInfo.setMsg("结果解析为空!");
                    resultInfo.setCode(15115);
                    return resultInfo;
                }
            } catch (JSONException e) {
    
                resultInfo.setMsg("结果解析不正确!");
                resultInfo.setCode(15114);
                return resultInfo;
            }
    
            String openId = openIdResult.getString("openid");
            if (StringUtil.isEmpty(openId)) {
    
                resultInfo.setMsg("获取用户openId出错,获得空的openId!");
                resultInfo.setCode(15114);
                return resultInfo;
            }
            //生成订单
            //这块我就不贴出来了,毕竟涉及到具体的业务了
    
    
    
            SortedMap<String, String> params = new TreeMap<>();
            //生成32位随机数
            String nonce_str = create_nonce_str().replace("-", "");
            String timestamp = create_timestamp();
            //商户的秘钥,在https://pay.weixin.qq.com 中获取
            String apiKey = ConstantWeChat.API_KEY;
    
            //小程序ID
            params.put("appid", ConstantWeChat.APPID);
            //商户号
            params.put("mch_id", ConstantWeChat.MCH_ID);
            //openId
            params.put("openid", openId);
            //商品描述
            params.put("body", "APP body");
            //随机字符串,要求在32位以内
            params.put("nonce_str", nonce_str);
            //回调地址
            params.put("notify_url","http://");
            //订单号
            params.put("out_trade_no", oderInfo.getOrderId());
            //终端ip,调用微信支付API的机器IP,即本机的ip
            params.put("spbill_create_ip", "");
            //支付金额,total_fee传的单位是分,不能有小数点
            int totalFee = (int) (oderInfo.getPayAmount() * 100);
            params.put("total_fee", String.valueOf(totalFee));
            //交易类型,小程序取值如下:JSAPI
            params.put("trade_type", "JSAPI");
            //生成签名
            String sign = WechatUtil.createSign(params, apiKey);
            params.put("sign", sign);
    
            String prepayId = null;
            String wxResult;
    
            try {
    
                // 将post的数据转换从xml格式
                String xmlObj = WechatUtil.parseXML(params);
                wxResult = WechatUtil.sendPost(HttpClients.createDefault(),
                        "https://api.mch.weixin.qq.com/pay/unifiedorder", xmlObj);
    
    
                // 解析xml获取prepayId
                // 记得引入dom4j的依赖
                Document doc = DocumentHelper.parseText(wxResult);
                //objMap 的大小为10的时候就是调用成功了
                Map<String, Object> objMap = WechatUtil.Dom2Map(doc);
                String returnCode = (String) objMap.get("return_code");
                String resultCode = (String) objMap.get("result_code");
                if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
                    prepayId = (String) objMap.get("prepay_id");
                }
                params.put("package", "prepay_id=" + prepayId);
                params.put("timestamp", timestamp);
    
            } catch (Exception e) {
    
                resultInfo.setMsg("解析错误!");
                resultInfo.setCode(15114);
                return resultInfo;
            }
            if (null == prepayId) {
    
                resultInfo.setMsg("解析有误!");
                resultInfo.setCode(15114);
                return resultInfo;
            }
    
    
            SortedMap<String, String> params2 = new TreeMap<>();
    
            params2.put("appId", params.get("appid"));
            params2.put("timeStamp", params.get("timestamp"));
            params2.put("nonceStr", params.get("nonce_str"));
            params2.put("package", "prepay_id=" + prepayId);
            params2.put("signType", "MD5");
            String sign2 = WechatUtil.createSign(params2, apiKey);
            params2.put("paySign", sign2);
    
            resultInfo.setData(params2);
            resultInfo.setMsg("获取准备ID成功!");
    
            return resultInfo;
        }
    
    
        private static String create_nonce_str() {
            return UUID.randomUUID().toString();
        }
    
        private static String create_timestamp() {
            return Long.toString(System.currentTimeMillis() / 1000);
    
        }
    

    用到的包

    package com.miao.api.controller;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONException;
    import com.alibaba.fastjson.JSONObject;
    import com.miao.common.utils.ResultInfo;
    import com.miao.common.utils.StringUtil;
    import io.swagger.annotations.ApiOperation;
    import io.swagger.annotations.ApiParam;
    import org.apache.commons.lang.RandomStringUtils;
    import org.apache.http.impl.client.HttpClients;
    import org.dom4j.Document;
    import org.dom4j.DocumentHelper;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpServletResponse;
    import java.util.Map;
    import java.util.SortedMap;
    import java.util.TreeMap;
    import java.util.UUID;
    

    dom4j依赖

            <!--dom4j-->
            <dependency>
                <groupId>dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>1.6.1</version>
            </dependency>
    

    这是WechatUtil里面的方法

    /**
    	 * 生成微信支付签名
    	 *
    	 * @param parameters 参数
    	 * @param apiKey     商户秘钥
    	 * @return String
    	 */
    	public static String createSign(SortedMap<String, String> parameters,
    									String apiKey) {
    
    		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 + "&");
    			}
    		}
    		// 最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8
    		sb.append("key=" + apiKey);
    		try {
    			String sign = MD5.encrypt(sb.toString(), "UTF-8").toUpperCase();
    			return sign;
    		} catch (Exception e) {
    
    		}
    
    		return null;
    	}
    
    	public static String parseXML(SortedMap<String, String> 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 (null != v && !"".equals(v) && !"appkey".equals(k)) {
    
    				sb.append("<" + k + ">" + parameters.get(k) + "</" + k + ">\n");
    			}
    		}
    		sb.append("</xml>");
    		return sb.toString();
    	}
    public static String sendPost(CloseableHttpClient httpclient, String url, String xmlObj) throws IOException {
    		String result;
    		HttpPost httpPost = new HttpPost(url);
    		// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
    		StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
    		httpPost.addHeader("Content-Type", "text/xml");
    		httpPost.setEntity(postEntity);
    		log.info(xmlObj);
    		try {
    			CloseableHttpResponse response = httpclient.execute(httpPost);
    			try {
    				HttpEntity entity = response.getEntity();
    				result = EntityUtils.toString(entity, "UTF-8");
    			} finally {
    				response.close();
    			}
    		} finally {
    			httpclient.close();
    		}
    		return result;
    	}
    
    public static Map<String, Object> Dom2Map(Document doc) {
    		Map<String, Object> map = new HashMap<>(16);
    		if (doc == null) {
    			return map;
    		}
    		Element root = doc.getRootElement();
    		for (Iterator iterator = root.elementIterator(); iterator.hasNext(); ) {
    			Element e = (Element) iterator.next();
    			List list = e.elements();
    			if (list.size() > 0) {
    				map.put(e.getName(), Dom2Map(e));
    			} else {
    				map.put(e.getName(), e.getText());
    			}
    		}
    		return map;
    	}
    

    参数校验地址:

    //检验发送参数是否规范的地址
    https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1
    

    提醒:

    sign参数必须要在最后面生成,否则将生成错误的sign

    觉得还可以的话可以的话点个赞呗

    展开全文
  • 统一下单时候的请求对象,需要把这个转为xml 文件格式所以需要在pom.xml 文件中导入 <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId>...

    首先插入微信支付的时序图
    在这里插入图片描述
    统一下单时候的请求对象,需要把这个转为xml 文件格式所以需要在pom.xml 文件中导入 、和微信支付的sdk

    <dependency>     
    	<groupId>com.github.wxpay</groupId>     
    	<artifactId>wxpay-sdk</artifactId>     
    	<version>0.0.3</version> 
    </dependency>
    
    <deendency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.4.10</version>
    </dependency>
    

    微信统一下单接口需要的所有参数可以微信官方查看 微信官方统一下单接口

    @XmlRootElement(name = "xml")
    public class UnifiedOrderRequest {
        private java.lang.String appid;//微信分配的小程序IDwxd678efh567hg6787,NotNull
        private java.lang.String mch_id;//微信支付分配的商户号1230000109,NotNull
        private java.lang.String device_info;//自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"
        private java.lang.String nonce_str;//随机字符串,必须,NotNull
        private java.lang.String sign;//签名,必须,,
        private java.lang.String sign_type;//"HMAC-SHA256"或者"MD5",非必须,默认MD5
        private java.lang.String body;//商品简单描述,必须
        private java.lang.String detail;//商品详细列表,使用json格式
        private java.lang.String attach;//附加数据,如"贵阳分店",非必须(1,代表采购订单  2,代表租凭酒机订单)
        private java.lang.String out_trade_no;//商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
        private java.lang.String fee_type;//默认人民币:CNY,非必须
        private Integer total_fee;//订单金额,单位分,必须
        private java.lang.String spbill_create_ip;//支付提交客户端IP,如“123.123.123.123”,必须
        private java.lang.String time_start;//订单生成时间,格式为yyyyMMddHHmmss,如20170324094700,非必须
        private java.lang.String time_expire;//订单结束时间,格式同上,非必须
        private java.lang.String goods_tag;//订单优惠标记,代金券或立减优惠功能的参数,非必须
        private java.lang.String notify_url;//接收微信支付异步通知回调地址,不能携带参数,必须
        private java.lang.String trade_type;//交易类型,小程序写"JSAPI",必须
        private java.lang.String product_id;//商品ID trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
        private java.lang.String limit_pay;//限制某种支付方式,非必须
        private java.lang.String openid;//微信用户唯一标识,必须
        private String scene_info;//h5支付必须  (场景信息)
    
        public UnifiedOrderRequest(java.lang.String openid, java.lang.String appid, java.lang.String mchId, java.lang.String deviceId,
                                   java.lang.String nonceStr, java.lang.String body, java.lang.String outTradeNo, Integer totalFee,
                                   java.lang.String spbillCreateIp, java.lang.String notifyUrl, java.lang.String tradeType, java.lang.String productId) {
            this.openid = openid;
            this.appid = appid;
            this.mch_id = mchId;
            this.device_info = deviceId;
            this.nonce_str = nonceStr;
            this.body = body;
            this.out_trade_no = outTradeNo;
            this.total_fee = totalFee;
            this.spbill_create_ip = spbillCreateIp;
            this.notify_url = notifyUrl;
            this.trade_type = tradeType;
            this.product_id = productId;
        }
    
        public UnifiedOrderRequest(java.lang.String appid, java.lang.String machid, java.lang.String nonceStr, java.lang.String body, java.lang.String outTradeNo, Integer totalFee, java.lang.String spbillCreateIp, java.lang.String notifyUrl, java.lang.String trade_type, String string) {
            this.appid = appid;
            this.mch_id = machid;
            this.nonce_str = nonceStr;
            this.body = body;
            this.out_trade_no = outTradeNo;
            this.total_fee = totalFee;
            this.spbill_create_ip = spbillCreateIp;
            this.notify_url = notifyUrl;
            this.trade_type = trade_type;
            this.scene_info = string;
        }
    
    
        public String getScene_info() {
            return scene_info;
        }
    
        public void setScene_info(String scene_info) {
            this.scene_info = scene_info;
        }
    
        public java.lang.String getAppid() {
            return appid;
        }
    
        public void setAppid(java.lang.String appid) {
            this.appid = appid;
        }
    
        public java.lang.String getMch_id() {
            return mch_id;
        }
    
        public void setMch_id(java.lang.String mch_id) {
            this.mch_id = mch_id;
        }
    
        public java.lang.String getDevice_info() {
            return device_info;
        }
    
        public void setDevice_info(java.lang.String device_info) {
            this.device_info = device_info;
        }
    
        public java.lang.String getNonce_str() {
            return nonce_str;
        }
    
        public void setNonce_str(java.lang.String nonce_str) {
            this.nonce_str = nonce_str;
        }
    
        public java.lang.String getSign() {
            return sign;
        }
    
        public void setSign(java.lang.String sign) {
            this.sign = sign;
        }
    
        public java.lang.String getSign_type() {
            return sign_type;
        }
    
        public void setSign_type(java.lang.String sign_type) {
            this.sign_type = sign_type;
        }
    
        public java.lang.String getBody() {
            return body;
        }
    
        public void setBody(java.lang.String body) {
            this.body = body;
        }
    
        public java.lang.String getDetail() {
            return detail;
        }
    
        public void setDetail(java.lang.String detail) {
            this.detail = detail;
        }
    
        public java.lang.String getAttach() {
            return attach;
        }
    
        public void setAttach(java.lang.String attach) {
            this.attach = attach;
        }
    
        public java.lang.String getOut_trade_no() {
            return out_trade_no;
        }
    
        public void setOut_trade_no(java.lang.String out_trade_no) {
            this.out_trade_no = out_trade_no;
        }
    
        public java.lang.String getFee_type() {
            return fee_type;
        }
    
        public void setFee_type(java.lang.String fee_type) {
            this.fee_type = fee_type;
        }
    
        public Integer getTotal_fee() {
            return total_fee;
        }
    
        public void setTotal_fee(Integer total_fee) {
            this.total_fee = total_fee;
        }
    
        public java.lang.String getSpbill_create_ip() {
            return spbill_create_ip;
        }
    
        public void setSpbill_create_ip(java.lang.String spbill_create_ip) {
            this.spbill_create_ip = spbill_create_ip;
        }
    
        public java.lang.String getTime_start() {
            return time_start;
        }
    
        public void setTime_start(java.lang.String time_start) {
            this.time_start = time_start;
        }
    
        public java.lang.String getTime_expire() {
            return time_expire;
        }
    
        public void setTime_expire(java.lang.String time_expire) {
            this.time_expire = time_expire;
        }
    
        public java.lang.String getGoods_tag() {
            return goods_tag;
        }
    
        public void setGoods_tag(java.lang.String goods_tag) {
            this.goods_tag = goods_tag;
        }
    
        public java.lang.String getNotify_url() {
            return notify_url;
        }
    
        public void setNotify_url(java.lang.String notify_url) {
            this.notify_url = notify_url;
        }
    
        public java.lang.String getTrade_type() {
            return trade_type;
        }
    
        public void setTrade_type(java.lang.String trade_type) {
            this.trade_type = trade_type;
        }
    
        public java.lang.String getProduct_id() {
            return product_id;
        }
    
        public void setProduct_id(java.lang.String product_id) {
            this.product_id = product_id;
        }
    
        public java.lang.String getLimit_pay() {
            return limit_pay;
        }
    
        public void setLimit_pay(java.lang.String limit_pay) {
            this.limit_pay = limit_pay;
        }
    
        public java.lang.String getOpenid() {
            return openid;
        }
    
        public void setOpenid(java.lang.String openid) {
            this.openid = openid;
        }
    
        public UnifiedOrderRequest() {
        }
    
        public UnifiedOrderRequest(java.lang.String appid, java.lang.String mch_id, java.lang.String device_info, java.lang.String nonce_str, java.lang.String sign, java.lang.String sign_type, java.lang.String body, java.lang.String detail, java.lang.String attach, java.lang.String out_trade_no, java.lang.String fee_type, Integer total_fee, java.lang.String spbill_create_ip, java.lang.String time_start, java.lang.String time_expire, java.lang.String goods_tag, java.lang.String notify_url, java.lang.String trade_type, java.lang.String product_id, java.lang.String limit_pay, java.lang.String openid) {
            this.appid = appid;
            this.mch_id = mch_id;
            this.device_info = device_info;
            this.nonce_str = nonce_str;
            this.sign = sign;
            this.sign_type = sign_type;
            this.body = body;
            this.detail = detail;
            this.attach = attach;
            this.out_trade_no = out_trade_no;
            this.fee_type = fee_type;
            this.total_fee = total_fee;
            this.spbill_create_ip = spbill_create_ip;
            this.time_start = time_start;
            this.time_expire = time_expire;
            this.goods_tag = goods_tag;
            this.notify_url = notify_url;
            this.trade_type = trade_type;
            this.product_id = product_id;
            this.limit_pay = limit_pay;
            this.openid = openid;
        }
    
        @Override
        public java.lang.String toString() {
            return "UnifiedOrderRequest{" +
                    "appid='" + appid + '\'' +
                    ", mch_id='" + mch_id + '\'' +
                    ", device_info='" + device_info + '\'' +
                    ", nonce_str='" + nonce_str + '\'' +
                    ", sign='" + sign + '\'' +
                    ", sign_type='" + sign_type + '\'' +
                    ", body='" + body + '\'' +
                    ", detail='" + detail + '\'' +
                    ", attach='" + attach + '\'' +
                    ", out_trade_no='" + out_trade_no + '\'' +
                    ", fee_type='" + fee_type + '\'' +
                    ", total_fee=" + total_fee +
                    ", spbill_create_ip='" + spbill_create_ip + '\'' +
                    ", time_start='" + time_start + '\'' +
                    ", time_expire='" + time_expire + '\'' +
                    ", goods_tag='" + goods_tag + '\'' +
                    ", notify_url='" + notify_url + '\'' +
                    ", trade_type='" + trade_type + '\'' +
                    ", product_id='" + product_id + '\'' +
                    ", limit_pay='" + limit_pay + '\'' +
                    ", openid='" + openid + '\'' +
                    '}';
        }
    }
    

    微信支付统一下单的响应字段 可以通过 微信官方统一下单接口

    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement(name = "xml")
    public class UnifiedOrderResponse {
        private String return_code;//返回状态码
        private String return_msg;//返回信息
        private String appid;//小程序ID
        private String mch_id;//商户号
        private String device_info;//设备号
        private String nonce_str;//随机字符串
        private String sign;//签名
        private String result_code;//业务结果
        private String err_code;//错误代码
        private String err_code_des;//错错误代码描述
        private String trade_type;//交易类型
        private String prepay_id;//预支付交易会话标识
        private String code_url;//二维码链接
        private String mweb_url;//支付跳转链接	(h5支付返回)
    
        public String getMweb_url() {
            return mweb_url;
        }
    
        public void setMweb_url(String mweb_url) {
            this.mweb_url = mweb_url;
        }
    
        public String getReturn_code() {
            return return_code;
        }
    
        public void setReturn_code(String return_code) {
            this.return_code = return_code;
        }
    
        public String getReturn_msg() {
            return return_msg;
        }
    
        public void setReturn_msg(String return_msg) {
            this.return_msg = return_msg;
        }
    
        public String getAppid() {
            return appid;
        }
    
        public void setAppid(String appid) {
            this.appid = appid;
        }
    
        public String getMch_id() {
            return mch_id;
        }
    
        public void setMch_id(String mch_id) {
            this.mch_id = mch_id;
        }
    
        public String getDevice_info() {
            return device_info;
        }
    
        public void setDevice_info(String device_info) {
            this.device_info = device_info;
        }
    
        public String getNonce_str() {
            return nonce_str;
        }
    
        public void setNonce_str(String nonce_str) {
            this.nonce_str = nonce_str;
        }
    
        public String getSign() {
            return sign;
        }
    
        public void setSign(String sign) {
            this.sign = sign;
        }
    
        public String getResult_code() {
            return result_code;
        }
    
        public void setResult_code(String result_code) {
            this.result_code = result_code;
        }
    
        public String getErr_code_des() {
            return err_code_des;
        }
    
        public void setErr_code_des(String err_code_des) {
            this.err_code_des = err_code_des;
        }
    
        public String getTrade_type() {
            return trade_type;
        }
    
        public void setTrade_type(String trade_type) {
            this.trade_type = trade_type;
        }
    
        public String getPrepay_id() {
            return prepay_id;
        }
    
        public void setPrepay_id(String prepay_id) {
            this.prepay_id = prepay_id;
        }
    
        public String getCode_url() {
            return code_url;
        }
    
        public void setCode_url(String code_url) {
            this.code_url = code_url;
        }
    
        public String getErr_code() {
            return err_code;
        }
    
        public void setErr_code(String err_code) {
            this.err_code = err_code;
        }
    
        public UnifiedOrderResponse() {
        }
    
        public UnifiedOrderResponse(String return_code, String return_msg, String appid, String mch_id, String device_info, String nonce_str, String sign, String result_code, String err_code_des, String trade_type, String prepay_id, String code_url) {
            this.return_code = return_code;
            this.return_msg = return_msg;
            this.appid = appid;
            this.mch_id = mch_id;
            this.device_info = device_info;
            this.nonce_str = nonce_str;
            this.sign = sign;
            this.result_code = result_code;
            this.err_code_des = err_code_des;
            this.trade_type = trade_type;
            this.prepay_id = prepay_id;
            this.code_url = code_url;
        }
    
        @Override
        public String toString() {
            return "UnifiedOrderResponse{" +
                    "return_code='" + return_code + '\'' +
                    ", return_msg='" + return_msg + '\'' +
                    ", appid='" + appid + '\'' +
                    ", mch_id='" + mch_id + '\'' +
                    ", device_info='" + device_info + '\'' +
                    ", nonce_str='" + nonce_str + '\'' +
                    ", sign='" + sign + '\'' +
                    ", result_code='" + result_code + '\'' +
                    ", err_code='" + err_code + '\'' +
                    ", err_code_des='" + err_code_des + '\'' +
                    ", trade_type='" + trade_type + '\'' +
                    ", prepay_id='" + prepay_id + '\'' +
                    ", code_url='" + code_url + '\'' +
                    '}';
        }
    }
    
    

    新建一个微信支付的service

    
     public interface WechatPayService {
        //统一下单
        UnifiedOrderResponse unifiedOrder(UnifiedOrderRequest request, String key) throws UnsupportedEncodingException;
        
        }
    

    接口类的实现

    public class WechatPayServiceImpl implements WechatPayService {
        private static final Logger logger = LoggerFactory.getLogger(WechatPayServiceImpl.class);
    
    		/**
    		request 接口请求对象
    		*/
        @Override
        public UnifiedOrderResponse unifiedOrder(UnifiedOrderRequest request, String key) {
            logger.info("WechatPayServiceImpl, unifiedOrder(), 返回值类型:UnifiedOrderResponse, request:{}", request);
            //String tmp = request.getBody();
            /** 获取签名 这里我的签名方式是MD5 WxConst.PAYKEY为你的密钥 */
            request.setSign(WxUtil.getSign(request,WxConst.PAYKEY));
            //request.setBody(tmp);
            String unifiedOrderRequestStr = XMLUtil.convertToXml(request);
    
            logger.info(unifiedOrderRequestStr);
    
            /** 设置https 请求*/
            HttpClient client = createSSLClientDefault();
            /** post请求*/
            HttpPost post = new HttpPost(WxConst.WXUNIFIEDORDERURL);
            /** 设置请求头*/
            post.setHeader("Content-Type", "application/xml");
            /** 转换实体参数*/
            StringEntity stringEntity = new StringEntity(unifiedOrderRequestStr, "UTF-8");
            /*stringEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
                    "application/xml"));*/
            post.setEntity(stringEntity);
    
            try {
                HttpResponse httpResponse = client.execute(post);
                String resp = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
                UnifiedOrderResponse unifiedOrderResponse = (UnifiedOrderResponse) XMLUtil.convertXmlStrToObject(UnifiedOrderResponse.class, resp);
                logger.info(unifiedOrderResponse.toString());
                //成功
                if ("SUCCESS".equals(unifiedOrderResponse.getReturn_code()) && "SUCCESS".equals(unifiedOrderResponse.getResult_code())) {
                    logger.info(unifiedOrderResponse.toString());
                }
                return unifiedOrderResponse;
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
      }
    

    !!!!微信支付签名的MD5加密的 方式通过参数拼接这里可以参考微信官方的参考文档 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3, 这个地方拼接的参数有个坑 (在做APP 调用支付的时候 我一直以为请求参数的商务id 是mch_id 其实 partnerid,要不然在 APP端调用支付会失败,但是自己在后端请求查询没问题。所以在app 调用支付的时候签名如果是后端返回的商务端id 字段是 partnerid )
    在这里插入图片描述

    public class WxUtil {
        public static String getSign(Object request,String key) {
            //logger.info("WechatPayServiceImpl, getSign(), 返回值类型:String, request:{}", request);
            String sign = "";
            Field[] fields = request.getClass().getDeclaredFields();
            int index = 0;
            int fieldNum = 0;
            String[] signArray = new String[fields.length - 1];
            for (int i = 0; i < fields.length; i++) {
                try {
                /** 这里通过反射获得到请求的参数,通过参数拼接这里可以参考微信官方			 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
                */
                    fields[i].setAccessible(true);
                    if ("Sign".equals(fields[i].getName()) || fields[i].get(request) == null ||
                            "".equals(String.valueOf(fields[i].get(request)))) {
                        continue;
                    }
                 	
                    if (fields[i].getName().equals("packAge")) {
                        signArray[index] = String.format("%s=%s", "package", String.valueOf(fields[i].get(request)));
                    } else {
                        signArray[index] = String.format("%s=%s", fields[i].getName(), String.valueOf(fields[i].get(request)));
                    }
                    index++;
                    fieldNum++;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            if (fieldNum < signArray.length) {
                signArray = Arrays.copyOf(signArray, fieldNum);
            }
            Arrays.sort(signArray);
            //logger.info(toString(signArray) + String.format("&%s=%s", "key", WxConst.PAYKEY));
            sign = MD5Util.md5(toString(signArray) + String.format("&%s=%s", "key", key)).toUpperCase();
            return sign;
        }
    
        private static String toString(String[] a) {
            if (a == null)
                return "null";
    
            int iMax = a.length - 1;
            if (iMax == -1)
                return "";
    
            StringBuilder b = new StringBuilder();
    
            for (int i = 0; ; i++) {
                b.append(a[i]);
                if (i == iMax)
                    return b.toString();
                b.append("&");
            }
        }
    }
     
    
    • 测试调用
    public class Test1{
    	
    	public void test (){
    	   /** 给予统一支付下单 对象赋值*/
            /**
             * 小程序支付必须传参为
             *  @Param  [user.getOpenId() 用户标识] [WxConst.APPID3小程序ID] [WxConst.MACHID3 商户号] [nonceStr 随机字符串] [MD5 表示加密的方式] ,["原材料采购" 表示采购的名称]
             *  @Param  [body 商品描述 依照微信小程序规范上传] [purchaseOrder.getOrderNumber() 自己生成的订单号 ] [totalFee 订单总金额 单位为分]
             *  @Param [spbillCreateIp 调用微信支付API的机器IP 终端IP ] [notify_url 回调地址] [trade_type 交易类型  APP 表示APP 支付] [product_id 商品id 商品ID trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。]
              */
    	   UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest(WxConstMobile.APPID3, WxConstMobile.MACHID3,
                        null, nonceStr, null, "MD5", "原材料采购", null, "1",
                        purchaseOrder.getOrderNumber(), null, totalFee, spbillCreateIp, null, null,
                        null, WxConstMobile.WXNOTIFYURL, "APP", null, null, null);
    
                try {
                /** 调用统一下单接口*/
                    UnifiedOrderResponse unifiedOrderResponse = wechatPayService.unifiedOrder(unifiedOrderRequest, WxConstMobile.PAYKEY3);
                    /** 可以查看统一下单接口里面的参如果是成功 */
                    if (!unifiedOrderResponse.getReturn_code().equals("SUCCESS")) {
                        return new BaseResponse<>(ErrorCode.ERROR_CUSTOM.getValue(), unifiedOrderResponse.getReturn_msg());
                    }
                    WechatUnifiedOrder wechatUnifiedOrder = new WechatUnifiedOrder(unifiedOrderResponse);
                   	/** 对于返回的参数二次签名返回给APP 参数为上图所示的请求参数 */
                    appPayResponse = new AppPayResponse(purchaseOrder.getWechatUnifiedOrder());
                    appPayResponse.setSign(WxUtil.getSign(appPayResponse, WxConstMobile.PAYKEY3));
                } catch (Exception e) {
                    e.printStackTrace();
                }
    	}
    }
    
    展开全文
  • 本文是【浅析微信支付】系列文章的第五篇,主要讲解如何调用统一下单接口生成预支付单及调起支付页面。 浅析微信支付系列已经更新四篇了哟~,没有看过的朋友们可以看一下哦。 浅析微信支付:微信公众号网页授权 ...

    本文是【浅析微信支付】系列文章的第五篇,主要讲解如何调用统一下单接口生成预支付单及调起支付页面。


    浅析微信支付系列已经更新四篇了哟~,没有看过的朋友们可以看一下哦。

    浅析微信支付:微信公众号网页授权

    浅析微信支付:开发前的准备

    上面是本文的前置文章,有前面几篇文章的基础以后看会更加明了,如果已经看过的小伙伴可以忽略。

    1、什么是[统一下单接口]?

    首先我们要明白这个问题,需要先行看一下微信的官方文档: pay.weixin.qq.com/wiki/doc/ap…

    官方解释如下:

    除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。
    复制代码

    什么意思?简单理解:就是说我们要在调起微信支付窗口之前,需要先生成一个 预支付交易单,这个单子相当于和我们自身系统的 支付交易单 一一对应,也就是我们每次支付需要记录的订单支付交易单。

    从上面我们可以得到,在调用此接口之前,首先,我们系统中肯定已经需要有以下步骤:订单提交 -> 生成订单 -> 生成订单对应的支付单 -> 调用统一下单接口

    好了,假设系统现在已经生成支付交易单,准备调用统一下单接口,我们来看一下具体的实现方式。

    PS:调用统一下单接口时,需要注意的是必须传入异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。示例如下:

    https://xxx.com/v1/weixin/pay/wxnotify

    2、调用接口

    示例代码:

    /**
     * [微信支付统一下单] - 保存调用的相关记录 <p>
     * [微信支付小程序] - 返回支付唤醒参数
     * @param payment 付款对象
     * @param user 当前用户
     * @return map
     * @throws Exception e
     *
     * @author yclimb
     * @date 2018/6/15
     */
    public Map<String, String> saveWxPayUnifiedOrder(Payment payment, User user) throws Exception {
        if (payment == null || user == null) {
            return null;
        }
    
        // 1.调用微信统一下单接口
        WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
        Map<String, String> resultMap = wxPay.unifiedOrder(...);
    
        // 1.1.记录付款流水
        ...
    
        // 下单失败,进行处理
        if (WXPayConstants.FAIL.equals(resultMap.get(WXPayConstants.RETURN_CODE)) ||
                WXPayConstants.FAIL.equals(resultMap.get(WXPayConstants.RESULT_CODE))) {
    
            // 处理结果返回,无需继续执行
            resultMap.put(WXPayConstants.RESULT_CODE, WXPayConstants.FAIL);
            resultMap.put(WXPayConstants.ERR_CODE_DES, resultMap.get(WXPayConstants.RETURN_MSG));
            return resultMap;
        }
    
        // 1.2.获取prepay_id、nonce_str
        String prepay_id = resultMap.get("prepay_id");
        String nonce_str = resultMap.get("nonce_str");
    
        // 2.根据微信统一下单接口返回数据组装微信支付参数,返回结果
        return wxPay.chooseWXPayMap(prepay_id, nonce_str);
    }
    复制代码

    以上为一个调用统一下单的接口代码,主要在于以下两句:

    // 实例化一个微信支付对象,使用单例配置的方式
    WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
    
    // 直接调用统一下单接口
    Map<String, String> resultMap = wxPay.unifiedOrder(...);
    复制代码

    微信支付对象WXPay统一下单接口:

    
    /**
     * 作用:统一下单<br>
     * 场景:商户在小程序中先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易后调起支付。
     * 接口链接:URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
     * 是否需要证书:否
     * 接口文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
     *
     * @param notify_url       公众号用户openid
     * @param body             商品简单描述,该字段请按照规范传递,例:腾讯充值中心-QQ会员充值
     * @param out_trade_no     商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
     * @param total_fee        订单总金额,传入参数单位为:元
     * @param spbill_create_ip APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
     * @param goods_tag        订单优惠标记,用于区分订单是否可以享受优惠
     * @param detail           商品详情	,单品优惠活动该字段必传
     * @param timeStart        订单生成时间,格式为yyyyMMddHHmmss
     * @param timeExpire       订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010
     * @return API返回数据
     * @throws Exception e
     */
    public Map<String, String> unifiedOrder(String notify_url, String openid, String body, String out_trade_no, String total_fee, String spbill_create_ip, String goods_tag, String detail, Date timeStart, Date timeExpire) throws Exception {
    
        /** 构造请求参数数据 **/
        Map<String, String> data = new HashMap<>();
    
        // 字段名	变量名	必填	类型	示例值	描述
        // 标价币种	fee_type	否	String(16)	CNY	符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型
        data.put("fee_type", WXPayConstants.FEE_TYPE_CNY);
        // 通知地址	notify_url	是	String(256)	http://www.weixin.qq.com/wxpay/pay.php	异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
        data.put("notify_url", notify_url);
        // 交易类型	trade_type	是	String(16)	JSAPI	小程序取值如下:JSAPI,详细说明见参数规定
        data.put("trade_type", WXPayConstants.TRADE_TYPE);
        // 用户标识	openid	否	String(128)	oUpF8uMuAJO_M2pxb1Q9zNjWeS6o	trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。
        data.put("openid", openid);
        // 商品描述	body	是	String(128)	腾讯充值中心-QQ会员充值 商品简单描述,该字段请按照规范传递,具体请见参数规定
        data.put("body", body);
        // 商户订单号	out_trade_no	是	String(32)	20150806125346	商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
        data.put("out_trade_no", out_trade_no);
        // 标价金额	total_fee	是	Int	88	订单总金额,单位为分,详见支付金额
        // 默认单位为分,系统是元,所以需要*100
        data.put("total_fee", String.valueOf(new BigDecimal(total_fee).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue()));
        // 终端IP	spbill_create_ip	是	String(16)	123.12.12.123	APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
        data.put("spbill_create_ip", spbill_create_ip);
    
        /** 以下参数为非必填参数 **/
        // 订单优惠标记	goods_tag	否	String(32)	WXG	订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠
        if (StringUtils.isNotBlank(goods_tag)) {
            data.put("goods_tag", goods_tag);
        }
        // 商品详情	detail	否	String(6000)	 	商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见“单品优惠参数说明”
        if (StringUtils.isNotBlank(detail)) {
            data.put("detail", detail);
            // 接口版本号 新增字段,接口版本号,区分原接口,默认填写1.0。入参新增version后,则支付通知接口也将返回单品优惠信息字段promotion_detail,请确保支付通知的签名验证能通过。
            data.put("version", "1.0");
        }
        // 设备号	device_info	否	String(32)	013467007045764	自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"
        data.put("device_info", "WEB");
    
    	// 交易起始时间	time_start	否	String(14)	20091225091010	订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    	data.put("time_start", DateTimeUtil.getTimeShortString(timeStart));
    	// 交易结束时间	time_expire	否	String(14)	20091227091010	订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。
    	// 订单失效时间是针对订单号而言的,由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期,所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则,建议:最短失效时间间隔大于1分钟
    	data.put("time_expire", DateTimeUtil.getTimeShortString(timeExpire));
    	/*// 商品ID	product_id	否	String(32)	12235413214070356458058	trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
    	data.put("product_id", null);
    	// 指定支付方式	limit_pay	否	String(32)	no_credit	上传此参数no_credit--可限制用户不能使用信用卡支付
    	data.put("limit_pay", null);
    	// 附加数据	attach	否	String(127)	深圳分店	附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
    	data.put("attach", null);*/
    
        /** 以下五个参数,在 this.fillRequestData 方法中会自动赋值 **/
    	/*// 小程序ID	appid	是	String(32)	wxd678efh567hg6787	微信分配的小程序ID
        data.put("appid", WXPayConstants.APP_ID);
        // 商户号	mch_id	是	String(32)	1230000109	微信支付分配的商户号
        data.put("mch_id", WXPayConstants.MCH_ID);
    	// 随机字符串	nonce_str	是	String(32)	5K8264ILTKCH16CQ2502SI8ZNMTM67VS	随机字符串,长度要求在32位以内。推荐随机数生成算法
    	data.put("nonce_str", nonce_str);
    	// 签名类型	sign_type	否	String(32)	MD5	签名类型,默认为MD5,支持HMAC-SHA256和MD5。
        data.put("sign_type", WXPayConstants.MD5);
    	// 签名	sign	是	String(32)	C380BEC2BFD727A4B6845133519F3AD6	通过签名算法计算得出的签名值,详见签名生成算法
    	data.put("sign", sign);*/
    
        // 微信统一下单接口请求地址
        Map<String, String> resultMap = this.unifiedOrder(data);
    
        WXPayUtil.getLogger().info("wxPay.unifiedOrder:" + resultMap);
    
        return resultMap;
    }
    复制代码

    以上代码详细说明了每个字段的含义,具体的代码可以看一下作者的github,文末有对应的地址。

    下面说一个特殊情况,在我们支付的时候,有时候用户会取消支付,等一段时间再重新调起,这时候,需要用到另外一个方法,那就是二次支付,所以,在我们数据库中,必须保存两个字段,用于二次支付时使用:预支付IDprepay_id、随机字符串nonce_str,此两个参数可以生成微信支付调起时需要的验证签名。

    以下为二次调用时的代码:

    /**
     * 根据付款单号查询微信付款参数
     * @param relationId 交易编号
     * @param type 支付类型
     * @return payment
     *
     * @author yclimb
     * @date 2018/6/28
     */
    public Map<String, String> queryChooseWXPayMapByRelationId(Integer relationId, String type) throws Exception {
    
            // 微信支付对象
            WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
    
            // 1.查询付款对象
            Payment payment = this.queryPaymentByRelationId(relationId, type);
    
            // 2.根据微信统一下单接口返回数据组装微信支付参数,返回结果
            return wxPay.chooseWXPayMap(payment.getPrepayId(), payment.getNonceStr());
        }
    复制代码

    此时我们已经调用微信统一下单接口成功,并为我们返回了需要的参数,下一步需要组装为微信支付调起时前端需要的参数。

    生成支付签名

    下面为组装支付签名的代码:

    /**
     * 作用:生成微信支付所需参数,微信支付二次签名<br>
     * 场景:根据微信统一下单接口返回的 prepay_id 生成微信支付所需的参数
     * 接口文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
     *
     * @param prepay_id 预支付id
     * @param nonce_str 随机字符串
     * @return 支付方法调用所需参数map
     * @throws Exception e
     */
    public Map<String, String> chooseWXPayMap(String prepay_id, String nonce_str) throws Exception {
    
        // 支付方法调用所需参数map
        Map<String, String> chooseWXPayMap = new HashMap<>();
        chooseWXPayMap.put("appId", config.getAppID());
        chooseWXPayMap.put("timeStamp", String.valueOf(WXPayUtil.getCurrentTimestamp()));
        chooseWXPayMap.put("nonceStr", nonce_str);
        chooseWXPayMap.put("package", "prepay_id=" + prepay_id);
        chooseWXPayMap.put("signType", WXPayConstants.MD5);
    
        WXPayUtil.getLogger().info("wxPay.chooseWXPayMap:" + chooseWXPayMap.toString());
    
        // 生成支付签名
        String paySign = WXPayUtil.generateSignature(chooseWXPayMap, config.getKey());
        chooseWXPayMap.put("paySign", paySign);
    
        WXPayUtil.getLogger().info("wxPay.paySign:" + paySign);
    
        return chooseWXPayMap;
    }
    复制代码

    组装好需要的参数以后,就可以调起微信支付窗口了,如果是微信公众号支付,需要使用以下的方式调起微信支付:

    function weixinConfig(appid, timestamp, noncestr, signature) {
    	wx.config({
    		debug : false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            // debug : true, 
    		appId :appid, // 必填,公众号的唯一标识
    		timestamp : timestamp, // 必填,生成签名的时间戳
    		nonceStr : noncestr, // 必填,生成签名的随机串
    		signature : signature,// 必填,签名,见附录1
            jsApiList: [
                'checkJsApi',
                'chooseWXPay' // 必须增加此参数,用户微信支付功能
    		]
    		// 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    	});
    }
    
    wx.chooseWXPay({
        appId: appId,
        timestamp: timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
        nonceStr: nonceStr, // 支付签名随机串,不长于 32 位
        package: package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
        signType: signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
        paySign: paySign, // 支付签名
        success: function (res) {
            // 支付成功后的回调函数
            alert('支付成功!');
        },
        cancel: function (res) {
            // 支付取消
        },
        fail: function (res) {
            // 支付失败
            alert("支付失败!");
        }
    });
    复制代码

    PS:小程序调用方法类似,参数一致。

    结语

    以上就是微信支付统一下单接口的调用方式了,具体的源码可以参考作者github,最好在开发之前先通读一遍微信官方文档,此时再使用作者源码开发事半功倍,更易理解。

    预告:下一篇文章,作者将讲 支付结果通知,敬请期待!!!

    ​如果想要提前一览源码的小伙伴,可以先看看我的 github,地址如下: ​https://github.com/YClimb/wxpay-sdk/blob/master/README.md

    加作者私人微信,作者微信号如下 yclimb,标明 微信支付 可拉入微信支付讨论群与小伙伴一起探讨哦,一定要标明 微信支付 哦~

    到此本文就结束了,关注公众号查看更多推送!!!



    展开全文
  • 微信小程序支付统一下单接口

    千次阅读 2018-09-05 13:50:58
    微信小程序——支付 1.通过code获取openId code:用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 api,使用 code 换取 openid 和 session_key 等... * 构建统一下单参数,发给微信服务器...
  • 支付宝统一下单接口的接入(1)

    千次阅读 2020-03-21 11:46:38
    } } 到这里支付宝的下单接口就完成了 这里说下我碰到的一些问题: 1、支付页面没有展示出来-----因为当时是用ajax异步直接请求支付接口的,但是因为异步会堵塞导致支付宝的页面不能异步跳转展示。(这个不知道...
  • 之前在做微信支付的时候,按照微信官方给的统一下单接口文档进行开发时,因为用到统一下单接口的支付方式有很多种,里面的参数有时是必填的有时是非必填的,以及参数形式等各个方面带的模糊带来了很多问题。...
  • 微信支付-----统一下单接口对接

    千次阅读 2018-11-30 16:32:39
    本以为没有机会接触鼎鼎大名的支付宝和微信接口(公司本身是做第三方支付的),最近由于一个售货机项目需要对接银联,支付宝和微信接口,因为我自身已经对接了银联,之后根据安排,由我对接微信的相关接口。话不多说,...
  • 上一篇讲到成功获取 openid,本篇要调用微信统一接口创建预支付交易单 ...第三步,调用微信统一下单接口创建预支付交易单 微信统一下单API是微信支付的一个“统一”处理入口,官方给出的地址是 https://ap...
  • 公司项目要用到微信支付的jsapi统一下单接口调用,由于官方缺乏nodejs版的代码范例,而网上查到的也是五花八门的代码,却没有一个能用,也没有一个能讲清楚的,害得我花了好大的劲,各方求证并积极联系微信支付的...
  • 签名失败由很多原因导致 编码错误:有中文的要注意转utf-8格式 ...参数错误:在拼接签名时可能粗心大意打错了参数名或者其他格式问题,要对比文档确认每个参数名和文档一致 签名装配错误:加密时出现错误,可以使用...
  • 2.2)要注意统一下单请求参数中total_fee参数的类型是int类型。 3) 如果和微信的在线签名工具不一致,说明程序有错误,常见的错误可能是: 3.1) 编码问题,确保所有的都是utf-8的. 如果有中文, 可以先把中文...
  • 整个流程如下: 调用[统一下单接口] -->把某个订单, 某个商品下单----> 调用成功收到微信的 code_url ------> 自己用二维码工具生成二维码 ------> 客户扫描二维码 ------> 每隔几秒调用[订单查询接口] -------> ...
  • 微信支付统一下单接口返回的必须类似是wx18150336246279f98d3de78d1950474601 的一串,才算统一下单成功了。 首先一定要检查商户号的密钥是否正确,然后查看请求https://api.mch.weixin.qq.com/pay/unifiedorder...
  • 因为这个版本的WXPayConfig中的方法仅同包可以实现,所以我这里把它copy到工程中进行实现,在实现其getWXPayDomain方法时需要返回一个IWXPayDomain接口的实现类,注意实现getDomain方法时DomainInfo
  • 在统一下单前需要数据签名和获取用户id详情可参看另外两篇文章 http://www.cnblogs.com/itBulls/articles/8645619.html ... 统一下单接口 index.php 文件地址 session_start(); include './...
  • 调用微信小程序统一下单接口

    千次阅读 2019-01-17 16:46:20
    最近刚接触微信小程序项目,踩了几个坑,以此记录一下 1、微信小程序需要绑定商户 【mch_id=商户号】 ... //微信统一下单URL String payUrl="https://api.mch.weixin.qq.com/pay/unifiedorder";...
  • 接口API URL前缀const API_URL_PREFIX = 'https://api.mch.weixin.qq.com';//下单地址URLconst UNIFIEDORDER_URL = "/pay/unifiedorder";//查询订单URLconst ORDERQUERY_URL = "/pay/orderquery";//关
  • .NET C# 【微信支付】API对应的SDK,完整版,可直接运行。最新微信支付DEMO 统一下单服务端客户端完整代码示例
  • 首先来看看官方支付文档的一些相关...3、统一下单接口返回支付相关参数给商户后台,如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页 4、中间页进行H5权限的校验,安全性检查(此处常见...
  • 在groovy中使用https调用微信app支付统一下单接口,和java基本相同,解析xml需要导入dom4j,jaxen的jar包。import groovy.sql.Sql import org.dom4j.Document import org.dom4j.DocumentException import org.dom4j....
  • 程序早就上线,放阿里云,代理无任何改动,怎么会突然签名错误,怎么想都不通。   第一:排除代码错误 之前能正常支付的,现在突然不行,代码是没问题的。 可以验证 微信签名验证工具 ...验证工具只能校验签名...
  • 微信支付统一下单接口body中文乱码问题,接口一直报错“body不是utf-8类型”的解决
  • [这个是统一下单接口调用传值](https://img-ask.csdn.net/upload/201806/25/1529914137_850962.png) ![xml](https://img-ask.csdn.net/upload/201806/25/1529914178_589085.png) ![官网上测试了签名没有问题]...
  • **微信支付统一下单接口签名错误,用微信支付接口签名校验工具效验通过, 参数都看了不少没错,纠结半天没找到问题所在** ![图片说明](https://img-ask.csdn.net/upload/201705/24/1495622310_480673.png) !...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,248
精华内容 6,899
关键字:

统一下单接口