zoukankan      html  css  js  c++  java
  • 微信第三方支付接口java调用详细文档

    1.显示支付二维码

    1. 支付流程
      首先用户选好商品后
      跳到结算页面

    在点击支付提交时应先将表单的数据保存到数据库(一般都会有订单表一二级)
    经过后台保存数据后再转发到前台(二维码是后台调用微信生成的)
    看到扫码页面

    当用户扫码成功后 更改订单状态为已支付(一般情况 根据业务定)

    开始
    首先第一步

    1. 当用户选好商品后 点击 支付按钮时发送一个请求到后台控制层

    让后跳到扫码页面显示商品信息页面

    这个页面应该有一个标签

    <img src=”请求的控制类地址” />
    

    注意下面写的方法 就是输出一个二维码
    跳到二维码页面img标签会去访问src这个路径方法
    让后开始准备

    一般会吧微信支付用到的参数封装成一个类,或者properties,xml,yml等等这样的文件(在企业中基本都是后者)这里使用类 方便一些
    在这里插入图片描述
    导入要使用的jar包和工具类
    在这里插入图片描述
    在这里插入图片描述
    在控制类中加入以下几个工具方法(也可以封装成工具类)

    /**
         * 获取本机IP地址
         * @return IP
         */
    	private static String getRemortIP(HttpServletRequest request) {  
            if (request.getHeader("x-forwarded-for") == null) {  
                return request.getRemoteAddr();  
            }  
            return request.getHeader("x-forwarded-for");  
        }
    
    
    /**
         * 微信支付签名算法sign
         */
        private String getSign(Map<String,Object> map) {
            StringBuffer sb = new StringBuffer();
            String[] keyArr = (String[]) map.keySet().toArray(new String[map.keySet().size()]);//获取map中的key转为array
            Arrays.sort(keyArr);//对array排序
            for (int i = 0, size = keyArr.length; i < size; ++i) {
                if ("sign".equals(keyArr[i])) {
                    continue;
                }
                sb.append(keyArr[i] + "=" + map.get(keyArr[i]) + "&");
            }
            sb.append("key=" + WeixinPayConfig.key);
            String sign = Md5Util.string2MD5(sb.toString());
            return sign;
        }
        
    
    /**
    	 * 通过返回IO流获取支付地址
    	 * @param in
    	 * @return
    	 */
    	private String getElementValue(InputStream in,String key){
    		SAXReader reader = new SAXReader();
            Document document=null;
    		try {
    			document = reader.read(in);
    		} catch (DocumentException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
            Element root = document.getRootElement();
            List<Element> childElements = root.elements();
            for (Element child : childElements) {
            	System.out.println(child.getName()+":"+child.getStringValue());
            	if(key.equals(child.getName())){
            		return child.getStringValue();
            	}
            }
            return null;
    	}
    	
    
    /** 
    	   * 类型转换 
    	   * @author chenp 
    	   * @param matrix 
    	   * @return 
    	   */  
    	 public static BufferedImage toBufferedImage(BitMatrix matrix) {  
          int width = matrix.getWidth();  
          int height = matrix.getHeight();  
          BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);  
          for (int x = 0; x < width; x++) {  
              for (int y = 0; y < height; y++) {  
                  image.setRGB(x, y, matrix.get(x, y) == true ? 0xff000000 : 0xFFFFFFFF);  
              }  
          }  
          return image;  
    	 }
    	
    

    以上方法都要用到 可以封住成一个工具类 由于是演示这里直接放在当前类中了

    上面这些弄完后就可以开工了

    1. 首先要测试参数
    2. 把刚刚复制的 通过返回IO流获取支付地址getElementValue这个方法里的代码注掉

    在这里插入图片描述

    请求的方法里面 有如下代码

    		 String orderNo=DateUtil.getCurrentDateStr(); // 生成订单号(由项目定)
    		Map<String,Object> map=new HashMap<String,Object>();
    		map.put("appid", WeixinPayConfig.appid); // 公众账号ID(配置文件里的属性)
    		map.put("mch_id", WeixinPayConfig.mch_id); // 商户号(配置文件里的属性)
    		map.put("device_info", WeixinPayConfig.device_info); // 设备号(配置文件里的属性)
    		map.put("notify_url", WeixinPayConfig.notify_url); // 异步通知地址(配置文件里的属性)
    		map.put("trade_type", "NATIVE"); // 交易类型(表示扫码支付 详情看微信公众平台)
    		map.put("out_trade_no", orderNo); // 商户订单号(由项目定)
    		map.put("body", "测试商品"); // 商品描述(由项目定)
    		map.put("total_fee", 100); // 标价金额(单位 '分')
    		// map.put("spbill_create_ip", getRemortIP(request)); // 终端IP(正式环境下用)
    		map.put("spbill_create_ip", "127.0.0.1"); // 终端IP(测试环境)
    		map.put("nonce_str", StringUtil.getRandomString(30)); // 随机字符串(StringUtil工具类提供)
    		map.put("sign", getSign(map)); // 签名(同类中的getSign方法提供)
    		String xml=XmlUtil.genXml(map); //发送xml  (XmlUtil工具类提供)
    		System.out.println(xml);
    		// 发现xml消息(由httpclient-4.5.2.jar,httpcore-4.4.9.jar提供)
    		InputStream in=HttpClientUtil.sendXMLDataByPost(WeixinPayConfig.url, xml).getEntity().getContent(); 
    		String code_url=getElementValue(in,""); // 获取二维码地址(同类中的getElementValue方法提供)
    		```
    

    让后测试看到下面这段就说明成功了(控制台)
    在这里插入图片描述
    如果错误了可以看一下错误原因

    成功后在将getElementValue方法里刚刚注掉的代码解注
    在这里插入图片描述

    然后在访问的方法里继续添加代码
    在这里插入图片描述

    String orderNo=DateUtil.getCurrentDateStr(); // 生成订单号(由项目定)
       	Map<String,Object> map=new HashMap<String,Object>();
       	map.put("appid", WeixinPayConfig.appid); // 公众账号ID(配置文件里的属性)
       	map.put("mch_id", WeixinPayConfig.mch_id); // 商户号(配置文件里的属性)
       	map.put("device_info", WeixinPayConfig.device_info); // 设备号(配置文件里的属性)
       	map.put("notify_url", WeixinPayConfig.notify_url); // 异步通知地址(配置文件里的属性)
       	map.put("trade_type", "NATIVE"); // 交易类型(表示扫码支付 详情看微信公众平台)
       	map.put("out_trade_no", orderNo); // 商户订单号(由项目定)
       	map.put("body", "测试商品"); // 商品描述(由项目定)
       	map.put("total_fee", 100); // 标价金额(单位 '分')
       	// map.put("spbill_create_ip", getRemortIP(request)); // 终端IP(正式环境下用)
       	map.put("spbill_create_ip", "127.0.0.1"); // 终端IP(测试环境)
       	map.put("nonce_str", StringUtil.getRandomString(30)); // 随机字符串(StringUtil工具类提供)
       	map.put("sign", getSign(map)); // 签名(同类中的getSign方法提供)
       	String xml=XmlUtil.genXml(map); //发送xml  (XmlUtil工具类提供)
       	System.out.println(xml);
       	// 发现xml消息(由httpclient-4.5.2.jar,httpcore-4.4.9.jar提供)
       	InputStream in=HttpClientUtil.sendXMLDataByPost(WeixinPayConfig.url, xml).getEntity().getContent(); 
       	String code_url=getElementValue(in,"code_url"); // 获取二维码地址(同类中的getElementValue方法提供)
       	MultiFormatWriter multiFormatWriter = new MultiFormatWriter();  
           Map hints = new HashMap();  
           BitMatrix bitMatrix = null;  
           try {  
                bitMatrix = multiFormatWriter.encode(code_url, BarcodeFormat.QR_CODE, 250, 250,hints);  
                BufferedImage image = toBufferedImage(bitMatrix);  
                //输出二维码图片流  
                ImageIO.write(image, "png", response.getOutputStream());  
       	 } catch (WriterException e1) {  
       		 e1.printStackTrace();  
       	 }  
    
    

    完成后 测试一下

    出现二维码就说明成功了
    在这里插入图片描述

    2.异步请求处理

    在1中生成的二维码,用户扫描支付后 将会调用公众平台设置的回调地址
    接下来根据回调地址来写方法

    然后先是拷贝方法 根1一样 封住成工具类或者放在本类

    /**  
         * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。  
         * @return boolean  
         */    
        public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {    
            StringBuffer sb = new StringBuffer();    
            Set es = packageParams.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(!"sign".equals(k) && null != v && !"".equals(v)) {    
                    sb.append(k + "=" + v + "&");    
                }    
            }    
            sb.append("key=" + API_KEY);    
                
            //算出摘要    
            String mysign = Md5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();    
            String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();    
                
            return tenpaySign.equals(mysign);    
        }  
    
    

    然后回调地址的方法里写

    //读取参数    
            InputStream inputStream ;    
            StringBuffer sb = new StringBuffer();    
            inputStream = req.getInputStream();    
            String s ;    
            BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));    
            while ((s = in.readLine()) != null){    
                sb.append(s);    
            }    
            in.close();    
            inputStream.close();  
            System.out.println("sb:"+sb.toString());
            
            //解析xml成map    
            Map<String, String> m = new HashMap<String, String>();    
            try {
    			m = XmlUtil.doXMLParse(sb.toString());
    		} catch (JDOMException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}    
                
            //过滤空 设置 TreeMap    
            SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();          
            Iterator<String> it = m.keySet().iterator();    
            while (it.hasNext()) {    
                String parameter = it.next();    
                String parameterValue = m.get(parameter);    
                    
                String v = "";    
                if(null != parameterValue) {    
                    v = parameterValue.trim();    
                } 
                packageParams.put(parameter, v);    
            }  
            
            // 微信支付的API密钥    
            String key = WeixinPayConfig.key;
            
            if(isTenpaySign("UTF-8", packageParams, key)){ // 验证通过
            	if("SUCCESS".equals((String)packageParams.get("result_code"))){  
            		System.out.println("验证通过");
            	}else{
            		System.out.println("支付失败");
            	}
            }else{
            	System.out.println("验证未通过");
            }
    

    在验证通过后更改 订单的状态 为已支付

    在编写根据订单查询订单状态的方法
    在二维码页面每隔多少秒就调用一次查看状态
    若为已支付状态就可以跳转页面了

    3.HTML在线支付

    环境准备
    加入
    HMACSHA256Uitl.java 工具类

    接口调用url
    private static String url="https://api.mch.weixin.qq.com/pay/downloadfundflow";
    使用到的工具方法

    /**
         * 微信支付签名算法sign
         */
        private static String getSign(Map<String,Object> map) {
            StringBuffer sb = new StringBuffer();
            String[] keyArr = (String[]) map.keySet().toArray(new String[map.keySet().size()]);//获取map中的key转为array
            Arrays.sort(keyArr);//对array排序
            for (int i = 0, size = keyArr.length; i < size; ++i) {
                if ("sign".equals(keyArr[i])) {
                    continue;
                }
                sb.append(keyArr[i] + "=" + map.get(keyArr[i]) + "&");
            }
            sb.append("key=" + WeixinPayConfig.key);
            // String sign = Md5Util.string2MD5(sb.toString());
            String sign=HMACSHA256Uitl.HMACSHA256(sb.toString().getBytes(), WeixinPayConfig.key.getBytes());
            return sign;
        }
    
    

    然后编写方法 因为是测试 这里使用的是main 实际根据业务更改

    public static void main(String[] args) throws UnsupportedOperationException, ClientProtocolException, IOException {
    		Map<String,Object> map=new HashMap<String,Object>();
    		map.put("appid", WeixinPayConfig.appid); // 公众账号ID
    		map.put("mch_id", WeixinPayConfig.mch_id); // 商户号
    		map.put("nonce_str", StringUtil.getRandomString(30)); // 随机字符串
    		map.put("bill_date", "20180419"); // 资金账单日期
    		map.put("account_type", "Basic"); // 资金账户类型
    		map.put("sign", getSign(map)); // 签名
    		String xml=XmlUtil.genXml(map); 
    		System.out.println(xml);
    		InputStream in=HttpClientUtil.sendXMLDataByHttpsPost(url, xml).getEntity().getContent(); // 发现xml消息
    		StringBuffer out=new StringBuffer();
    		byte []b=new byte[4096];
    		for(int n;(n=in.read(b))!=-1;){
    			out.append(new String(b,0,n));
    		}
    		System.out.println(out.toString());
    	}
    
    

    运行结果
    在这里插入图片描述
    出现以上结果就成功了

    出现报错的话 根据提示 更改参数即可

    自行根据以上逻辑嵌入到页面中即可

    3.查询订单

    创建参数

    private static String url="https://api.mch.weixin.qq.com/pay/orderquery";
    

    写入配置文件里即可 由于是测试 这里写在类中

    再添加工具方法(getElementValue这个方法有重载 跟1,2里的不一样)

    /**
    	 * 通过返回IO流获取支付地址
    	 * @param in
    	 * @return
    	 */
    	private static void getElementValue(InputStream in){
    		SAXReader reader = new SAXReader();
            Document document=null;
    		try {
    			document = reader.read(in);
    		} catch (DocumentException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
            Element root = document.getRootElement();
            List<Element> childElements = root.elements();
            for (Element child : childElements) {
            	System.out.println(child.getName()+":"+child.getStringValue());
            }
    	}
    
    

    这个和2,1中的方法是同一个

    /**
         * 微信支付签名算法sign
         */
        private static String getSign(Map<String,Object> map) {
            StringBuffer sb = new StringBuffer();
            String[] keyArr = (String[]) map.keySet().toArray(new String[map.keySet().size()]);//获取map中的key转为array
            Arrays.sort(keyArr);//对array排序
            for (int i = 0, size = keyArr.length; i < size; ++i) {
                if ("sign".equals(keyArr[i])) {
                    continue;
                }
                sb.append(keyArr[i] + "=" + map.get(keyArr[i]) + "&");
            }
            sb.append("key=" + WeixinPayConfig.key);
            String sign = Md5Util.string2MD5(sb.toString());
            return sign;
        }
    
    

    这里直接写的main方法 具体要求根据业务更改

    public static void main(String[] args) throws UnsupportedOperationException, ClientProtocolException, IOException {
    		Map<String,Object> map=new HashMap<String,Object>();
    		map.put("appid", WeixinPayConfig.appid); // 公众账号ID
    		map.put("mch_id", WeixinPayConfig.mch_id); // 商户号
    		//map.put("transaction_id", "4200000087201804105653326283"); // 微信订单号
    		map.put("out_trade_no", "20180405055656553"); // 商户订单号(项目生成的订单号)
    		map.put("mch_id", WeixinPayConfig.mch_id); // 商户号
    		map.put("nonce_str", StringUtil.getRandomString(30)); // 随机字符串
    		map.put("sign", getSign(map)); // 签名
    		String xml=XmlUtil.genXml(map); 
    		System.out.println(xml);
    		InputStream in=HttpClientUtil.sendXMLDataByPost(url, xml).getEntity().getContent(); // 发现xml消息
    		getElementValue(in);
    	}
    
    

    然后运行
    在这里插入图片描述
    看见结果后 就成功了
    如果有错误根据错误提示 排错即可

    4.关闭订单

    创建参数

    private static String url="https://api.mch.weixin.qq.com/pay/closeorder";
    

    写入配置文件里即可 由于是测试 这里写在类中

    这里直接写的main方法 具体要求根据业务更改(还是需要3里面的两个方法)

    public static void main(String[] args) throws UnsupportedOperationException, ClientProtocolException, IOException {
    		Map<String,Object> map=new HashMap<String,Object>();
    		map.put("appid", WeixinPayConfig.appid); // 公众账号ID
    		map.put("mch_id", WeixinPayConfig.mch_id); // 商户号
    		map.put("out_trade_no", "20180404022005421"); // 商户订单号
    		map.put("nonce_str", StringUtil.getRandomString(30)); // 随机字符串
    		map.put("sign", getSign(map)); // 签名
    		String xml=XmlUtil.genXml(map); 
    		System.out.println(xml);
    		InputStream in=HttpClientUtil.sendXMLDataByPost(url, xml).getEntity().getContent(); // 发现xml消息
    		getElementValue(in);
    	}
    
    

    然后运行

    在这里插入图片描述
    看见结果后 就成功了
    如果有错误根据错误提示 排错即可

    5.申请退款

    首先 退款要用到安全证书
    在微信公众平台登陆后 在下面下载

    在这里插入图片描述
    下载完后放在磁盘内

    然后准备工作

    将CertUtil.java 导入在工程内(更改里面的安全证书路径,项目中要配置在配置文件中)

    在这里插入图片描述

    接口调用url
    private static String url="https://api.mch.weixin.qq.com/secapi/pay/refund";
    使用到的工具方法

    		/**
    	 * 通过返回IO流获取支付地址
    	 * @param in
    	 * @return
    	 */
    	private static void getElementValue(InputStream in){
    		SAXReader reader = new SAXReader();
            Document document=null;
    		try {
    			document = reader.read(in);
    		} catch (DocumentException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
            Element root = document.getRootElement();
            List<Element> childElements = root.elements();
            for (Element child : childElements) {
            	System.out.println(child.getName()+":"+child.getStringValue());
            }
    	}
    	
    	/**
         * 微信支付签名算法sign
         */
        private static String getSign(Map<String,Object> map) {
            StringBuffer sb = new StringBuffer();
            String[] keyArr = (String[]) map.keySet().toArray(new String[map.keySet().size()]);//获取map中的key转为array
            Arrays.sort(keyArr);//对array排序
            for (int i = 0, size = keyArr.length; i < size; ++i) {
                if ("sign".equals(keyArr[i])) {
                    continue;
                }
                sb.append(keyArr[i] + "=" + map.get(keyArr[i]) + "&");
            }
            sb.append("key=" + WeixinPayConfig.key);
            String sign = Md5Util.string2MD5(sb.toString());
            return sign;
        }
    

    然后编写方法 因为是测试 这里使用的是main 实际根据业务更改

    public static void main(String[] args) throws UnsupportedOperationException, ClientProtocolException, IOException {
    		Map<String,Object> map=new HashMap<String,Object>();
    		map.put("appid", WeixinPayConfig.appid); // 公众账号ID
    		map.put("mch_id", WeixinPayConfig.mch_id); // 商户号
    		map.put("transaction_id", "4200000094201804192059258077"); // 微信订单号
    		//map.put("out_trade_no", "20180419105343760"); // 商户订单号
    		map.put("nonce_str", StringUtil.getRandomString(30)); // 随机字符串
    		map.put("out_refund_no", DateUtil.getCurrentDateStr()); // 商户退款单号
    		map.put("total_fee", 100); // 订单金额
    		map.put("refund_fee", 10); // 退款金额
    		map.put("sign", getSign(map)); // 签名
    		String xml=XmlUtil.genXml(map); 
    		System.out.println(xml);
    		InputStream in=HttpClientUtil.sendXMLDataByHttpsPost(url, xml).getEntity().getContent(); // 发现xml消息
    		getElementValue(in);
    	}
    
    

    运行程序查看结果

    在这里插入图片描述

    出现以上结构就成功了

    出现报错的话 根据提示 更改参数即可

    7 查询退款

    接口调用url
    private static String url="https://api.mch.weixin.qq.com/pay/refundquery ";

    使用到的工具方法

    /**
    	 * 通过返回IO流获取支付地址
    	 * @param in
    	 * @return
    	 */
    	private static void getElementValue(InputStream in){
    		SAXReader reader = new SAXReader();
            Document document=null;
    		try {
    			document = reader.read(in);
    		} catch (DocumentException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
            Element root = document.getRootElement();
            List<Element> childElements = root.elements();
            for (Element child : childElements) {
            	System.out.println(child.getName()+":"+child.getStringValue());
            }
    	}
    	
    	/**
         * 微信支付签名算法sign
         */
        private static String getSign(Map<String,Object> map) {
            StringBuffer sb = new StringBuffer();
            String[] keyArr = (String[]) map.keySet().toArray(new String[map.keySet().size()]);//获取map中的key转为array
            Arrays.sort(keyArr);//对array排序
            for (int i = 0, size = keyArr.length; i < size; ++i) {
                if ("sign".equals(keyArr[i])) {
                    continue;
                }
                sb.append(keyArr[i] + "=" + map.get(keyArr[i]) + "&");
            }
            sb.append("key=" + WeixinPayConfig.key);
            String sign = Md5Util.string2MD5(sb.toString());
            return sign;
        }
    

    然后编写方法 因为是测试 这里使用的是main 实际根据业务更改

    public static void main(String[] args) throws UnsupportedOperationException, ClientProtocolException, IOException {
    		Map<String,Object> map=new HashMap<String,Object>();
    		map.put("appid", WeixinPayConfig.appid); // 公众账号ID
    		map.put("mch_id", WeixinPayConfig.mch_id); // 商户号
    		// map.put("transaction_id", "4200000094201804192059258077"); // 微信订单号
    		// map.put("out_trade_no", "20180419105343760"); // 商户订单号
    		// map.put("out_refund_no", "20180427112536831"); // 商户退款单号
    		map.put("refund_id", "50000506552018042704327042706"); // 微信退款单号
    		map.put("nonce_str", StringUtil.getRandomString(30)); // 随机字符串
    		// map.put("offset", 1); // 偏移量
    		map.put("sign", getSign(map)); // 签名
    		String xml=XmlUtil.genXml(map); 
    		System.out.println(xml);
    		InputStream in=HttpClientUtil.sendXMLDataByPost(url, xml).getEntity().getContent(); // 发现xml消息
    		getElementValue(in);
    	}
    

    运行程序查看结果

    在这里插入图片描述

    出现以上结构就成功了

    出现报错的话 根据提示 更改参数即可

    本文资料+工具类+Demo https://pan.baidu.com/s/1eXpUBxmkVagxg00-sAttFQ

    如果对你有帮助的话,请赏下小弟呗
    在这里插入图片描述

  • 相关阅读:
    webpack.config.js====entry入口文件的配置
    准备工作、安装webpack、初始化项目
    git的安装
    Spring validation 后端校验【转】
    http get post 参数校验
    Spring AOP execution表达式
    spring中Constructor、@Autowired、@PostConstruct的顺序【转】
    Mockito
    Mybatis LIKE模糊查询
    PowerMock
  • 原文地址:https://www.cnblogs.com/chengxiaolong/p/10210814.html
Copyright © 2011-2022 走看看