• JAVA微信支付~


    1,简单说明

      现在好多项目上都需要用到微信支付接口,官方文档上也是简单的描述了下,技术不高深的真的难以理解(我自己看官方文档就看不懂),还是需要自己收集,总结,

    网上看了好多

    有些照着弄最后还是没法成功。接下来我分享下自己的微信支付。这个微信支付的微信公众号或者小程序,都是需要微信认证的,不然无法申请微信支付,这个就不说了

    ,既然到了这一步,相信所有的前提都已经准备好了。直接上代码吧~

    2,java微信支付

    一,首先了解下各个参数的意义

      https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1  这个是微信官方的说明 截取部分,详细的请到这个链接查看。

    二,微信支付代码

    首先创建service层插入如下代码:

    public interface WeiXinPayService {
    
        Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) throws UnsupportedEncodingException;
    }
    

      

    @Service
    public class WeiXinPayServiceImpl implements WeiXinPayService {
        private static final Logger LOGGER = LoggerFactory
                .getLogger(WeiXinPayServiceImpl.class);
    
        @Override
        public Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) {
    
        	String appid = "" // 公众号--》“开发者ID”  微信小程序,或者公众号的APPID
            String mch_id = "" // 商户号,将该值赋值给partner
            String key =  "" // 微信支付商户平台登录)--》“API安全”--》“API密钥”--“设置密钥”(设置之后的那个值就是partnerkey,32位)
            LOGGER.debug(appid);
            LOGGER.debug(mch_id);
           LOGGER.debug(key);
    //        String body = body; // 描述           int total_fee = total_fee; // 支付金额
            String notify_url = ""; // 回调链接
    //        String out_trade_no = IdUtils.genOrderName();//生成订单号
    //        LOGGER.debug("outTradeNo---:"+out_trade_no);
            LOGGER.debug("openid是----"+openid);
            LOGGER.debug("appid---"+appid);
            LOGGER.debug("mch_id---"+mch_id);
            Map<Object, Object> map = null;
            try {
                map = WeiXinAtcion.me.weixinPlay(mch_id, appid,
                        key, openid, total_fee, outTradeNo, notify_url, body);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            return map;
        }
    }
    

      然后创建一个corcontroller层:

    /**
     * <p>Title: WeiXinPayController</p>  
      * <p>Description: 微信支付</p>  
      * @author Mr Lin  
      * @date 2018年7月7日
     */
    @Controller
    public class WeiXinPayController {
        private static final Logger LOGGER = LoggerFactory
                .getLogger(WeiXinPayController.class);
    
        @Autowired
        private WeiXinPayService weiXinPayService;
    
        /**
         * 支付接口
         *
         * @param openid
         * @param body      说明
         * @param total_fee 总价
         * @return
         */
        @RequestMapping("/WeiXinPay")
        public @ResponseBody
        Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) {
            LOGGER.debug("outTradeNo-------------"+outTradeNo);
            LOGGER.debug("openid-------------"+openid);
            LOGGER.debug("body-------------"+body);
            LOGGER.debug("total_fee-------------"+total_fee);
            try {
                return weiXinPayService.WeiXinPay(outTradeNo,openid, body, total_fee);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return TTResult.fail();
        }
    }
    

      以上代码中使用到的工具类:首先WeiXinAtcion

    /**
     * <p>Title: WeiXinAtcion</p>  
      * <p>Description: </p>  
      * @author Mr Lin  
      * @date 2018年7月7日
     */
    @Component
    public class WeiXinAtcion {
        //密钥
        public static final WeiXinAtcion me = new WeiXinAtcion();
        private static final Logger LOGGER = LoggerFactory
                .getLogger(WeiXinAtcion.class);
    
        /**
         * 生成微信订单
         *
         * @param mch_id
         * @param appid
         * @param key
         * @param openid
         * @param total_fee
         * @param out_trade_no
         * @param notify_url
         * @param body
         * @return
         * @throws UnsupportedEncodingException
         * @throws DocumentException
         */
        public SortedMap<Object, Object> weixinPlay(String mch_id, String appid, String key, String openid, int total_fee, String out_trade_no, String notify_url, String body) throws UnsupportedEncodingException, DocumentException, DocumentException {
    
            SortedMap<Object, Object> paymentPo = new TreeMap<Object, Object>();
            paymentPo.put("appid", appid);
            paymentPo.put("mch_id", mch_id);
            paymentPo.put("nonce_str", WXUtil.generate());
            paymentPo.put("body", body);
            paymentPo.put("out_trade_no", out_trade_no);
            paymentPo.put("total_fee", String.valueOf(total_fee));
            paymentPo.put("spbill_create_ip","服务器的ip地址");//此处是公网ip
            paymentPo.put("notify_url", notify_url);
            paymentPo.put("trade_type", "JSAPI");
            paymentPo.put("openid", openid);
            String sign = WXUtil.createSign_ChooseWXPay("UTF-8", paymentPo, key);
            paymentPo.put("sign", sign);
            String param = WXUtil.getRequestXml(paymentPo);
            //将参数通过post请求传给微信端
            String request = WXUtil.httpRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", param);
            Map<String, String> map = new HashMap<String, String>();         // 将解析结果存储在HashMap中
            InputStream in = new ByteArrayInputStream(request.getBytes());
            SAXReader reader = new SAXReader();                              // 读取输入流
            Document document = reader.read(in);
    
            Element root = document.getRootElement();                        // 得到xml根元素
            @SuppressWarnings("unchecked")                                   // 得到根元素的所有子节点
                    List<Element> elementList = root.elements();
            for (Element element : elementList) {
                map.put(element.getName(), element.getText());
            }
            SortedMap<Object, Object> result = new TreeMap<Object, Object>();
    
            LOGGER.debug("第一次签名返回码" + map.get("return_code"));
            LOGGER.debug("第一次签名返回结果" + map.get("return_msg"));
            //第一次签名成功
            if (map.get("return_code").equals("SUCCESS")) {                  // 业务结果
                String nonceStr = WXUtil.generate();
                Long timeStamp = System.currentTimeMillis() / 1000;
                SortedMap<Object, Object> params = new TreeMap<Object, Object>();
    
                params.put("appId", appid);
                params.put("nonceStr", nonceStr);
                params.put("package", "prepay_id=" + map.get("prepay_id"));
                params.put("signType", "MD5");
                params.put("timeStamp", timeStamp);
    
                //第二次签名成功
                LOGGER.debug("开始第二次签名");
                String paySign = WXUtil.createSign_ChooseWXPay("UTF-8", params, key);
                result.put("paySign", paySign);
                result.put("timeStamp", timeStamp + "");
                result.put("nonceStr", nonceStr);
                result.put("out_trade_no", paymentPo.get("out_trade_no"));
                result.put("package", "prepay_id=" + map.get("prepay_id"));
                result.put("return_code", "SUCCESS");
            } else {
                result.put("return_code", "Fail");
                result.put("return_msg", map.get("return_msg"));
    
            }
            return result;
        }
    
    
    
    }
    

      工具类:WXUtil

    public class WXUtil {
    	
    	private static final Logger LOGGER = LoggerFactory.getLogger(WXUtil.class);
    	/**
    	 * 随机字符串
    	 * @return
    	 */
    	public static String generate() {
    		return UUID.randomUUID().toString().trim().replaceAll("-", "");
    	}
    	
    	/**
    	 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
    	 * @param strxml
    	 * @return
    	 * @throws JDOMException
    	 * @throws IOException
    	 */
    	public static Map doXMLParse(String strxml) throws JDOMException, IOException {
    		strxml = strxml.replaceFirst("encoding=".*"", "encoding="UTF-8"");
    
    		if(null == strxml || "".equals(strxml)) {
    			return null;
    		}
    		Map m = new HashMap();
    		InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
    		SAXBuilder builder = new SAXBuilder();
    		Document doc = builder.build(in);
    		Element root = doc.getRootElement();
    		List list = root.getChildren();
    		Iterator it = list.iterator();
    		while(it.hasNext()) {
    			Element e = (Element) it.next();
    			String k = e.getName();
    			String v = "";
    			List children = e.getChildren();
    			if(children.isEmpty()) {
    				v = e.getTextNormalize();
    			} else {
    				v = WXUtil.getChildrenText(children);
    			}
    			m.put(k, v);
    		}
    		
    		//关闭流
    		in.close();
    		
    		return m;
    	}
    	
    	/**
    	 * 获取子结点的xml
    	 * @param children
    	 * @return String
    	 */
    	public static String getChildrenText(List children) {
    		StringBuffer sb = new StringBuffer();
    		if(!children.isEmpty()) {
    			Iterator it = children.iterator();
    			while(it.hasNext()) {
    				Element e = (Element) it.next();
    				String name = e.getName();
    				String value = e.getTextNormalize();
    				List list = e.getChildren();
    				sb.append("<" + name + ">");
    				if(!list.isEmpty()) {
    					sb.append(WXUtil.getChildrenText(list));
    				}
    				sb.append(value);
    				sb.append("</" + name + ">");
    			}
    		}
    		return sb.toString();
    	}
    	/**
    	 * 将请求参数转换为xml格式的string字符串,微信服务器接收的是xml格式的字符串
    	 * @param parameters
    	 * @return
    	 */
    	public static String getRequestXml(SortedMap<Object, Object> parameters) {
    		StringBuffer sb = new StringBuffer();
    		sb.append("<xml>");
    		Set<Entry<Object, Object>> es = parameters.entrySet();
    		Iterator<Entry<Object, Object>> it = es.iterator();
    		while (it.hasNext()) {
    			Entry<Object, Object> entry = (Entry<Object, Object>) it.next();
    			String k = (String) entry.getKey();
    			String v = (String) entry.getValue();
    			
    			if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
    				sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
    			} else {
    				sb.append("<" + k + ">" + v + "</" + k + ">");
    			}
    		}
    		sb.append("</xml>");
    		return sb.toString();
    	}
    
    
    	
    	/**
    	 * sign签名,必须使用MD5签名,且编码为UTF-8
    	 * @param characterEncoding
    	 * @param parameters
    	 * @return
    	 */
    	public static String createSign_ChooseWXPay(String characterEncoding, SortedMap<Object, Object> parameters, String key) {
    		StringBuffer sb = new StringBuffer();
    		Set<Entry<Object, Object>> es = parameters.entrySet();
    		Iterator<Entry<Object, Object>> it = es.iterator();
    		while (it.hasNext()) {
    			Entry<Object, Object> entry = (Entry<Object, Object>) it.next();
    			String k = (String) entry.getKey();
    			Object v = entry.getValue();
    			if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
    				sb.append(k + "=" + v + "&");
    			}
    		}
    		/** 支付密钥必须参与加密,放在字符串最后面 */
    		sb.append("key=" + key);
    		String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    		return sign;
    	}
    
    
    
    
    
        public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
            // 创建SSLContext
            StringBuffer buffer=null;
            try{
            URL url = new URL(requestUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(requestMethod);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();
    
            //往服务器端写内容
            if(null !=outputStr){
                OutputStream os=conn.getOutputStream();
                os.write(outputStr.getBytes("utf-8"));
                os.close();
            }
            // 读取服务器端返回的内容
            InputStream is = conn.getInputStream();
            InputStreamReader isr = new InputStreamReader(is, "utf-8");
            BufferedReader br = new BufferedReader(isr);
            buffer = new StringBuffer();
            String line = null;
            while ((line = br.readLine()) != null) {
                 buffer.append(line);
             }
            }catch(Exception e){
                e.printStackTrace();
            }
            return buffer.toString();
            }   
        public static String urlEncodeUTF8(String source){
            String result=source;
            try {
                result=java.net.URLEncoder.encode(source, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return result;
        }
        	
    	/**
    	 * 退款和企业付款到银行卡
         */
       public static Map<String, String> doRefund(HttpServletRequest request,String url,String data,String partner,String apiclient_certLocation) throws Exception {
    	// p12证书的位置
    			// 微信公众平台:“微信支付”--》“商户信息”--》“交易数据”--》“详情请登录微信支付商户平台查看”(登录)--》“API安全”--》“API证书”--》“下载证书”
    			// 下载证书后将apiclient_cert.p12放在src目录下面(出于安全考虑,请自行下载自己的证书)
    			KeyStore keyStore = KeyStore.getInstance("PKCS12");
    			String url2 = request.getSession().getServletContext().getRealPath("/")
    					+ "cert/" + apiclient_certLocation;
    			LOGGER.debug("url2--->"+url2);
    			File file=new File(url2);
    			FileInputStream instream = new FileInputStream(file);// P12文件目录
            try {
                keyStore.load(instream, partner.toCharArray());
            } finally {
                instream.close();
            }
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, partner.toCharArray()).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            try {
            	HttpPost httpost = new HttpPost(url); // 设置响应头信息
            	httpost.addHeader("Connection", "keep-alive");
            	httpost.addHeader("Accept", "*/*");
            	httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
    //        	httpost.addHeader("Host", "api.mch.weixin.qq.com");
            	httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            	httpost.addHeader("Cache-Control", "max-age=0");
            	httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
        		httpost.setEntity(new StringEntity(data, "UTF-8"));
                CloseableHttpResponse response = httpclient.execute(httpost);
                try {
            	    HttpEntity entity = response.getEntity();
                    String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                    EntityUtils.consume(entity);
                    return WXUtil.doXMLParse(jsonStr);
                } finally {
                    response.close();
                }
            } finally {
                httpclient.close();
            }
        }
    
    
        /**
         * 得到公钥
         * @param request
         * @param url 请求微信端的链接
         * @param data//请求的数据
         * @param partner
         * @param apiclient_certLocation
         * @return
         * @throws Exception
         */
        public static Object getPublicKey(HttpServletRequest request,String url,String data,String partner,String apiclient_certLocation) throws Exception {
            // p12证书的位置
            // 微信公众平台:“微信支付”--》“商户信息”--》“交易数据”--》“详情请登录微信支付商户平台查看”(登录)--》“API安全”--》“API证书”--》“下载证书”
            // 下载证书后将apiclient_cert.p12放在src目录下面(出于安全考虑,请自行下载自己的证书)
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            String url2 = request.getSession().getServletContext().getRealPath("/")
                    + "cert/" + apiclient_certLocation;
            LOGGER.debug("url2--->"+url2);
            File file=new File(url2);
            FileInputStream instream = new FileInputStream(file);// P12文件目录
            try {
                keyStore.load(instream, partner.toCharArray());
            } finally {
                instream.close();
            }
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, partner.toCharArray()).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            try {
                HttpPost httpost = new HttpPost(url); // 设置响应头信息
                httpost.addHeader("Connection", "keep-alive");
                httpost.addHeader("Accept", "*/*");
                httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
                httpost.addHeader("X-Requested-With", "XMLHttpRequest");
                httpost.addHeader("Cache-Control", "max-age=0");
                httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
                httpost.setEntity(new StringEntity(data, "UTF-8"));
                CloseableHttpResponse response = httpclient.execute(httpost);
                try {
                    HttpEntity entity = response.getEntity();
                    String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                    EntityUtils.consume(entity);
                    return jsonStr;
                } finally {
                    response.close();
                }
            } finally {
                httpclient.close();
            }
        }
    
        /** 
         * 是否签名正确,规则是:按参数名称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()) {  
                Entry entry = (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);
        }
    
    	public static Map<String, String> doRefund(String string, String xML, String mCH_ID, String cERT) {
    		// TODO Auto-generated method stub
    		return null;
    	}
    }
    

      返回结果的工具类:TTResult

    public class TTResult {
    	// 定义jackson对象
    	private static final ObjectMapper MAPPER = new ObjectMapper();
    
    	// 响应业务状态
    	private Integer status;  // 200 代表成功, 500 代表失败
    
    	// 响应消息
    	private String msg;
    
    	// 响应中的数据
    	private Object data;
    
    	public static TTResult build(Integer status, String msg, Object data) {
    		return new TTResult(status, msg, data);
    	}
    
    	public static TTResult ok(Object data) {
    		return new TTResult(data);
    	}
    	
    	
    	public static TTResult ok() {
    		return new TTResult(null);
    	}
    
    	public static TTResult fail(){
    		return new TTResult(500,"fail",null);
    	}
    	
    	public static TTResult fail(Object data){
    		return new TTResult(500,"fail",data);
    	}
    	
    	public TTResult() {
    
    	}
    
    	public static TTResult build(Integer status, String msg) {
    		return new TTResult(status, msg, null);
    	}
    	
    	public TTResult(Integer status, String msg, Object data) {
    		this.status = status;
    		this.msg = msg;
    		this.data = data;
    	}
    
    	public TTResult(Object data) {
    		this.status = 200;
    		this.msg = "OK";
    		this.data = data;
    	}
    	
    
    	// public Boolean isOK() {
    	// return this.status == 200;
    	// }
    
    	public Integer getStatus() {
    		return status;
    	}
    
    	public void setStatus(Integer status) {
    		this.status = status;
    	}
    
    	public String getMsg() {
    		return msg;
    	}
    
    	public void setMsg(String msg) {
    		this.msg = msg;
    	}
    
    	public Object getData() {
    		return data;
    	}
    
    	public void setData(Object data) {
    		this.data = data;
    	}
    
    	/**
    	 * 将json结果集转化为TTResult对象
    	 * 
    	 * @param jsonData
    	 *            json数据
    	 * @param clazz
    	 *            TTResult中的object类型
    	 * @return
    	 */
    	public static TTResult formatToPojo(String jsonData, Class<?> clazz) {
    		try {
    			if (clazz == null) {
    				return MAPPER.readValue(jsonData, TTResult.class);
    			}
    			JsonNode jsonNode = MAPPER.readTree(jsonData);
    			JsonNode data = jsonNode.get("data");
    			Object obj = null;
    			if (clazz != null) {
    				if (data.isObject()) {
    					obj = MAPPER.readValue(data.traverse(), clazz);
    				} else if (data.isTextual() || data.isNumber()) {
    					obj = MAPPER.readValue(data.asText(), clazz);
    				}
    			}
    			return build(jsonNode.get("status").intValue(), jsonNode.get("msg")
    					.asText(), obj);
    		} catch (Exception e) {
    			return null;
    		}
    	}
    
    	/**
    	 * 没有object对象的转化
    	 * 
    	 * @param json
    	 * @return
    	 */
    	public static TTResult format(String json) {
    		try {
    			return MAPPER.readValue(json, TTResult.class);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    
    	/**
    	 * Object是集合转化
    	 * 
    	 * @param jsonData
    	 *            json数据
    	 * @param clazz
    	 *            集合中的类型
    	 * @return
    	 */
    	public static TTResult formatToList(String jsonData, Class<?> clazz) {
    		try {
    			JsonNode jsonNode = MAPPER.readTree(jsonData);
    			JsonNode data = jsonNode.get("data");
    			Object obj = null;
    			if (data.isArray() && data.size() > 0) {
    				obj = MAPPER.readValue(data.traverse(), MAPPER.getTypeFactory()
    						.constructCollectionType(List.class, clazz));
    			}
    			return build(jsonNode.get("status").intValue(), jsonNode.get("msg")
    					.asText(), obj);
    		} catch (Exception e) {
    			return null;
    		}
    	}
    }
    

      上面代码中所涉及到的参数,要去微信官方文档看说明,每个参数都有解释的,按照我这个来,微信支付是肯定能跑通的。有啥不足之处请指教。

  • 相关阅读:
    Linux日志清除
    QT vs x64编译
    VC调用QT的UIDLL
    QT UI获得控件ID(HWND)
    安卓黑客工具
    VMware+Windgb+Win7内核驱动调试
    ubuntu 键盘布局修改
    魅族MX3 smart bar处失灵
    把NDK的工具链提取出来单独使用
    arm指令版本
  • 原文地址:https://www.cnblogs.com/lxwt/p/10213245.html
Copyright © 2020-2023  润新知