
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)
        {
            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", DateTime.Now.ToString("yyyyMMddHHmmss"));
            req.SetParameter("time_expire", DateTime.Now.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.Write(null, "订单号【" + 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.Write(null, "订单号【" + sOrderNo + "】支付错误：" + xe);
            try
            {

                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.Write(null, "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"))
                    {
                        //LogHelper.WriteLog("支付回调：", sbResult.ToString() + "notify > success \r\n",EnumService.LogType.Debug);
                        //在外面回写订单
                        //var returnUrl = "";
                        //if (tradeType.Equals("JSAPI"))
                        //    returnUrl = res.GetParameter("code_url");
                        //if (tradeType.Equals("MWEB"))
                        //    returnUrl = res.GetParameter("mweb_url");
                        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.Write(null, "Notify支付回调：returnMsg-" + returnMsg);
                    }
                }
                else
                {
                    LogHelper.Write(null, "Notify签名验证失败：");
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex, "Notify支付错误：");
            }
            return new BaseResult() { IsSuccess = false };
        }





        //public  ProcessNotify(HttpContext context)
        //{
        //    WxPayData notifyData = GetNotifyData(context);

        //    //检查支付结果中transaction_id是否存在
        //    if (!notifyData.IsSet("transaction_id"))
        //    {
        //        //若transaction_id不存在，则立即返回结果给微信支付后台
        //        WxPayData res = new WxPayData();
        //        res.SetValue("return_code", "FAIL");
        //        res.SetValue("return_msg", "支付结果中微信订单号不存在");
        //        context.Response.WriteAsync(res.ToXml());
        //        return (false, null);
        //    }

        //    string transaction_id = notifyData.GetValue("transaction_id").ToString();
        //    string out_trade_no = notifyData.GetValue("out_trade_no").ToString();

        //    //查询订单，判断订单真实性
        //    if (!QueryOrder(transaction_id))
        //    {
        //        //若订单查询失败，则立即返回结果给微信支付后台
        //        WxPayData res = new WxPayData();
        //        res.SetValue("return_code", "FAIL");
        //        res.SetValue("return_msg", "订单查询失败");
        //        context.Response.WriteAsync(res.ToXml());
        //        return (false, null);
        //    }
        //    //查询订单成功
        //    else
        //    {
        //        WxPayData res = new WxPayData();
        //        res.SetValue("return_code", "SUCCESS");
        //        res.SetValue("return_msg", "OK");
        //        context.Response.WriteAsync(res.ToXml());
        //        ProcessNotifyReturn notifyReturn = new ProcessNotifyReturn
        //        {
        //            out_trade_no = out_trade_no,
        //            transaction_id = transaction_id
        //        };
        //        return (true, notifyReturn);
        //    }
        //}


        ///// <summary>
        ///// 接收从微信支付后台发送过来的数据并验证签名
        ///// </summary>
        ///// <returns>微信支付后台返回的数据</returns>
        //public WxPayData GetNotifyData(HttpContext context)
        //{
        //    #region 
        //    //注意：如果用以下读取流的方法，.net core 3.0 以后一定要加下边那段
        //    //.net core 3.0以后需加下边这段，否则Stream会报错
        //    var syncIOFeature = context.Features.Get<IHttpBodyControlFeature>();
        //    if (syncIOFeature != null)
        //    {
        //        syncIOFeature.AllowSynchronousIO = true;
        //    }
        //    #endregion

        //    //接收从微信后台POST过来的数据
        //    System.IO.Stream s = context.Request.Body;
        //    int count = 0;
        //    byte[] buffer = new byte[1024];
        //    StringBuilder builder = new StringBuilder();
        //    while ((count = s.Read(buffer, 0, 1024)) > 0)
        //    {
        //        builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
        //    }
        //    s.Flush();
        //    s.Close();
        //    s.Dispose();
        //    LogHelper.Write($"GetNotifyData Receive data from WeChat :{builder.ToString()}");

        //    //转换数据格式并验证签名
        //    WxPayData data = new WxPayData();
        //    try
        //    {
        //        data.FromXml(builder.ToString());
        //    }
        //    catch (Exception ex)
        //    {
        //        //若签名错误，则立即返回结果给微信支付后台
        //        WxPayData res = new WxPayData();
        //        res.SetValue("return_code", "FAIL");
        //        res.SetValue("return_msg", ex.Message);
        //        context.Response.WriteAsync(res.ToXml());
        //    }
        //    LogHelper.Write($"GetNotifyData Check sign success");
        //    return data;
        //}







        // <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();

            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);
            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;
                    //在外面回写订单
                    return new BaseResult()
                    {
                        IsSuccess = true,
                        Data = new Dictionary<string, string>
                            {
                                {"OrderNumber", outTradeNo}
                            }
                    };
                }
            }

            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)
        {

            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", "赞羊商城佣金提现");
            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;
        }
    }
}
