微信公众号之微信支付

一、前言

   这次的项目主要是关于微信公众号的一个开发,本人这次分配的模块是后台微信公众号的支付和退款,第一次接触微信公众的项目刚开始一脸懵逼,开发过程中遇到各种坑,所以想自己写一篇详细的关于微信公众号的开发,希望能对小伙伴们有所帮助!

二、统一支付接口

支付开发文档:微信公众支付开发文档

支付流程:首先由前端调用后台的统一支付接口,通过统一支付接口拿到返回参数,主要是要拿到前端调用支付时的prepay_id,将统一支付接口返回的参数进行签名、封装之后返回给前端,前端通过H5或者JSSDK调用微信支付功能,调用成功后微信会根据统一支付接口配置的通知URL来调用开发中的通知接口,同时微信端会将数据发送到通知接口来,然后在通知接口中做其他业务的处理就可以了,处理成功给微信端返回成功状态码,失败返回失败状态码。

统一下单接口代码:

/**
	 * 
	 * @Title: wxPay
	 * @Description: 微信统一支付
	 * @param request
	 * @param response
	 * @throws Exception
	 * @return Map<String,String>
	 */
	@RequestMapping("/wxPay")
	@ResponseBody
	public JsPayResult wxPay(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String usid = request.getParameter("usid");
		Account ac = userCache.get(usid);
		// 交易类型
		String trade_type = "JSAPI";
		// 用户标识
		String openid = ac.getOpenid();
		// 公众账号ID
		String appid = Constants.APPID;
		// 商户号
		String mch_id = Constants.MCHID;
		// 通知地址
		String notify_url = Constants.PAY_NOTIFY_URL;
		// 随机字符串
		String nonce_str = CommonUtil.getRandomStr();
		// 商品描述
		String body = request.getParameter("body");
		//String body = "测试";
		// 终端IP
		String spbill_create_ip = request.getRemoteAddr();
		// 商户订单号
		String out_trade_no = request.getParameter("out_trade_no");
		//String out_trade_no = "111";
		// 金额
		String total_fee = CommonUtil.getMoney(request.getParameter("total_fee"));
	    //String total_fee = CommonUtil.getMoney("0.01");
		//String total_fee = "1";
		// 将请求参数封装至Map集合中
		SortedMap<String, String> paramMap = new TreeMap<String, String>();
		paramMap.put("appid", appid);
		paramMap.put("mch_id", mch_id);
		paramMap.put("nonce_str", nonce_str);
		paramMap.put("body", body);
		paramMap.put("out_trade_no", out_trade_no);
		paramMap.put("total_fee", total_fee);
		paramMap.put("spbill_create_ip", spbill_create_ip);
		paramMap.put("notify_url", notify_url);
		paramMap.put("trade_type", trade_type);
		paramMap.put("openid", openid);
		logger.info("支付IP" + spbill_create_ip);
		// 签名
		String sign = SignUtil.createSign(paramMap, Constants.PARTNER_KEY);
		paramMap.put("sign", sign);
		// 请求的xml数据
		String requestXml = XMLUtil.map2Xml(paramMap,"xml");
		// 调用post请求,同时返回的xml数据
		String resposeXmL = CommonUtil.weChatPayhttpsRequest(Constants.ORDER_PAY_URL, Constants.POST, requestXml);

		Map<String, String> responseMap = XMLUtil.xml2Map(resposeXmL);
		SortedMap<String, String> rspMap = new TreeMap<String, String>();
		JsPayResult result = new JsPayResult();
		if (Constants.RETURN_CODE.equals(responseMap.get("return_code"))) {
			
			String nonceStr = CommonUtil.getRandomStr();
			String timeStamp = CommonUtil.createTimeStamp();
			result.setAppId(responseMap.get("appid"));
			result.setTimeStamp(timeStamp);
			result.setNonceStr(nonceStr);
			result.setSignType("MD5");
			rspMap.put("appId", responseMap.get("appid"));
			rspMap.put("timeStamp",timeStamp);
			rspMap.put("nonceStr",nonceStr );
			rspMap.put("signType", "MD5");
		
			if (Constants.RESULT_CODE.equals(responseMap.get("result_code"))) {
				result.setPackaged("prepay_id=" + responseMap.get("prepay_id"));
				rspMap.put("package", result.getPackaged());
				String paySign = SignUtil.createSign(rspMap, Constants.PARTNER_KEY);
				result.setPaySign(paySign);
				result.setResultCode(Constants.SUCCESS_CODE);
				result.setMessage("支付成功!");
			} 
			logger.info("*****统一支付:支付成功!*****"+responseMap.get("return_code")+"--"+responseMap.get("return_msg"));

		} else {
			result.setResultCode(Constants.FAIL_CODE);
			result.setMessage("签名失败!");
			logger.info("*****统一支付:签名失败!*****"+responseMap.get("return_code")+"--"+responseMap.get("return_msg"));
		}

		return result;
	}

微信支付请求:

/**
	 * 
	 * @Title: weChatPayhttpsRequest @Description: 微信支付请求 @param @param
	 * requestUrl @param @param requestMethod @param @param
	 * outputStr @param @return @return String @throws
	 */
	public static String weChatPayhttpsRequest(String requestUrl, String requestMethod, String outputStr) {

		StringBuffer buffer = new StringBuffer();
		try {
			// 创建SSLContext对象,并使用我们指定的信任管理器初始化
			TrustManager[] tm = { new MyX509TrustManager() };
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();

			URL url = new URL(requestUrl);
			HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
			httpUrlConn.setSSLSocketFactory(ssf);

			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			// 设置请求方式(GET/POST)
			httpUrlConn.setRequestMethod(requestMethod);

			if ("GET".equalsIgnoreCase(requestMethod)) {
				httpUrlConn.connect();
			}

			// 当有数据需要提交时
			if (null != outputStr) {
				OutputStream outputStream = httpUrlConn.getOutputStream();
				// 注意编码格式,防止中文乱码
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}

			// 将返回的输入流转换成字符串
			InputStream inputStream = httpUrlConn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

			String str = null;
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			bufferedReader.close();
			inputStreamReader.close();
			// 释放资源
			inputStream.close();
			inputStream = null;
			httpUrlConn.disconnect();
		} catch (ConnectException ce) {
			ce.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return buffer.toString();
	}

签名方法:

/**
	 * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
	 */
	public static String createSign(SortedMap<String, String> packageParams,String key) {
		StringBuffer sb = new StringBuffer();
		Set<Entry<String,String>> es = packageParams.entrySet();
		Iterator<Entry<String, String>> it = es.iterator();
		while (it.hasNext()) {
			Map.Entry<String,String> entry = it.next();
			String pName = entry.getKey();
			String pValue = entry.getValue();
			if (StringUtils.isNotBlank(pValue) && !"sign".equals(pName)&& !"key".equals(pName)) {
				sb.append(pName).append("=").append(pValue).append("&");
			}
		}
		sb.append("key=" + key);
		String sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
		return sign;

	}

将返回的xml转换成map:

public static Map<String, String> xml2Map(String xml) {
        // 将解析结果存储在HashMap中
        HashMap<String, String> map = new HashMap<String, String>();
        SAXReader reader = new SAXReader();
        InputStream xmlIs;
        Document document = null;
		try {
			xmlIs = new ByteArrayInputStream(xml.getBytes("utf-8"));
			document = reader.read(xmlIs);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        // 得到xml根元素
        Element root = document.getRootElement();
        
        recursiveParseXML(root,map);
        return map;
    }

三、支付结果通知接口

支付结果通知接口是是统一支付接口中配置的,主要是为了获取微信端的交易订单号,同时在支付结果通知中处理其他的业务逻辑,处理完成后需要给微信端返回成功码,如果返回失败码微信端会一直发送微信支付结果通知,发送次数达到一定数量后就停止发送。

支付结果通知接口:

/**
	 * 
	* @Title: notify  
	* @Description: 微信支付结果通知  
	* @param @param request
	* @param @param response
	* @param @return
	* @param @throws Exception     
	* @return Map<String,String>    
	* @throws
	 */
	@ResponseBody
	@RequestMapping("/notify")
	public JsPayResult notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		JsPayResult result = new JsPayResult();
		logger.info("开始处理支付返回的请求");
		// 微信支付系统发送的数据(<![CDATA[product_001]]>格式)
		Map<String, String> xmlMap = CommonUtil.parseXmlForPay(request);
		logger.info("微信支付系统发送的数据" + xmlMap);
		// 返回给微信服务器的xml
		String respXml = "";
		// 判断返回是否成功
		if (Constants.RETURN_CODE.equals(xmlMap.get("return_code"))) {
			
			String time_end = xmlMap.get("time_end");
			ResponseResult r = payRegistration(Constants.BRANCHCODE,xmlMap.get("out_trade_no"),xmlMap.get("transaction_id"),
					Constants.PAYMODE,xmlMap.get("total_fee"),DateUtil.getTradeTime(time_end),Constants.YYSOURCE);
			if (Constants.RESULTCODE.equals(r.getResultCode())) {
				respXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
						+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
				result.setMessage("支付成功!");
				logger.info("*******支付结果通知**********"+"支付成功!"+xmlMap.get("return_code") + "**************" + xmlMap.get("return_msg"));
			}
			else {
				respXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
						+ "<return_msg><![CDATA[ERROR]]></return_msg>" + "</xml> ";
				result.setMessage("支付失败!");
				logger.error("*******支付结果通知**********"+"支付失败!"+xmlMap.get("return_code") + "**************" + xmlMap.get("return_msg"));
			}

			result.setResultCode(Constants.SUCCESS_CODE);
			
		} else {

			respXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA["
					+ xmlMap.get("return_code") + "]]></return_msg>" + "</xml> ";
			result.setResultCode(Constants.FAIL_CODE);
			result.setMessage("支付失败!");
			logger.error("*******支付结果通知**********"+"支付失败!"+xmlMap.get("return_code") + "**************" + xmlMap.get("return_msg"));
		}

		BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
		out.write(respXml.getBytes());
		out.flush();
		out.close();
		return result;
	}


猜你喜欢

转载自blog.csdn.net/dc282614966/article/details/80858948
今日推荐