天天看點

微信支付和支付寶支付

微信支付和支付寶支付開發案列

以下基礎方法可以參考支付寶支付開放平台(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;
	}

}
           

繼續閱讀