app微信支付

這兩天 app上面要加上微信支付 挖了兩天的坑終於是有點頭緒了 最後竟然是

  1. 模型搭好 網上找尋 一些工具 值得一提 微信是比較頭疼的 他傳入的參數必須是xml 所以要做好解析 xml 和 拼接xml 文件的打算
    xml 工具方法 還有微信支付是發送post 請求 不像支付寶那樣 是sdk 回調也是在request 裏面
package com.trilink.common.bean;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.aliyun.opensearch.util.URLEncoder;
import com.trilink.common.util.MD5Util;
import com.trilink.common.util.StringUtil;

public class WeiXinPayUtil {

	/**
	 * 日誌記錄
	 */
	private static Logger logger = Logger.getLogger(WeiXinPayUtil.class);
	
	
	
	
	/**
	 * 簽名
	 * @param params
	 * @param paternerKey
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	
	
	/**
	 * 簽名2
	 * @param params
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	
	
	 public static String createSign(String characterEncoding,SortedMap<String,String> 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=" + ConfigUtils.WXappkey);//最後加密時添加商戶密鑰,由於key值放在最後,所以不用添加到SortMap裏面去,單獨處理,編碼方式採用UTF-8
	        String aa=sb.toString();
	        System.err.println("aaaaaaaaaaaaaaaa:"+aa);
	        String sign = MD5Util.MD5Encode(aa.trim(), characterEncoding).toUpperCase();
	        return sign;
	    }
	
	
	/**
	 * 排序
	 * @param params
	 * @param encode
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String createSign(Map<String, String> params, boolean encode) throws UnsupportedEncodingException {
	    Set<String> keysSet = params.keySet();
	    Object[] keys = keysSet.toArray();
	    Arrays.sort(keys);
	    StringBuffer temp = new StringBuffer();
	    boolean first = true;
	    for (Object key : keys) {
	        if (first) {
	            first = false;
	        } else {
	            temp.append("&");
	    }
	    temp.append(key).append("=");
	    Object value = params.get(key);
	    String valueString = "";
	    if (null != value) {
	        valueString = value.toString();
	    }
	    if (encode) {
	        temp.append(URLEncoder.encode(valueString, "UTF-8"));
	        } else {
	            temp.append(valueString);
	        }
	    }
	    return temp.toString();
	}
	
	/**
	 * 將map轉換爲xml
	 * @param arr
	 * @return
	 */
	public static String ArrayToXml(Map<String, String> arr) {
		String xml = "<xml>";
 
		Iterator<Entry<String, String>> iter = arr.entrySet().iterator();
		while (iter.hasNext()) {
			Entry<String, String> entry = iter.next();
			String key = entry.getKey();
			String val = entry.getValue();
			xml += "<" + key + ">" + val + "</" + key + ">";
		}
 
		xml += "</xml>";
		return xml;
	}
	
	
	/**
	 * 解析xml爲map
	 * @param xml
	 * @return
	 * @throws Exception 
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	public static Map<String, String> xmlToMap(String xml) throws Exception{
		if(xml!=null){
			Map<String, String> map = new HashMap<String, String>();
			Document document = DocumentHelper.parseText(xml); 
			Element root = document.getRootElement(); 
			for (Iterator iterator = root.elementIterator(); iterator.hasNext();) { 
	            Element e = (Element) iterator.next(); 
	            map.put(e.getName(), e.getText()); 
	            System.out.println(e.getName()+"="+e.getText());
	        } 
			return map;
		}else{
			return null;
		}
	}
	
	
	
    /**
     * 請求方法
     * @param requestUrl
     * @param requestMethod
     * @param outputStr
     * @return
     */
    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);  
              }  
              System.err.println("返回結果"+buffer.toString());
              // 釋放資源  
              bufferedReader.close();  
              inputStreamReader.close();  
              inputStream.close();  
              inputStream = null;  
              conn.disconnect();  
              return buffer.toString();  
          } catch (ConnectException ce) {  
              logger.info("連接超時:{}"+ ce);  
          } catch (Exception e) {  
        	  logger.info("https請求異常:{}"+ e);  
          }  
          return null;  
      } 
	    
	/**
	 * 微信回調參數解析
	 * @param request
	 * @return
	 */
	public static String getPostStr(HttpServletRequest request){
    	 StringBuffer sb = new StringBuffer();
    	 try {
         InputStream is = request.getInputStream();  
         InputStreamReader isr = new InputStreamReader(is, "UTF-8");  
         BufferedReader br = new BufferedReader(isr);  
         String s = "";  
        
			while ((s = br.readLine()) != null) {  
			     sb.append(s);  
			 }
		} catch (IOException e) {			
			e.printStackTrace();
		}  
         String xml = sb.toString(); //次即爲接收到微信端發送過來的xml數據  
         logger.info(xml+"========================");
         return xml;
    }
	




}

這上面有一個是生成 簽名 的方法 這個必須要按照文檔裏面傳入參數 多了 無所謂 要按照順序
字典順序 可以用 SortedMap 進行有序化

  1. 中轉站 將參數封裝
package com.trilink.common.bean;

import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

import org.apache.log4j.Logger;

import com.trilink.common.util.MD5Util;

public class WeiXinPay {

	/**
	 * 日誌記錄
	 */
	private static Logger logger = Logger.getLogger(WeiXinPay.class);
	
	/**
	 * 微信統一下單
	 * @param appid 微信開發者id/微信公衆號id
	 * @param mch_id 商戶號
	 * @param key 簽名密匙
	 * @param openid	appid爲微信公衆號id時爲用戶標識,appid微信開發者id爲null
	 * @param orderCode	訂單號
	 * @param total_fee	總價 單位爲分
	 * @param attach	自定義數據
	 * @param ip	終端ip
	 * @param notify_url	回調地址
	 * @param trade_type	支付類型(APP,JSAPI)
	 * @param body	支付描述
	 * @return
	 * @throws Exception
	 */
	public static Map<String, String> weixinUnifiedOrder(String appid,String mch_id,String orderCode,String total_fee,String ip,String notify_url,String trade_type,String body,String key) throws Exception{
		 SortedMap<String, String> condition = new TreeMap<String, String>();
		//1應用ID
		condition.put("appid", appid);
		System.out.println(appid);
		//2商戶號
		condition.put("mch_id", mch_id);
		System.out.println(mch_id);
		//3隨機字符串
		condition.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
		System.out.println(UUID.randomUUID().toString().replace("-", ""));
		//4商品描述
		condition.put("body", body);
		System.out.println(body);
		//5商戶訂單號
		condition.put("out_trade_no", orderCode);
		System.out.println(orderCode);
		//6訂單總金額,單位爲分
		condition.put("total_fee", total_fee);
		System.out.println(total_fee);
		
		
		
		
		//7終端IP
		condition.put("spbill_create_ip", ip);
		System.out.println(ip);
		String encode = URLEncoder.encode(notify_url, "utf-8");
		//8接收微信支付異步通知回調地址,通知url必須爲直接可訪問的url,不能攜帶參數。
		condition.put("notify_url", encode);
		System.out.println(notify_url);
		//9支付類型
		condition.put("trade_type", trade_type);
		System.out.println(trade_type);
		//簽名
		String sign = WeiXinPayUtil.createSign("UTF-8",condition);
		
		System.out.println(condition);
		condition.put("sign", sign);
		
//		String str="";
//		String sign1 ="appid=wx66411860150db74c&body=test&mch_id=1528762281&nonce_str=bca79d1bb2e04205b8840131a509d600&key=d6e576ec4a1e11e98646d663bd87jade";
//		
//		sign=MD5Util.MD5Encode("UTF-8", sign1);
		
		System.out.println("!!!!!!"+sign);
		String xml = WeiXinPayUtil.ArrayToXml(condition);
		System.out.println(xml);
		String result = WeiXinPayUtil.httpsRequest(ConfigUtils.weixinUnifiedOrderUrl, "POST", xml);
		Map<String, String> resuleMap = WeiXinPayUtil.xmlToMap(result);
		resuleMap.put("ip", ip);
		logger.info("1111111111111111111"+result);
		if (result.indexOf("SUCCESS") != -1) {
			if(resuleMap.get("return_code")!=null && "SUCCESS".equals(resuleMap.get("return_code"))){
				//成功
				return resuleMap;
			}else{
				return resuleMap;
			}
		}else{
			return resuleMap;
		}
	}
}

  1. MD5 加密工具類 簽名是需要 MD5加密的
public class MD5Util {
	 
	     private static String byteArrayToHexString(byte b[]) {
	         StringBuffer resultSb = new StringBuffer();
	        for (int i = 0; i < b.length; i++)
	             resultSb.append(byteToHexString(b[i]));
	 
	          return resultSb.toString();
	      }
	 
	     private static String byteToHexString(byte b) {
	         int n = b;
	         if (n < 0)
	             n += 256;
	         int d1 = n / 16;
	         int d2 = n % 16;
	         return hexDigits[d1] + hexDigits[d2];
	     }
	 
     public static String MD5Encode(String origin, String charsetname) {
	         String resultString = null;
	         try {
             resultString = new String(origin);
	             MessageDigest md = MessageDigest.getInstance("MD5");
	             if (charsetname == null || "".equals(charsetname))
	                 resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
	             else
	                 resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
	         } catch (Exception exception) {
	         }
	         return resultString;
	     }
	 
	     private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

		
	 }
  1. 配置必要參數
public class ConfigUtils {

	
	//請求地址
	public static String weixinUnifiedOrderUrl="";
	
	//祕鑰
	public static String WXappkey="";
	//appID
	public static String WXAPPappid="";
	//商戶ID
	public static String WXmch_id="";
	//回調地址
	public static String WXnotify_url="";
	
	
	
}

5.開搞



import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.trilink.common.bean.ConfigUtils;
import com.trilink.common.bean.R;
import com.trilink.common.bean.WeiXinPay;
import com.trilink.common.bean.WeiXinPayUtil;
import com.trilink.common.dao.zhyl.AppDiscountMapper;
import com.trilink.common.dao.zhyl.AppMeetingDiscountMapper;
import com.trilink.common.dao.zhyl.AppPayRecordMapper;
import com.trilink.common.dao.zhyl.AppSequenceMapper;
import com.trilink.common.dao.zhyl.AppSignRecordMapper;
import com.trilink.common.dao.zhyl.AppSignUpMapper;
import com.trilink.common.dao.zhyl.DoctorMapper;
import com.trilink.common.dao.zhyl.PatientMapper;
import com.trilink.common.entity.AppDiscount;
import com.trilink.common.entity.AppDiscountExample;
import com.trilink.common.entity.AppPayRecord;
import com.trilink.common.entity.AppSequence;
import com.trilink.common.entity.AppSequenceExample;
import com.trilink.common.entity.AppSignRecord;
import com.trilink.common.entity.AppSignRecordExample;
import com.trilink.common.entity.AppSignUp;
import com.trilink.common.entity.Doctor;
import com.trilink.common.entity.Patient;
import com.trilink.common.service.AppPayService;
import com.trilink.common.util.StringUtil;


@Controller
@RequestMapping("weixin")
public class AppWeixinController {

	@Autowired
	private DoctorMapper doctorMapper;
	@Autowired
	private PatientMapper patientMapper;
	@Autowired
	private AppSignUpMapper appSignUpMapper;
	@Autowired
	private AppSignRecordMapper appSignRecordMapper;
	@Autowired
	private AppPayRecordMapper appPayRecordMapper;
	@Autowired
	private AppDiscountMapper appDiscountMapper;
	@Autowired
	private AppMeetingDiscountMapper appMeetingDiscountMapper;
	@Autowired
	private AppSequenceMapper appSequenceMapper;
	@Autowired
	private AppPayService appPayService;

	/**
	 * 微信app支付統一下單
	 * @param request
	 * @param orderNumber
	 * @param payMoney
	 * @param orderType
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value="weixinPay",method={RequestMethod.POST})
	@ResponseBody
	/**
	 *  ResultState  是我自己封裝的一個返回結果的對象,有需要的後邊我會附代碼,很簡單的,畢竟我本人水平也不好。
	 *    在這裏,訂單加簽我讓前端穿了3個參數  orderNumber 訂單編號 需要提供給微信的  payMoney 用戶支付金額  需要提供給微信的 orderType訂單類型 確定body字段的
	 *  這裏大家可以根據自己的業務邏輯確定參數,沒必要跟我的一致,返回結果也是,大家也可應自己定義的
	 */
	public  R weixinUnifiedOrder(@RequestParam() Map<String, String> allParams,HttpServletRequest request) throws Exception{

		String userId = allParams.get("userId");
		String userType = allParams.get("roleType");
		//用,隔開
		String signUpIds = allParams.get("signUpIds");
		//用,隔開
		String recommand = allParams.get("recommand");
		String userName=allParams.get("userName");
		String mobile=allParams.get("mobile");
		String meetingId=allParams.get("meetingId");
		String payType=allParams.get("payType");
		String money=allParams.get("money");
		
		
		
		
	
			
		
		String outTradeNo = getOutTradeNo();
		
		
		AppPayRecord appPay=new AppPayRecord();
		appPay.setcTime(new Date());
		appPay.setUserType(Integer.parseInt(userType));
		appPay.setOutTradeNo(outTradeNo);
		appPay.setPayUser(Integer.parseInt(userId));
		appPay.setPayType(Integer.parseInt(payType));
		appPay.setPayStatus(2);
		appPay.setStatus(0);
		appPay.setPayFee(payFee);
		
		int insert = appPayRecordMapper.insert(appPay);
		
		if(insert>0) {
			AppSignRecord record=new AppSignRecord();
			record.setStatus(0);
			record.setMeetingId(Integer.parseInt(meetingId));
			record.setMobile(mobile);
			record.setUserType(Integer.parseInt(userType));
			record.setUserId(Integer.parseInt(userId));
			record.setUserName(userName);
			record.setSignUpIds(signUpIds);
			record.setRecommand(recommand);
			record.setTandeNo(appPay.getId());
			record.setReportTime(new Date());
			record.setPayMoney(payFee);
	
		
		if(appSignRecordMapper.insert(record)>0) {
			Map<String, String> result=new HashMap<String, String>();
				//微信支付需要傳參使用者的ip,這裏獲取了一下,可以自己網上找,懶得找的下邊我會附代碼
				String remoteAddr = getIpAddress(request);
				String ip="";
				if (remoteAddr.length()>16) {
					String[] split = remoteAddr.split(",");
					for (int i = 0; i < split.length; i++) {
						ip=split[0];
					}
				}else{
					ip=remoteAddr;
				}
				
				//這裏就是把訂單信息封裝好了統一下單,微信會返回一個加完籤的結果,只要判斷一下結果的正確性就可以吧這個結果直接返回給前端,讓前端去調起支付了
				//我把上邊提到的幾個參數封裝到了ConfigUtils裏,這裏你取你自己的             appid                          微信商戶號     上邊數組封裝的值,自己對應  ip            微信回調地址(自己寫的,上邊提到的第二部)  APP,看文檔,你是什麼支付就傳//什麼,body 上邊定義的字段,必傳,加簽的時候有body的解釋,也可以自己看微信官方文檔   自己封裝的appkey         
				result=WeiXinPay.weixinUnifiedOrder(ConfigUtils.WXAPPappid, ConfigUtils.WXmch_id,outTradeNo, "1", ip, ConfigUtils.WXnotify_url, "APP", "test", ConfigUtils.WXappkey);
				if(result.get("prepay_id")!=null){
					System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"+result);
					//						//result是微信返回的字段,詳細見微信開發者文檔
					//						 Map<String, String> sign = WeiXinPay.getSign3(result);
					//						 System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"+sign);
					result.put("out_trade_no", appPay.getOutTradeNo());
					System.err.println("result:::::::+"+result);
					return R.ok().put("data", result);
			}else {
				return R.error();
			}
		}else {
			return R.error("簽名錯誤");
		}
		}
		return R.error("錯誤");
		

	}

	/*
	 * 微信支付成功回調
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	//微信支付要在服務起上正式環境調試,所以這個回調接口必須是在外網上可以訪問到的,不能加任何參數的
	@ResponseBody
	@RequestMapping(value="weiXinPayCallback")
	//這裏是微信支付回調,支付完成後,微信會回調這個接口,確定是否支付成功,支付成功給微信返回一個success,讓微信知道成功支付了,不然微信會隔幾分鐘回調一次,對於你處理成功業務邏輯很麻煩
	public  String weiXinPayCallback(HttpServletRequest request,HttpServletResponse response) throws Exception{
		//獲取微信支付回調結果
		String result = WeiXinPayUtil.getPostStr(request);
		System.out.println(result);
		Map<String, String> resuleMap = WeiXinPayUtil.xmlToMap(result);
		if(resuleMap.get("result_code")!=null && "SUCCESS".equals(resuleMap.get("result_code"))){
			//成功
			//獲取實際支付的錢數
			String totalFee = resuleMap.get("total_fee");
			//獲取訂單號
			String orderNo = resuleMap.get("out_trade_no").split("_")[0];
			//獲取交易流水號
			String tradeNo = resuleMap.get("transaction_id");
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			AppPayRecord record = appPayService.queryRecordByOutTradeNo(request.getParameter("out_trade_no"),
					null);
			if (record == null) {
				System.out.println("---------------------record is null");
				return "failure";
			}
			
			if(record.getUserType()==1) {
				Doctor doctor = doctorMapper.selectByPrimaryKey(record.getPayUser());
				if(doctor==null) {
					System.out.println("---------------------doctor is null");
					return "failure";
				}
			}
			if(record.getUserType()==2) {
				Patient patient=patientMapper.selectByPrimaryKey(record.getPayUser());
				if(patient==null) {
					System.out.println("---------------------patient is null");
					return "failure";
				}
			}
			record.setBuyerLogonId(request.getParameter("buyer_logon_id"));
			record.setTradeNo(request.getParameter("trade_no"));
			record.setPayFee(Double.parseDouble(request.getParameter("total_amount")));
			record.setStatus(1);
			record.setPayTime(format.parse(request.getParameter("gmt_payment")));
			//  更新支付記錄
			Integer updateRecord = appPayService.updateRecord(record);
			
			System.err.println("支付訂單11111111111111成功"+updateRecord);
			if(updateRecord>0) {
				
				

					return "success";
				}else {
					record.setStatus(0);
					appPayService.updateRecord(record);
					return "failure";
				}
				
			}
		}
		return "fail";
	}

	public String getOutTradeNo() {
		Date d = new Date();
		SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
		Double i = (Math.random() * 9 + 1) * 100000;
		return "NSZDX" + format.format(d) + i.intValue();
	}

	public static String getIpAddress(HttpServletRequest request) {  
		String ip = request.getHeader("x-forwarded-for");  
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
			ip = request.getHeader("Proxy-Client-IP");  
		}  
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
			ip = request.getHeader("WL-Proxy-Client-IP");  
		}  
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
			ip = request.getHeader("HTTP_CLIENT_IP");  
		}  
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");  
		}  
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
			ip = request.getRemoteAddr();  
		}  
		return ip;  
	}  


}

這裏訂單自己生成自己的商戶訂單 是需要傳給app端的

  1. 發起支付返回的參數

我碰到的坑 就是 生成的 WXappkey 有問題 害的我一直返回

<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[簽名錯誤]]></return_msg></xml>

微信返回結果又不做任何說明 我們只能一個一個排查 首先判斷 生成的簽名是不是正確的 知道找到
一個地方(別說 微信藏得夠深的 還真找不到 這地)

https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1

這裏 可以驗證你的簽名是否生成有誤 微信文檔是真的坑 什麼提示都沒有 接下來就是 app端的坑

app 那邊也要生成簽名 不能用你生成的簽名

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章