servlet注入spring service

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

平时一般用spring都是使用controller,但有时候会使用到servlet。

场景:最近在接入中国银联代收产品支付,不得不说银联的技术人员牛X,为了大家方便统一接入,使用的是servlet

废话不多说,代码:

package com.pinyu.system.web.servlet.unionpay;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import com.pinyu.system.entity.SpaceTenantOrderEntity;
import com.pinyu.system.global.GlobalConstants;
import com.pinyu.system.sdk.AcpService;
import com.pinyu.system.sdk.DemoBase;
import com.pinyu.system.sdk.LogUtil;
import com.pinyu.system.sdk.SDKConfig;
import com.pinyu.system.sdk.SDKConstants;
import com.pinyu.system.sdk.SDKUtil;
import com.pinyu.system.service.SpaceTenantOrderService;
import com.pinyu.system.utils.unipay.UnionpayPropertiesUtils;

/** 
* @author ypp
* 创建时间:2018年11月13日 下午3:08:13 
* @Description: TODO(用一句话描述该文件做什么) 
*/
@WebServlet(urlPatterns="/tenantBillBack/")
public class TenantBillBack extends HttpServlet{
	
	@Autowired
	private SpaceTenantOrderService spaceTenantOrderService;
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 5219090621327772888L;

	@Override
	public void init(ServletConfig config) throws ServletException {
		//注入spring @Autowired Service
		SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
		/**
		 * 请求银联接入地址,获取证书文件,证书路径等相关参数初始化到SDKConfig类中
		 * 在java main 方式运行时必须每次都执行加载
		 * 如果是在web应用开发里,这个方法可使用监听的方式写入缓存,无须在这出现
		 */
		//这里已经将加载属性文件的方法挪到了web/AutoLoadServlet.java中
		//SDKConfig.getConfig().loadPropertiesFromSrc(); //从classpath加载acp_sdk.properties文件
		super.init();
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		LogUtil.writeLog("TenantBillBack接收后台通知开始");

		String encoding = req.getParameter(SDKConstants.param_encoding);
		// 获取银联通知服务器发送的后台通知参数
		Map<String, String> reqParam = getAllRequestParam(req);
		LogUtil.printRequestLog(reqParam);
		
		//重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过
		if (!AcpService.validate(reqParam, encoding)) {
			LogUtil.writeLog("验证签名结果[失败].");
			//验签失败,需解决验签问题
			
		} else {
			LogUtil.writeLog("验证签名结果[成功].");
			//交易成功,更新商户订单状态
			
			String orderId =reqParam.get("orderId"); //获取后台通知的数据,其他字段也可用类似方式获取
			String customerInfo = reqParam.get("customerInfo");
			if(null!=customerInfo){
				Map<String,String>  customerInfoMap = AcpService.parseCustomerInfo(customerInfo, "UTF-8");
				LogUtil.writeLog("customerInfoMap明文: "+ customerInfoMap);
			}
			
			String accNo = reqParam.get("accNo");
			//如果配置了敏感信息加密证书,可以用以下方法解密,如果不加密可以不需要解密
			if(null!=accNo){
				accNo = AcpService.decryptData(accNo, "UTF-8");
				LogUtil.writeLog("accNo明文: "+ accNo);
			}
			
			String tokenPayData = reqParam.get("tokenPayData");
			if(null!=tokenPayData){
				Map<String,String> tokenPayDataMap = SDKUtil.parseQString(tokenPayData.substring(1, tokenPayData.length() - 1));
				String token = tokenPayDataMap.get("token");//这样取
				LogUtil.writeLog("tokenPayDataMap明文: " + tokenPayDataMap);
			}
			
			String respCode = reqParam.get("respCode");
			//判断respCode=00、A6后,对涉及资金类的交易,请再发起查询接口查询,确定交易成功后更新数据库。
			if(respCode.equals("00")||respCode.equals("A6")){
				Map<String, String> contentData = new HashMap<String, String>();
				/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
				contentData.put("version", DemoBase.version);                   //版本号
				contentData.put("encoding", DemoBase.encoding);            //字符集编码 可以使用UTF-8,GBK两种方式
				contentData.put("bizType", "000501");                          //业务类型 token支付
				contentData.put("txnType", "11");                              //交易类型 11-代收
				contentData.put("txnSubType", "02");    
				//交易子类型 00-默认开通
				contentData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
				/***商户接入参数***/
				contentData.put("accessType", "0");                            //接入类型,商户接入固定填0,不需修改	
				contentData.put("merId",UnionpayPropertiesUtils.getMerId());   
				//商户号码(本商户号码仅做为测试调通交易使用,该商户号配置了需要对敏感信息加密)测试时请改成自己申请的商户号,【自己注册的测试777开头的商户号不支持代收产品】
				contentData.put("orderId", orderId);             			   //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则	
				contentData.put("txnTime",reqParam.get("txnTime"));  //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效
				contentData.put("queryId", reqParam.get("queryId"));
				
				/**对请求参数进行签名并发送http post请求,接收同步应答报文**/
				Map<String, String> reqData = AcpService.sign(contentData,DemoBase.encoding);			  //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
				String requestBackUrl = SDKConfig.getConfig().getSingleQueryUrl();   								  //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
				Map<String, String> rspData = AcpService.post(reqData,requestBackUrl,DemoBase.encoding);
				String reCode = rspData.get("respCode");
				if(reCode.equals("00 ")||reCode.equals("A6 ")){
					String tenantOrderId = reqParam.get("reqReserved");
					SpaceTenantOrderEntity order = new SpaceTenantOrderEntity();
					order.setId(Integer.valueOf(tenantOrderId));
					order.setStatus(GlobalConstants.PayMoneyStatus.ALREADY_DELIVERED.getStatus());
					order.setPayRemark(rspData.get("respMsg"));
					spaceTenantOrderService.updateStatus(order);
				}
			}else {
				String tenantOrderId = reqParam.get("reqReserved");
				SpaceTenantOrderEntity order = new SpaceTenantOrderEntity();
				order.setId(Integer.valueOf(tenantOrderId));
				order.setStatus(GlobalConstants.PayMoneyStatus.DELIVERED_FAIL.getStatus());
				order.setPayRemark("银联应答码:"+respCode+","+reqParam.get("respMsg"));
				spaceTenantOrderService.updateStatus(order);
			}
			LogUtil.writeLog("respCode: " + respCode);
		}
		LogUtil.writeLog("TenantBillBack接收后台通知结束");
		//返回给银联服务器http 200状态码
		resp.getWriter().print("ok");
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
			IOException {
		this.doPost(req, resp);
	}

	/**
	 * 获取请求参数中所有的信息
	 * 当商户上送frontUrl或backUrl地址中带有参数信息的时候,
	 * 这种方式会将url地址中的参数读到map中,会导多出来这些信息从而致验签失败,这个时候可以自行修改过滤掉url中的参数或者使用getAllRequestParamStream方法。
	 * @param request
	 * @return
	 */
	public static Map<String, String> getAllRequestParam(
			final HttpServletRequest request) {
		Map<String, String> res = new HashMap<String, String>();
		Enumeration<?> temp = request.getParameterNames();
		if (null != temp) {
			while (temp.hasMoreElements()) {
				String en = (String) temp.nextElement();
				String value = request.getParameter(en);
				res.put(en, value);
				// 在报文上送时,如果字段的值为空,则不上送<下面的处理为在获取所有参数数据时,判断若值为空,则删除这个字段>
				if (res.get(en) == null || "".equals(res.get(en))) {
					// System.out.println("======为空的字段名===="+en);
					res.remove(en);
				}
			}
		}
		return res;
	}
	
	/**
	  * 获取请求参数中所有的信息。
	  * 非struts可以改用此方法获取,好处是可以过滤掉request.getParameter方法过滤不掉的url中的参数。
	  * struts可能对某些content-type会提前读取参数导致从inputstream读不到信息,所以可能用不了这个方法。理论应该可以调整struts配置使不影响,但请自己去研究。
	  * 调用本方法之前不能调用req.getParameter("key");这种方法,否则会导致request取不到输入流。
	  * @param request
	  * @return
	  */
	 public static Map<String, String> getAllRequestParamStream(
	   final HttpServletRequest request) {
		  Map<String, String> res = new HashMap<String, String>();
		  try {
		   String notifyStr = new String(IOUtils.toByteArray(request.getInputStream()),DemoBase.encoding);
		   LogUtil.writeLog("收到通知报文:" + notifyStr);
		   String[] kvs= notifyStr.split("&");
		   for(String kv : kvs){
		    String[] tmp = kv.split("=");
		    if(tmp.length >= 2){
		     String key = tmp[0];
		     String value = URLDecoder.decode(tmp[1],DemoBase.encoding);
		     res.put(key, value);
		    }
		   }
		  } catch (UnsupportedEncodingException e) {
		   LogUtil.writeLog("getAllRequestParamStream.UnsupportedEncodingException error: " + e.getClass() + ":" + e.getMessage());
		  } catch (IOException e) {
		   LogUtil.writeLog("getAllRequestParamStream.IOException error: " + e.getClass() + ":" + e.getMessage());
		  }
		  return res;
	 }
}

其他的不需要的话,不用看,只看init方法里面的代码,需要在servlet里面注入到spring上下文中的service bean

主要代码:

@Override
	public void init(ServletConfig config) throws ServletException {
		//注入spring @Autowired Service
		SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
		super.init();
	}

后面就可以在这个servlet里面的doPost等方法里面使用service了

猜你喜欢

转载自blog.csdn.net/ypp91zr/article/details/84066200