package com.xxfc.platform.universal.biz;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.github.wxiaoqi.security.common.config.rabbit.RabbitConstant;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.util.HTTPSUtils;
import com.github.wxiaoqi.security.common.util.OrderUtil;
import com.github.wxiaoqi.security.common.util.UUIDUtils;
import com.github.wxiaoqi.security.common.util.process.ResultCode;
import com.github.wxiaoqi.security.common.util.process.SystemConfig;
import com.github.wxiaoqi.security.common.util.result.JsonResultUtil;
import com.xxfc.platform.universal.constant.PayWay;
import com.xxfc.platform.universal.entity.Dictionary;
import com.xxfc.platform.universal.entity.OrderPay;
import com.xxfc.platform.universal.mapper.OrderPayMapper;
import com.xxfc.platform.universal.utils.SignUtils;
import com.xxfc.platform.universal.vo.FundPayVo;
import com.xxfc.platform.universal.vo.OrderPayVo;
import com.xxfc.platform.universal.weixin.api.*;
import com.xxfc.platform.universal.constant.WxResponseProperties;
import com.xxfc.platform.universal.weixin.util.HTTPUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;

import static com.xxfc.platform.universal.constant.DictionaryKey.PAY_DEMOTION;
import static com.xxfc.platform.universal.constant.DictionaryKey.UNIVERSAL_PAY;

/**
 * 订单支付
 *
 * @author zjw
 * @email nishijjo@qq.com
 * @date 2019-05-28 16:17:42
 */
@Service
@Slf4j
public class OrderPayBiz extends BaseBiz<OrderPayMapper, OrderPay>{

    @Autowired
    DictionaryBiz dictionaryBiz;

    @Autowired
    HttpServletRequest request;

    @Autowired
    MQServiceBiZ mqServiceBiZ;
    @Value("${universal.url}")
    String weixinHost;
    @Value("${wx.appid}")
    private String wy_appid;
    @Value("${wx.appSercet}")
    private String wy_secret;

    private static final String SUCCESS = "SUCCESS";

    public JSONObject preparepay(OrderPayVo orderPayVo) {
        if (null == orderPayVo) {
            log.error("-----参数为空-----------");
            return JsonResultUtil.createFailedResult(ResultCode.NULL_CODE, "参数为空");
        }
        if (orderPayVo.getAmount() == null || orderPayVo.getAmount() == 0) {
            log.error("-----金额不为为0-----------");
            return JsonResultUtil.createFailedResult(ResultCode.NULL_CODE, "金额不为为0");
        }
        if (StringUtils.isBlank(orderPayVo.getOrderNo())) {
            log.error("-----订单不能为空-----------");
            return JsonResultUtil.createFailedResult(ResultCode.NULL_CODE, "订单不能为空");
        }
        log.info("订单支付参数：orderVo = {}", orderPayVo.toString());
        String trade_no = OrderUtil.GetOrderNumber("");
        orderPayVo.setTradeNo(trade_no);
        String order_no = orderPayVo.getOrderNo();
        Integer amount = orderPayVo.getAmount();
        log.error("---下单---order_no=====" + order_no + "--支付订单号--trade_no====" + trade_no + "--金额---amount====" + amount);
        //报名费回调路径

        //临时处理
        Map<String, Dictionary> dictionaryMap = dictionaryBiz.getAll4Map();
        Integer demotion = Integer.valueOf(dictionaryMap.get(UNIVERSAL_PAY + "_" + PAY_DEMOTION).getDetail());
        amount = amount / demotion;
        if (amount <= 0) {
            amount = 1;
        }
        Integer type = orderPayVo.getType() == null ? 1 : orderPayVo.getType();
        String jsParam = "";
        String notify_url = weixinHost + "/api/universal/pay/app/unauth/notify";
        String notifyUrl = weixinHost + "/api/universal/pay/app/unauth/notify/alipay";
        log.info("报名费回调路径notify_url:" + notify_url);
        Integer payWay = orderPayVo.getPayWay() == null ? 1 : orderPayVo.getPayWay();
        String sellerAccount = null;
        if (type == 2 && payWay == 1) {
            sellerAccount = SystemConfig.APP_PARTNER;
            jsParam = WXPay.webPay(amount + "", orderPayVo.getBody(), notify_url, trade_no, orderPayVo.getBuyerIp(), orderPayVo.getBuyerAccount(), null);
        } else if (type == 1 && payWay == 1) {
            sellerAccount = SystemConfig.APP_PARTNER;
            jsParam = WXPay.apppay(amount + "", orderPayVo.getBody(), notify_url, trade_no, orderPayVo.getBuyerIp(), 0);
        } else if (type == 1 && payWay == 2) {
            sellerAccount = SystemConfig.ALIPAY_PID;
            orderPayVo.setPayType(2);
            jsParam = generateAliPayment(orderPayVo, notifyUrl);
        } else if (type == 3 && payWay == 1) {
            sellerAccount = SystemConfig.APP_PARTNER;
            jsParam = WXPay.webPay(amount + "", orderPayVo.getBody(), notify_url, trade_no, orderPayVo.getBuyerIp(), orderPayVo.getBuyerAccount(), wy_appid);
        }
        log.info("报名费回调路径jsParam:" + jsParam);
        if (!StringUtils.isBlank(jsParam)) {
            try {
                OrderPay orderPay = new OrderPay();
                BeanUtils.copyProperties(orderPay, orderPayVo);
                orderPay.setSellerAccount(sellerAccount);
                insertSelective(orderPay);
                log.error("---下单---order_no=====" + order_no + "----成功");
            } catch (Exception e) {
                log.error(e.getMessage(), e);;
                log.error("---下单---order_no=====" + order_no + "----异常---msg===" + e.getMessage());
                return JsonResultUtil.createFailedResult(ResultCode.EXCEPTION_CODE, "出现异常");
            }
            if (payWay == 1) {
                JSONObject temp = JSON.parseObject(jsParam);
                return JsonResultUtil.createSuccessResultWithObj(temp);
            } else {
                return JsonResultUtil.createSuccessResultWithObj(jsParam);
            }
        } else {
            return JsonResultUtil.createDefaultFail();
        }
    }

    //支付回调
    public void notice(String orderNo, String serialNumber) {
        log.error("---支付回调---trade_no=====" + orderNo + "----开始处理");
        Example example = new Example(OrderPay.class);
        example.createCriteria().andEqualTo("tradeNo", orderNo).andEqualTo("isDel", 0).andEqualTo("status", 0);
        List<OrderPay> list = mapper.selectByExample(example);
        if (list.size() == 0) {
            log.error("---支付回调---trade_no=====" + orderNo + "----订单不存在或已处理");
            return;
        }
        example.clear();
        example.createCriteria().andEqualTo("tradeNo", orderNo);
        OrderPay orderPay = new OrderPay();
        orderPay.setFinishTime(System.currentTimeMillis());
        orderPay.setStatus(1);
        orderPay.setSerialNumber(serialNumber);
        int num = mapper.updateByExampleSelective(orderPay, example);

        log.error("---支付回调处理---num=====" + num + "----orderNo=======" + orderNo);
        if (num > 0) {
            //支付成功，添加积分
            OrderPay pay = list.get(0);
            OrderPay newValue = mapper.selectOne(orderPay);
            log.info("支付回调信息：newValue = {}", newValue);
            if (newValue.getStatus() == 1) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("userId", newValue.getUserId());
                jsonObject.put("amount", newValue.getAmount());
                jsonObject.put("channelId", newValue.getOrderNo());
                if (newValue.getChannel() == 1) {//租车
                    jsonObject.put("integralRuleCode", "RENTRV");
                } else if (newValue.getChannel() == 2) { //旅游
                    jsonObject.put("integralRuleCode", "BUYROUT");
                } else if (newValue.getChannel() == 3) {  //会员
                    jsonObject.put("integralRuleCode", "BUYMEMBER");
                }
                log.info("支付订单号：orderNo = {}, orderType = {}", newValue.getOrderNo(), newValue.getChannel());
                log.info("支付成功获取积分：发送消息 exchange = {}, routingKey = {}, json = {}", RabbitConstant.INTEGRAL_TOPIC, RabbitConstant.INTEGRAL_ROUTING_KEY, jsonObject.toJSONString());
                mqServiceBiZ.sendMessage(RabbitConstant.INTEGRAL_TOPIC, RabbitConstant.INTEGRAL_ROUTING_KEY, jsonObject.toJSONString());
            }
            if (StringUtils.isNotBlank(pay.getNotifyUrl())) {
                String url = pay.getNotifyUrl();
                Integer type = pay.getType() == null ? 1 : pay.getType();
                Integer payWay = pay.getPayWay();
                url += "&tradeNo=" + orderNo + "&type=" + type+"&payWay="+payWay;
                log.error("---支付回调处理---orderNo=======" + orderNo + "----notifyUrl====" + url);
                String result = "";
                if (url.contains("https") || url.contains("HTTPS")) {
                    result = HTTPSUtils.httpRequest(url, "GET", null);
                } else {
                    result = HTTPUtils.doGet(url);
                }
                log.error("---支付回调处理---orderNo=======" + orderNo + "---result===" + result);

            }
        }
    }

    /**
     * 支付宝回调接口
     *
     * @return
     */
    public String alipayNotify() {
        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<>();
        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] + ",";
            }
            params.put(name, valueStr);
        }
        String msg = JSONObject.toJSONString(params);
        log.info("alipay notify message={}", msg);
        //支付宝回调验签
        try {
            boolean flag = AlipaySignature.rsaCheckV1(params, SystemConfig.ALIPAY_PUBLIC_KEY, AlipayConstants.CHARSET_UTF8, AlipayConstants.SIGN_TYPE_RSA2);
            if (!flag) {
                log.info("alipay order rsaCheckV1 fail, result={}", msg);
                return ObjectRestResponse.createDefaultFail().getMessage();
            }
            String rsTradeStatus = params.get("trade_status");
            if (StringUtils.isBlank(rsTradeStatus)) { //订单预授权支付回调
				String status = params.get("status");
				if (!status.equals("SUCCESS")) {
					log.info("alipay order trade_status has problem, result={}", msg);
					return ObjectRestResponse.createDefaultFail().getMessage();
				}
			//订单APP支付回调
			} else if (!SystemConfig.ALIPAY_TRADE_FINISHED.equals(rsTradeStatus) && !SystemConfig.ALIPAY_TRADE_SUCCESS.equals(rsTradeStatus)) {
                log.info("alipay order trade_status has problem, result={}", msg);
                return ObjectRestResponse.createDefaultFail().getMessage();
            }

            String tradeNo = params.get("out_trade_no");
			if (StringUtils.isBlank(tradeNo)) {
				tradeNo = params.get("out_request_no");
			}
            String operationId = params.get("trade_no");
            if (StringUtils.isBlank(operationId)) {
            	operationId = params.get("auth_no");
			}
            notice(tradeNo, operationId);
            return ObjectRestResponse.succ().getMessage();
        } catch (AlipayApiException e) {
            log.error("", e);
        }
        return ObjectRestResponse.createDefaultFail().getMessage();
    }
    /**
     * 支付宝生成支付信息
     *
     * @return
     */
    @SuppressWarnings("rawtypes")
    private String generateAliPayment(OrderPayVo orderPayVo, String notifyUrl) {
        //实例化客户端
        AlipayClient alipayClient = getAlipayClient();
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        try {
            return fundAuthOrderAppFreeze(alipayClient, orderPayVo, notifyUrl);
        } catch (Exception e) {
            log.error(e.getMessage(), e);;
        }
        return null;
    }

    private AlipayClient getAlipayClient() {
        AlipayClient alipayClient = new DefaultAlipayClient(SystemConfig.ALIPAY_PAY_BASE_URL + "/gateway.do",
                SystemConfig.ALIPAY_APPID, SystemConfig.ALIPAY_PRIVATE_KEY, AlipayConstants.FORMAT_JSON,
                "utf-8", SystemConfig.ALIPAY_PUBLIC_KEY, AlipayConstants.SIGN_TYPE_RSA2);
        return alipayClient;
    }

    //balance	余额
    //moneyFund	余额宝
    //coupon	红包
    //pcredit	花呗
    //pcreditpayInstallment	花呗分期
    //creditCard	信用卡
    //creditCardExpress	信用卡快捷
    //creditCardCartoon	信用卡卡通
    //credit_group	信用支付类型（包含信用卡卡通、信用卡快捷、花呗、花呗分期）
    //debitCardExpress	借记卡快捷
    //mcard	商户预存卡
    //pcard	个人预存卡
    //promotion	优惠（包含实时优惠+商户优惠）
    //voucher	营销券
    //point	积分
    //mdiscount	商户优惠
    //bankPay
    //支付宝APP支付方法
    private String appOrderPay(AlipayClient alipayClient, OrderPayVo orderPayVo, String notifyUrl) {
        BigDecimal realAmount = new BigDecimal(orderPayVo.getAmount().toString()).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_UP);
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        request.setBizContent("{" +
                "\"total_amount\":\"" + realAmount + "\"," +
                "\"subject\":\"" + orderPayVo.getSubject() + "\"," +
                "\"enable_pay_channels\":\"balance,coupon,creditCard,creditCardExpress,creditCardCartoon,pcredit,credit_group,moneyFund,debitCardExpress\"," +
                "\"out_trade_no\":\"" + orderPayVo.getTradeNo() + "\"" +
                "  }");
        request.setNotifyUrl(notifyUrl);
        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            log.info(response.getBody());//就是orderString 可以直接给客户端请求，无需再做处理。
            return response.getBody();
        } catch (AlipayApiException e) {
            log.error(e.getMessage(), e);;
        }
        return null;
    }


    //预授权冻结
    public String fundAuthOrderAppFreeze(AlipayClient alipayClient, OrderPayVo orderPayVo, String notifyUrl) throws AlipayApiException {
        AlipayFundAuthOrderAppFreezeRequest request = new AlipayFundAuthOrderAppFreezeRequest();
        BigDecimal realAmount = new BigDecimal(orderPayVo.getAmount().toString()).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_UP);
        request.setBizContent("{" +
                "\"out_order_no\":\"" + orderPayVo.getTradeNo() + "\"," +
                "\"out_request_no\":\"" + orderPayVo.getTradeNo() + "\"," +
                "\"order_title\":\"" + orderPayVo.getSubject() + "\"," +
                "\"product_code\":\"PRE_AUTH_ONLINE\"," +
                "\"pay_timeout\":\"1d\"," +
                "\"amount\": \"" + realAmount.toString() + "\"  }");
        request.setNotifyUrl(notifyUrl);//异步通知地址，必填，该接口只通过该参数进行异步通知
        AlipayFundAuthOrderAppFreezeResponse response = alipayClient.sdkExecute(request);//注意这里是sdkExecute，可以获取签名参数
        log.info("response: {}" + response.getBody());//签名后的参数，直接入参到
        if (response.isSuccess()) {
            log.info("预授权冻结调用成功");
            return response.getBody();
        } else {
            log.info("预授权冻结调用失败");
            return null;
        }
    }

    /**
     * 转账功能  微信 | 支付宝
     *
     * @param fundPayVo
     * @return
     * @throws Exception
     */
    public String fundTrans(FundPayVo fundPayVo) throws BaseException {
        String cono = OrderUtil.GetOrderNumber("");
        fundPayVo.setOutBizNo(cono);
        if (fundPayVo.getType() == PayWay.WX_PAY.getCode()) {
            //微信
            fundPayVo.setCheckName("NO_CHECK");
            return wxpayfundTrans(fundPayVo);
        }
        if (fundPayVo.getType() == PayWay.ALI_PAY.getCode()) {
            //支付宝
            return alipayfundTrans(getAlipayClient(), fundPayVo);
        }
        throw new BaseException("提现方式不对");
    }

    /**
     * 支付宝转账功能 单笔最小转账0.1 最大小数点位数前支持13位，实际限额和签约一致
     *
     * @param alipayClient
     * @throws AlipayApiException
     */
    public String alipayfundTrans(AlipayClient alipayClient, FundPayVo fundPayVo){
        AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
        request.setBizContent("{" +
                "\"out_biz_no\":\"" + fundPayVo.getOutBizNo() + "\"," +
                "\"payee_type\":\"ALIPAY_USERID\"," +
                "\"payee_account\":\"" + fundPayVo.getPayeeAccount() + "\"," +
                "\"amount\":\"" + fundPayVo.getAmount() + "\"," +
                "\"payer_show_name\":\"" + fundPayVo.getPayerShowName() + "\"," +
                "\"remark\":\"" + fundPayVo.getRemark() + "\"" +
                "  }");
        AlipayFundTransToaccountTransferResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            throw new BaseException("支付宝转账api调用错误",e);
        }
        if (response.isSuccess()) {
            return fundPayVo.getOutBizNo();
        } else {
            throw  new BaseException(response.getSubMsg(),response.getSubCode());
        }
    }

    public String wxpayfundTrans(FundPayVo fundPayVo) {
        Map<String, String> map = WXSuppToUserPay.WeiXinTiXian(fundPayVo.getAmount(),fundPayVo.getOutBizNo(),fundPayVo.getCheckName(), fundPayVo.getAmount(), fundPayVo.getRemark(), fundPayVo.getCreatIp());
        if (SUCCESS.equals(map.get(WxResponseProperties.RETURN_CODE)) && SUCCESS.equals(map.get(WxResponseProperties.RESULT_CODE))) {
            return map.get(WxResponseProperties.PARTNER_TRADE_NO);
        }
        throw new BaseException(map.get(WxResponseProperties.ERR_CODE_DES),map.get(WxResponseProperties.ERROR_CODE));
    }


    /**
     *  支付宝授权获取用户信息
     * @param code
     * @return
     * @throws AlipayApiException
     */
    public String getAlipayToken(String code) throws AlipayApiException{
        AlipayClient alipayClient = getAlipayClient();
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setGrantType("authorization_code");
        request.setCode(code);
        AlipaySystemOauthTokenResponse response = alipayClient.execute(request);
        log.info("获取用户token调用成功,获取用户信息 {}", response.getBody());
        if(response.isSuccess()){
            log.info("获取用户token调用成功,获取用户信息 {}", response.getBody());
            if(response.getAccessToken() != null) {
                AlipayUserInfoShareRequest alipayUserInfoShareRequest = new AlipayUserInfoShareRequest();
                AlipayUserInfoShareResponse alipayUserInfoShareResponse = alipayClient.execute(alipayUserInfoShareRequest,response.getAccessToken());
                if(alipayUserInfoShareResponse.isSuccess()){
                    log.info("获取用户支付宝信息调用成功, {}", alipayUserInfoShareResponse.getBody());
                    return alipayUserInfoShareResponse.getBody();
                } else {
                    log.info("获取用户支付宝信息调用失败, {}", alipayUserInfoShareResponse.getBody());
                }
            }
        } else {
            log.info("获取用户token调用失败, {}", response.getBody());
        }
        return response.getBody();
    }


    public String generateAliPayInfo(String apiName, String appName) {
        //apiname=com.alipay.account.auth&app_id=xxxxx&app_name=mc&auth_type=AUTHACCOUNT&biz_type=openservice&method=alipay.open.auth.sdk.code.get&pid=xxxxx&product_id=APP_FAST_LOGIN&scope=kuaijie&sign_type=RSA2&target_id=20141225xxxx&sign=fMcp4GtiM6rxSIeFnJCVePJKV43eXrUP86CQgiLhDHH2u%2FdN75eEvmywc2ulkm7qKRetkU9fbVZtJIqFdMJcJ9Yp%2BJI%2FF%2FpESafFR6rB2fRjiQQLGXvxmDGVMjPSxHxVtIqpZy5FDoKUSjQ2%2FILDKpu3%2F%2BtAtm2jRw1rUoMhgt0%3D
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("apiname=");
        stringBuilder.append("com.alipay.account.auth");
        stringBuilder.append("&app_id=");
        stringBuilder.append(SystemConfig.ALIPAY_APPID);
        stringBuilder.append("&app_name=");
        stringBuilder.append("mc");
        stringBuilder.append("&auth_type=AUTHACCOUNT");
        stringBuilder.append("&biz_type=openservice");
        stringBuilder.append("&method=alipay.open.auth.sdk.code.get");
        stringBuilder.append("&pid=");
        stringBuilder.append(SystemConfig.ALIPAY_PID);
        stringBuilder.append("&product_id=APP_FAST_LOGIN");
        stringBuilder.append("&scope=kuaijie");
        stringBuilder.append("&sign_type=RSA2");
        stringBuilder.append("&target_id=");
        stringBuilder.append(UUIDUtils.generateShortUuid());
        String sign = SignUtils.sign(stringBuilder.toString(), SystemConfig.ALIPAY_PRIVATE_KEY, true);
        stringBuilder.append("&sign=");
        stringBuilder.append(sign);
        return stringBuilder.toString();
    }

    public String alipayUserAuth() throws AlipayApiException {
        AlipayClient alipayClient = getAlipayClient();
        AlipayUserInfoAuthRequest request = new AlipayUserInfoAuthRequest();
        request.setBizContent("{" +
                "      \"scopes\":[" +
                "        \"auth_base\"" +
                "      ]," +
                "\"state\":\"init\"," +
                "\"is_mobile\":\"true\"" +
                "  }");
        AlipayUserInfoAuthResponse response = alipayClient.execute(request);
        log.info("解冻预授权response: {}" + response.getBody());
        log.info(response.toString());
        if(response.isSuccess()){
            log.info("用户授权调用成功");
        } else {
            log.info("用户授权调用失败");
        }
        return response.getBody();
    }


    //解冻预授权
    public boolean fundAuthOrderUnFreeze(String outTradeNo, String serialNumber, Integer refundAmount, String refundReason) throws AlipayApiException {
        String notifyUrl = weixinHost + "/api/universal/pay/app/unauth/notify/alipay";
        AlipayClient alipayClient = getAlipayClient();
        BigDecimal realAmount = new BigDecimal(refundAmount.toString()).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_UP);
        AlipayFundAuthOrderUnfreezeRequest request = new AlipayFundAuthOrderUnfreezeRequest();
        AlipayFundAuthOrderUnfreezeModel model = new AlipayFundAuthOrderUnfreezeModel();
        model.setAuthNo(serialNumber); // 支付宝资金授权订单号，在授权冻结成功时返回需要入库保存
        model.setOutRequestNo(outTradeNo);//同一商户每次不同的资金操作请求，商户请求流水号不能重复,且与冻结流水号不同
        model.setAmount(realAmount.toString()); // 本次操作解冻的金额，单位为：元（人民币），精确到小数点后两位
        model.setRemark(refundReason); // 商户对本次解冻操作的附言描述，长度不超过100个字母或50个汉字
        //选填字段，信用授权订单，针对信用全免订单，传入该值完结信用订单，形成芝麻履约记录
        // model.setExtraParam("{\"unfreezeBizInfo\":\"{\\\"bizComplete\\\":\\\"true\\\"}\"}");
        request.setBizModel(model);
//        request.setNotifyUrl(notifyUrl);//异步通知地址，必填，该接口只通过该参数进行异步通知
        AlipayFundAuthOrderUnfreezeResponse response = alipayClient.execute(request);
        log.info("解冻预授权response: {}" + response.getBody());
        if (response.isSuccess()) {
            log.info("解冻预授权调用成功");
            return true;
        } else {
            log.info("解冻预授权调用失败");
            return false;
        }
    }

    //取消预授权
    public String fundAuthCancel(String outTradeNo, String serialNumber, String refundReason) throws AlipayApiException {
        AlipayClient alipayClient = getAlipayClient();
        String notifyUrl = weixinHost + "/api/universal/pay/app/unauth/notify/alipay";
        AlipayFundAuthOperationCancelRequest request = new AlipayFundAuthOperationCancelRequest();
        AlipayFundAuthOperationCancelModel model = new AlipayFundAuthOperationCancelModel();
        model.setAuthNo(outTradeNo); // 支付宝资金授权订单号，在授权冻结成功时返回参数中获得
        //model.setOutOrderNo(orderPayVo.getOrderNo()); //商户的授权资金订单号，与支付宝的授权资金订单号不能同时为空
        //model.setOperationId("20171201317348823902"); //支付宝的授权资金操作流水号
        model.setOutRequestNo(serialNumber);//与支付宝的授权资金操作流水号不能同时为空，与冻结流水号相同
        model.setRemark(refundReason); // 商户对本次撤销操作的附言描述，长度不超过100个字母或50个汉字
        request.setBizModel(model);
//        request.setNotifyUrl(notifyUrl);//异步通知地址，必填，该接口只通过该参数进行异步通知

        AlipayFundAuthOperationCancelResponse response = alipayClient.execute(request);
        log.info("取消预授权response: {}" + response.getBody());
        if (response.isSuccess()) {
            log.info("取消预授权调用成功");
            return response.getBody();
        } else {
            log.info("取消预授权调用失败");
            return null;
        }
    }

    //预授权转支付
    public boolean tradePay(String outTradeNo, String serialNumber, Integer refundAmount, String refundReason, String subject) throws AlipayApiException {
        String notifyUrl = weixinHost + "/api/universal/pay/app/unauth/notify/alipay";
        AlipayClient alipayClient = getAlipayClient();
        BigDecimal realAmount = new BigDecimal(refundAmount.toString()).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_UP);
        AlipayTradePayRequest request = new AlipayTradePayRequest();
        AlipayTradePayModel model = new AlipayTradePayModel();
        model.setOutTradeNo(outTradeNo); // 预授权转支付商户订单号，为新的商户交易流水号；如果重试发起扣款，商户订单号不要变；
        model.setProductCode("PRE_AUTH_ONLINE"); // 固定值PRE_AUTH_ONLINE
        model.setAuthNo(serialNumber); // 填写预授权冻结交易号
        if (StringUtils.isNotBlank(subject)) {
            model.setSubject(subject); // 解冻转支付标题，用于展示在支付宝账单中
        } else {
            model.setSubject("订单费用");
        }
        model.setTotalAmount(realAmount.toString()); // 结算支付金额
        model.setSellerId(SystemConfig.ALIPAY_PID); // 填写卖家支付宝账户pid
        model.setBuyerId(SystemConfig.ALIPAY_APPID); // 填写预授权用户uid，通过预授权冻结接口返回的payer_user_id字段获取
        if (StringUtils.isNotBlank(refundReason)) {
            model.setBody(refundReason); // 可填写备注信息
        } else {
            model.setBody("订单费用");
        }
        //如果需要从一笔授权中完成多笔订单支付，保持auth_no不变，不同订单根据outTradeNo进行标识，此时auth_confirm_mode不传或者传入NOT_COMPLETE；进行到最后一笔转支付时，auth_confirm_mode传入COMPLETE由支付宝完成剩余金额自动解冻，或者商户自行调用解冻接口将剩余金额解冻。
        model.setAuthConfirmMode("NOT_COMPLETE");//传入该值用户剩余金额不会自动解冻
        request.setBizModel(model);
//        request.setNotifyUrl(notifyUrl);//异步通知地址，必填，该接口只通过该参数进行异步通知

        AlipayTradePayResponse response = alipayClient.execute(request);
        log.info("预授权转支付response: {}" + response.getBody());
        if (response.isSuccess()) {
            return true;
        } else {
            log.info("预授权转支付调用失败");
            return false;
        }
    }

    /**
     * APP支付退款
     *
     * @param
     * @param outTradeNo   订单号
     * @param tradNo       流水号
     * @param refundAmount 退款金额
     * @param refundReason 退款原因
     *                     outRequestNo  退款请求标示 用于分批退款，每笔退款标示不一样，同样标示不会重复退款
     * @return
     */
    public boolean alipayOrderRefund(String outTradeNo, String tradNo, Integer refundAmount, String
            refundReason, String outRequestNo) {
        AlipayClient alipayClient = getAlipayClient();
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        BigDecimal realAmount = new BigDecimal(refundAmount.toString()).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_UP);
        request.setBizContent("{" +
                "\"out_trade_no\":\"" + outTradeNo + "\"," +
                "\"trade_no\":\"" + tradNo + "\"," +
                "\"refund_amount\":" + realAmount + "," +
                "\"refund_reason\":\"" + refundReason + "\"," +
                "\"out_request_no\":\"" + outRequestNo + "\"" +
                "  }");
        try {
            log.info("支付宝退款中：outTradeNo = {}， tradNo = {}, refundAmount = {}, refundReason = {}", outTradeNo, tradNo, refundAmount, refundReason);
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                return true;
            } else {
                log.info(response.getBody());
                return false;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);;
            log.info("退款失败请重试");
        }
        return false;
    }

    /**
     * 查询预授权订单
     * @param tradNo
     * @throws AlipayApiException
     */
    public ObjectRestResponse fundAuthQuery(String tradNo){
        AlipayClient alipayClient = getAlipayClient();
        AlipayFundAuthOperationDetailQueryRequest request = new AlipayFundAuthOperationDetailQueryRequest();
        AlipayFundAuthOperationDetailQueryModel model = new AlipayFundAuthOperationDetailQueryModel();
        //model.setAuthNo("2017120110002001390206804295"); // 支付宝资金授权订单号，在授权冻结成功时返回参数中获得
        model.setOutOrderNo(tradNo); //商户的授权资金订单号，与支付宝的授权资金订单号不能同时为空
        //model.setOperationId("20171201317348823902"); //支付宝的授权资金操作流水号，冻结成功同步返回
        model.setOutRequestNo(tradNo);//商户的授权资金操作流水号，与支付宝的授权资金操作流水号不能同时为空，该值为冻结或解冻是的outRequestNo
        request.setBizModel(model);
        AlipayFundAuthOperationDetailQueryResponse response = null;
        try {
            response = alipayClient.execute(request);
            log.info("response: {}"+response.getBody());
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return ObjectRestResponse.succ(response.getBody());
    }

    /**
     * 预授权转支付退款
     * @param tradNo 预授权转支付商户订单号
     * @param refundAmount 退款金额
     * @param refundReason 退款原因
     * @param outRequestNo 退款标志号
     */
    public AlipayTradeRefundResponse testTradeRefund(String tradNo, Integer refundAmount,  String refundReason, String outRequestNo) {
        AlipayClient alipayClient = getAlipayClient();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        BigDecimal realAmount = new BigDecimal(refundAmount.toString()).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_UP);
        model.setOutTradeNo(tradNo); //与预授权转支付商户订单号相同，代表对该笔交易退款
        model.setRefundAmount(realAmount.toString());
        model.setRefundReason(refundReason);
        model.setOutRequestNo(outRequestNo);//标识一次退款请求，同一笔交易多次退款需要保证唯一，如部分退款则此参数必传。
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        request.setBizModel(model);
        AlipayTradeRefundResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        log.info("response: {}"+response.getBody());
        return response;
    }
    
    public static void main(String[] args) throws AlipayApiException {
        OrderPayBiz orderPayBiz = new OrderPayBiz();
        OrderPayVo orderPayVo = new OrderPayVo();
        orderPayVo.setOrderNo("20191024153859000003");
        orderPayVo.setTradeNo("2019102410002001530572359246");
        orderPayVo.setSerialNumber("2019102410002001530572351411");
        orderPayVo.setAmount(3);
        orderPayVo.setBody("扣除租车订单费用");
        orderPayVo.setSubject("租车订单交易费用");
        //orderPayBiz.testTradeRefund("222522054304468992", 180000, "退还违约金1800元", "222522054304461800");
        //orderPayBiz.fundAuthOrderUnFreeze(orderPayVo, "");
        //orderPayBiz.alipayOrderRefund("20191024153859000003","2019102422001421530513773694", 2, "xxxx", "");
        //orderPayBiz.tradePay(orderPayVo, "");
        //orderPayBiz.fundAuthCancel(orderPayVo, "");
        //orderPayBiz.tradePay("20191114182254000019", "2019111410002001530505959461", 1,"扣除违约金", "扣除违约金");
        //orderPayBiz.fundAuthQuery("20191031172653000026");
    }
}
