package org.jeecg.modules.pay;


import org.jeecg.modules.pay.core.LakalaPayClient;
import org.jeecg.modules.pay.core.LakalaPayUrl;
import org.jeecg.modules.pay.request.ReqLocationInfo;
import org.jeecg.modules.pay.request.v3.LabsRequest;
import org.jeecg.modules.pay.request.v3.labs.*;
import org.jeecg.modules.pay.request.v3.labs.accbusifields.*;

import java.io.IOException;
import java.math.BigDecimal;

/**
 * 拉卡拉V3.0下支付类接口API
 *
 * @author Mr丶xie
 * @version 1.0.0
 * @mail xielinke@lklink.cn
 */
public class V3LabsApi {

    /**
     * 主扫交易接口
     *
     * @param preorderRequest 请求报文
     * @return
     */
    public static String transPreorder(PreorderRequest preorderRequest) throws IOException {
        return LakalaPayClient.execute(LakalaPayUrl.V3_LABS_TRANS_PREORDER, new LabsRequest(preorderRequest));
    }

    /**
     * 主扫支付-支付宝(41-NATIVE)场景下
     *
     * @param out_trade_no 商户交易流水号
     * @param total_amount 金额
     * @param request_ip   请求方IP地址
     * @param subject      订单标题
     * @param notify_url   商户通知地址
     * @param goods_id     商品的编号
     * @param goods_name   商品名称
     * @param quantity     商品数量
     * @return
     * @throws IOException
     */
    public static String transPreorder_alipay_native(String out_trade_no, Integer total_amount, String request_ip, String subject, String notify_url, String goods_id, String goods_name, Integer quantity) throws IOException {
        // 钱包类型
        String account_type = "ALIPAY";
        // 接入方式
        String trans_type = "41";
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化主扫交易接口请求报文
        PreorderRequest request = new PreorderRequest(out_trade_no, account_type, trans_type, Integer.toString(total_amount), location_info);
        // 订单标题，用于简单描述订单或商品主题，会传递给账户端 （账户端控制，实际最多42个字符），微信支付必送。
        request.setSubject(subject);
        // 商户通知地址
        request.setNotify_url(notify_url);

        // 账户端业务信息域
        AliPayAccBusiFieldsRequest accBusiFieldsRequest = new AliPayAccBusiFieldsRequest();
        // 商品价格 单位元
        Double price = new BigDecimal(total_amount).divide(new BigDecimal(100)).doubleValue();
        accBusiFieldsRequest.setGoods_detail(new AliPayGoodsDetailRequest(goods_id, goods_name, quantity, price));

        request.setAcc_busi_fields(accBusiFieldsRequest);
        return transPreorder(request);
    }

    /**
     * 主扫支付-支付宝(51-JSAPI)场景下
     *
     * @param out_trade_no 商户交易流水号
     * @param total_amount 金额
     * @param request_ip   请求方IP地址
     * @param subject      订单标题
     * @param notify_url   商户通知地址
     * @param user_id      买家在支付宝的用户id（支付宝的buyer_user_id）
     * @param goods_id     商品的编号
     * @param goods_name   商品名称
     * @param quantity     商品数量
     * @return
     * @throws IOException
     */
    public static String transPreorder_alipay_jsapi(String out_trade_no, Integer total_amount, String request_ip, String subject, String notify_url, String user_id, String goods_id, String goods_name, Integer quantity) throws IOException {
        // 钱包类型
        String account_type = "ALIPAY";
        // 接入方式
        String trans_type = "51";
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化主扫交易接口请求报文
        PreorderRequest request = new PreorderRequest(out_trade_no, account_type, trans_type, Integer.toString(total_amount), location_info);
        // 订单标题，用于简单描述订单或商品主题，会传递给账户端 （账户端控制，实际最多42个字符），微信支付必送。
        request.setSubject(subject);
        // 商户通知地址
        request.setNotify_url(notify_url);

        // 账户端业务信息域
        AliPayAccBusiFieldsRequest accBusiFieldsRequest = new AliPayAccBusiFieldsRequest();
        accBusiFieldsRequest.setUser_id(user_id);
        // 商品价格 单位元
        Double price = new BigDecimal(total_amount).divide(new BigDecimal(100)).doubleValue();
        accBusiFieldsRequest.setGoods_detail(new AliPayGoodsDetailRequest(goods_id, goods_name, quantity, price));

        request.setAcc_busi_fields(accBusiFieldsRequest);
        return transPreorder(request);
    }

    /**
     * 主扫支付-微信(71-小程序)场景下
     *
     * @param out_trade_no 商户交易流水号
     * @param total_amount 金额
     * @param request_ip   请求方IP地址
     * @param subject      订单标题
     * @param notify_url   商户通知地址
     * @param sub_appid    子商户公众账号ID（微信分配的子商户公众账号ID，sub_appid）
     * @param user_id      用户标识（用户在子商户sub_appid下的唯一标识，sub_openid）
     * @param goods_id     商品编码
     * @param goods_name   商品的实际名称
     * @param quantity     用户购买的数量
     * @return
     * @throws IOException
     */
    public static String transPreorder_wxpay_applet(String out_trade_no, Integer total_amount, String request_ip, String subject, String notify_url, String sub_appid, String user_id, String goods_id, String goods_name, Integer quantity) throws IOException {
        // 钱包类型
        String account_type = "WECHAT";
        // 接入方式
        String trans_type = "71";
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化主扫交易接口请求报文
        PreorderRequest request = new PreorderRequest(out_trade_no, account_type, trans_type, Integer.toString(total_amount), location_info);
        // 订单标题，用于简单描述订单或商品主题，会传递给账户端 （账户端控制，实际最多42个字符），微信支付必送。
        request.setSubject(subject);
        // 商户通知地址
        request.setNotify_url(notify_url);

        // 账户端业务信息域
        WxPayAccBusiFieldsRequest accBusiFieldsRequest = new WxPayAccBusiFieldsRequest();
        // 子商户公众账号ID
        accBusiFieldsRequest.setSub_appid(sub_appid);
        // 用户标识
        accBusiFieldsRequest.setUser_id(user_id);
        // 商品价格 单位元
        Integer price = new BigDecimal(total_amount).divide(new BigDecimal(100)).intValue();
        accBusiFieldsRequest.setDetail(new WxPayDetailRequest(new WxPayGoodsDatailRequest(goods_id, goods_name, quantity, price)));

        request.setAcc_busi_fields(accBusiFieldsRequest);

        return transPreorder(request);
    }

    /**
     * 主扫支付-微信(51-JSAPI)场景下
     *
     * @param out_trade_no 商户交易流水号
     * @param total_amount 金额
     * @param request_ip   请求方IP地址
     * @param subject      订单标题
     * @param notify_url   商户通知地址
     * @param sub_appid    子商户公众账号ID（微信分配的子商户公众账号ID，sub_appid）
     * @param user_id      用户标识（用户在子商户sub_appid下的唯一标识，sub_openid）
     * @param goods_id     商品编码
     * @param goods_name   商品的实际名称
     * @param quantity     用户购买的数量
     * @return
     * @throws IOException
     */
    public static String transPreorder_wxpay_jsapi(String out_trade_no, Integer total_amount, String request_ip, String subject, String notify_url, String sub_appid, String user_id, String goods_id, String goods_name, Integer quantity) throws IOException {
        // 钱包类型
        String account_type = "WECHAT";
        // 接入方式
        String trans_type = "51";
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化主扫交易接口请求报文
        PreorderRequest request = new PreorderRequest(out_trade_no, account_type, trans_type, Integer.toString(total_amount), location_info);
        // 订单标题，用于简单描述订单或商品主题，会传递给账户端 （账户端控制，实际最多42个字符），微信支付必送。
        request.setSubject(subject);
        // 商户通知地址
        request.setNotify_url(notify_url);

        // 账户端业务信息域
        WxPayAccBusiFieldsRequest accBusiFieldsRequest = new WxPayAccBusiFieldsRequest();
        // 子商户公众账号ID
        accBusiFieldsRequest.setSub_appid(sub_appid);
        // 用户标识
        accBusiFieldsRequest.setUser_id(user_id);
        // 商品价格 单位元
        Integer price = new BigDecimal(total_amount).divide(new BigDecimal(100)).intValue();
        accBusiFieldsRequest.setDetail(new WxPayDetailRequest(new WxPayGoodsDatailRequest(goods_id, goods_name, quantity, price)));

        request.setAcc_busi_fields(accBusiFieldsRequest);

        return transPreorder(request);
    }

    /**
     * 被扫交易
     * <p>
     * 说明：被扫支付场景，可能存在支付中需要用户输入密码的情况。该情况下一般交易会返回“BBS10000-支付中”状态。
     * 在该场景下，建议发起方发起多次查询，查询频率推荐1分钟内按 5s+5s+5s+5s+10S+10S+20S方式联机查询，并保留手工发起查询的能力。
     * <p>
     * 场景说明: 如果用户支付密码用时比较长，商户多次查询完成时，用户还没有输密码完成，拉卡拉这边会按照一定的轮询策略
     * （总时间范围大概在两个小时的多次查询）继续向账户端发起查询，查询成功后再按照接口中通知地址通知商户。
     * 如果用户在拉卡拉的轮询查询都完成了还没有完成输入密码，此时交易还处于中间态，
     * 只能依赖商户发起手工查询，触发拉卡拉再次向账户端发起交易结果查询
     *
     * @param micropayRequest 请求报文
     * @return
     */
    public static String transMicropay(MicropayRequest micropayRequest) throws IOException {
        return LakalaPayClient.execute(LakalaPayUrl.V3_LABS_TRANS_MICROPAY, new LabsRequest(micropayRequest));
    }

    /**
     * 被扫支付-支付宝支付
     *
     * @param out_trade_no 商户交易流水号
     * @param auth_code    支付授权码
     * @param total_amount 金额
     * @param request_ip   请求方IP地址
     * @param subject      订单标题
     * @param notify_url   商户通知地址
     * @param goods_id     商品的编号
     * @param goods_name   商品名称
     * @param quantity     商品数量
     * @return
     * @throws IOException
     */
    public static String transMicropay_alipay(String out_trade_no, String auth_code, String total_amount, String request_ip, String subject, String notify_url, String goods_id, String goods_name, Integer quantity) throws IOException {
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化被扫交易接口请求报文
        MicropayRequest request = new MicropayRequest(out_trade_no, auth_code, total_amount, location_info);
        // 订单标题，用于简单描述订单或商品主题，会传递给账户端 （账户端控制，实际最多42个字符），微信支付必送。
        request.setSubject(subject);
        // 商户通知地址
        request.setNotify_url(notify_url);
        // 账户端业务信息域
        AliPayAccBusiFieldsRequest accBusiFieldsRequest = new AliPayAccBusiFieldsRequest();
        // 商品价格 单位元
        Double price = new BigDecimal(total_amount).divide(new BigDecimal(100)).doubleValue();
        accBusiFieldsRequest.setGoods_detail(new AliPayGoodsDetailRequest(goods_id, goods_name, quantity, price));
        request.setAcc_busi_fields(accBusiFieldsRequest);

        return transMicropay(request);
    }

    /**
     * 被扫支付-微信支付
     *
     * @param out_trade_no 商户交易流水号
     * @param auth_code    支付授权码
     * @param total_amount 金额
     * @param request_ip   请求方IP地址
     * @param subject      订单标题
     * @param notify_url   商户通知地址
     * @param goods_id     商品的编号
     * @param goods_name   商品名称
     * @param quantity     商品数量
     * @return
     * @throws IOException
     */
    public static String transMicropay_wxpay(String out_trade_no, String auth_code, String total_amount, String request_ip, String subject, String notify_url, String goods_id, String goods_name, Integer quantity) throws IOException {
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化被扫交易接口请求报文
        MicropayRequest request = new MicropayRequest(out_trade_no, auth_code, total_amount, location_info);
        // 订单标题，用于简单描述订单或商品主题，会传递给账户端 （账户端控制，实际最多42个字符），微信支付必送。
        request.setSubject(subject);
        // 商户通知地址
        request.setNotify_url(notify_url);
        // 账户端业务信息域
        WxPayAccBusiFieldsRequest accBusiFieldsRequest = new WxPayAccBusiFieldsRequest();
        // 商品价格 单位元
        Integer price = new BigDecimal(total_amount).divide(new BigDecimal(100)).intValue();
        accBusiFieldsRequest.setDetail(new WxPayDetailRequest(new WxPayGoodsDatailRequest(goods_id, goods_name, quantity, price)));
        request.setAcc_busi_fields(accBusiFieldsRequest);

        return transMicropay(request);
    }


    /**
     * 退款交易
     *
     * @param refundRequest 请求报文
     * @return
     */
    public static String relationRefund(RefundRequest refundRequest) throws IOException {
        return LakalaPayClient.execute(LakalaPayUrl.V3_LABS_RELATION_REFUND, new LabsRequest(refundRequest));
    }

    /**
     * 退款交易-根据原商户交易流水号
     *
     * @param out_trade_no        商户交易流水号
     * @param refund_amount       退款金额
     * @param refund_reason       退款原因
     * @param origin_out_trade_no 原商户交易流水号
     * @param request_ip          请求方IP地址
     * @return
     * @throws IOException
     */
    public static String relationRefundByOriginOutTradeNo(String out_trade_no, String refund_amount, String refund_reason, String origin_out_trade_no, String request_ip) throws IOException {
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化主扫交易接口请求报文
        RefundRequest request = new RefundRequest(out_trade_no, refund_amount, location_info);
        // 退款原因
        request.setRefund_reason(refund_reason);
        // 原商户交易流水号（下单时的商户请求流水号（退款时origin_out_trade_no，origin_trade_no，origin_log_no必送其一））
        request.setOrigin_out_trade_no(origin_out_trade_no);

        return relationRefund(request);
    }

    /**
     * 撤销交易
     *
     * @param revokedRequest 请求报文
     * @return
     */
    public static String relationRevoked(RevokedRequest revokedRequest) throws IOException {
        return LakalaPayClient.execute(LakalaPayUrl.V3_LABS_RELATION_REVOKED, new LabsRequest(revokedRequest));
    }


    /**
     * 撤销交易-根据原商户交易流水号
     *
     * @param out_trade_no        商户交易流水号
     * @param origin_out_trade_no 原商户交易流水号
     * @param request_ip          请求方IP地址
     * @return
     * @throws IOException
     */
    public static String relationRevokedByOriginOutTradeNo(String out_trade_no, String origin_out_trade_no, String request_ip) throws IOException {
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化撤销交易接口请求报文
        RevokedRequest request = new RevokedRequest(out_trade_no, location_info);
        request.setOrigin_out_trade_no(origin_out_trade_no);

        return relationRevoked(request);
    }

    /**
     * 关单交易
     *
     * @param closeRequest 请求报文
     * @return
     */
    public static String relationClose(CloseRequest closeRequest) throws IOException {
        return LakalaPayClient.execute(LakalaPayUrl.V3_LABS_RELATION_CLOSE, new LabsRequest(closeRequest));
    }

    /**
     * 关单交易-根据原商户交易流水号
     *
     * @param origin_out_trade_no 原商户交易流水号
     * @param request_ip          请求方IP地址
     * @return
     * @throws IOException
     */
    public static String relationCloseByOriginOutTradeNo(String origin_out_trade_no, String request_ip) throws IOException {
        // 地址位置信息（地址位置信息，风控要求必送）
        ReqLocationInfo location_info = new ReqLocationInfo(request_ip);
        // 初始化关单交易接口请求报文
        CloseRequest request = new CloseRequest(location_info);
        // 原商户交易流水号
        request.setOrigin_out_trade_no(origin_out_trade_no);

        return relationClose(request);
    }

    /**
     * 查询交易
     *
     * @param tradeQueryRequest 请求报文
     * @return
     */
    public static String queryTradeQuery(TradeQueryRequest tradeQueryRequest) throws IOException {
        return LakalaPayClient.execute(LakalaPayUrl.V3_LABS_QUERY_TRADEQUERY, new LabsRequest(tradeQueryRequest));
    }

    /**
     * 查询交易-根据商户交易流水号
     *
     * @param out_trade_no 商户交易流水号
     * @return
     * @throws IOException
     */
    public static String queryTradeQueryByOutTradeNo(String out_trade_no) throws IOException {
        // 初始化查询交易接口请求报文
        TradeQueryRequest request = new TradeQueryRequest();
        // 商户交易流水号
        request.setOut_trade_no(out_trade_no);

        return queryTradeQuery(request);
    }
}
