
using Mall.WeChat.Helper;
using Mall.Common.Pay.WeChatPat.Model;
using Mall.Common.Pay.WeChatPat;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;
using Microsoft.AspNetCore.Http;
using Mall.Common.Plugin;
using Mall.Model.Entity.User;
using Mall.Common;
using Mall.Model.Extend.User;
using NPOI.SS.Formula.Functions;
using Org.BouncyCastle.Utilities.Net;
using Microsoft.AspNetCore.Http.Features;

namespace Mall.WebApi.App_Code
{
    /// <summary>
    /// 支付帮助类
    /// </summary>
    public class PayUtil
    {
        /// <summary>
        /// 商户小程序处理类
        /// </summary>
        private Module.User.MiniProgramModule programModule = new Module.User.MiniProgramModule();



        public PayUtil() { }



        /// <summary>
        /// 微信小程序支付
        /// </summary>
        /// <param name="sOrderNo"></param>
        /// <param name="sProductName"></param>
        /// <param name="dPrice"></param>
        /// <param name="CustomerId"></param>
        /// <param name="openid"></param>
        /// <returns></returns>
        public static string GetMinUnifiedOrder(string sOrderNo, string sProductName, decimal dPrice, string CustomerId, string openid, string notify_url, RB_MiniProgram_Extend model, IHttpContextAccessor _accessor, string IPAddress)
        {
            DateTime time_start = DateTime.Now;
            var req = new Common.Pay.WeChatPat.RequestHandler();
            req.SetKey(model.WeChatApiSecret);
            req.SetParameter("appid", model.MiniAppId);//微信开放平台审核通过的应用APPID
            req.SetParameter("mch_id", model.WeChatPayMerchants);//微信支付分配的商户号
            req.SetParameter("nonce_str", GetNoncestr());//随机字符串，不长于32位
            req.SetParameter("body", sProductName);
            req.SetParameter("attach", CustomerId);
            req.SetParameter("out_trade_no", sOrderNo);
            req.SetParameter("total_fee", (dPrice * 100).ToString("f0"));
            req.SetParameter("spbill_create_ip", IPAddress);
            req.SetParameter("time_start", time_start.ToString("yyyyMMddHHmmss"));
            req.SetParameter("time_expire", time_start.AddMinutes(10).ToString("yyyyMMddHHmmss"));
            req.SetParameter("notify_url", notify_url);
            req.SetParameter("trade_type", "JSAPI");
            req.SetParameter("openid", openid);
            req.SetParameter("sign", req.CreateMd5Sign(_accessor, model.WeChatApiSecret));

            var reqXml = req.ParseXml();

            LogHelper.WriteInfo("订单号【" + sOrderNo + "】请求参数：" + reqXml);
            var result = Common.Pay.WeChatPat.HttpHelper.Post(new HttpParam()
            {
                Url = Mall.Common.Pay.WeChatPat.Model.ApiList.UnifiedOrderUrl,
                PostParam = reqXml,
                Encoding = Common.Pay.WeChatPat.HttpHelper.GetRequestEncoding(_accessor.HttpContext.Request),
            });

            var xe = XElement.Parse(result, LoadOptions.SetLineInfo);
            LogHelper.WriteInfo("订单号【" + sOrderNo + "】成功回调：" + xe);
            try
            {
                //存入缓存，方便自动取消的时候不被清理
                TimeSpan ts = time_start.AddMinutes(10) - time_start;
                string orderId = sOrderNo[17..];
                System.Threading.Tasks.Task.Run(() => CacheManager.AppletWeChat.OrderPayReidsCache.Set(orderId, orderId, ts));


                var prepayId = xe.GetElement("prepay_id").Value;
                var payReq = new RequestHandler();
                payReq.SetKey(model.WeChatApiSecret);
                payReq.SetParameter("appId", model.MiniAppId);
                payReq.SetParameter("timeStamp", GetTimestamp());
                payReq.SetParameter("nonceStr", GetNoncestr());
                payReq.SetParameter("package", "prepay_id=" + prepayId);
                payReq.SetParameter("signType", "MD5");
                //创建签名
                payReq.SetParameter("sign", payReq.CreateMd5Sign(_accessor, model.WeChatApiSecret));
                var payReqXml = payReq.ParseXml();
                var payReqJson = payReq.ParseJson();
                return payReqJson;
            }
            catch (Exception)
            {
                var returnCode = xe.GetElement("return_code").Value;
                var returnMsg = xe.GetElement("return_msg").Value;
                return "{Error:'" + returnCode + returnMsg + "'}";
            }
        }




        /// <summary>
        /// 支付
        /// </summary>
        /// <returns></returns>
        public BaseResult Notify(IHttpContextAccessor accessor)
        {
            try
            {
                var res = new ResponseHandler(accessor);

                var tradeType = res.GetParameter("trade_type");
                LogHelper.WriteInfo("Notify支付回调：res-" + res.ToString());
                RB_MiniProgram_Extend model = new RB_MiniProgram_Extend();
                model = programModule.GetMiniProgramModule(new RB_MiniProgram_Extend { MallBaseId = 0, TenantId = 0 });
                res.SetKey(model.WeChatApiSecret);
                //判断签名
                if (res.IsWXsign(out string error))
                {
                    #region 参数
                    var returnCode = res.GetParameter("return_code");
                    //返回信息，如非空，为错误原因签名失败参数格式校验错误
                    var returnMsg = res.GetParameter("return_msg");
                    var appid = res.GetParameter("appid");

                    //以下字段在 return_code 为 SUCCESS 的时候有返回--------------------------------
                    var mchId = res.GetParameter("mch_id");
                    var deviceInfo = res.GetParameter("device_info");
                    var nonceStr = res.GetParameter("nonce_str");
                    var resultCode = res.GetParameter("result_code");
                    var errCode = res.GetParameter("err_code");
                    var errCodeDes = res.GetParameter("err_code_des");

                    //以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回---------------
                    var openid = res.GetParameter("openid");
                    //Y-关注，N-未关注，仅在公众账号类型支付有效
                    var isSubscribe = res.GetParameter("is_subscribe");

                    //银行类型，采用字符串类型的银行标识
                    var bankType = res.GetParameter("bank_type");
                    var totalFee = res.GetParameter("total_fee");
                    //货币类型，符合 ISO 4217 标准的三位字母代码，默认人民币：CNY
                    var feeType = res.GetParameter("fee_type");
                    //微信支付订单号
                    var transactionId = res.GetParameter("transaction_id");
                    //商户系统的订单号，与请求一致。
                    var outTradeNo = res.GetParameter("out_trade_no");
                    var attach = res.GetParameter("attach");
                    //格 式 为yyyyMMddhhmmss
                    var timeEnd = res.GetParameter("time_end");
                    #endregion
                    //支付成功
                    if (!outTradeNo.Equals("") && returnCode.Equals("SUCCESS") && resultCode.Equals("SUCCESS"))
                    {
                        return new BaseResult()
                        {
                            IsSuccess = true,
                            Data = new Dictionary<string, string>
                            {
                                {"sOrderNo", outTradeNo},
                                {"sTradeNo", transactionId},
                                {"dPrice", totalFee},
                                {"sPayerOpenID", openid},
                                {"bIsRecharge", attach},
                                {"OrderPayType", tradeType},
                                {"PayDate",timeEnd }
                            }
                        };
                    }
                    else
                    {
                        LogHelper.WriteInfo("Notify支付回调：returnMsg-" + returnMsg);
                    }
                }
                else
                {
                    LogHelper.Write(null, "Notify签名验证失败：");
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex, "Notify支付错误：");
            }
            return new BaseResult() { IsSuccess = false };
        }


        /// <summary>
        /// 退款  传入订单号OrderNumber，RefundNumber,总金额total_fee（分）,RefundFee退款金额(分)，
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public BaseResult Refund(PayParam param, RB_MiniProgram_Extend model, IHttpContextAccessor _accessor)
        {
            if (param.TotalFee == null || param.RefundFee == null || string.IsNullOrWhiteSpace(param.OrderNumber) || string.IsNullOrWhiteSpace(param.RefundNumber))
            {
                return new BaseResult() { IsSuccess = false, Message = "参数错误！" };
            }
            #region 微信退款
            var packageReq = new RequestHandler();
            packageReq.SetKey(model.WeChatApiSecret);
            packageReq.SetParameter("appid", model.MiniAppId);
            packageReq.SetParameter("mch_id", model.WeChatPayMerchants);
            packageReq.SetParameter("nonce_str", GetNoncestr());
            //packageReq.SetParameter("transaction_id", param.OrderNumber);
            packageReq.SetParameter("out_trade_no", param.OrderNumber);
            packageReq.SetParameter("out_refund_no", param.RefundNumber);
            packageReq.SetParameter("total_fee", (param.TotalFee.Value).ToString(System.Globalization.CultureInfo.InvariantCulture));
            packageReq.SetParameter("refund_fee", param.RefundFee.Value.ToString(System.Globalization.CultureInfo.InvariantCulture));
            packageReq.SetParameter("sign", packageReq.CreateMd5Sign(_accessor, model.WeChatApiSecret));
            var reqXml = packageReq.ParseXml();
            LogHelper.Write(reqXml);
            LogHelper.WriteInfo("退款正式路径" + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data/Certs/WeChatApp/" + model.WeChatPayCertificateUrl));
            if (!System.IO.File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data/Certs/WeChatApp/" + model.WeChatPayCertificateUrl)))
            {
                return new BaseResult() { IsSuccess = false, Message = "证书不存在" };
            }
            var result = Common.Pay.WeChatPat.HttpHelper.Post(new HttpParam()
            {
                Url = "https://api.mch.weixin.qq.com/secapi/pay/refund",
                PostParam = reqXml,
                Encoding = Common.Pay.WeChatPat.HttpHelper.GetRequestEncoding(_accessor.HttpContext.Request),
                CertPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data/Certs/WeChatApp/" + model.WeChatPayCertificateUrl),//"App_Data/Certs/WeChatApp/apiclient_cert.p12"),// model.WeChatPayCertificateUrl,
                CertPwd = model.WeChatPayMerchants//证书密码默认为您的商户ID
            });

            var xe = XElement.Parse(result, LoadOptions.SetLineInfo);
            LogHelper.Write(null, "退款申请【" + param.OrderNumber + "】回调接口：" + xe);
            var returnCode = xe.GetElement("return_code").Value;
            //退款成功
            if (returnCode.Equals("SUCCESS"))
            {
                var resultCode = xe.GetElement("result_code").Value;
                if (resultCode.Equals("SUCCESS"))
                {
                    var outTradeNo = xe.GetElement("out_trade_no").Value;
                    var transactionId = xe.GetElement("transaction_id").Value;
                    var outRefundNo = xe.GetElement("out_refund_no").Value;
                    var refundid = xe.GetElement("refund_id").Value;
                    var totalFee = xe.GetElement("refund_fee").Value;

                    //在外面回写订单
                    return new BaseResult()
                    {
                        IsSuccess = true,
                        Data = new Dictionary<string, string>
                            {
                              {"sOrderNo", outTradeNo},//商户订单号
                              {"sTradeNo", transactionId},//微信订单号
                              {"refundid", refundid},//微信退款单号
                              {"outRefundNo", outRefundNo},//商户退款单号
                              {"dPrice", totalFee}//退款金额
                            }
                    };
                }
            }

            var errCodeDes = xe.GetElement("err_code_des") == null ? "" : xe.GetElement("err_code_des").Value;
            var returnMsg = xe.GetElement("return_msg") == null ? "" : xe.GetElement("return_msg").Value;

            return new BaseResult() { IsSuccess = false, Message = returnMsg + errCodeDes };
            #endregion
        }




        // <summary>
        /// 查询退款状态  
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public BaseResult Refundquery(PayParam param, RB_MiniProgram_Extend model, IHttpContextAccessor _accessor)
        {
            if (param.TotalFee == null || param.RefundFee == null || string.IsNullOrWhiteSpace(param.OrderNumber) || string.IsNullOrWhiteSpace(param.RefundNumber))
            {
                return new BaseResult() { IsSuccess = false, Message = "参数错误！" };
            }
            #region 微信退款
            var packageReq = new RequestHandler();
            packageReq.SetKey(model.WeChatApiSecret);
            packageReq.SetParameter("appid", model.MiniAppId);
            packageReq.SetParameter("mch_id", model.WeChatPayMerchants);
            packageReq.SetParameter("nonce_str", GetNoncestr());
            packageReq.SetParameter("sign", packageReq.CreateMd5Sign(_accessor, model.WeChatApiSecret));
            packageReq.SetParameter("out_refund_no", param.RefundNumber);//商户系统内部的退款单号

            var reqXml = packageReq.ParseXml();

            var result = Common.Pay.WeChatPat.HttpHelper.Post(new HttpParam()
            {
                Url = "https://api.mch.weixin.qq.com/pay/refundquery",
                PostParam = reqXml,
                Encoding = Common.Pay.WeChatPat.HttpHelper.GetRequestEncoding(_accessor.HttpContext.Request),
            });

            var xe = XElement.Parse(result, LoadOptions.SetLineInfo);

            var returnCode = xe.GetElement("return_code").Value;
            string outTradeNo = xe.GetElement("out_refund_no_0").Value;
            if (returnCode.Equals("SUCCESS"))
            {

                string refundStatus = xe.GetElement("refund_status_0").Value;
                if (!string.IsNullOrWhiteSpace(refundStatus) && refundStatus.Equals("SUCCESS"))
                {
                    //在外面回写订单
                    return new BaseResult()
                    {
                        IsSuccess = true,
                        Data = new Dictionary<string, string>
                        {
                            { "OrderNumber", outTradeNo},
                            { "refundStatus",refundStatus}

                        }
                    };
                }
                else
                {
                    //在外面回写订单
                    return new BaseResult()
                    {
                        IsSuccess = false,
                        Data = new Dictionary<string, string>
                        {
                            { "OrderNumber", outTradeNo},
                            { "refundStatus",refundStatus}

                        }
                    };
                }
            }
            var errCodeDes = xe.GetElement("err_code_des") == null ? "" : xe.GetElement("err_code_des").Value;
            var returnMsg = xe.GetElement("return_msg") == null ? "" : xe.GetElement("return_msg").Value;

            return new BaseResult()
            {
                IsSuccess = false,
                Message = returnMsg + errCodeDes,
                Data = new Dictionary<string, string>
                        {
                            { "OrderNumber", outTradeNo}
                        }
            };
            #endregion
        }





        /// <summary>
        /// 企业付款
        /// </summary>
        /// <param name="sOrderNo"></param>
        /// <param name="sProductName"></param>
        /// <param name="dPrice"></param>
        /// <param name="CustomerId"></param>
        /// <param name="openid"></param>
        /// <returns></returns>
        public static bool GetTransfersOrder(string sOrderNo, decimal dPrice, string CustomerId, string openid, RB_MiniProgram_Extend model, IHttpContextAccessor _accessor, string Remark = "")
        {
            if (!System.IO.File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data/Certs/WeChatApp/" + model.WeChatPayCertificateUrl)))
            {
                return false;
            }
            var req = new Common.Pay.WeChatPat.RequestHandler();
            req.SetKey(model.WeChatApiSecret);
            //
            req.SetParameter("mch_appid", model.MiniAppId);//微信开放平台审核通过的应用APPID
            req.SetParameter("mchid", model.WeChatPayMerchants);//微信支付分配的商户号
            req.SetParameter("nonce_str", GetNoncestr());//随机字符串，不长于32位
            req.SetParameter("partner_trade_no", sOrderNo);//商户订单号，需保持唯一性(只能是字母或者数字，不能包含有其它字符)
            req.SetParameter("openid", openid);
            req.SetParameter("check_name", "NO_CHECK");
            req.SetParameter("amount", (dPrice * 100).ToString("f0"));
            req.SetParameter("desc", Remark == "" ? "赞羊商城佣金提现" : Remark);
            req.SetParameter("sign", req.CreateMd5Sign(_accessor, model.WeChatApiSecret));
            var reqXml = req.ParseXml();
            var result = Common.Pay.WeChatPat.HttpHelper.Post(new HttpParam()
            {
                Url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers",
                PostParam = reqXml,
                Encoding = Common.Pay.WeChatPat.HttpHelper.GetRequestEncoding(_accessor.HttpContext.Request),
                CertPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data/Certs/WeChatApp/" + model.WeChatPayCertificateUrl),//"App_Data/Certs/WeChatApp/apiclient_cert.p12"),// model.WeChatPayCertificateUrl,
                CertPwd = model.WeChatPayMerchants//证书密码默认为您的商户ID
            });

            var xe = XElement.Parse(result, LoadOptions.SetLineInfo);
            var returnCode = xe.GetElement("return_code").Value;
            var resultCode = xe.GetElement("result_code").Value;
            if (returnCode.Equals("SUCCESS") && resultCode.Equals("SUCCESS"))
            {
                return true;
            }
            return false;
        }







        /// <summary>
        /// 当面支付
        /// </summary>
        /// <param name="sOrderNo"></param>
        /// <param name="dPrice"></param>
        /// <param name="CustomerId"></param>
        /// <param name="model"></param>
        /// <param name="_accessor"></param>
        /// <returns></returns>

        public static BaseResult GetNativeOrder(string sOrderNo, decimal dPrice, string ProductName, string CustomerId, RB_MiniProgram_Extend model, string IPAddress, IHttpContextAccessor _accessor)
        {
            var req = new Common.Pay.WeChatPat.RequestHandler();
            req.SetKey(model.WeChatApiSecret);
            req.SetParameter("appid", model.MiniAppId);//微信开放平台审核通过的应用APPID
            req.SetParameter("mch_id", model.WeChatPayMerchants);//微信支付分配的商户号
            req.SetParameter("nonce_str", GetNoncestr());//随机字符串，不长于32位
            req.SetParameter("out_trade_no", sOrderNo);//商户订单号，需保持唯一性(只能是字母或者数字，不能包含有其它字符)
            req.SetParameter("body", ProductName);
            req.SetParameter("total_fee", (dPrice * 100).ToString("f0"));
            req.SetParameter("spbill_create_ip", IPAddress);
            req.SetParameter("notify_url", Config.sTenpayNotify);//回调地址20200604  后面用的时候修改，现在只是把支付调通
            req.SetParameter("trade_type", "NATIVE");//
            req.SetParameter("sign", req.CreateMd5Sign(_accessor, model.WeChatApiSecret));
            var reqXml = req.ParseXml();
            var result = Common.Pay.WeChatPat.HttpHelper.Post(new HttpParam()
            {
                Url = Mall.Common.Pay.WeChatPat.Model.ApiList.UnifiedOrderUrl,
                PostParam = reqXml,
                Encoding = Common.Pay.WeChatPat.HttpHelper.GetRequestEncoding(_accessor.HttpContext.Request),
            });

            var xe = XElement.Parse(result, LoadOptions.SetLineInfo);
            var returnCode = xe.GetElement("return_code") == null ? "" : xe.GetElement("return_code").Value;
            var returnMsg = xe.GetElement("return_msg") == null ? "" : xe.GetElement("return_msg").Value;

            if (returnCode.Equals("SUCCESS") && returnCode.Equals("SUCCESS"))
            {
                var code_url = xe.GetElement("code_url") == null ? "" : xe.GetElement("code_url").Value;
                //在外面回写订单
                return new BaseResult()
                {
                    IsSuccess = true,
                    Data = new Dictionary<string, string>
                        {
                            { "code_url", code_url}
                        }
                };
            }
            //在外面回写订单
            return new BaseResult()
            {
                IsSuccess = false,
                Message = returnMsg + returnCode,
                Data = new Dictionary<string, string>
                        {
                            { "code_url", ""}
                        }
            };
        }





        /// <summary>
        /// 获取随机字符串
        /// </summary>
        /// <returns></returns>
        public static string GetNoncestr()
        {
            var random = new Random();
            return SecurityHelper.MD5EncryptWeChat(random.Next(1000).ToString(CultureInfo.InvariantCulture), "GBK");
        }

        /// <summary>
        /// 获取时间戳
        /// </summary>
        /// <returns></returns>
        public static string GetTimestamp()
        {
            var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString(CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// 对字符串进行URL编码
        /// </summary>
        /// <param name="instr"></param>
        /// <param name="charset">默认值：utf-8</param>
        /// <returns></returns>
        public static string UrlEncode(string instr, string charset)
        {
            if (instr == null || instr.Trim() == "")
                return "";
            var res = HttpUtility.UrlEncode(instr, !string.IsNullOrWhiteSpace(charset)
               ? Encoding.GetEncoding(charset)
               : Encoding.GetEncoding("utf-8"));
            return res;
        }
    }
}
