微信公众号支付详细步骤(整理)

公司做公众号时需要接入微信支付,个人根据网上的demo摸索着完成了公司公众号的支付和退款功能。现也将代码分享出来,希望对需要朋友有帮助。
一.提交支付的toPay.jsp页面代码:

<%
	String basePath = request.getScheme() + "://"+ request.getServerName() + request.getContextPath()+ "/";
%>
<html>
<body>
	....
    <div class="zdx3"><button onclick="pay()">共需支付${sumPrice }元&nbsp;&nbsp;确认支付</button></div>
</body>
</html> 
   
<script>
	function pay() {
		var url="<%=basePath%>wechat/pay?money=${sumPrice}"; //注意此处的basePath是没有端口号的域名地址。如果包含:80,在提交给微信时有可能会提示 “redirect_uri参数错误” 。
		//money为订单需要支付的金额
		//state中存放的为商品订单号
		var weixinUrl="https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri="+encodeURI(url)+"&response_type=code&scope=snsapi_userinfo&state=${orderId}#wechat_redirect";
		window.location.href=encodeURI(weixinUrl);
	}
	
</script>

二.后台处理支付功能代码
(包含两部分:
1.处理支付信息,通过微信接口生成订单号,返回支付页面
2.提供一个微信支付完成后的回调接口)
第1部分代码:

/**
 * 用户提交支付,获取微信支付订单接口
 */
@RequestMapping(value="/pay")
public ModelAndView pay(HttpServletRequest request,HttpServletResponse response){
	ModelAndView mv = new ModelAndView();
	String GZHID = "wxfd7c065eee11112222";// 微信公众号id
	String GZHSecret = "b5b3a627f5d1f8888888888888";// 微信公众号密钥id
	String SHHID = "111111111";// 财付通商户号
	String SHHKEY = "mmmmmmmmmmmmmmm";// 商户号对应的密钥

	/*------1.获取参数信息------- */
	//商户订单号
	String out_trade_no= request.getParameter("state"); 
	//价格
	String money = request.getParameter("money");
	//金额转化为分为单位
	String finalmoney = WeChat.getMoney(money);
	//获取用户的code
	String code = request.getParameter("code");
	
	/*------2.根据code获取微信用户的openId和access_token------- */
	//注: 如果后台程序之前已经得到了用户的openId 可以不需要这一步,直接从存放openId的位置或session中获取就可以。
	//toPay.jsp页面中提交的url路径也就不需要再经过微信重定向。写成:http://localhost:8080/项目名/wechat/pay?money=${sumPrice}&state=${orderId}
	String openid=null;
	try {
		List<Object> list = accessToken(code);
		openid=list.get(1).toString();
	} catch (IOException e) {
		logger.error("根据code获取微信用户的openId出现错误", e);
		mv.setViewName("error");
		return mv;
	}
	
	/*------3.生成预支付订单需要的的package数据------- */
	//随机数 
	String nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
	//订单生成的机器 IP
	String spbill_create_ip = request.getRemoteAddr();
	//交易类型 :jsapi代表微信公众号支付
	String trade_type = "JSAPI";
	//这里notify_url是 微信处理完支付后的回调的应用系统接口url。
	String notify_url ="http://69a6a38e.ngrok.natapp.cn/heyi-console/wechat/weixinNotify";
	
	SortedMap<String, String> packageParams = new TreeMap<String, String>();
	packageParams.put("appid",  GZHID);  
	packageParams.put("mch_id",  SHHID);  
	packageParams.put("nonce_str", nonce_str);  
	packageParams.put("body", "费用");  
	packageParams.put("out_trade_no", out_trade_no);  
	packageParams.put("total_fee", finalmoney);  
	packageParams.put("spbill_create_ip", spbill_create_ip);  
	packageParams.put("notify_url", notify_url);  
	packageParams.put("trade_type", trade_type); 
	packageParams.put("openid", openid); 
	
	/*------4.根据package数据生成预支付订单号的签名sign------- */
	RequestHandler reqHandler = new RequestHandler(request, response);
	reqHandler.init( GZHID,  GZHSecret,  SHHKEY);
	String sign = reqHandler.createSign(packageParams);
	
	/*------5.生成需要提交给统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 的xml数据-------*/
	String xml="<xml>"+
			"<appid>"+ GZHID+"</appid>"+
			"<mch_id>"+ SHHID+"</mch_id>"+
			"<nonce_str>"+nonce_str+"</nonce_str>"+
			"<sign>"+sign+"</sign>"+
			"<body><![CDATA["+"费用"+"]]></body>"+
			"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
			"<total_fee>"+finalmoney+"</total_fee>"+
			"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
			"<notify_url>"+notify_url+"</notify_url>"+
			"<trade_type>"+trade_type+"</trade_type>"+
			"<openid>"+openid+"</openid>"+
			"</xml>";
	
	/*------6.调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder 生产预支付订单----------*/
	String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	String prepay_id="";
	try {
		prepay_id = GetWxOrderno.getPayNo(createOrderURL, xml);
		if(prepay_id.equals("")){
			mv.addObject("ErrorMsg", "支付错误");
			mv.setViewName("error");
			return mv;
		}
	} catch (Exception e) {
		logger.error("统一支付接口获取预支付订单出错", e);
		mv.setViewName("error");
		return mv;
	}
	/*将prepay_id存到库中*/
	PageData p = new PageData();
	p.put("shopId", out_trade_no);
	p.put("prePayId", prepay_id);
	activityService.updatePrePayId(p);

	
	/*------7.将预支付订单的id和其他信息生成签名并一起返回到jsp页面 ------- */
	nonce_str= MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
	SortedMap<String, String> finalpackage = new TreeMap<String, String>();
	String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
	String packages = "prepay_id="+prepay_id;
	finalpackage.put("appId",  GZHID);  
	finalpackage.put("timeStamp", timestamp);  
	finalpackage.put("nonceStr", nonce_str);  
	finalpackage.put("package", packages);  
	finalpackage.put("signType", "MD5");
	String finalsign = reqHandler.createSign(finalpackage);
	
	mv.addObject("appid",  GZHID);
	mv.addObject("timeStamp", timestamp);
	mv.addObject("nonceStr", nonce_str);
	mv.addObject("packageValue", packages);
	mv.addObject("paySign", finalsign);
	mv.addObject("success","ok");
	mv.setViewName("wechat/pay");
   return mv;
}

第2部分代码:

/**
 * 提交支付后的微信异步返回接口
 */
@RequestMapping(value="/weixinNotify")
public void weixinNotify(HttpServletRequest request, HttpServletResponse response){
	String out_trade_no=null;
	String return_code =null;
	try {
		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);
		}
		outSteam.close();
		inStream.close();
		String resultStr  = new String(outSteam.toByteArray(),"utf-8");
		logger.info("支付成功的回调:"+resultStr);
		Map<String, Object> resultMap = parseXmlToList(resultStr);
		String result_code = (String) resultMap.get("result_code");
		String is_subscribe = (String) resultMap.get("is_subscribe");
		String transaction_id = (String) resultMap.get("transaction_id");
		String sign = (String) resultMap.get("sign");
		String time_end = (String) resultMap.get("time_end");
		String bank_type = (String) resultMap.get("bank_type");
		
		out_trade_no = (String) resultMap.get("out_trade_no");
		return_code = (String) resultMap.get("return_code");
		
		request.setAttribute("out_trade_no", out_trade_no);
		//通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
		response.getWriter().write(RequestHandler.setXML("SUCCESS", ""));
	}  catch (Exception e) {
		logger.error("微信回调接口出现错误:",e);
		try {
			response.getWriter().write(RequestHandler.setXML("FAIL", "error"));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	} 
	if(return_code.equals("SUCCESS")){
		//支付成功的业务逻辑
	}else{
		//支付失败的业务逻辑
	}
}

三.微信app中具体支付的jsp页面

<html>
<head>
<script src="js/jquery-1.8.2.min.js" type="text/javascript"></script>
</head>
<body onload="javascript:pay();">
	<script type="text/javascript">
	    function pay(){
			if (typeof WeixinJSBridge == "undefined"){
			   if( document.addEventListener ){
			       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
			   }else if (document.attachEvent){
			       document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
			       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
			   }
			}else{
			   onBridgeReady();
			}   	
	    }
		function onBridgeReady(){
		   WeixinJSBridge.invoke(
		       'getBrandWCPayRequest', {
		           "appId" : "${appid}",     //公众号名称,由商户传入     
		           "timeStamp": "${timeStamp}",         //时间戳,自1970年以来的秒数     
		           "nonceStr" : "${nonceStr}", //随机串     
		           "package" : "${packageValue}",     
		           "signType" : "MD5",         //微信签名方式:     
		           "paySign" : "${paySign}"    //微信签名 
		       },function(res){
	            if(res.err_msg == "get_brand_wcpay_request:ok"){  
	                alert("微信支付成功!");
	            }else if(res.err_msg == "get_brand_wcpay_request:cancel"){  
	                alert("用户取消支付!");
	            }else{  
	               alert("支付失败!");
	            }  
			}); 
		}
	</script>
</body>
</html>

其他需要用到的相关类和方法:

金额 元转分:

	  /**
		 * 元转换成分
		 * @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(); 
		}

通过微信用户code获取用户的openId:

    /**
	 * 通过微信用户的code换取网页授权access_token
	 * @return
	 * @throws IOException
	 * @throws
	 */
	public List<Object> accessToken(String code) throws IOException {
		List<Object> list = new ArrayList<Object>();
		String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
				+ WeChat.HYGZHID + "&secret=" + WeChat.HYGZHSecret+ "&code=" + code + "&grant_type=authorization_code";
		HttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(url);
		HttpResponse res = client.execute(post);
		if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
			HttpEntity entity = res.getEntity();
			String str = org.apache.http.util.EntityUtils.toString(entity, "utf-8");
			ObjectMapper mapper=new com.fasterxml.jackson.databind.ObjectMapper.ObjectMapper();
			Map<String,Object> jsonOb=mapper.readValue(str, Map.class);
			list.add(jsonOb.get("access_token"));
			list.add(jsonOb.get("openid"));
		}
		return list;
	}
  

MD5提取摘要:

/**
 * MD5加密
 */
public class MD5 {

	private MD5() {}
	
	/**
	 * 对传入的数据提取摘要
	 * @param buffer
	 * @return
	 */
	public final static String getMessageDigest(byte[] buffer) {
		char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
		try {
			MessageDigest mdTemp = MessageDigest.getInstance("MD5");
			mdTemp.update(buffer);
			byte[] md = mdTemp.digest();
			int j = md.length;
			char str[] = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte byte0 = md[i];
				str[k++] = hexDigits[byte0 >>> 4 & 0xf];
				str[k++] = hexDigits[byte0 & 0xf];
			}
			return new String(str);
		} catch (Exception e) {
			return null;
		}
	}
}

解析微信回调的xml数据:

	/**
	 * description: 解析微信通知xml
	 * 
	 * @param xml
	 * @return
	 * @author ex_yangxiaoyi
	 * @see
	 */
	@SuppressWarnings({ "unused", "rawtypes", "unchecked" })
	private static Map parseXmlToList(String xml) {
		Map retMap = new HashMap();
		try {
			StringReader read = new StringReader(xml);
			// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
			InputSource source = new InputSource(read);
			// 创建一个新的SAXBuilder
			SAXBuilder sb = new org.jdom.input.SAXBuilder.SAXBuilder();
			// 通过输入源构造一个Document
			Document doc = (Document) sb.build(source);
			Element root = doc.getRootElement();// 指向根节点
			List<Element> es = root.getChildren();
			if (es != null && es.size() != 0) {
				for (Element element : es) {
					retMap.put(element.getName(), element.getValue());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return retMap;
	}

java文件和需要用到的工具类,我都放到csdn的下载中了。
地址:https://download.csdn.net/download/aofavx/10606894 (免积分)。
如果有不对或者少文件的地方,请留言。我给大家补正。

  • 25
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值