package com.unionpay.acp.demo;

import com.unionpay.acp.sdk.Acp7Service;
import com.unionpay.acp.sdk.SDKConstants;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;


public class Acp7NotifyDemo extends HttpServlet {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = -660479751732985585L;
	/**
	 * 
	 */
	private final static Logger logger = Logger.getLogger(Acp7NotifyDemo.class);
	private final static String encoding = "utf-8";

	@Override
	public void init() throws ServletException {
		super.init();
	}
	
	/**
	 * http头理论上是大小写不敏感的，所以各servlet底层方法可能获取的头都是纯小写，但我们要求大小写要按规范一直，这里把它转换回原大小写的值。
	 * @param headerKey
	 * @return
	 */
	public static String fmtHeaderKey(String headerKey) {
		String key = headerFmtMap.get(headerKey);
		if (key == null)
			return headerKey;
		return key;
	}
	
	private static Map<String, String> headerFmtMap; //fmtHeaderKey方法使用
	
	static {
		//fmtHeaderKey方法使用
		headerFmtMap = new TreeMap<String, String>();
		String[] keys = { "signMethod", "reqId", "version", 
				"appId", "appType", "bizMethod" , "sign", "signId"};
		for (String key : keys) {
			headerFmtMap.put(key.toLowerCase(), key);
		}
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html; charset=UTF-8");
        resp.setCharacterEncoding("UTF-8");
		
        String n = "";
        if(req.getServletPath().toLowerCase().contains("front")){
            n = "前台";
        } else if(req.getServletPath().toLowerCase().contains("back")){
            n = "后台";
        }
        
		logger.info(String.format("%s接收%s通知开始", req.getServletPath(), n));

		Map<String, String> reqHeaderMap = new HashMap<String, String>();
		Enumeration<String> headerNames = req.getHeaderNames();
		while(headerNames.hasMoreElements()){
			String name = headerNames.nextElement().toLowerCase();//不加转小写时似乎也是自动转小写（可能和web容器处理有关，这里都转小写先，后续再转回规范大小写）
    		reqHeaderMap.put(fmtHeaderKey(name), req.getHeader(name));
		}
		logger.info("请求头：[" + reqHeaderMap + "]");
		
		// 请求体
		String body = new String(IOUtils.toByteArray(req.getInputStream()), encoding);
		logger.info("请求体：[" + body + "]");

        //TODO 实际前台通知需向用户展示结果页面，后台通知仅http200即可，所以建议分开处理，此处为演示方便一样处理了。
        try {
			analyzeMessage(reqHeaderMap, body, resp.getWriter());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

        //不抛异常或者特意修改状态码时，默认就是返回给银联服务器http200状态码
        logger.debug(String.format("%s接收%s通知结束", req.getServletPath(), n));
	}

    @SuppressWarnings("unused")
	private void analyzeMessage(Map<String, String> reqHeaders, String body, Writer writer) throws IOException, JSONException {

        StringBuffer info = new StringBuffer();
        boolean check = true;
        info.append("收到的请求头参数列表：\n");
        for(String key : reqHeaders.keySet()) {
            info.append(key + "=" + reqHeaders.get(key) + "\n");
        }
        info.append("\n");
        
        JSONObject reqObject = new JSONObject(body);
        info.append("收到的请求体参数列表：\n");
        info.append(reqObject.toString(4) + "\n");
        info.append("\n");

        String version = reqHeaders.get(SDKConstants.param_version);

        if (!"7.0.1".equals(version)){
            logger.error("无法识别的version=" + version);
            info.append("无法识别的version=" + version + "\n");
        } else {
            //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
        	Map<String, String> reqMap = new HashMap<String, String>(reqHeaders);
        	reqMap.put("body", body);
            if (!Acp7Service.validate(reqMap)) {
                logger.info("验证签名结果[失败].");
                info.append("验证签名结果[失败]."+ "\n");
            } else {
                logger.info("验证签名结果[成功].");
                info.append("验证签名结果[成功]."+ "\n");
                //【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态
                @SuppressWarnings("unused")
                String reqId = reqHeaders.get("reqId"); 
                @SuppressWarnings("unused")
                String code = reqObject.optString("code");
                String subCode = reqObject.optString("subCode");
            }
        }

        writer.write(DemoUtil.escapeHtmlAndReplaceLfCr(info.toString()));
    }

	
}
