package cn.common.service.impl;


import cn.common.common.alipay.AliPayUniTransferParam;
import cn.common.common.constants.BaseConstant;
import cn.common.common.enums.ErrorCode;
import cn.common.common.exception.BusinessException;
import cn.common.common.utils.common.CheckParam;
import cn.common.common.utils.common.DateTimeUtil;
import cn.common.interaction.internal.alipay.AliPayCallBack;
import cn.common.interaction.internal.alipay.AliPayTradeParam;
import cn.common.interaction.req.alipay.AliPaySignReq;
import cn.common.service.AliPayService;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.*;
import java.security.Security;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author Singer create by singer email:singer-coder@qq.com
 * @projectName health-platform-api
 * @Description: 支付宝支付组件
 * @date 2020-04-27
 */
@Component
@Slf4j
public class AliPayServiceImpl implements AliPayService {

    @Resource
    private BaseConstant baseConstant;

    @Resource
    private HttpServletRequest request;

    /**
      * 格式编码
      */
    private static final String CHARSET = "utf-8";

    /**
     * 签名方式
     */
    private static final String SIGN_TYPE = "RSA2";

    /**
     * 请求网关地址
     */
    private static final String ALI_PAY_GATEWAY = "https://openapi.alipay.com/gateway.do";

    //此处如果不生命此静态代码块验签则会报错
    static{
            try{
                Security.addProvider(new BouncyCastleProvider());
             }catch(Exception e){
           log.info(">>>>>>>>>>>>>>>新增BouncyCastleProvider出错<<<<<<<<<<<<<<"+e.getMessage(),e);
               }
    }




    /**
      * 支付宝回调
      * JAVA服务端验证异步通知信息参数示例（证书）
      * @author: create by singer - Singer email:singer-coder@qq.com
      * @date 2020-07-01
      */
    @Override
    public void aliPayCallBack(){
        //拿到支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<>(16);

        Map requestParams = request.getParameterMap();

        log.info(">>>>>>>>>>>>支付宝回调参数 : <<<<<<<<<<<"+JSON.toJSONString(requestParams));

        log.info(">>>>>>>>>>>>>>>支付宝异步回调，异步执行校验结果,当前线程ID : <<<<<<<<<<<<<"+ Thread.currentThread().getId());

        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] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。
            params.put(name, valueStr);
        }

        log.info(">>>>>>>>>>>>支付宝回调参数解析 : <<<<<<<<<<<"+JSON.toJSONString(params));

        //切记AliPayPublicKey是支付宝的公钥，请去open.alipay.com对应应用下查看。
        try {

            ClassPathResource appCertPublicKeyResource = new ClassPathResource("aliPayCert/alipayCertPublicKey_RSA2.crt");

            String aliPayPublicCertPath = appCertPublicKeyResource.getFile().getAbsolutePath();

            log.info(">>>>>>>>>>>>>>>>aliPayPublicCertPath的位置 : <<<<<<<<<<<<<"+aliPayPublicCertPath);

            boolean flag = AlipaySignature.rsaCertCheckV1(params, aliPayPublicCertPath, "utf-8","RSA2");
            log.info(">>>>>>>>>>>>>>>支付宝回调，校验结果 : <<<<<<<<<<<<<"+flag);
            if(!flag) {
                return;
            }
            AliPayCallBack aliPayCallBack = JSON.parseObject(JSON.toJSONString(params), AliPayCallBack.class);
        } catch (AlipayApiException e) {
            log.info(">>>>>>>>>>>支付宝回调，校验出现异常<<<<<<<<<"+e.getMessage()+"|"+e.getErrMsg(),e);
        } catch (IOException e) {
            log.info(">>>>>>>>>>>支付宝回调，校验出现异常,公钥文件不存在<<<<<<<<<"+e.getMessage(),e);
        }
    }

    /**
      * APP内支付签名 JAVA服务端 SDK 生成 APP支付订单信息示例（证书）
      * 访问地址 https://opendocs.alipay.com/open/54/106370/
      *
      * @author: create by singer - Singer email:singer-coder@qq.com
      * @date 2020-07-01
      * @param aliPaySignReq APP内支付签名
      * @return String
      */
    @Override
    public String appTradeSign(AliPaySignReq aliPaySignReq){

        if(CheckParam.isNull(aliPaySignReq.getNeedSignContent())) {
            throw new BusinessException(ErrorCode.NO_REQUIRED_PARAM_ERROR.getCode(),"缺少必填参数:needSignContent");
        }

        log.info(">>>>>>>>>>>>>>>>>需要签名的字符串为 : <<<<<<<<<<<<<<<"+JSON.toJSONString(aliPaySignReq));

        CertAlipayRequest certAlipayRequest = returnCertAliPayRequest();

        try {
            DefaultAlipayClient aliPayClient  = new DefaultAlipayClient(certAlipayRequest);

            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

            AliPayTradeParam aliPayTradeParam = JSON.parseObject(aliPaySignReq.getNeedSignContent(), AliPayTradeParam.class);

            //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setBody(aliPayTradeParam.getBody());
            model.setSubject(aliPayTradeParam.getSubject());
            model.setOutTradeNo(aliPayTradeParam.getOutTradeNo());
            model.setTimeoutExpress(aliPayTradeParam.getTimeoutExpress());
            model.setTotalAmount(aliPayTradeParam.getTotalAmount());
            model.setProductCode("QUICK_MSECURITY_PAY");
            request.setBizModel(model);
            //回调地址
            request.setNotifyUrl("https://www.jinyitiancheng.com/app-api/api/v1/aliPay/aliPayCallBack");

            AlipayTradeAppPayResponse alipayTradeAppPayResponse = aliPayClient.sdkExecute(request);

            //就是orderString 可以直接给客户端请求，无需再做处理。
            log.info("签名结果："+alipayTradeAppPayResponse.getBody());
            return alipayTradeAppPayResponse.getBody();
        }catch (AlipayApiException e) {
            e.printStackTrace();
        }

        return null;
    }


    /**
      * 对指定的字符串进行签名
      * @author: create by singer - Singer email:singer-coder@qq.com
      * @date 2020-06-30
      * @param aliPaySignReq 签名所需参数
      * @return String
      */
    @Override
    public String aliPaySign(AliPaySignReq aliPaySignReq){

        if(CheckParam.isNull(aliPaySignReq.getNeedSignContent())) {
            throw new BusinessException(ErrorCode.NO_REQUIRED_PARAM_ERROR.getCode(),"缺少必填参数:needSignContent");
        }

        log.info(">>>>>>>>>>>>>>>>>需要签名的字符串为 : <<<<<<<<<<<<<<<"+JSON.toJSONString(aliPaySignReq));

        StringBuffer paramBuffer = new StringBuffer();

        paramBuffer.append("alipay_sdk=").append("alipay-sdk-java-4.9.124.ALL");
        paramBuffer.append("&app_id=2021001170611154");
        paramBuffer.append("&biz_content=").append(aliPaySignReq.getNeedSignContent());
        paramBuffer.append("&charset=utf-8");
        paramBuffer.append("&method=alipay.trade.app.pay");
        paramBuffer.append("&sign_type=RSA2");
        paramBuffer.append("&timestamp=").append(DateTimeUtil.localDateTime(null, LocalDateTime.now()));
        paramBuffer.append("&version=1.0");

        CertAlipayRequest certAlipayRequest = returnCertAliPayRequest();

        try {
            DefaultAlipayClient aliPayClient  = new DefaultAlipayClient(certAlipayRequest);


            log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>被签名的字符串为: <<<<<<<<<<<<<<<<<<<<<<<"+paramBuffer.toString());

            String signResult = aliPayClient.getSigner().sign(paramBuffer.toString(), SIGN_TYPE, CHARSET);

            boolean verifyResult = aliPayClient.getSignChecker().check(paramBuffer.toString(), signResult, "RSA2", "utf-8");

            System.out.println(verifyResult);

            paramBuffer.append("&sign=").append(signResult);

            String resultStr = URLEncoder.encode(paramBuffer.toString(), "utf-8");

            log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>签名结果为: <<<<<<<<<<<<<<<<<<<<<<<"+resultStr);

            return resultStr;

        }catch (UnsupportedEncodingException e){
            throw new BusinessException(ErrorCode.ERROR.getCode(), ErrorCode.ERROR.getMessage()+",签名异常");
        }catch (AlipayApiException e) {
            throw new BusinessException(ErrorCode.ERROR.getCode(), ErrorCode.ERROR.getMessage()+",初始化支付宝支付客户端失败");
        }

    }


    /**
      * 返回支付宝的CertAlipayRequest对象
      * @author: create by singer - Singer email:singer-coder@qq.com
      * @date 2020-05-09
      * @return CertAlipayRequest
      */
    @Override
    public CertAlipayRequest returnCertAliPayRequest(){
        //应用id
        String appId = baseConstant.getAliPayAppId();
        //pkcs8私钥
        String privateKey=baseConstant.getAliPayPrivateKey();

        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        certAlipayRequest.setServerUrl(ALI_PAY_GATEWAY);
        certAlipayRequest.setAppId(appId);
        certAlipayRequest.setPrivateKey(privateKey);
        certAlipayRequest.setFormat("json");
        certAlipayRequest.setCharset(CHARSET);
        certAlipayRequest.setSignType(SIGN_TYPE);

        ClassPathResource aliPayPublicCertResource = new ClassPathResource("aliPayCert/alipayCertPublicKey_RSA2.crt");
        ClassPathResource aliPayRootCertResource = new ClassPathResource("aliPayCert/alipayRootCert.crt");
        ClassPathResource appCertPublicKeyResource = new ClassPathResource("aliPayCert/appCertPublicKey.crt");
        try {
            String aliPayPublicPath = aliPayPublicCertResource.getFile().getAbsolutePath();
            String appCertPublicKeyPath = appCertPublicKeyResource.getFile().getAbsolutePath();
            String aliPayRootCertPath = aliPayRootCertResource.getFile().getAbsolutePath();

            log.info(">>>>>>>>>>>>>>>>>>>>>>>>应用公钥证书地址<<<<<<<<<<<<<<<<<"+appCertPublicKeyPath);
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>阿里公钥证书地址<<<<<<<<<<<<<<<<<"+aliPayPublicPath);
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>阿里根证书地址<<<<<<<<<<<<<<<<<"+aliPayRootCertPath);

            certAlipayRequest.setCertPath(appCertPublicKeyPath);
            certAlipayRequest.setAlipayPublicCertPath(aliPayPublicPath);
            certAlipayRequest.setRootCertPath(aliPayRootCertPath);
            System.out.println(aliPayPublicPath);
        } catch (IOException e) {
            log.error("封装支付宝证书，本地证书初始化异常"+e.getMessage(),e);
        }

        return certAlipayRequest;
    }

    /**
      * 调用阿里单笔转账请求接口
      * @author: create by singer - Singer email:singer-coder@qq.com
      * @date 2020-04-27
      * @param aliPayUniTransferParam 支付宝单笔转账请求参数
      * @return
      */
    @Override
    public AlipayFundTransUniTransferResponse aliPayUniTransfer(AliPayUniTransferParam aliPayUniTransferParam){

        log.info(">>>>>>>>>>>>>>支付宝单笔转账接口调用核心参数<<<<<<<<<<<<<<<<<{}", JSON.toJSONString(aliPayUniTransferParam));

        //应用id
        String appId = baseConstant.getAliPayAppId();
        //pkcs8私钥
        String privateKey=baseConstant.getAliPayPrivateKey();

        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        certAlipayRequest.setServerUrl(ALI_PAY_GATEWAY);
        certAlipayRequest.setAppId(appId);
        certAlipayRequest.setPrivateKey(privateKey);
        certAlipayRequest.setFormat("json");
        certAlipayRequest.setCharset(CHARSET);
        certAlipayRequest.setSignType(SIGN_TYPE);

        ClassPathResource alipayPublicCertResource = new ClassPathResource("aliPayCert/alipayCertPublicKey_RSA2.crt");
        ClassPathResource alipayRootCertResource = new ClassPathResource("aliPayCert/alipayRootCert.crt");
        ClassPathResource appCertPublicKeyResource = new ClassPathResource("aliPayCert/appCertPublicKey.crt");
        try {
            String aliPayPublicPath = alipayPublicCertResource.getFile().getAbsolutePath();
            String appCertPublicKeyPath = appCertPublicKeyResource.getFile().getAbsolutePath();
            String aliPayRootCertPath = alipayRootCertResource.getFile().getAbsolutePath();

            log.info(">>>>>>>>>>>>>>>>>>>>>>>>应用公钥证书地址<<<<<<<<<<<<<<<<<"+appCertPublicKeyPath);
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>阿里公钥证书地址<<<<<<<<<<<<<<<<<"+aliPayPublicPath);
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>阿里根证书地址<<<<<<<<<<<<<<<<<"+aliPayRootCertPath);

            certAlipayRequest.setCertPath(appCertPublicKeyPath);
            certAlipayRequest.setAlipayPublicCertPath(aliPayPublicPath);
            certAlipayRequest.setRootCertPath(aliPayRootCertPath);
            System.out.println(aliPayPublicPath);
        } catch (IOException e) {
            log.error("调用阿里单笔转账请求接口，本地证书初始化异常"+e.getMessage(),e);
        }
        DefaultAlipayClient aliPayClient = null;
        try {
            aliPayClient = new DefaultAlipayClient(certAlipayRequest);
        } catch (AlipayApiException e) {
            log.error("调用阿里单笔转账请求接口初始化根证失败"+e.getErrMsg(),e);
        }
        AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
        request.setBizContent(JSON.toJSONString(aliPayUniTransferParam));
        AlipayFundTransUniTransferResponse response = null;
        try {
            response = aliPayClient.certificateExecute(request);
        } catch (AlipayApiException e) {
            log.error("调用阿里单笔转账请求接口发生AlipayApiException异常"+e.getErrMsg(),e);
        }
        if(response.isSuccess()){
            log.error(">>>>>>>>>>>>>>调用阿里单笔转账请求接口成功>>>>>>>>>>>");
        } else {
            log.error(">>>>>>>>>>>>>>调用阿里单笔转账请求接口失败>>>>>>>>>>>");
        }

        return response;


    }


    /**
      * 通过支付宝授权认证码拿到支付宝的UserId
      * @author: create by singer - Singer email:singer-coder@qq.com
      * @date 2020-05-09
      * @param authCode 支付宝授权认证码
      * @return 支付宝的用户ID
      */
    @Override
    public String getAliPayUserId(String authCode) {
        try {
            CertAlipayRequest certAlipayRequest = returnCertAliPayRequest();

            DefaultAlipayClient aliPayClient  = new DefaultAlipayClient(certAlipayRequest);

            //alipayClient.getSigner().sign()

            AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
            request.setGrantType("authorization_code");
            request.setCode(authCode);
            AlipaySystemOauthTokenResponse response = aliPayClient.certificateExecute(request);

            System.out.println(JSON.toJSONString(response));

            if (response.isSuccess()) {
                log.error("调用阿里alipay.system.oauth.token(换取授权访问令牌) 接口成功" + response.getUserId());

                return response.getUserId();
            } else {
                log.error("调用阿里alipay.system.oauth.token(换取授权访问令牌) 接口失败");
                throw new BusinessException(ErrorCode.USER_ALI_PAY_AUTH_CODE_ERROR.getCode(), ErrorCode.USER_ALI_PAY_AUTH_CODE_ERROR.getMessage()+",支付宝返回: "+response.getMsg()+","+response.getSubMsg());
            }
        } catch (AlipayApiException e) {
            log.error("调用阿里alipay.system.oauth.token(换取授权访问令牌) 接口异常"+e.getErrMsg(),e);
            throw new BusinessException(ErrorCode.ERROR.getCode(), ErrorCode.ERROR.getMessage());
        }
    }



    public static void main(String[] args) {
        System.out.println(ClassUtils.getDefaultClassLoader().getResource("").getPath());
        System.out.println(System.getProperty("user.dir"));

        ClassPathResource classPathResource = new ClassPathResource("aliPayCert/alipayCertPublicKey_RSA2.crt");
        try {
            String absolutePath = classPathResource.getFile().getAbsolutePath();
            System.out.println(absolutePath);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }



    /**
     *
     * JAVA服务端 SDK 生成 APP支付订单信息示例（证书）
     * @author: create by singer - Singer email:singer-coder@qq.com
     * @date 2020-07-01
     * @param
     * @return
     */
    public String appPayTrade(){
        //构造client
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        //设置网关地址
        certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do");
        //设置应用Id
        certAlipayRequest.setAppId("app_id");
        //设置应用私钥
        certAlipayRequest.setPrivateKey("privateKey");
        //设置请求格式，固定值json
        certAlipayRequest.setFormat("json");
        //设置字符集
        certAlipayRequest.setCharset("charset");
        //设置签名类型
        certAlipayRequest.setSignType("sign_type");
        //设置应用公钥证书路径
        certAlipayRequest.setCertPath("app_cert_path");
        //设置支付宝公钥证书路径
        certAlipayRequest.setAlipayPublicCertPath("alipay_cert_path");
        //设置支付宝根证书路径
        certAlipayRequest.setRootCertPath("alipay_root_cert_path");
        //构造client
        AlipayClient alipayClient = null;
        try {
            alipayClient = new DefaultAlipayClient(certAlipayRequest);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("我是测试数据");
        model.setSubject("App支付测试Java");
        model.setOutTradeNo("outtradeno");
        model.setTimeoutExpress("30m");
        model.setTotalAmount("0.01");
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl("商户外网可以访问的异步地址");
        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            //就是orderString 可以直接给客户端请求，无需再做处理。
            System.out.println(response.getBody());

            return response.getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        return null;
    }
}
