package com.qxgmat.service.pay;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.nuliji.pay.common.PayInfo;
import com.nuliji.pay.common.ResultInfo;
import com.nuliji.util.Configure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

/**
 * Created by gaojie on 2017/5/10.
 */
public class Alipay implements PaySource{
    //添加一个日志器
    private static final Logger logger = LoggerFactory.getLogger(Alipay.class);
    private static Alipay instance = null;
    private AlipayClient alipayClient = null;
    static{
        instance = new Alipay();
    }
    public static Alipay getInstance(){
        return instance;
    }
    public Alipay(){
        alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", Configure.alipayAppId(), Configure.alipayAppKey(), "json", "utf-8", Configure.alipayAppPublicKey(), "RSA2");
    }
    @Override
    public ResultInfo notifyTrade(HttpServletRequest request) throws Exception{
        //获取支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        logger.debug("alipay notify:"+params.toString());
//切记alipaypublickey是支付宝的公钥，请去open.alipay.com对应应用下查看。
//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        boolean flag = AlipaySignature.rsaCheckV1(params, Configure.alipayAppPublicKey(), "utf-8", "RSA2");
        ResultInfo info = new ResultInfo();
        info.setResponse(params);
        if(!flag){
            info.setStatus("system");
            info.setMessage("签名校验错误");
            return info;
        }

        String status = params.get("trade_status");
        if(Objects.equals(status, "WAIT_BUYER_PAY")){
            info.setStatus("wait");
        }else if(Objects.equals(status, "TRADE_CLOSE")){
            info.setStatus("close");
        }else if(Objects.equals(status, "TRADE_SUCCESS")){
            info.setStatus("success");
        }else if(Objects.equals(status, "TRADE_FINISHED")){
            info.setStatus("finish");
        }
        info.setId(params.get("out_trade_no"));
        info.setTransaction_no(params.get("trade_no"));
        info.setMoney(BigDecimal.valueOf(Float.valueOf(params.get("total_amount"))));
        info.setOpen_id(params.get("buyer_user_id"));
        info.setMessage("success");
        info.setAppId(Configure.alipayAppId());
        info.setPid(params.get("seller_id"));
        return info;
    }

    @Override
    public ResultInfo returnTrade(ResultInfo info) {
        Map<String,String> response = (Map)info.getResponse();
        if(response.get("code").isEmpty()){
            // 手机返回
            // let {out_trade_no, trade_no, app_id, total_amount, seller_id, msg, charset, timestamp, code} = content;
            switch(response.get("code")){
                case "9000":
                    info.setStatus("wait");
                    info.setId(response.get("out_trade_no"));
                    info.setTransaction_no(response.get("trade_no"));
                    info.setMoney(BigDecimal.valueOf(Float.valueOf(response.get("total_amount"))));
                    break;
                case "6004":
                case "8000":
                    info.setStatus("wait");
                    info.setMessage("支付处理中");
                    break;
                case "4000":
                    info.setStatus("error");
                    info.setMessage("支付失败");
                    break;
                case "6001":
                    info.setStatus("system");
                    info.setMessage("用户取消支付");
                    break;
                case "6002":
                    info.setStatus("system");
                    info.setMessage("网络链接错误");
                    break;
                case "5000":
                default:
                    info.setStatus("system");
                    info.setMessage("请联系管理员");
            }
        }else{
            // wap返回
            // let {app_id, method, sign_type, sign, charset, timestamp, version, out_trade_no, trade_no, total_amount, seller_id} = content;
            info.setId(response.get("out_trade_no"));
            info.setTransaction_no(response.get("trade_no"));
            info.setMoney(BigDecimal.valueOf(Float.valueOf(response.get("total_amount"))));
            info.setMessage("success");
            info.setStatus("wait");
        }
        return info;
    }

    @Override
    public boolean validTrade(ResultInfo info, PayInfo payInfo) {
        if(info.getAppId() != payInfo.getAppId()) return false;
//        if(info.getPid() != payInfo.getPid()) return false;

        return true;
    }

    @Override
    public PayInfo appTrade(String payId, String pid, BigDecimal money, String subject, String body, String notifyUrl, String ip) throws Exception{
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody(body);
        model.setSubject(subject);
        model.setOutTradeNo(payId);
        model.setTimeoutExpress("30m");
        model.setTotalAmount(money.toString());
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl(notifyUrl);

        PayInfo payInfo = new PayInfo();
        payInfo.setPid(pid);
        payInfo.setAppId(Configure.alipayAppId());
        payInfo.setMoney(money);

        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
//            System.out.println(response.getBody());//就是orderString 可以直接给客户端请求，无需再做处理。
            payInfo.setRequest(response.getBody());
            payInfo.setStatus("ok");
        } catch (AlipayApiException e) {
            e.printStackTrace();
            payInfo.setStatus("fail");
            payInfo.setRequest(request);
        }
        return payInfo;
    }

    @Override
    public ResultInfo getTrade(String payId, String transactionNo, String pid) throws Exception {
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizContent("{" +
                "    \"out_trade_no\":\""+payId+"\"," +
                "    \"trade_no\":\""+transactionNo+"\"" +
                "  }");
        AlipayTradeQueryResponse response = alipayClient.execute(request);
        logger.debug("alipay getTrade:"+ JSON.toJSONString(response));

        ResultInfo info = parseResult(response, "ok");
        String status = info.getStatus();
        if(Objects.equals(status, "ok")) {
            String trade_status = response.getTradeStatus();
            logger.debug(trade_status);
            // 交易状态：WAIT_BUYER_PAY（交易创建，等待买家付款）、TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）、TRADE_SUCCESS（交易支付成功）、TRADE_FINISHED（交易结束，不可退款）
            if(Objects.equals(trade_status, "WAIT_BUYER_PAY")){
                info.setStatus("wait");
            }else if(Objects.equals(trade_status, "TRADE_CLOSE")){
                info.setStatus("close");
            }else if(Objects.equals(trade_status, "TRADE_SUCCESS")){
                info.setStatus("success");
            }else if(Objects.equals(trade_status, "TRADE_FINISHED")){
                info.setStatus("finish");
            }
            logger.debug(info.getStatus());
        }
        logger.debug("alipay getTrade:"+ JSON.toJSONString(info));
        return info;
    }

    private ResultInfo parseResult(AlipayTradeQueryResponse response, String status){
        ResultInfo info = new ResultInfo();
        info.setAppId(Configure.alipayAppId());
        info.setResponse(response);
        info.setId(response.getOutTradeNo());
        info.setMoney(BigDecimal.valueOf(Float.valueOf(response.getTotalAmount())));
        info.setOpen_id(response.getOpenId());
        info.setTransaction_no(response.getTradeNo());

        if (response == null || response.getCode() == null) {
            return formatInfo(info, "system", "请求错误");
        }
        switch(response.getCode()){
            case "10000": // 调用接口成功
                return formatInfo(info, status, "");
            case "40004": // 业务处理失败
                switch(response.getSubCode()){
                    case "ACQ.EXIST_FORBIDDEN_WORD":
                        return formatInfo(info, "error", response.getSubMsg());
                    case "ACQ.TRADE_HAS_SUCCESS":
                        return formatInfo(info, "success", "");
                    case "ACQ.TRADE_HAS_CLOSE":
                        return formatInfo(info, "close", "当前订单已关闭，请重新支付");
                    case "ACQ.BUYER_BALANCE_NOT_ENOUGH":
                        return formatInfo(info, "error", "用户余额不足");
                    case "ACQ.BUYER_BANKCARD_BALANCE_NOT_ENOUGH":
                        return formatInfo(info, "error", "用户余额不足");
                    case "ACQ.ERROR_BALANCE_PAYMENT_DISABLE":
                        return formatInfo(info, "error", "余额支付需开启");
                    case "ACQ.BUYER_SELLER_EQUAL":
                        return formatInfo(info, "error", "买卖家不能相同");
                    case "ACQ.TRADE_BUYER_NOT_MATCH":
                        return formatInfo(info, "error", "交易买家不匹配");
                    case "ACQ.BUYER_ENABLE_STATUS_FORBID":
                        return formatInfo(info, "error", "用户状态非法");
                    case "ACQ.PULL_MOBILE_CASHIER_FAIL":
                        return formatInfo(info, "error", "请重新支付");
                    case "ACQ.MOBILE_PAYMENT_SWITCH_OFF":
                        return formatInfo(info, "error", "请开启无线支付功能");
                    case "ACQ.PAYMENT_FAIL":
                        return formatInfo(info, "error", "系统错误，请重新支付");
                    case "ACQ.BUYER_PAYMENT_AMOUNT_DAY_LIMIT_ERROR":
                        return formatInfo(info, "error", "买家付款日限额超限");
                    case "ACQ.BEYOND_PAY_RESTRICTION":
                        return formatInfo(info, "error", "商家收款额度超限");
                    case "ACQ.BEYOND_PER_RECEIPT_RESTRICTION":
                        return formatInfo(info, "error", "商家月收款额度超限");
                    case "ACQ.BUYER_PAYMENT_AMOUNT_MONTH_LIMIT_ERROR":
                        return formatInfo(info, "error", "买家月支付额度超限");
                    case "ACQ.SELLER_BEEN_BLOCKED":
                        return formatInfo(info, "error", "商家账号被冻结");
                    case "ACQ.ERROR_BUYER_CERTIFY_LEVEL_LIMIT":
                        return formatInfo(info, "error", "买家未通过人行认证");
                    case "ACQ.PAYMENT_REQUEST_HAS_RISK":
                        return formatInfo(info, "error", "支付方式存在风险");
                    case "ACQ.NO_PAYMENT_INSTRUMENTS_AVAILABLE":
                        return formatInfo(info, "error", "没有可用的支付工具");
                    case "ACQ.USER_FACE_PAYMENT_SWITCH_OFF":
                        return formatInfo(info, "error", "请开启当面付款开关");
                    case "ACQ.SELLER_BALANCE_NOT_ENOUGH":
                        return formatInfo(info, "error", "商户的支付宝账户中无足够的资金进行撤销");
                    case "ACQ.REASON_TRADE_BEEN_FREEZEN":
                        return formatInfo(info, "exception", "当前交易被冻结，不允许进行撤销");
                    case "ACQ.TRADE_NOT_EXIST"://query
                        return formatInfo(info, "close", "查询的交易不存在，请重新支付");
                    default:
                        return formatInfo(info, "system", response.getSubMsg());
                }
            case "20000":// 服务不可用
                return formatInfo(info, "wait", "等待支付处理");
            case "20001":// 授权权限不足
            case "40001":// 缺少必要参数
            case "40002":// 非法参数
            case "40006":// 权限不足
            default:
                return formatInfo(info, "system", response.getMsg());
        }
    }

    private ResultInfo formatInfo(ResultInfo info, String status, String message){
        info.setStatus(status);
        info.setMessage(message);
        return info;
    }
}
