【java web spring mvc】服务端 微信支付+微信异步通知【精】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_29739821/article/details/53466614

首先 这个微信支付 官网的demo 是有bug的 研究了好几天  吸取了一些大神的指点才成功,还有csdn一些人发的代码都不全太坑,资源还少,今天我就整合一下,

把我的一些心得分享给大家 。


之前遇到的一些问题:
预支付id获取失败,签名失败,您没有JSAPI支付权
预支付id获取失败的原因是: 交易类型这是成默认APP ,一些 随机数位数 都要跟api 对应上,加密的时候api的那些字段都要排序好按照(a-b),获取的时候 需要传递两个参数 (统一下单接口地址,拼接成的xml)。
签名失败的原因是:预支付id获取失败,加密的时候排序的顺序,加密用MD5 32位转换大写!
您没有JSAPI支付权: 商户的一些id 一些号 都要对应上 
以上就是大致的错误!  现在上代码 !
首先 这个微信支付 官网的demo 是有bug的 研究了好几天  吸取了一些大神的指点才成功,还有csdn一些人发的代码都不全太坑,资源还少,今天我就整合一下,
把我的一些心得分享给大家 。



实体层代码

package com.model;




/**
 * 微信支付实体
 * @author 裴广庭
 *
 */
public class WxPay {


	private String orderId;//订单号
	private String totalFee;//金额
	//private String spbillCreateIp;//订单生成的机器 IP
	private String notifyUrl;//这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等
	private String body;// 商品描述根据情况修改
	private String openId;//微信用户对一个公众号唯一
	
	
	
	public String getOrderId() {
		return orderId;
	}
	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}
	public String getTotalFee() {
		return totalFee;
	}
	public void setTotalFee(String totalFee) {
		this.totalFee = totalFee;
	}
//	public String getSpbillCreateIp() {
//		return spbillCreateIp;
//	}
//	public void setSpbillCreateIp(String spbillCreateIp) {
//		this.spbillCreateIp = spbillCreateIp;
//	}
	public String getNotifyUrl() {
		return notifyUrl;
	}
	public void setNotifyUrl(String notifyUrl) {
		this.notifyUrl = notifyUrl;
	}
	public String getBody() {
		return body;
	}
	public void setBody(String body) {
		this.body = body;
	}
	public String getOpenId() {
		return openId;
	}
	public void setOpenId(String openId) {
		this.openId = openId;
	}
}


单独写的微信商户平台信息的类


package com.util.weixin;

public class WeixinPayConstants {
	//微信支付商户开通后 微信会提供appid和appsecret和商户号partner
	public static final String appid = "****";//在微信开发平台登记的app应用
	public static final String appsecret = "****";
	public static final String partner = "****";//商户号
	//这个参数partnerkey是在商户后台配置的一个32位的key,微信商户平台-账户设置-安全设置-api安全
	public static final String partnerkey = "****";
	public static final String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	//openId 是微信用户针对公众号的标识,授权的部分这里不解释
	public static final String openId = "";
	//微信支付成功后通知地址 必须要求80端口并且地址不能带参数
	public static final String notifyurl = "http://**.**.**.**:8080/zhihuishe/weixin/notify";	
}




后台请求入口
	// 商户相关资料
	private static String appid = WeixinPayConstants.appid;
	private static String appsecret = WeixinPayConstants.appsecret;
	private static String partner = WeixinPayConstants.partner;
	private static String partnerkey = WeixinPayConstants.partnerkey;


	private static String notifyurl = WeixinPayConstants.notifyurl;
@RequestMapping(value = "/wxPay", method = RequestMethod.GET)
	public void wxPay(HttpServletRequest request, HttpSession session,
			HttpServletResponse response) throws IOException {
		Tool tool = new Tool();
		String totalFee = request.getParameter("totalFee");
		String body = request.getParameter("body");
		WxPay tpWxPay = new WxPay();
		tpWxPay.setTotalFee(totalFee);
		tpWxPay.setBody(body);
		tpWxPay.setOrderId(tool.orderNo());
		Object obj = getPackage(request,response, tpWxPay);
		JSONArray json = JSONArray.fromObject(obj);
		response.setCharacterEncoding(Constants.ENCODING_UTF8);
		response.getWriter().print(json.toString());
	}


getPackage方法  加密,生成签名,请求手机端,吊起微信支付!
/**
	 * 获取请求预支付id报文
	 * @return
	 * @throws IOException
	 */
	public static Object getPackage(HttpServletRequest request,HttpServletResponse response,WxPay tpWxPay)
			throws IOException {
		JSONObject retMsgJson = new JSONObject();
		String openId = WeixinPayConstants.openId;
		// 订单号
		String orderId = tpWxPay.getOrderId();
		// 附加数据 原样返回
		String attach = "";
		// 总金额以分为单位,不带小数点
		String totalFee = getMoney(tpWxPay.getTotalFee());
		// 订单生成的机器 IP
		//String spbill_create_ip = tpWxPay.getSpbillCreateIp();
		// 这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。
		String notify_url = notifyurl;
		//交易类型
		String trade_type = "APP";

		// ---必须参数
		// 商户号
		String mch_id = partner;
		// 随机字符串
		String nonce_str = Tool.getRandom();
		// 商品描述根据情况修改
		String body = tpWxPay.getBody();
		// 商户订单号
		String out_trade_no = orderId;

		SortedMap<String, String> packageParams = new TreeMap<String, String>();
		packageParams.put("appid", appid);//应用ID   ---微信开放平台审核通过的应用APPID
		packageParams.put("attach", attach);//附加数 	   ---附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
		packageParams.put("body", body);//商品描述
		packageParams.put("mch_id", mch_id);//商户号
		packageParams.put("nonce_str", nonce_str);//随机字符串
		packageParams.put("notify_url", notify_url);//通知地址	---微信给发的回调地址,要放在公网
		packageParams.put("openid", openId);////用户标识
		packageParams.put("out_trade_no", out_trade_no);//商户订单号
		//packageParams.put("spbill_create_ip", spbill_create_ip);//手机端ip
		packageParams.put("total_fee", totalFee);//总金额
		packageParams.put("trade_type", trade_type);//交易类型

		RequestHandler reqHandler = new RequestHandler(request, response);
		reqHandler.init(appid, appsecret, partnerkey);
		//MD5加密
		String sign = reqHandler.createSign(packageParams);
		//拼接成xml 形式 生成预支付id---prepay_id
		String xml = "<xml>" + "<appid><![CDATA[" + appid + "]]></appid>"
				+ "<attach><![CDATA[" + attach + "]]></attach>"
				+ "<body><![CDATA[" + body + "]]></body>"
				+ "<mch_id><![CDATA[" +mch_id + "]]></mch_id>"
				+ "<nonce_str><![CDATA[" + nonce_str + "]]></nonce_str>"
				+ "<notify_url><![CDATA[" + notify_url + "]]></notify_url>"
				+ "<out_trade_no><![CDATA[" + out_trade_no + "]]></out_trade_no>"
				//+ "<spbill_create_ip><![CDATA[" + spbill_create_ip + "]]></spbill_create_ip>"
				+ "<total_fee><![CDATA[" + totalFee + "]]></total_fee>"
				+ "<trade_type><![CDATA[" + trade_type + "]]></trade_type>"
				+ "<sign><![CDATA[" + sign + "]]></sign>" + "</xml>";

		String prepay_id = "";
		//统一下单接口地址
		String createOrderURL = WeixinPayConstants.createOrderURL;
		//获取预支付id
		prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml);

		System.out.println("获取到的预支付ID:" + prepay_id);

		// 获取到prepayid后对以下字段进行签名最终发送给app  
		SortedMap<String, String> finalpackage = new TreeMap<String, String>();
		String timestamp = Sha1Util.getTimeStamp();
		finalpackage.put("appid", appid);
		finalpackage.put("timestamp", timestamp);
		finalpackage.put("noncestr", nonce_str);
		finalpackage.put("package", "Sign=WXPay");
		finalpackage.put("partnerid", mch_id);
		finalpackage.put("prepayid", prepay_id);
		//MD5签名
		String finalsign = reqHandler.createSign(finalpackage);
		System.out.println("签名" + finalsign);
		//打印方便看
		String finaPackage = "appid=" + appid + "&partnerid=" + mch_id
				+ "&prepayid=" + prepay_id + "&package=" + "Sign=WXPay"
				+ "&noncestr=" + nonce_str + "×tamp=" + timestamp
				+ "&sign=" + finalsign;
		System.out.println("V3 jsApi package:" + finaPackage);
		//最后发给发给手机端
		retMsgJson.put("msg", "ok");
		retMsgJson.put("appid", appid);
		retMsgJson.put("prepayid", prepay_id);
		retMsgJson.put("partnerid", mch_id);
		retMsgJson.put("package", "Sign=WXPay");
		retMsgJson.put("noncestr", nonce_str);
		retMsgJson.put("timestamp", timestamp);
		retMsgJson.put("sign", finalsign);
		retMsgJson.put("sendUrl", notify_url);
		return retMsgJson;
	}

以上是 微信支付  , 支付完成后 微信会回调一些服务器 首先这里 项目一定要放在公网上面 因为 方便 微信访问! 下面是回调代码!
方便在服务器上排除错 我用最笨的方法 就是加打印 都打印到日志上了。。  shell 没有研究明白
在声明一下 !! 这个回调地址 必须放在公网上 言外之意 你想测试项目 必须把项目放在公网上 注意 端口号必须是8080!!!!!!!!!!!
public static final String notifyurl = "http://123.**.**.**:8080/zhihuishe/weixin/notify";


微信异步通知

// 微信异步通知
	@RequestMapping(value = "/notify")
	@ResponseBody
	public String notify(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		String result;//返回给微信的处理结果
		String inputLine;
		String notityXml = "";
		request.setCharacterEncoding("UTF-8");
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		response.setHeader("Access-Control-Allow-Origin", "*");
		//微信给返回的东西
		try {
			while ((inputLine = request.getReader().readLine()) != null) {
				notityXml += inputLine;
			}
			request.getReader().close();
		} catch (Exception e) {
			e.printStackTrace();
			result = setXml("fail","xml获取失败");
		}
		if (StringUtils.isEmpty(notityXml)) {
			result = setXml("fail","xml为空");
		}
		Map map = new GetWxOrderno().doXMLParse(notityXml);
		// 解析各种数据
		String appid = (String) map.get("appid");//应用ID
		String attach = (String) map.get("attach");//商家数据包
		String bank_type = (String) map.get("bank_type");//付款银行
		String cash_fee = (String) map.get("cash_fee");//现金支付金额
		String fee_type = (String) map.get("fee_type");//货币种类
		String is_subscribe = (String) map.get("is_subscribe");//是否关注公众账号
		String mch_id = (String) map.get("mch_id");//商户号
		String nonce_str = (String) map.get("nonce_str");//随机字符串
		String openid = (String) map.get("openid");//用户标识
		String out_trade_no = (String) map.get("out_trade_no");// 获取商户订单号
		String result_code = (String) map.get("result_code");// 业务结果
		String return_code = (String) map.get("return_code");// SUCCESS/FAIL
		String sign = (String) map.get("sign");// 获取签名
		String time_end = (String) map.get("time_end");//支付完成时间
		String total_fee = (String) map.get("total_fee");// 获取订单金额
		String trade_type = (String) map.get("trade_type");//交易类型
		String transaction_id = (String) map.get("transaction_id");//微信支付订单号
		
		SortedMap<String, String> parameters = new TreeMap<String, String>();
		// 数据加密
		parameters.put("appid", appid);//应用ID
		parameters.put("attach", attach);//商家数据包
		parameters.put("bank_type", bank_type);//付款银行
		parameters.put("cash_fee", cash_fee);//现金支付金额
		parameters.put("fee_type", fee_type);//货币种类
		parameters.put("is_subscribe", is_subscribe);//是否关注公众账号
		parameters.put("mch_id", mch_id);//商户号
		parameters.put("nonce_str", nonce_str);//随机字符串
		parameters.put("openid", openid);//用户标识
		parameters.put("out_trade_no", out_trade_no);// 商户订单号
		parameters.put("result_code", result_code);// 业务结果
		parameters.put("return_code", return_code);// SUCCESS/FAIL
		parameters.put("time_end", time_end);// 支付完成时间
		parameters.put("total_fee", total_fee);// 获取订单金额
		parameters.put("trade_type", trade_type);//交易类型
		parameters.put("transaction_id", trade_type);//微信支付订单号
		//加密前验证notify支付订单网关---https://gw.tenpay.com/gateway/simpleverifynotifyid.xml
		RequestHandler reqHandler = new RequestHandler(request, response);
		reqHandler.init(appid, appsecret, partnerkey);
		//MD5加密
		String endsign = reqHandler.createSign(parameters);
		
		
		System.out.println("**************************************************************************************************");
		System.out.println(appid+"-------------------应用ID");
		System.out.println(attach+"-------------------商家数据包");
		System.out.println(bank_type+"-------------------付款银行");
		System.out.println(cash_fee+"-------------------现金支付金额");
		System.out.println(fee_type+"-------------------货币种类");
		System.out.println(is_subscribe+"-------------------是否关注公众账号");
		System.out.println(mch_id+"-------------------商户号");
		System.out.println(nonce_str+"-------------------随机字符串");
		System.out.println(openid+"-------------------用户标识");
		System.out.println(out_trade_no+"-------------------获取商户订单号");
		System.out.println(result_code+"-------------------业务结果");
		System.out.println(return_code+"------------------- SUCCESS/FAIL");
		System.out.println(sign+"-------------------获取签名-微信回调的签名");
		System.out.println(time_end+"-------------------支付完成时间");
		System.out.println(total_fee+"-------------------获取订单金额");
		System.out.println(trade_type+"-------------------交易类型");
		System.out.println(transaction_id+"-------------------微信支付订单号");
		System.out.println(endsign+"-------------------第二次加密sign");
		System.out.println("**************************************************************************************************");
		

		// 验证签名
		if (sign.equals(endsign)) {
			result = setXml("SUCCESS", "OK");
		} else {
			System.err.println("签名不一致!");
			result = setXml("fail", "签名不一致!");
		}
		if (!"SUCCESS".equals("SUCCESS")) {
			System.err.println("微信返回的交易状态不正确(result_code=" + "SUCCESS" + ")");
			result =  setXml("fail", "微信返回的交易状态不正确(result_code=" + "SUCCESS" + ")");
		}
		//如果成功写入数据库
		if ("SUCCESS".equals("SUCCESS")) {// 如果微信返回的结果是success,则修改订单状态
			//判断订单号是否重复
			List<CapitalTable> userList = cpitalTableService.queryOrder(out_trade_no);
			if (userList.size() > 0) {
				result = setXml("fail", "订单号重复");
			}else{
				CapitalTable capitalTable = new CapitalTable();
				UserAdmin userAdmin = new UserAdmin();
				userAdmin.setId(6);
				capitalTable.setCapital(total_fee);//金币
				capitalTable.setState("1");//类型:1充值2提问3回答
				capitalTable.setDatetime(new Date());//流入时间
				capitalTable.setOrderNo(out_trade_no);
				capitalTable.setUserAdmin(userAdmin);
				cpitalTableService.insertCapital(capitalTable);
			}
		}
		System.out.println("回调成功");
		System.out.println("----返回给微信的xml:" + result);
		return result;
	}

	//通过xml 发给微信消息
	public static String setXml(String return_code, String return_msg) {
		SortedMap<String, String> parameters = new TreeMap<String, String>();
		parameters.put("return_code", return_code);
		parameters.put("return_msg", return_msg);
		return "<xml><return_code><![CDATA[" + return_code + "]]>" + 
				"</return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
	}

有的需求需要将元转成分

/**
	 * 元转换成分
	 * @param money
	 * @return
	 */
	public static String getMoney(String amount) {
		if(amount==null){
			return "";
		}
		// 金额转化为分为单位
		String currency =  amount.replaceAll("\\$|\\¥|\\,", "");  //处理包含, ¥ 或者$的金额  
        int index = currency.indexOf(".");  
        int length = currency.length();  
        Long amLong = 0l;  
        if(index == -1){  
            amLong = Long.valueOf(currency+"00");  
        }else if(length - index >= 3){  
            amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));  
        }else if(length - index == 2){  
            amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);  
        }else{  
            amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");  
        }  
        return amLong.toString(); 
	}


以上就是 微信支付的全过程 需要用到的工具包 我已经发布在csdn了    地址是 http://download.csdn.net/detail/qq_29739821/9702230  


希望以上内容可以帮助大家!谢谢!

猜你喜欢

转载自blog.csdn.net/qq_29739821/article/details/53466614