天天看点

微信支付和支付宝支付

微信支付和支付宝支付开发案列

以下基础方法可以参考支付宝支付开放平台(https://doc.open.alipay.com/docs/doc.htm?treeId=204&articleId=105051&docType=1)

和微信支付平台相关文档(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3)

//===========================【weixinPay】==========2017.01.09=======by.Tommy==================
	 String timeMillis = String.valueOf(System.currentTimeMillis() / 1000);
	 String randomString = PayCommonUtil.getRandomString(32);
	 public static String wxnotify = "/commodityOrderbase/weixinpayNotify.do";
	 public static String alipayNotifyUrl = "/commodityOrderbase/alipayNotify.do";
	 
	 /**
	 * @throws UnsupportedEncodingException 
	  * 
	     * @Title: weixinPrePay  
	     * @Description: 调用微信支付系统 ---统一下单API()
	     * API地址:https://pay.weixin.QQ.com/wiki/doc/api/jsapi.php?chapter=9_1
	     * 看文档,主要流程就是把20个左右的参数封装为xml格式发送到微信给的接口地址,然后就可以获取到返回的内容了,如果成功里面就有支付所需要的预支付ID
	     * @param @param trade_no
	     * @param @param totalAmount
	     * @param @param description
	     * @param @param openid
	     * @param @param sym
	     * @param @param request
	     * @param @return    参数  
	     * @return Map<String,String>    返回类型  
	     * @throws
	  */
	 @SuppressWarnings("unchecked")
	 public Map<String, String> weixinPrePay(String trade_no,String totalAmount,  
		 String description, String openid,String sym, HttpServletRequest request) throws UnsupportedEncodingException { 
	     SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();  
		 parameterMap.put("appid", PayCommonUtil.APPID);  //应用ID
		 parameterMap.put("mch_id", PayCommonUtil.MCH_ID);  //商户号
		 parameterMap.put("nonce_str", randomString);  
		 /**
		  * 商品描述  商品描述交易字段格式根据不同的应用场景按照以下格式: APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
		  */
		 parameterMap.put("body", description);  
		 //商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
		 parameterMap.put("out_trade_no", trade_no); 
		 //货币类型
		 parameterMap.put("fee_type", "CNY");  
		 //订单总金额  支付金额单位为【分】,参数值不能带小数。
		 parameterMap.put("total_fee", totalAmount);  
		 //用户端实际ip
		 parameterMap.put("spbill_create_ip", request.getRemoteAddr()); 
		 /**
		  * 通知地址  接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
		  */
		 //parameterMap.put("notify_url", sym + wxnotify);
		 String basePath = request.getScheme() + "://"
                      + request.getServerName() + ":" + request.getServerPort()+ request.getContextPath();
          parameterMap.put("notify_url", basePath + wxnotify);   
		 //交易类型 支付类型  
		 parameterMap.put("trade_type", "APP");
		 //trade_type为JSAPI是 openid为必填项
		 //parameterMap.put("openid", openid); 
		 //生成签名  
		 String sign = PayCommonUtil.createSign("UTF-8", parameterMap);   
		 parameterMap.put("sign", sign);  
		 String requestXML = PayCommonUtil.getRequestXml(parameterMap);  
		 //发起HTTP POST 请求 并获得微信支付系统返回结果
		 String result = PayCommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST",requestXML);    
		 Map<String, String> map = null;  
		 try {  
			 map = PayCommonUtil.doXMLParse(result);  
			 log.info("【微信APP支付】请求微信统一下单接口结果:"+result);
		 } catch (JDOMException e) {  
			 e.printStackTrace();  
		 } catch (IOException e) {  
			 e.printStackTrace();  
		 }  
		 return map;          
	 }
	 
	 
	 /**
	 * @throws Exception 
	  * 
	     * @Title: weixinRefund  
	     * @Description: 微信退款 
	     * @param @param trade_no 商户侧传给微信的订单号
	     * @param @param out_refund_no 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
	     * @param @param totalAmount
	     * @param @param openid
	     * @param @param sym
	     * @param @param request
	     * @param @return
	     * @param @throws UnsupportedEncodingException    参数  
	     * @return Map<String,String>    返回类型  
	     * @throws
	  */
	 @SuppressWarnings("unchecked")
	 public Map<String, String> weixinRefund(String trade_no,String out_refund_no,String totalAmount
			 ,HttpServletRequest request) throws Exception { 
	     SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();  
		 parameterMap.put("appid", PayCommonUtil.APPID);  //应用ID
		 parameterMap.put("mch_id", PayCommonUtil.MCH_ID);  //商户号
		 parameterMap.put("nonce_str", randomString);  
		 parameterMap.put("out_trade_no", trade_no); 
		 parameterMap.put("out_refund_no", out_refund_no);  
		 //订单总金额  支付金额单位为【分】,参数值不能带小数。
		 parameterMap.put("total_fee", totalAmount);  
		 //订单退款总金额  支付金额单位为【分】,参数值不能带小数。
		 parameterMap.put("refund_fee", totalAmount);  
		 //操作员帐号, 默认为商户号 
		 parameterMap.put("op_user_id", PayCommonUtil.MCH_ID);  
		 //生成签名  
		 String sign = PayCommonUtil.createSign("UTF-8", parameterMap);   
		 parameterMap.put("sign", sign);  
		 String requestXML = PayCommonUtil.getRequestXml(parameterMap);  
		 //发起HTTP POST 请求 并获得微信支付系统返回结果
		 String result = PayCommonUtil.httpsRequest2("https://api.mch.weixin.qq.com/secapi/pay/refund", "POST",requestXML);     
		 Map<String, String> map = null;  
		 try {  
			 map = PayCommonUtil.doXMLParse(result);  
			 log.info("【微信APP支付】提交微信退款业务结果:"+result); 
		 } catch (JDOMException e) {  
			 log.info("【微信APP支付】提交微信退款业务结果:"+result);  
		 } catch (IOException e) {  
			 log.info("【微信APP支付】提交微信退款业务结果:"+result); 
		 }  
		 return map;          
	 }
	 
	 
	 /**
	  * 
	     * @Title: weixinpayNotify  
	     * @Description: 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 
	     * @param @param request
	     * @param @param response
	     * @param @return
	     * @param @throws UserExecuteFailedException
	     * @param @throws IOException
	     * @param @throws JDOMException    参数  
	     * @return String    返回类型  
	     * @throws
	  */
	 @RequestMapping(value = "weixinpayNotify.do")
	 public String weixinpayNotify(HttpServletRequest request, HttpServletResponse response) 
	        throws UserExecuteFailedException, IOException, JDOMException {
		// /
		 log.info("=【微信APP支付】异步通知=");
		 InputStream inStream = request.getInputStream();
		 ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
		 byte[] buffer = new byte[1024];
		 int len = 0;
		 while ((len = inStream.read(buffer)) != -1) {
			 outSteam.write(buffer, 0, len);
		 }
		 String resultxml = new String(outSteam.toByteArray(), "UTF-8");
		 Map<String, String> params = PayCommonUtil.doXMLParse(resultxml); 
		 outSteam.close();
		 inStream.close();
		 if (!PayCommonUtil.isTenpaySign(params)) {
			 log.info("【微信APP支付】异步通知结果:fail"); 
			 return "fail";
		 }else{
		  // ------------------------------
		  // 处理业务开始
		  // ------------------------------
		  // 此处处理订单状态,结合自己的订单数据完成订单状态的更新
		  // ------------------------------
		  String out_trade_no = params.get("out_trade_no"); //商户系统的订单号,与请求一致。
		  String transaction_id = params.get("transaction_id"); //商户系统的订单号,与请求一致。
		  CommodityOrderBase cob =  orderBaseService.queryByOrderNum(out_trade_no);
		  if(cob != null){
			  //避免付款后立马取消了订单,异步通知还未处理,仍将取消状态改为待发货的bug 
			  if(cob.getOrderStatus() == 0 ){
				  cob.setOrderStatus(1); //订单状态:0 待付款 1待发货 2已发货 3已完成 4 已取消  
				  cob.setPayTime(DateUtils.formatNowDate());  //支付时间 
				  cob.setTransactionId(transaction_id); //微信支付订单号 
				  cob.setCheckCancel(1); //是否可以退款:1可以2订单处理中 
				  orderBaseService.updateCommodityOrderBase(cob);   
			  } 
		  }
		  // 处理业务完毕
		  // ------------------------------
		   log.info("【微信APP支付】异步通知结果:"+"订单号为:"+out_trade_no);  
		   return "success";
		  }
		// /
	 }
	 
           
//======================【alipay】=======================================================

	 /**
	 * @throws AlipayApiException 
	  * 
	     * @Title: aliPay  
	     * @Description: 【支付宝APP支付】下订单
	     * @param body   对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。  示例:Iphone6 16G
	     * @param subject 商品的标题/交易标题/订单标题/订单关键字等。 示例:大乐透
	     * @param out_trade_no   商户网站唯一订单号
	     * @param total_amount   商户网站唯一订单号订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] 示例:9.00
	     * @param request 
	     * @param @return
	     * @param @throws UnsupportedEncodingException    参数  
	     * @return Map<String,String>    返回类型  
	     * @throws
	  */
	 @SuppressWarnings("unchecked")
	 public String aliPay(String body, String subject, String out_trade_no, String total_amount
			 , HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException { 
		 log.info("【支付宝APP支付】下订单开始==========-》");
		 //1.公共参数=========->
         Map<String, String> param = new HashMap<String, String>();
         param.put("app_id", AliPayUtil.APPID); 
         param.put("method", "alipay.trade.app.pay");
         param.put("format", AliPayUtil.FORMAT);
         param.put("charset",AliPayUtil.CHARSET);
         param.put("sign_type", AliPayUtil.SIGN_TYPE);
         //发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 
         param.put("timestamp", DateUtils.formatNowDate()); 
         param.put("version", "1.0");
         String notify_url = request.getScheme() + "://"
                 + request.getServerName() + ":" + request.getServerPort()+ request.getContextPath()
                 +alipayNotifyUrl;
         param.put("notify_url", notify_url); 
         
         //2.业务参数=========->
         Map<String, String> pcont  = new HashMap<String, String>();
         pcont.put("body", body);
         pcont.put("subject", subject);
         pcont.put("out_trade_no", out_trade_no);
         /**
          * 该笔订单允许的最晚付款时间,逾期将关闭交易。
          * 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 
          * 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
          */
         pcont.put("timeout_express", "90m"); 
         pcont.put("total_amount", total_amount);
         pcont.put("seller_id", AliPayUtil.PARTNER_ID); 
         //销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY 
         pcont.put("product_code", "QUICK_MSECURITY_PAY");

         //请求参数按照key=value&key=value方式拼接的未签名原始字符串: 
         JSONObject bizcontentJson= JSONObject.fromObject(pcont);
         //业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 
         param.put("biz_content", bizcontentJson.toString()); // 业务请求参数  不需要对json字符串转义 
         
         /**
          * 方式一==============================================================================
          */
         param.put("sign", AliPayUtil.getSign(param, AliPayUtil.APP_PRIVATE_KEY)); // 业务请求参数
         String orderStr = AliPayUtil.getSignEncodeUrl(param, true);
         log.info("【支付宝APP支付】请求参数为:"+orderStr);   
         return orderStr;       
         
         /**
          * 方式二==============================================================================
          */
        /* //3.对未签名原始字符串进行签名=========->       
         String rsaSign = AlipaySignature.rsaSign(param, AliPayUtil.APP_PRIVATE_KEY, "UTF-8"); 
         Map<String, String> map4 = new HashMap<String, String>();
         map4.put("app_id", AliPayUtil.APPID); 
         map4.put("method", "alipay.trade.app.pay");
         map4.put("format",  AliPayUtil.FORMAT);
         map4.put("charset", AliPayUtil.CHARSET);
         map4.put("sign_type", AliPayUtil.SIGN_TYPE);
         map4.put("timestamp", URLEncoder.encode(DateUtils.formatNowDate(),"UTF-8"));
         map4.put("version", "1.0");
         map4.put("notify_url",  URLEncoder.encode(notify_url,"UTF-8")); 
         
         //4.最后对请求字符串的所有一级value(biz_content作为一个value)进行encode,编码格式按请求串中的charset为准,没传charset按UTF-8处理,获得最终的请求字符串: 
         map4.put("biz_content", URLEncoder.encode(bizcontentJson.toString(), "UTF-8"));
         Map par = AlipayCore.paraFilter(map4); //除去数组中的空值和签名参数
         String json4 = AlipayCore.createLinkString(map4);   //拼接后的字符串 
         json4 = json4 + "&sign=" + URLEncoder.encode(rsaSign, "UTF-8");    
         log.info("【支付宝APP支付】请求参数为:"+json4.toString());    
         return json4.toString();       */
         
	 }
	 
	 /**
	  * 
      * @Title: alipayNotify  
      * @Description: TODO 支付宝APP支付异步通知
      *  对于APP支付产生的交易,支付宝会根据原始支付API中传入的异步通知地址notify_url,通过POST请求的形式将支付结果作为参数通知到商户系统。
      *  参考链接:
      *  http://blog.csdn.net/fengshizty/article/details/53215196  
      *  https://doc.open.alipay.com/docs/api.htm?spm=a219a.7395905.0.0.sS4WFQ&docType=4&apiId=759
      *  https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.y9tReT&treeId=193&articleId=105303&docType=1
      * @param @param request
      * @param @throws Exception    参数  
      * @return void    返回类型  
      * @throws
	  */
	 @ResponseBody
	 @RequestMapping(value = "/alipayNotify.do",method={RequestMethod.POST})
	 public String alipayNotify( HttpServletRequest request ){  
		 /**
		  * 获取到返回的所有参数 先判断是否交易成功trade_status 再做签名校验
		  * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号
		  * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额)
		  * 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
		  * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,
		  *   正确的进行不同的业务处理,并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
		  *   
          * 交易状态:  WAIT_BUYER_PAY 交易创建,等待买家付款   TRADE_CLOSED    未付款交易超时关闭,或支付完成后全额退款
          *          TRADE_SUCCESS	交易支付成功                      TRADE_FINISHED  交易结束,不可退款
          */
         String trade_status = request.getParameter("trade_status");
         String out_trade_no = request.getParameter("out_trade_no");
         String trade_no = request.getParameter("trade_no"); //支付宝交易凭证号
		 log.info("【支付宝APP支付】支付异步通知结果:"+trade_status+" ====-》订单号为:"+out_trade_no);     
		 if ("TRADE_SUCCESS".equals(trade_status)) {
	            Enumeration<?> pNames = request.getParameterNames();
	            Map<String, String> param = new HashMap<String, String>();
	            try {
	            	 //将异步通知中收到的待验证所有参数都存放到map中
	                while (pNames.hasMoreElements()) {
	                    String pName = (String) pNames.nextElement();
	                    param.put(pName, request.getParameter(pName)); 
	                }
	                //log.info("【支付宝APP支付】异步通知返回参数--param:"+param);
	                boolean signVerified = AlipaySignature.rsaCheckV1(param, AliPayUtil.ALIPAY_PUBLIC_KEY,
	                        AliPayUtil.CHARSET);  
	                log.info("【支付宝APP支付】校验签名是否正确--signVerified:"+signVerified); 
	                //调用SDK验证签名
	                if (signVerified) {
	                    // TODO 验签成功后
	                    // 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
                	   CommodityOrderBase cob =  orderBaseService.queryByOrderNum(out_trade_no);
            		   if(cob != null){
            			  //避免付款后立马取消了订单,异步通知还未处理,仍将取消状态改为待发货的bug
            			  if(cob.getOrderStatus() == 0){
            				  cob.setOrderStatus(1); //订单状态:0 待付款 1待发货 2已发货 3已完成 4 已取消  
            				  cob.setPayTime(DateUtils.formatNowDate());  //支付时间 
                			  cob.setAlipayTradeNo(trade_no); //支付宝交易凭证号
                			  cob.setCheckCancel(1); //是否可以退款:1可以2订单处理中
                			  orderBaseService.updateCommodityOrderBase(cob);     
            			  } 
            			 
            		   }
            		   
            		   log.info("【支付宝APP支付】订单支付成功:" + JSON.toJSONString(param));   
            		   return "success"; 
	                } else {
	                   // TODO 验签失败则记录异常日志,并在response中返回failure.
	                   log.info("【支付宝APP支付】订单支付失败:" + JSON.toJSONString(param));   
	                   return "failure"; 
	                } 
	            } catch (Exception e) {
	            	   log.info("【支付宝APP支付】订单支付抛出异常:" + e.getMessage());     
	            	   return "failure";
	            }
	           
	        }else{
	            return "failure";
	        }
	     }

	 /**
	  * 
	     * @Title: alipayRefund  
	     * @Description: TODO【阿里APP支付】申请退款
	     * @param trade_no  支付宝交易号,和商户订单号不能同时为空
	     * @param out_trade_no 	订单支付时传入的商户订单号,不能和 trade_no同时为空。
	     * @param refund_amount 需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数
	     * @param request
	     * @param @return
	     * @param @throws Exception    参数  
	     * @return Map<String,String>    返回类型  
	     * @throws
	  */
	 public Map<String, String> alipayRefund(String alipay_trade_no,String out_trade_no,String refund_amount
			 ,HttpServletRequest request) throws Exception { 
		 	// 统一收单交易退款接口
			AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();  
			 //只需要传入业务参数
	        Map<String, Object> param = new HashMap<>();
	        param.put("trade_no", alipay_trade_no);
	        param.put("out_trade_no", out_trade_no); 
	        param.put("refund_amount", refund_amount);
	        param.put("refund_reason", "正常退款");//退款的原因说明
	        alipayRequest.setBizContent(JSON.toJSONString(param)); // 请求参数的集合 ,不需要对json字符串转义 
	        
	        //返回支付宝退款信息
	        Map<String, String> restmap = new HashMap<>();
	        try {
	        	AlipayTradeRefundResponse alipayResponse = AlipayAPIClientFactory.getAlipayClient().execute(alipayRequest);
	            if (alipayResponse.isSuccess()) {
	            	/**
	            	 * 调用成功,则处理业务逻辑
	            	 * code:
	            	 * 10000 接口调用成功
	            	 * 20000 服务不可用
	            	 * 查看地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105806&docType=1
	            	 */
	                if ("10000".equals(alipayResponse.getCode())) {
	                    restmap.put("out_trade_no", alipayResponse.getOutTradeNo());
	                    restmap.put("trade_no", alipayResponse.getTradeNo());
	                    restmap.put("buyer_logon_id", alipayResponse.getBuyerLogonId());// 用户的登录id
	                    restmap.put("gmt_refund_pay", DateUtils.formatWithDefaultPattern(alipayResponse.getGmtRefundPay())); //退款支付时间
	                    restmap.put("buyer_user_id", alipayResponse.getBuyerUserId());// 买家在支付宝的用户id
	                    restmap.put("return_code", "SUCCESS");
	                    restmap.put("return_msg", "SUCCESS");
	                    log.info("【支付宝APP支付】订单退款结果:退款成功");
	                } else {
	                	log.info("【支付宝APP支付】订单退款失败:" + alipayResponse.getMsg() + ":" + alipayResponse.getSubMsg());
	                	restmap.put("return_code", "FAIL");
	                    restmap.put("return_msg", "支付宝申请退款失败原因:"+alipayResponse.getMsg()+" 错误码查看地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105806&docType=1"); 
	                }
	            }
	        } catch (AlipayApiException e) {
	        	 restmap.put("return_code", "FAIL");  
	        	 log.info("【支付宝APP支付】订单退款失败:"+e.getErrMsg()); 
	        }
	        return restmap;           
	 }
	 
           

提交订单部分逻辑代码

//1 微信支付 2支付宝支付
if(payway.equals(1)){
	log.info("=【微信APP支付】进入微信支付统一下单接口=");
	//微信支付  总金额单位为 分
	String  weixinTotalAmount = ""+ (int)(StringUtil.getDouble(totalAmount)*100);
	Map<String, String> map = weixinPrePay(ob.getOrderNum(),weixinTotalAmount, description,"", "", request); 
	String return_code = map.get("return_code");
	String result_code = map.get("result_code"); //业务结果  参考链接:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=9_1
	String return_msg = map.get("return_msg"); 
	if(return_code.equalsIgnoreCase("SUCCESS") && result_code.equalsIgnoreCase("SUCCESS")){
		//返回给APP端相关参数,用于调用微信支付
		SortedMap<String, Object> finalpackage = new TreeMap<String, Object>();
		finalpackage.put("appid", PayCommonUtil.APPID);  
		finalpackage.put("partnerid", PayCommonUtil.MCH_ID); 
		finalpackage.put("prepayid", map.get("prepay_id"));   
		finalpackage.put("package", "Sign=WXPay"); 
		finalpackage.put("noncestr", PayCommonUtil.getRandomString(32));     
		finalpackage.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));     
		String sign = PayCommonUtil.createSign("UTF-8", finalpackage);      
		finalpackage.put("sign", sign);
		finalpackage.put("packages", "Sign=WXPay");  //用于Android避免关键字 ,意义与上package同等  
		maps.putAll(finalpackage);  
								
		//修改订单状态
		ob.setOrderStatus(0); //订单状态:0 待支付  1待发货 2已发货 3已完成 4 已取消 
		ob.setPayway(1); 
		ob.setCheckCancel(2); //是否可以退款:1可以2订单处理中 
		orderBaseService.updateCommodityOrderBase(ob); 
	}else{
		errorCode = 222;  //下单失败 
		log.info("【微信APP支付】请求统一下单接口失败,订单号为:"+ob.getOrderNum()+" 原因为:"+return_msg);   
	}
							
}else if(payway.equals(2)){ 
	//支付宝  总金额单位为 元  保留小数点后2位
	String alipaySign = aliPay(description, "快递费", ob.getOrderNum(), totalAmount, request);      
	maps.put("alipaySign", alipaySign);   
	//修改订单支付类型
	ob.setPayway(2);   
	ob.setCheckCancel(2); //是否可以退款:1可以2订单处理中  
	orderBaseService.updateCommodityOrderBase(ob);  
} 
           

取消订单部分逻辑代码

int payway  = cob.getPayway(); //支付方式:1 微信 2 支付宝      
if(payway == 1){
	log.info("【微信APP支付】开始调用微信申请退款接口,订单号为:"+cob.getOrderNum());
	Double totalfee = cob.getTotalFee();
	String totalAmount = ""+(int)(totalfee*100); 
	Map<String, String> map = weixinRefund(orderNum, outRefundNo, totalAmount, request);
	String return_code = map.get("return_code"); //SUCCESS/FAIL
	String return_msg = map.get("return_msg"); //返回信息,如非空,为错误原因  签名失败  参数格式校验错误
	String result_code = map.get("result_code"); //SUCCESS/FAIL  SUCCESS退款申请接收成功,结果通过退款查询接口查询  FAIL 提交业务失败
	String err_code = map.get("err_code");
	if(!StringUtil.nullOrBlank(return_code) && return_code.equalsIgnoreCase("SUCCESS") && result_code.equalsIgnoreCase("SUCCESS")){
		log.info("【微信APP支付】原路退款至微信成功,商户订单号为:"+cob.getOrderNum());
										
		//更改订单为【已取消】
		orderBaseService.delOrderBase(orderNum, userId,diamond,outRefundNo); 
		cob.setOrderStatus(4);
		cob.setCancelTime(DateUtils.formatNowDate());
		cob.setOutRefundNo(outRefundNo);
		orderBaseService.updateCommodityOrderBase(cob); 
	}else{
		errorCode = 223; //微信申请退款失败
		log.info("【微信APP支付】原路退款至微信失败,商户订单号为:"+cob.getOrderNum()+"原因为:"+return_msg+" 错误码:"+err_code); 
		log.info("错误码参见:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=9_4");  
	}
}else if(payway == 2){
	log.info("【支付宝APP支付】原路退款至支付宝,商户订单号为:"+cob.getOrderNum());
	Map<String, String> map = alipayRefund(cob.getAlipayTradeNo(), cob.getOrderNum(), ""+cob.getTotalFee(), request); 
	String return_code = map.get("return_code");
	String return_msg = map.get("return_msg");
	String gmt_refund_pay = map.get("gmt_refund_pay");
	if(!StringUtil.nullOrBlank(return_code) && return_code.equals("SUCCESS")){
		log.info("【支付宝APP支付】申请原路退款至支付宝成功,商户订单号为:"+cob.getOrderNum()+"  退款支付时间为:"+gmt_refund_pay);
										
		//更改订单为【已取消】
		orderBaseService.delOrderBase(orderNum, userId,diamond,outRefundNo);  
		cob.setOrderStatus(4);
		cob.setCancelTime(DateUtils.formatNowDate());  
		cob.setOutRefundNo(outRefundNo); 
		orderBaseService.updateCommodityOrderBase(cob);  
	}else{
		errorCode = 224; //支付宝申请退款失败
		log.info("【支付宝APP支付】申请原路退款至支付宝失败,商户订单号为:"+cob.getOrderNum()+"原因为:"+return_msg); 
	}
}else{
	//更改订单为【已取消】
	orderBaseService.delOrderBase(orderNum, userId,diamond,outRefundNo); 
}
           

PayCommonUtil.java

package com.skin.pay.weixin;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import com.skin.util.MD5Util;

/**
 * 
    * @ClassName: PayCommonUtil  
    * @Description: 微信支付帮助类  
    * 参考链接:http://www.cnblogs.com/007sx/p/5811137.html  https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
    * @author by.Tommy  
    * @date 2017年1月9日  
    *
 */
public class PayCommonUtil {
	//微信支付参数配置  
    public static String API_KEY="*****";  
    public static String APPID="****";  
    public static String MCH_ID="****";   
    
    protected static Logger log = Logger.getLogger(PayCommonUtil.class);
    
    /**
     * 
        * @Title: getRandomString  
        * @Description: 随机字符串生成  
        * @param @param length  生成字符串的长度      
        * @param @return    参数  
        * @return String    返回类型  
        * @throws
     */
    public static String getRandomString(int length) {
         String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";         
         Random random = new Random();         
         StringBuffer sb = new StringBuffer();         
         for (int i = 0; i < length; i++) {         
             int number = random.nextInt(base.length());         
             sb.append(base.charAt(number));         
         }         
         return sb.toString();         
    }   
    
    /**
     * 
        * @Title: getRequestXml  
        * @Description: 请求xml组装  
        * @param @param parameters
        * @param @return    参数  
        * @return String    返回类型  
        * @throws
     */
    public static String getRequestXml(SortedMap<String,Object> parameters){  
          StringBuffer sb = new StringBuffer();  
          sb.append("<xml>");  
          Set es = parameters.entrySet();  
          Iterator it = es.iterator();  
          while(it.hasNext()) {  
              Map.Entry entry = (Map.Entry)it.next();  
              String key = (String)entry.getKey();  
              String value = (String)entry.getValue();  
              if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) {  
                  //sb.append("<"+key+">"+"<![CDATA["+value+"]]></"+key+">");  
                  sb.append("<"+key+">"+value+"</"+key+">");  
              }else {  
                  sb.append("<"+key+">"+value+"</"+key+">");  
              } 
          }  
          sb.append("</xml>");  
          return sb.toString();  
    } 
    
    /**
     * 
        * @Title: createSign  
        * @Description: 生成签名  
        * @param characterEncoding
        * @param parameters
        * @param @return    参数  
        * @return String    返回类型  
        * @throws
     */
    public static String createSign(String characterEncoding,SortedMap<String,Object> parameters){  
          StringBuffer sb = new StringBuffer();  
          Set es = parameters.entrySet();  
          Iterator it = es.iterator();  
          while(it.hasNext()) {  
              Map.Entry entry = (Map.Entry)it.next();  
              String k = (String)entry.getKey();  
              Object v = entry.getValue();  
              if(null != v && !"".equals(v)  
                      && !"sign".equals(k) && !"key".equals(k)) {  
                  sb.append(k + "=" + v + "&");  
              }  
          }  
          sb.append("key=" + API_KEY);  
          String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();  
          return sign;  
    }
    
    
    /**
     * 验证回调签名
     * @param packageParams
     * @param key
     * @param charset
     * @return
     */
    public static boolean isTenpaySign(Map<String, String> map) {
    	   String charset = "UTF-8";
    	   String signFromAPIResponse = map.get("sign");
    	   if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
    		   log.info("==API返回的数据签名数据不存在,有可能被第三方篡改!!!==");  
    		   return false;
    	    }
    	   //System.out.println("服务器回包里面的签名是:" + signFromAPIResponse); 
    	   //过滤空 设置 TreeMap
    	   SortedMap<String,String> packageParams = new TreeMap<>();
    	   for (String parameter : map.keySet()) {
    		   String parameterValue = map.get(parameter);
    		   String v = "";
    		   if (null != parameterValue) {
    			   v = parameterValue.trim();
    			   }
    		   packageParams.put(parameter, v);
    		  }
    	    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);
            //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
            //算出签名
            String resultSign = "";
            String tobesign = sb.toString();
            if (null == charset || "".equals(charset)) {
            	//resultSign = MD5Util.MD5Encode(tobesign,characterEncoding).toUpperCase();
            	resultSign = MD5Util.MD5Encode(tobesign,"").toUpperCase();
            } else {
            	try {
					//resultSign = MD5Util.MD5Encode(tobesign,characterEncoding).toUpperCase();
					resultSign = MD5Util.MD5Encode(tobesign,"").toUpperCase();
				} catch (Exception e) {
					//resultSign = MD5Util.MD5Encode(tobesign,characterEncoding).toUpperCase();
					resultSign = MD5Util.MD5Encode(tobesign,"").toUpperCase(); 
				} 
            }
            String tenpaySign = ((String)packageParams.get("sign")).toUpperCase(); 
            return tenpaySign.equals(resultSign);
    }
    
    
    /**
     * 发起HTTP请求方法 
     */ 
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {  
          try {  
              URL url = new URL(requestUrl);  
              HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
              conn.setDoOutput(true);  
              conn.setDoInput(true);  
              conn.setUseCaches(false);  
              // 设置请求方式(GET/POST)  
              conn.setRequestMethod(requestMethod);  
              conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");  
              // 当outputStr不为null时向输出流写数据  
              if (null != outputStr) {  
                  OutputStream outputStream = conn.getOutputStream();  
                  // 注意编码格式  
                  outputStream.write(outputStr.getBytes("UTF-8"));  
                  outputStream.close();  
              }  
              // 从输入流读取返回内容  
              InputStream inputStream = conn.getInputStream();  
              InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");  
              BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  
              String str = null;  
              StringBuffer buffer = new StringBuffer();  
              while ((str = bufferedReader.readLine()) != null) {  
                  buffer.append(str);  
              }  
              // 释放资源  
              bufferedReader.close();  
              inputStreamReader.close();  
              inputStream.close();  
              inputStream = null;  
              conn.disconnect();  
              return buffer.toString();  
          } catch (ConnectException ce) {  
             log.info("https连接超时:{}"+ ce);   
          } catch (Exception e) {  
        	 log.info("https请求异常:{}"+ e);   
          }  
          return null;  
    }  
      
    /**
     * 退款的请求方法  
     * 
     * 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
     * 注意:
     * 1、交易时间超过一年的订单无法提交退款;
     * 2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交,要采用原来的退款单号。总退款金额不能超过用户实际支付金额。
     * 3、退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
     * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund
     */
    public static String httpsRequest2(String requestUrl, String requestMethod, String outputXml) throws Exception {  
    	   /*----1.读取证书文件,这一段是直接从微信支付平台提供的demo中copy的,所以一般不需要修改---- */
          KeyStore keyStore  = KeyStore.getInstance("PKCS12");  
          StringBuilder res = new StringBuilder("");  
          FileInputStream instream = new FileInputStream(new File("/home/weixin/apiclient_cert.p12"));   
          try {  
              keyStore.load(instream, MCH_ID.toCharArray());
          } finally {  
              instream.close();  
          }  
          // Trust own CA and all self-signed certs  
          SSLContext sslcontext = SSLContexts.custom()  
                  .loadKeyMaterial(keyStore, MCH_ID.toCharArray())  
                  .build();  
          // Allow TLSv1 protocol only  
          SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(  
                  sslcontext,  
                  new String[] { "TLSv1" },  
                  null,  
                  SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);  
          CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();  
          try {  
        	   /*----2.发送数据到微信的退款接口---- */
              HttpPost httpost = new HttpPost(requestUrl);   
              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) ");  
              StringEntity stringEntity = new StringEntity(outputXml ,Consts.UTF_8);  
              httpost.setEntity(stringEntity);   
              CloseableHttpResponse response = httpclient.execute(httpost);   
              try {  
                  HttpEntity entity = response.getEntity();   
                  if (entity != null) {  
                      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));  
                      String text = "";
                      res.append(text);  
                      while ((text = bufferedReader.readLine()) != null) {  
                          res.append(text);  
                          //System.out.println(text);    
                      }  
                  }  
                  EntityUtils.consume(entity);    
              } finally {  
                  response.close();  
              }  
          } finally {  
              httpclient.close();  
          }  
          return  res.toString();  
            
    }
      
    /**
     * 
        * @Title: doXMLParse  
        * @Description: xml解析  
        * @param strxml
        * @param @return
        * @param @throws JDOMException
        * @param @throws IOException    参数  
        * @return Map    返回类型  
        * @throws
     */
    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 = getChildrenText(children);  
              }  
              m.put(k, v);  
          }  
          //关闭流  
          in.close();  
          return m;  
    }  
      
    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(getChildrenText(list));  
                  }  
                  sb.append(value);  
                  sb.append("</" + name + ">");  
              }  
          }  
          return sb.toString();  
    }
	
}
           

AliPayUtil.java

package com.skin.pay.alipay;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

/**
 *  参考文档: https://doc.open.alipay.com/docs/api.htm?spm=a219a.7395905.0.0.Ewddf6&docType=4&apiId=757
    * @ClassName: AliPayUtil  
    * @Description: 阿里支付宝支付帮助类  
    * @author by.Tommy  
    * @date 2017年1月11日  
    *
 */
public class AliPayUtil {
	
	 protected static Logger log = Logger.getLogger(AliPayUtil.class);
	 
	//=============阿里支付宝支付参数配置  ===================================
    //支付宝网关(固定)
    public static String ALIPAY_GATEWAY="https://openapi.alipay.com/gateway.do"; 
    //APPID即创建应用后生成
    public static String APPID="*****";   
    //开发者应用私钥,由开发者自己生成
    public static String APP_PRIVATE_KEY="******";   
    //参数返回格式,只支持json
    public static String FORMAT="json";   
    //请求和签名使用的字符编码格式,支持GBK和UTF-8
    public static String CHARSET="utf-8";     
    //应用公钥,   由支付宝生成
    //public static String ALIPAY_PUBLIC_KEY="*******";
    //支付宝公钥,由支付宝生成
    public static String ALIPAY_PUBLIC_KEY="*********";
    //商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
    public static String SIGN_TYPE="RSA";  
    //合作伙伴身份(PID) 查看地址:https://openhome.alipay.com/platform/keyManage.htm?keyType=partner
    public static String PARTNER_ID="*******";    
   
    
    private static final String ALGORITHM = "RSA";
    private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
    private static final String DEFAULT_CHARSET = "UTF-8";

    
    public static String sign(String content, String privateKey) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
                    Base64.decode(privateKey));
            KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature
                    .getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(DEFAULT_CHARSET));
            byte[] signed = signature.sign();
            return new String(Base64.encode(signed)); 
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 对支付参数信息进行签名
     * @param map  待签名授权信息
     * @return
     */
    public static String getSign(Map<String, String> map, String rsaKey) {
        List<String> keys = new ArrayList<String>(map.keySet());
        // key排序
        Collections.sort(keys);
        StringBuilder authInfo = new StringBuilder();
        boolean first = true;
        for (String key : keys) {
            if (first) {
                first = false;
            } else {
                authInfo.append("&");
            }
            authInfo.append(key).append("=").append(map.get(key)); 
        }
        return sign(authInfo.toString(), rsaKey); 
    }
    
    
    /**
     * 返回签名编码拼接url
     * @param params
     * @param isEncode
     * @return
     */
    public static String getSignEncodeUrl(Map<String, String> map, boolean isEncode) {
        String sign = map.get("sign");
        String encodedSign = "";
        if (CollectionUtil.isNotEmpty(map)) {
            map.remove("sign");
            List<String> keys = new ArrayList<String>(map.keySet());
            // key排序
            Collections.sort(keys);
            StringBuilder authInfo = new StringBuilder(); 
            boolean first = true;// 是否第一个
            for (String key: keys) {
                if (first) {
                    first = false;
                } else {
                    authInfo.append("&");
                }
                authInfo.append(key).append("=");
                if (isEncode) {
                    try {
                        authInfo.append(URLEncoder.encode(map.get(key), CHARSET));
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                } else {
                    authInfo.append(map.get(key));
                }
            }
            try {
                encodedSign = authInfo.toString() + "&sign=" + URLEncoder.encode(sign, CHARSET); 
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return encodedSign.replaceAll("\\+", "%20");
    }
    
}
           

CollectionUtil.java

package com.skin.pay.alipay;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class CollectionUtil {

	private CollectionUtil() {
		super();
	}

	// 判断一个集合是否为空
	public static <T> boolean isEmpty(Collection<T> col) {
		if (col == null || col.isEmpty()) {
			return true;
		}

		return false;
	}

	// 判断一个集合是否不为空
	public static <T> boolean isNotEmpty(Collection<T> col) {
		return !isEmpty(col);
	}

	// 判断Map是否为空
	public static <K, V> boolean isEmpty(Map<K, V> map) {
		if (map == null || map.isEmpty()) {
			return true;
		}

		return false;
	}

	// 判断Map是否不为空为空
	public static <K, V> boolean isNotEmpty(Map<K, V> map) {
		return !isEmpty(map);
	}

	// 去除list中的重复数据
	public static <T> List<T> removeRepeat(List<T> list) {
		if (isEmpty(list)) {
			return list;
		}

		List<T> result = new ArrayList<T>();
		for (T e : list) {
			if (!result.contains(e)) {
				result.add(e);
			}
		}

		return result;
	}

	// 将集合转换为String数组
	public static <T> String[] toArray(List<T> list) {
		if (isEmpty(list)) {
			return null;
		}

		String[] result = new String[list.size()];
		for (int i = 0; i < list.size(); i++) {
			result[i] = String.valueOf(list.get(i));
		}

		return result;
	}

}
           

继续阅读