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 那边也要生成签名 不能用你生成的签名

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