版权声明:本文为博主原创文章,未经博主允许不得转载。 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了