Bläddra i källkod

收缴费系统微信支付相关功能实现

shengxuefei 3 år sedan
förälder
incheckning
a20608b8e9

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 201 - 9
PMS.NetCore/PMS.NetCore.BusinessCore/BaseCore/fdc_pm_meterrecordManager.cs


+ 12 - 0
PMS.NetCore/PMS.NetCore.Enties/DTO/BaoshiDTO.cs

@@ -42,6 +42,11 @@ namespace Sugar.Enties
         /// </summary>
         public  List<file> fileList { get; set; }
     }
+
+
+    /// <summary>
+    /// 上传文件相关属性
+    /// </summary>
     public class file
     {
         public string Name { get; set; }
@@ -49,4 +54,11 @@ namespace Sugar.Enties
         public long Size { get; set; }
         public string Content { get; set; }
     }
+    /// <summary>
+    /// 有赞图片上传预览组件类
+    /// </summary>
+    public class TheUrl
+    {
+        public string url { get; set; }
+    }
 }

+ 50 - 0
PMS.NetCore/PMS.NetCore.Enties/DbModels/Order.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Linq;
+using System.Text;
+using SqlSugar;
+
+namespace Sugar.Enties
+{
+    /// <summary>
+    /// 订单表
+    /// </summary>
+    [SugarTable("Order")]
+    public class Order
+    {
+        /// <summary>
+        /// 订单号
+        /// </summary>
+        [SugarColumn(IsPrimaryKey =true)]
+        public string OrderNo { get; set; }
+        /// <summary>
+        /// 缴费表fdc_pm_fare_b主键
+        /// </summary>
+        public string Farerowid { get; set; }
+        /// <summary>
+        /// 已缴费表fdc_pm_revfare主键
+        /// </summary>
+        public string Revfarecode { get; set; }
+        /// <summary>
+        /// 金额
+        /// </summary>
+        public decimal Money { get; set; }
+        /// <summary>
+        /// 业主名
+        /// </summary>
+        public string Ownername { get; set; }
+        /// <summary>
+        /// 房屋名
+        /// </summary>
+        public string Hname { get; set; }
+        /// <summary>
+        /// 状态 0:未支付 1:已支付  2:支付失败
+        /// </summary>
+        [SugarColumn(DefaultValue ="0")]
+        public int Status { get; set; }
+        /// <summary>
+        /// 添加时间
+        /// </summary>
+        [SugarColumn(IsOnlyIgnoreInsert =true)]
+        public DateTime AddTime { get; set; }
+    }
+}

+ 6 - 0
PMS.NetCore/PMS.NetCore.sln

@@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "Utils\Utils.csproj
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PMS.NetCore.Service", "Service\PMS.NetCore.Service.csproj", "{0736B6D8-C94E-49E3-AF9C-E9C4909AEE6F}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WxPayAPI", "WxPayAPI\WxPayAPI.csproj", "{FCE697CB-28B5-48EB-B886-3FCE69E2CA91}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
 		{0736B6D8-C94E-49E3-AF9C-E9C4909AEE6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{0736B6D8-C94E-49E3-AF9C-E9C4909AEE6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{0736B6D8-C94E-49E3-AF9C-E9C4909AEE6F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FCE697CB-28B5-48EB-B886-3FCE69E2CA91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FCE697CB-28B5-48EB-B886-3FCE69E2CA91}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FCE697CB-28B5-48EB-B886-3FCE69E2CA91}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FCE697CB-28B5-48EB-B886-3FCE69E2CA91}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 121 - 1
PMS.NetCore/PMS.NetCore/Controllers/MeterController.cs

@@ -10,6 +10,8 @@ using Utils;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
 using Utils.Jwt;
+using WxPayAPI;
+using SqlSugar;
 
 namespace PMS.NetCore.Controllers
 {
@@ -136,6 +138,40 @@ namespace PMS.NetCore.Controllers
             }
         }
 
+        /// <summary>
+        /// 绑定房屋信息
+        /// </summary>
+        /// <param name="phone"></param>
+        /// <returns></returns>
+        [HttpGet]        
+        [Route("BindHouse")]
+        public JsonResult BindHouse(string phone)
+        {
+
+            var res = new ApiResponse();
+            try
+            {
+                logger.Info($"【BindHouse 输入参数】dto:{phone}");              
+                var service = new fdc_pm_meterrecordManager();
+                var list = service.BindHouse(phone);
+ 
+
+                res.IsSuccess = true;
+                res.Data = list;
+                logger.Info($"【BindHouse 输出参数】res:{JsonConvert.SerializeObject(res)}");
+                return Json(res);
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                res.ErrMsg = ex.Message;
+                res.Code = 500;
+                return Json(res);
+            }
+        }
+
+
+
         #region 抄表单
         /// <summary>
         /// 抄表单添加
@@ -225,6 +261,87 @@ namespace PMS.NetCore.Controllers
             }
 
         }
+
+        /// <summary>
+        /// 生成微信缴费单url
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [Route("MakePayUrl")]
+        public JsonResult MakePayUrl([FromBody] PayDTO dto)
+        {
+            var res = new ApiResponse();
+
+            try
+            {
+                logger.Info($"【MakePayUrl 输入参数】dto:{JsonConvert.SerializeObject(dto)}");
+                if (dto.farwarid.IsNullOrEmpty())
+                {
+                    res.ErrMsg = "缴费编号错误";
+                    return Json(res);
+                }
+                if(!dto.money.IsDecimal())
+                {
+                    res.ErrMsg = "缴费金额不正确";
+                    return Json(res);
+                }
+                if (dto.ownername.IsNullOrEmpty())
+                {
+                    res.ErrMsg = "业主名称错误";
+                    return Json(res);
+                }
+                if (dto.hname.IsNullOrEmpty())
+                {
+                    res.ErrMsg = "房屋名称错误";
+                    return Json(res);
+                }
+                var order = new Order();
+                order.OrderNo = Utility.GetRandomNumber();
+                order.Farerowid = dto.farwarid;
+                order.Money = dto.money.ToDecimal();
+                order.Ownername = dto.ownername;
+                order.Hname = dto.hname;
+                order.Revfarecode = "";
+
+                //logger.Info("SqlFunc.ToDate", SqlFunc.ToDateShort(DateTime.Now));
+
+                var service = new fdc_pm_meterrecordManager();
+                var orderNew = service.Db.Queryable<Order>().First(m => m.Farerowid == order.Farerowid && m.Money == order.Money && SqlFunc.ToDateShort(m.AddTime) == SqlFunc.ToDateShort(service.Db.GetDate()));
+                if(orderNew==null)
+                {
+                    orderNew = service.Db.Insertable(order).ExecuteReturnEntity();
+                }                
+                if(orderNew!=null)   //表示生成订单成功
+                {
+                    var pay = new NativePay();
+                    var money = (int)(dto.money.ToDecimal() * 100);
+                    //logger.Info("MakePayUrl 输出参数】dto.money:"+ dto.money);
+                    //logger.Info("MakePayUrl 输出参数】money.ToDecimal:"+dto.money.ToDecimal() * 100);
+                    //logger.Info("MakePayUrl 输出参数】money:"+ money);
+                    var payurl = pay.GetPayUrl(dto.farwarid, money, orderNew.OrderNo);
+                    res.IsSuccess = true;
+                    res.Data = payurl;
+                    logger.Info($"MakePayUrl 输出参数】res:{JsonConvert.SerializeObject(res)}");
+                    return Json(res);
+                }
+                else
+                {
+                    res.IsSuccess = false;
+                    res.ErrMsg = "生成订单失败,重新操作";
+                    logger.Info($"MakePayUrl 输出参数】res:{JsonConvert.SerializeObject(res)}");
+                    return Json(res);
+                }
+             
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                res.ErrMsg = ex.Message;
+                res.Code = 500;
+                return Json(res);
+            }
+        }
         #endregion
 
         #region 报修和投诉
@@ -422,10 +539,13 @@ namespace PMS.NetCore.Controllers
 
             try
             {
+                //单独添加缴费单禁用,在订单完成支付后再进行处理
+                return Json(res);
                 logger.Info($"【InsertPay 输入参数】dto:{JsonConvert.SerializeObject(dto)}");
 
                 var service = new fdc_pm_meterrecordManager();
-                var resstr = service.InsertPay(dto.farwarid,dto.money,dto.ownername,dto.hname);
+                var revfarecode = "";
+                var resstr = service.InsertPay(dto.farwarid,dto.money,dto.ownername,dto.hname,ref revfarecode);
 
                 if (resstr != "success")
                 {

+ 1 - 0
PMS.NetCore/PMS.NetCore/PMS.NetCore.csproj

@@ -34,6 +34,7 @@
   <ItemGroup>
     <ProjectReference Include="..\Service\PMS.NetCore.Service.csproj" />
     <ProjectReference Include="..\Utils\Utils.csproj" />
+    <ProjectReference Include="..\WxPayAPI\WxPayAPI.csproj" />
   </ItemGroup>
 
   <ItemGroup>

+ 45 - 0
PMS.NetCore/PMS.NetCore/PMS.NetCore.xml

@@ -99,6 +99,13 @@
             <param name="phone"></param>
             <returns></returns>
         </member>
+        <member name="M:PMS.NetCore.Controllers.MeterController.BindHouse(System.String)">
+            <summary>
+            绑定房屋信息
+            </summary>
+            <param name="phone"></param>
+            <returns></returns>
+        </member>
         <member name="M:PMS.NetCore.Controllers.MeterController.Fdc_pm_meterAdd(System.Collections.Generic.List{Sugar.Enties.ChaobiaoDTO})">
             <summary>
             抄表单添加
@@ -120,6 +127,13 @@
             <param name="phone"></param>
             <returns></returns>
         </member>
+        <member name="M:PMS.NetCore.Controllers.MeterController.MakePayUrl(Sugar.Enties.PayDTO)">
+            <summary>
+            生成微信缴费单url
+            </summary>
+            <param name="dto"></param>
+            <returns></returns>
+        </member>
         <member name="M:PMS.NetCore.Controllers.MeterController.GetRepaireList(System.String)">
             <summary>
             获取报修记录
@@ -134,6 +148,13 @@
             <param name="phone"></param>
             <returns></returns>
         </member>
+        <member name="M:PMS.NetCore.Controllers.MeterController.GetRepairDetail(System.String)">
+            <summary>
+            获取报修详情
+            </summary>
+            <param name="code"></param>
+            <returns></returns>
+        </member>
         <member name="M:PMS.NetCore.Controllers.MeterController.Insertfdc_pm_repair(Sugar.Enties.BaoshiDTO)">
             <summary>
             添加报事单
@@ -584,6 +605,16 @@
             系统配置
             </summary>
         </member>
+        <member name="F:PMS.NetCore.Controllers.SystemConfigurationController.savePattern">
+            <summary>
+            存储模式  file:文件  ,database:数据库
+            </summary>
+        </member>
+        <member name="F:PMS.NetCore.Controllers.SystemConfigurationController.savePath">
+            <summary>
+            文件存储目录
+            </summary>
+        </member>
         <member name="M:PMS.NetCore.Controllers.SystemConfigurationController.#ctor">
             <summary>
             系统配置
@@ -841,6 +872,20 @@
             <param name="dto"></param>
             <returns></returns>
         </member>
+        <member name="M:PMS.NetCore.Controllers.SystemConfigurationController.UploadImage(Microsoft.AspNetCore.Http.IFormCollection)">
+            <summary>
+            /图片上传
+            </summary>
+            <param name="formCollection"></param>
+            <returns></returns>
+        </member>
+        <member name="M:PMS.NetCore.Controllers.SystemConfigurationController.ShowImg(System.String)">
+            <summary>
+            附件显示
+            </summary>
+            <param name="autoid"></param>
+            <returns></returns>
+        </member>
         <member name="T:PMS.NetCore.Controllers.UserController">
             <summary>
             用户信息

+ 13 - 0
PMS.NetCore/PMS.NetCore/Startup.cs

@@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.FileProviders;
 using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.Logging;
 using Microsoft.OpenApi.Models;
@@ -169,6 +170,18 @@ namespace PMS.NetCore
                 {
                     app.UseDeveloperExceptionPage();
                 }
+                //启用静态资源(无参默认wwwroot文件夹)
+                //app.UseStaticFiles();
+                logger.Info($"root:{AppDomain.CurrentDomain.BaseDirectory}uploadimg");
+                //自定义资源
+                app.UseStaticFiles(new StaticFileOptions
+                {
+                    //资源所在的绝对路径。
+                    FileProvider = new PhysicalFileProvider(AppDomain.CurrentDomain.BaseDirectory + "uploadimg"),
+                    
+                    //表示访问路径,必须'/'开头
+                    RequestPath = "/uploadimg"
+                }) ;
 
                 app.UseHttpsRedirection();
 

+ 9 - 2
PMS.NetCore/PMS.NetCore/appsettings.json

@@ -1,8 +1,8 @@
 {
   "isIIS": true,
   "virtualPath": "/wycore",
-  "company": "kunshan",       //系统属于哪个公司
-  "kunshanUrl": "https://www.s-p-c.com.cn/scc2/user/?route=communityActivity/sendWXMsg",       //昆山提供的短信接口地址
+  "company": "kunshan", //系统属于哪个公司
+  "kunshanUrl": "https://www.s-p-c.com.cn/scc2/user/?route=communityActivity/sendWXMsg", //昆山提供的短信接口地址
   "Logging": {
     "IncludeScopes": false,
     "Debug": {
@@ -24,5 +24,12 @@
     "Issuer": "lutao", //Token颁发者
     "AccessTokenExpiresMinutes": "600", //过期时间,分钟
     "Audience": "propertyMs" //Token接受者
+  },
+  "WxPay": { //微信支付配置
+    "AppID": "wx44b7856041918e01", //绑定支付的APPID
+    "AppSecret": "7f40e071886104b4ea01538db4402e1a", //公众号secret
+    "MCHID": "", //商户号
+    "KEY": "", //商户支付密钥
+    "NOTIFY_URL": "http://localhost/wxpay/getpayback.aspx"
   }
 }

+ 1 - 0
PMS.NetCore/Service/PMS.NetCore.Service.csproj

@@ -12,6 +12,7 @@
     <ProjectReference Include="..\PMS.NetCore.BusinessCore\PMS.NetCore.BusinessCore.csproj" />
     <ProjectReference Include="..\PMS.NetCore.Enties\Sugar.Enties.csproj" />
     <ProjectReference Include="..\Utils\Utils.csproj" />
+    <ProjectReference Include="..\WxPayAPI\WxPayAPI.csproj" />
   </ItemGroup>
 
 </Project>

+ 18 - 5
PMS.NetCore/Service/QuartzTask.cs

@@ -19,11 +19,7 @@ namespace PMS.NetCore.Service
         public async Task RunAsync()
         {
             NlogHelper.quartzLogger.Info("开始 RunAsync");
-            // 从工厂中获取调度程序实例
-            //NameValueCollection props = new NameValueCollection
-            //{
-            //     { "quartz.serializer.type", "binary" }
-            //};
+           
             StdSchedulerFactory factory = new StdSchedulerFactory();
             //通过工场类获得调度器
             var _scheduler = await factory.GetScheduler();
@@ -97,6 +93,23 @@ namespace PMS.NetCore.Service
             }
             #endregion
 
+
+            #region 物业缴费订单定时处理任务
+            ////创建触发器(也叫时间策略)
+            var triggerBywy = TriggerBuilder.Create()
+                              .StartNow()
+                               .WithSimpleSchedule(x => x.WithIntervalInSeconds(3).RepeatForever())//每3秒执行一次
+                             
+                              .Build();
+            //创建作业实例
+            //Jobs即我们需要执行的作业
+            var jobDetailBywy = JobBuilder.Create<WyOrderJob>()
+                            .WithIdentity("Myjobwy", "group")//我们给这个作业取了个“Myjobwy”的名字,并取了个组名为“group”
+                            .Build();
+            //将触发器和作业任务绑定到调度器中
+            await _scheduler.ScheduleJob(jobDetailBywy, triggerBywy);
+            #endregion
+
         }
     }
     }

+ 115 - 0
PMS.NetCore/Service/WyOrderJob.cs

@@ -0,0 +1,115 @@
+using Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Utils;
+using Sugar.Enties;
+using WxPayAPI;
+
+namespace PMS.NetCore.Service
+{
+    /// <summary>
+    /// 物业缴费订单定时处理任务
+    /// </summary>
+    public class WyOrderJob : IJob
+    {
+        public Task Execute(IJobExecutionContext context)
+        {
+            return Task.Run(() =>
+            {
+                try
+                {
+                    NlogHelper.quartzLogger.Info("开始物业缴费订单定时处理任务");
+                    var manager = new fdc_pm_meterrecordManager();
+                    //遍历最近5天未支付的订单
+                    var list = manager.Db.Queryable<Order>().Where(m => m.Status == 0 && m.AddTime > DateTime.Now.AddDays(-5)).ToList();
+                    foreach (var order in list)
+                    {
+                        orderQuery(order.OrderNo);
+                    }
+
+
+                }
+                catch (Exception ero)
+                {
+                    NlogHelper.quartzLogger.Error(ero, "物业缴费订单定时处理任务执行出错");
+                    throw;
+                }
+
+            });
+
+        }
+        /// <summary>
+        /// 向微信支付发起订单查询请求,根据查询结果,插入缴费记录,及更新order订单表的状态
+        /// </summary>
+        /// <param name="out_trade_no">订单号</param>
+        /// <returns></returns>
+        private string orderQuery(string out_trade_no)
+        {
+            WxPayData data = new WxPayData();
+
+            data.SetValue("out_trade_no", out_trade_no);
+
+
+            WxPayData result = WxPayApi.OrderQuery(data);//提交订单查询请求给API,接收返回数据
+
+            if (result.GetValue("return_code") == null || result.GetValue("result_code") == null || result.GetValue("trade_state") == null)
+            {
+
+                NlogHelper.quartzLogger.Debug("return_code...null");
+                return "";
+            }
+            if (result.GetValue("return_code").ToString() != "SUCCESS" || result.GetValue("result_code").ToString() != "SUCCESS" || result.GetValue("trade_state").ToString() != "SUCCESS")
+            {
+
+                NlogHelper.quartzLogger.Debug("return_code...不成功");
+                return "";
+            }
+            var manager = new fdc_pm_meterrecordManager();
+            manager.Db.BeginTran();
+            var order = manager.Db.Queryable<Order>().First(m => m.OrderNo == out_trade_no); 
+ 
+           
+            #region  扫码支付订单处理   
+            
+            if (order == null)
+            {
+                manager.Db.RollbackTran();
+                return "";
+            }
+            try
+            {
+               
+                string revfarecode="";
+                var res = manager.InsertPayWithNoTran(order.Farerowid, order.Money.ToString(), order.Ownername, order.Hname, ref revfarecode);
+                if(res!="success")
+                {
+                    manager.Db.RollbackTran();
+                    return "";
+                }
+                else
+                {
+                    order.Status = 1;
+                    order.Revfarecode = revfarecode;
+                    //更新订单状态为已支付 更新生成的已缴费主键到order表
+                    manager.Db.Updateable<Order>().UpdateColumns(it => new { it.Status, it.Revfarecode }).ExecuteCommand();   
+                }
+                manager.Db.CommitTran();
+                return "SUCCESS";
+            }
+            catch (Exception ex)
+            {
+                manager.Db.RollbackTran();
+                NlogHelper.quartzLogger.Error(ex,$"{out_trade_no}订单缴费和更新订单状态出错");
+                //throw;
+            }
+
+
+            #endregion
+            return "";
+
+        }
+    }
+}

+ 237 - 0
PMS.NetCore/Utils/Utility.cs

@@ -0,0 +1,237 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Utils
+{
+    public class Utility
+    {
+        //创建一个随机数实例
+        private static Random ro = new Random();
+
+        /// <summary>
+        /// right函数
+        /// </summary>
+        /// <param name="sSource"></param>
+        /// <param name="iLength"></param>
+        /// <returns></returns>
+        public static string Right(string sSource, int iLength)
+        {
+            if (iLength == -1)
+            {
+                return "";
+            }
+            return sSource.Substring(iLength > sSource.Length ? 0 : sSource.Length - iLength);
+        }
+
+        /// <summary>
+        /// left函数
+        /// </summary>
+        /// <param name="sSource"></param>
+        /// <param name="iLength"></param>
+        /// <returns></returns>
+        public static string Left(string sSource, int iLength)
+        {
+            if (iLength == -1)
+            {
+                return "";
+            }
+            return sSource.Substring(0, iLength > sSource.Length ? sSource.Length : iLength);
+        }
+
+        #region 常用日期函数
+        /// <summary>
+        /// 上月第一天
+        /// </summary>
+        public static string PreMonthFirstDay
+        {
+            get { return DateTime.Now.AddDays(1 - DateTime.Now.Day).AddMonths(-1).ToString("yyyy-MM-dd"); }
+        }
+
+        /// <summary>
+        /// 上月最后一天
+        /// </summary>
+        public static string PreMonthLastDay
+        {
+            get { return DateTime.Now.AddDays(1 - DateTime.Now.Day).AddDays(-1).ToString("yyyy-MM-dd"); }
+        }
+        /// <summary>
+        /// 上周一
+        /// </summary>
+        public static string PreWeekMonday
+        {
+            get { return DateTime.Now.AddDays(1 - System.Convert.ToInt32(DateTime.Now.DayOfWeek.ToString("d"))).AddDays(-7).ToString("yyyy-MM-dd"); }
+        }
+
+        /// <summary>
+        /// 上周日
+        /// </summary>
+        public static string PreWeekSunday
+        {
+            get { return DateTime.Now.AddDays(1 - System.Convert.ToInt32(DateTime.Now.DayOfWeek.ToString("d"))).AddDays(-1).ToString("yyyy-MM-dd"); }
+        }
+
+        /// <summary>
+        /// 昨天
+        /// </summary>
+        public static string Yesterday
+        {
+            get { return DateTime.Now.Date.AddDays(-1).ToString("yyyy-MM-dd"); }
+        }
+        /// <summary>
+        /// 今天
+        /// </summary>
+        public static string Today
+        {
+            get { return DateTime.Now.ToString("yyyy-MM-dd"); }
+        }
+
+        /// <summary>
+        /// 本周一
+        /// </summary>
+        public static string ThisWeekMonday
+        {
+            get { return DateTime.Now.AddDays(1 - System.Convert.ToInt32(DateTime.Now.DayOfWeek.ToString("d"))).ToString("yyyy-MM-dd"); }
+        }
+
+        /// <summary>
+        /// 本周日
+        /// </summary>
+        public static string ThisWeekSunday
+        {
+            get { return DateTime.Now.AddDays(1 - System.Convert.ToInt32(DateTime.Now.DayOfWeek.ToString("d"))).AddDays(6).ToString("yyyy-MM-dd"); }
+        }
+
+        /// <summary>
+        /// 本月第一天
+        /// </summary>
+        public static string ThisMonthFirstDay
+        {
+            get { return DateTime.Now.AddDays(1 - DateTime.Now.Day).ToString("yyyy-MM-dd"); }
+        }
+
+        /// <summary>
+        /// 本月最后一天
+        /// </summary>
+        public static string ThisMonthLastDay
+        {
+            get { return DateTime.Now.AddDays(1 - DateTime.Now.Day).AddMonths(1).AddDays(-1).ToString("yyyy-MM-dd"); }
+        }
+
+        /// <summary>
+        /// 长日期转换为yyyy-mm-dd形式显示
+        /// </summary>
+        /// <param name="date"></param>
+        /// <returns></returns>
+        public static string ConvertDateToYYYYMMDD(object date)
+        {
+            if (date.IsNullOrEmpty())
+            {
+                return "";
+            }
+            return System.Convert.ToDateTime(date).ToString("yyyy-MM-dd");
+        }
+        #endregion
+
+
+        /// <summary>
+        /// 是否本地服务器
+        /// </summary>
+        /// <returns></returns>
+        public static bool IsLocal()
+        {
+            string address = RequestHelper.GetServerIp();
+            //本地不作处理
+            if (address.Contains("192.168."))
+            {
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// 根据IP获取webconfig中的本地域名或者网站域名
+        /// </summary>
+        /// <returns></returns>
+        public static string GetWebSiteDomain()
+        {
+            if (Utility.IsLocal())
+            {
+                return System.Configuration.ConfigurationManager.AppSettings["LocalDomain"].ToString();
+            }
+            else
+            {
+                return System.Configuration.ConfigurationManager.AppSettings["WebSiteDomain"].ToString();
+            }
+        }
+
+
+        /// <summary>
+        /// 时分秒+随机数字符串
+        /// </summary>
+        /// <returns></returns>
+        public static string GetRandomNumber()
+        {
+            string orderNo = DateTime.Now.ToString("yyyyMMddHHmmss");
+            //获取一个大于1000小于9999的随机数
+            string rnd = ro.Next(1000, 9999).ToString();
+            orderNo += rnd;
+            return orderNo;
+        }
+        /// <summary>
+        /// 获取随机6位数
+        /// </summary>
+        /// <returns></returns>
+        public static string GetRandomSix()
+        {
+            string rnd = ro.Next(100000, 999999).ToString();
+            return rnd;
+        }
+       
+       
+        
+        /// <summary>
+        /// 时间戳转为C#格式时间
+        /// </summary>
+        /// <param name="timeStamp">Unix时间戳格式</param>
+        /// <returns>C#格式时间</returns>
+        public static DateTime GetTime(string timeStamp)
+        {
+            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
+            long lTime = long.Parse(timeStamp + "0000000");
+            TimeSpan toNow = new TimeSpan(lTime);
+            return dtStart.Add(toNow);
+        }
+        /// <summary>
+        /// DateTime时间格式转换为Unix时间戳格式
+        /// </summary>
+        /// <param name="time"> DateTime时间格式</param>
+        /// <returns>Unix时间戳格式</returns>
+        public static int ConvertDateTimeInt(System.DateTime time)
+        {
+            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
+            return (int)(time - startTime).TotalSeconds;
+        }
+        /// <summary>
+        /// 比较两个一维字符串数组是否相同,这个相同是指元素个数相等,并且值相同,但是元素位置可能不同
+        /// </summary>
+        /// <param name="arr1"></param>
+        /// <param name="arr2"></param>
+        /// <returns></returns>
+        public static bool CompareArrayEqual(string[] arr1, string[] arr2)
+        {
+            if (arr1 == null || arr2 == null) return false;
+            if (arr1.Length != arr2.Length) return false;
+            Array.Sort(arr1);
+            Array.Sort(arr2);
+            for (int i = 0; i < arr1.Length; i++)
+            {
+                if (arr1[i] != arr2[i])
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+}

+ 205 - 0
PMS.NetCore/WxPayAPI/HttpService.cs

@@ -0,0 +1,205 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+
+namespace WxPayAPI
+{
+    /// <summary>
+    /// http连接基础类,负责底层的http通信
+    /// </summary>
+    public class HttpService
+    {
+        private static NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
+        public HttpService()
+        {
+            
+        }
+        public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
+        {
+            //直接确认,否则打不开    
+            return true;
+        }
+
+        public static string Post(string xml, string url, bool isUseCert, int timeout)
+        {
+            System.GC.Collect();//垃圾回收,回收没有正常关闭的http连接
+
+            string result = "";//返回结果
+
+            HttpWebRequest request = null;
+            HttpWebResponse response = null;
+            Stream reqStream = null;
+
+            try
+            {
+                //设置最大连接数
+                ServicePointManager.DefaultConnectionLimit = 200;
+                //设置https验证方式
+                if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
+                {
+                    ServicePointManager.ServerCertificateValidationCallback =
+                            new RemoteCertificateValidationCallback(CheckValidationResult);
+                }
+
+                /***************************************************************
+                * 下面设置HttpWebRequest的相关属性
+                * ************************************************************/
+                request = (HttpWebRequest)WebRequest.Create(url);
+
+                request.Method = "POST";
+                request.Timeout = timeout * 1000;
+
+                //设置代理服务器
+                //WebProxy proxy = new WebProxy();                          //定义一个网关对象
+                //proxy.Address = new Uri(WxPayConfig.PROXY_URL);              //网关服务器端口:端口
+                //request.Proxy = proxy;
+
+                //设置POST的数据类型和长度
+                request.ContentType = "text/xml";
+                byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
+                request.ContentLength = data.Length;
+
+                //是否使用证书
+                if (isUseCert)
+                {
+                    string path = AppDomain.CurrentDomain.BaseDirectory;
+                    X509Certificate2 cert = new X509Certificate2(path + WxPayConfig.SSLCERT_PATH, WxPayConfig.SSLCERT_PASSWORD);
+                    request.ClientCertificates.Add(cert);
+                    Log.Debug("WxPayApi", "PostXml used cert");
+                }
+
+                //往服务器写入数据
+                reqStream = request.GetRequestStream();
+                reqStream.Write(data, 0, data.Length);
+                reqStream.Close();
+
+                //获取服务端返回
+                response = (HttpWebResponse)request.GetResponse();
+
+                //获取服务端返回数据
+                StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
+                result = sr.ReadToEnd().Trim();
+                sr.Close();
+            }
+            catch (System.Threading.ThreadAbortException e)
+            {
+                Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting.");
+                Log.Error("Exception message: {0}", e.Message);
+                System.Threading.Thread.ResetAbort();
+            }
+            catch (WebException e)
+            {
+                Log.Error("HttpService", e.ToString());
+                if (e.Status == WebExceptionStatus.ProtocolError)
+                {
+                    Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
+                    Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
+                }
+                throw new WxPayException(e.ToString());
+            }
+            catch (Exception e)
+            {
+                Log.Error("HttpService", e.ToString());
+                throw new WxPayException(e.ToString());
+            }
+            finally
+            {
+                //关闭连接和流
+                if (response != null)
+                {
+                    response.Close();
+                }
+                if (request != null)
+                {
+                    request.Abort();
+                }
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 处理http GET请求,返回数据
+        /// </summary>
+        /// <param name="url">请求的url地址</param>
+        /// <returns>http GET成功后返回的数据,失败抛WebException异常</returns>
+        public static string Get(string url)
+        {
+            System.GC.Collect();
+            string result = "";
+
+            HttpWebRequest request = null;
+            HttpWebResponse response = null;
+
+            //请求url以获取数据
+            try
+            {
+                //设置最大连接数
+                ServicePointManager.DefaultConnectionLimit = 200;
+                //设置https验证方式
+                if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
+                {
+                    ServicePointManager.ServerCertificateValidationCallback =
+                            new RemoteCertificateValidationCallback(CheckValidationResult);
+                }
+
+                /***************************************************************
+                * 下面设置HttpWebRequest的相关属性
+                * ************************************************************/
+                request = (HttpWebRequest)WebRequest.Create(url);
+
+                request.Method = "GET";
+
+                //设置代理
+                //WebProxy proxy = new WebProxy();
+                //proxy.Address = new Uri(WxPayConfig.PROXY_URL);
+                //request.Proxy = proxy;
+
+                //获取服务器返回
+                response = (HttpWebResponse)request.GetResponse();
+
+                //获取HTTP返回数据
+                StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
+                result = sr.ReadToEnd().Trim();
+                sr.Close();
+            }
+            catch (System.Threading.ThreadAbortException e)
+            {
+                Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting.");
+                Log.Error("Exception message: {0}", e.Message);
+                System.Threading.Thread.ResetAbort();
+            }
+            catch (WebException e)
+            {
+                Log.Error("HttpService", e.ToString());
+                if (e.Status == WebExceptionStatus.ProtocolError)
+                {
+                    Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
+                    Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
+                }
+                throw new WxPayException(e.ToString());
+            }
+            catch (Exception e)
+            {
+                Log.Error("HttpService", e.ToString());
+                throw new WxPayException(e.ToString());
+            }
+            finally
+            {
+                //关闭连接和流
+                if (response != null)
+                {
+                    response.Close();
+                }
+                if (request != null)
+                {
+                    request.Abort();
+                }
+            }
+            return result;
+        }
+    }
+}

+ 235 - 0
PMS.NetCore/WxPayAPI/NativePay.cs

@@ -0,0 +1,235 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Senparc.Weixin;
+using Senparc.Weixin.TenPay;
+using Senparc.Weixin.TenPay.V3;
+using Newtonsoft.Json;
+
+namespace WxPayAPI
+{
+    public class NativePay
+    {
+        /// <summary>
+        /// nlog日志
+        /// </summary>
+        private static NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
+        /**
+        * 生成扫描支付模式一URL
+        * @param productId 商品ID
+        * @return 模式一URL
+        */
+        public string GetPrePayUrl(string productId)
+        {
+            Log.Info(this.GetType().ToString(), "Native pay mode 1 url is producing...");
+
+            WxPayData data = new WxPayData();
+            data.SetValue("appid", WxPayConfig.APPID);//公众帐号id
+            data.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            data.SetValue("time_stamp", WxPayApi.GenerateTimeStamp());//时间戳
+            data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串
+            data.SetValue("product_id", productId);//商品ID
+            data.SetValue("sign", data.MakeSign());//签名
+            string str = ToUrlParams(data.GetValues());//转换为URL串
+            string url = "weixin://wxpay/bizpayurl?" + str;
+
+            Log.Info(this.GetType().ToString(), "Get native pay mode 1 url : " + url);
+            return url;
+        }
+
+
+       
+        /// <summary>
+        /// 生成直接支付url,支付url有效期为2小时,模式二
+        /// </summary>
+        /// <param name="productId">商品ID</param>
+        /// <param name="total_fee">订单金额,单位为分,类型为整数</param>
+        /// <param name="out_trade_no">从商户传来的订单号</param>
+        /// <returns>模式二URL</returns>
+        public string GetPayUrl(string productId, int total_fee, string out_trade_no)
+        {
+            Log.Info(this.GetType().ToString()+ "-GetPayUrl");
+
+            WxPayData data = new WxPayData();
+
+
+            data.SetValue("body", "缴费");//商品描述
+            data.SetValue("attach", "缴费");//附加数据
+            //data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串
+            data.SetValue("out_trade_no", out_trade_no);//商户订单号
+            data.SetValue("total_fee", total_fee);//总金额
+            //data.SetValue("spbill_create_ip", WxPayConfig.IP);//终端IP
+            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
+            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
+            //data.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//通知地址
+            data.SetValue("goods_tag", "jjj");//商品标记
+            data.SetValue("trade_type", "NATIVE");//交易类型
+            data.SetValue("product_id", productId);//商品ID
+            //data.SetValue("sign", data.MakeSign());//签名
+            Log.Info($"【GetPayUrl】data组装:{JsonConvert.SerializeObject(data)}");
+            WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
+            Log.Info($"【GetPayUrl】调用统一下单接口 result:{JsonConvert.SerializeObject(result)}");
+            string url = result.GetValue("code_url")==null?"": result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
+
+            //Log.Info(this.GetType().ToString(), "Get native pay mode 2 url : " + url);
+            return url;
+        }
+        /// <summary>
+        /// 新闻查看支付获取url
+        /// </summary>
+        /// <param name="productId"></param>
+        /// <param name="total_fee"></param>
+        /// <param name="out_trade_no"></param>
+        /// <returns></returns>
+        public string GetNewsPayUrl(string productId, int total_fee, string out_trade_no)
+        {
+            //Log.Info(this.GetType().ToString(), "Native pay mode 2 url is producing...");
+
+            WxPayData data = new WxPayData();
+
+
+            data.SetValue("body", "新闻查看支付");//商品描述
+            data.SetValue("attach", "融资通微信新闻查看支付");//附加数据
+            //data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串
+            data.SetValue("out_trade_no", out_trade_no);//商户订单号
+            data.SetValue("total_fee", total_fee);//总金额
+            //data.SetValue("spbill_create_ip", WxPayConfig.IP);//终端IP
+            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
+            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
+            //data.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//通知地址
+            data.SetValue("goods_tag", "jjj");//商品标记
+            data.SetValue("trade_type", "NATIVE");//交易类型
+            data.SetValue("product_id", productId);//商品ID
+            //data.SetValue("sign", data.MakeSign());//签名
+            WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
+
+            string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
+
+            //Log.Info(this.GetType().ToString(), "Get native pay mode 2 url : " + url);
+            return url;
+        }
+
+
+        /// <summary>
+        /// 项目查看支付获取url
+        /// </summary>
+        /// <param name="productId"></param>
+        /// <param name="total_fee"></param>
+        /// <param name="out_trade_no"></param>
+        /// <returns></returns>
+        public string GetXmPayUrl(string productId, int total_fee, string out_trade_no)
+        {
+            //Log.Info(this.GetType().ToString(), "Native pay mode 2 url is producing...");
+
+            WxPayData data = new WxPayData();
+
+
+            data.SetValue("body", "项目查看支付");//商品描述
+            data.SetValue("attach", "融资通微信项目查看支付");//附加数据
+            //data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串
+            data.SetValue("out_trade_no", out_trade_no);//商户订单号
+            data.SetValue("total_fee", total_fee);//总金额
+            //data.SetValue("spbill_create_ip", WxPayConfig.IP);//终端IP
+            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
+            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
+            //data.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//通知地址
+            data.SetValue("goods_tag", "jjj");//商品标记
+            data.SetValue("trade_type", "NATIVE");//交易类型
+            data.SetValue("product_id", productId);//商品ID
+            //data.SetValue("sign", data.MakeSign());//签名
+            WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
+
+            string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
+
+            //Log.Info(this.GetType().ToString(), "Get native pay mode 2 url : " + url);
+            return url;
+        }
+
+        /// <summary>
+        /// 留言查看支付获取url
+        /// </summary>
+        /// <param name="productId"></param>
+        /// <param name="total_fee"></param>
+        /// <param name="out_trade_no"></param>
+        /// <returns></returns>
+        public string GetGuestBookPayUrl(string productId, int total_fee, string out_trade_no)
+        {
+            //Log.Info(this.GetType().ToString(), "Native pay mode 2 url is producing...");
+
+            WxPayData data = new WxPayData();
+
+
+            data.SetValue("body", "留言查看支付");//商品描述
+            data.SetValue("attach", "融资通微信留言查看支付");//附加数据
+            //data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串
+            data.SetValue("out_trade_no", out_trade_no);//商户订单号
+            data.SetValue("total_fee", total_fee);//总金额
+            //data.SetValue("spbill_create_ip", WxPayConfig.IP);//终端IP
+            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
+            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
+            //data.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//通知地址
+            data.SetValue("goods_tag", "jjj");//商品标记
+            data.SetValue("trade_type", "NATIVE");//交易类型
+            data.SetValue("product_id", productId);//商品ID
+            //data.SetValue("sign", data.MakeSign());//签名
+            WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
+
+            string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
+
+            //Log.Info(this.GetType().ToString(), "Get native pay mode 2 url : " + url);
+            return url;
+        }
+        /// <summary>
+        /// 项目投递支付
+        /// </summary>
+        /// <param name="productId"></param>
+        /// <param name="total_fee"></param>
+        /// <param name="out_trade_no"></param>
+        /// <returns></returns>
+        public string GetDeliverProjectPayUrl(string productId, int total_fee, string out_trade_no)
+        {
+            //Log.Info(this.GetType().ToString(), "Native pay mode 2 url is producing...");
+
+            WxPayData data = new WxPayData();
+
+
+            data.SetValue("body", "项目投递支付");//商品描述
+            data.SetValue("attach", "融资通微信项目投递支付");//附加数据
+            //data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串
+            data.SetValue("out_trade_no", out_trade_no);//商户订单号
+            data.SetValue("total_fee", total_fee);//总金额
+            //data.SetValue("spbill_create_ip", WxPayConfig.IP);//终端IP
+            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
+            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
+            //data.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//通知地址
+            data.SetValue("goods_tag", "jjj");//商品标记
+            data.SetValue("trade_type", "NATIVE");//交易类型
+            data.SetValue("product_id", productId);//商品ID
+            //data.SetValue("sign", data.MakeSign());//签名
+            WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
+
+            string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
+
+            //Log.Info(this.GetType().ToString(), "Get native pay mode 2 url : " + url);
+            return url;
+        }
+        /**
+        * 参数数组转换为url格式
+        * @param map 参数名与参数值的映射表
+        * @return URL字符串
+        */
+        private string ToUrlParams(SortedDictionary<string, object> map)
+        {
+            string buff = "";
+            foreach (KeyValuePair<string, object> pair in map)
+            {
+                buff += pair.Key + "=" + pair.Value + "&";
+            }
+            buff = buff.Trim('&');
+            return buff;
+        }
+    }
+
+    
+    
+}

+ 607 - 0
PMS.NetCore/WxPayAPI/WxPayApi.cs

@@ -0,0 +1,607 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Utils;
+
+namespace WxPayAPI
+{
+    public class WxPayApi
+    {
+        
+        /**
+        * 提交被扫支付API
+        * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,
+        * 由商户收银台或者商户后台调用该接口发起支付。
+        * @param WxPayData inputObj 提交给被扫支付API的参数
+        * @param int timeOut 超时时间
+        * @throws WxPayException
+        * @return 成功时返回调用结果,其他抛异常
+        */
+        public static WxPayData Micropay(WxPayData inputObj, int timeOut = 10)
+        {
+            string url = "https://api.mch.weixin.qq.com/pay/micropay";
+            //检测必填参数
+            if (!inputObj.IsSet("body"))
+            {
+                throw new WxPayException("提交被扫支付API接口中,缺少必填参数body!");
+            }
+            else if (!inputObj.IsSet("out_trade_no"))
+            {
+                throw new WxPayException("提交被扫支付API接口中,缺少必填参数out_trade_no!");
+            }
+            else if (!inputObj.IsSet("total_fee"))
+            {
+                throw new WxPayException("提交被扫支付API接口中,缺少必填参数total_fee!");
+            }
+            else if (!inputObj.IsSet("auth_code"))
+            {
+                throw new WxPayException("提交被扫支付API接口中,缺少必填参数auth_code!");
+            }
+
+            inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+            string xml = inputObj.ToXml();
+
+            var start = DateTime.Now;//请求开始时间
+
+            NlogHelper.quartzLogger.Info("WxPayApi MicroPay request : " + xml);
+            string response = HttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
+            NlogHelper.quartzLogger.Debug("WxPayApi", "MicroPay response : " + response);
+
+            var end = DateTime.Now;
+            int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
+
+            //将xml格式的结果转换为对象以返回
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+
+            ReportCostTime(url, timeCost, result);//测速上报
+
+            return result;
+        }
+
+
+        /**
+        *    
+        * 查询订单
+        * @param WxPayData inputObj 提交给查询订单API的参数
+        * @param int timeOut 超时时间
+        * @throws WxPayException
+        * @return 成功时返回订单查询结果,其他抛异常
+        */
+        public static WxPayData OrderQuery(WxPayData inputObj, int timeOut = 6)
+        {
+            string url = "https://api.mch.weixin.qq.com/pay/orderquery";
+            //检测必填参数
+            if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
+            {
+                throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+
+            string xml = inputObj.ToXml();
+
+            var start = DateTime.Now;
+
+            NlogHelper.quartzLogger.Debug($"【OrderQuery】 xml request:{xml}");
+            string response = HttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口提交数据
+            NlogHelper.quartzLogger.Debug($"【OrderQuery】 xml response:{response}" );
+
+            var end = DateTime.Now;
+            int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
+
+            //将xml格式的数据转化为对象以返回
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+
+            ReportCostTime(url, timeCost, result);//测速上报
+
+            return result;
+        }
+
+
+        /**
+        * 
+        * 撤销订单API接口
+        * @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个
+        * @param int timeOut 接口超时时间
+        * @throws WxPayException
+        * @return 成功时返回API调用结果,其他抛异常
+        */
+        public static WxPayData Reverse(WxPayData inputObj, int timeOut = 6)
+        {
+            string url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
+            //检测必填参数
+            if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
+            {
+                throw new WxPayException("撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!");
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+            string xml = inputObj.ToXml();
+
+            var start = DateTime.Now;//请求开始时间
+
+            NlogHelper.quartzLogger.Debug("WxPayApi", "Reverse request : " + xml);
+
+            string response = HttpService.Post(xml, url, true, timeOut);
+
+            NlogHelper.quartzLogger.Debug("WxPayApi", "Reverse response : " + response);
+
+            var end = DateTime.Now;
+            int timeCost = (int)((end - start).TotalMilliseconds);
+
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+
+            ReportCostTime(url, timeCost, result);//测速上报
+
+            return result;
+        }
+
+
+        /**
+        * 
+        * 申请退款
+        * @param WxPayData inputObj 提交给申请退款API的参数
+        * @param int timeOut 超时时间
+        * @throws WxPayException
+        * @return 成功时返回接口调用结果,其他抛异常
+        */
+        public static WxPayData Refund(WxPayData inputObj, int timeOut = 6)
+        {
+            string url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
+            //检测必填参数
+            if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
+            {
+                throw new WxPayException("退款申请接口中,out_trade_no、transaction_id至少填一个!");
+            }
+            else if (!inputObj.IsSet("out_refund_no"))
+            {
+                throw new WxPayException("退款申请接口中,缺少必填参数out_refund_no!");
+            }
+            else if (!inputObj.IsSet("total_fee"))
+            {
+                throw new WxPayException("退款申请接口中,缺少必填参数total_fee!");
+            }
+            else if (!inputObj.IsSet("refund_fee"))
+            {
+                throw new WxPayException("退款申请接口中,缺少必填参数refund_fee!");
+            }
+            else if (!inputObj.IsSet("op_user_id"))
+            {
+                throw new WxPayException("退款申请接口中,缺少必填参数op_user_id!");
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+
+            string xml = inputObj.ToXml();
+            var start = DateTime.Now;
+
+            NlogHelper.quartzLogger.Debug("WxPayApi", "Refund request : " + xml);
+            string response = HttpService.Post(xml, url, true, timeOut);//调用HTTP通信接口提交数据到API
+            NlogHelper.quartzLogger.Debug("WxPayApi", "Refund response : " + response);
+
+            var end = DateTime.Now;
+            int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
+
+            //将xml格式的结果转换为对象以返回
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+
+            ReportCostTime(url, timeCost, result);//测速上报
+
+            return result;
+        }
+
+
+        /**
+	    * 
+	    * 查询退款
+	    * 提交退款申请后,通过该接口查询退款状态。退款有一定延时,
+	    * 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
+	    * out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
+	    * @param WxPayData inputObj 提交给查询退款API的参数
+	    * @param int timeOut 接口超时时间
+	    * @throws WxPayException
+	    * @return 成功时返回,其他抛异常
+	    */
+        public static WxPayData RefundQuery(WxPayData inputObj, int timeOut = 6)
+        {
+            string url = "https://api.mch.weixin.qq.com/pay/refundquery";
+            //检测必填参数
+            if (!inputObj.IsSet("out_refund_no") && !inputObj.IsSet("out_trade_no") &&
+                !inputObj.IsSet("transaction_id") && !inputObj.IsSet("refund_id"))
+            {
+                throw new WxPayException("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!");
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+
+            string xml = inputObj.ToXml();
+
+            var start = DateTime.Now;//请求开始时间
+
+            NlogHelper.quartzLogger.Debug("WxPayApi", "RefundQuery request : " + xml);
+            string response = HttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
+            NlogHelper.quartzLogger.Debug("WxPayApi", "RefundQuery response : " + response);
+
+            var end = DateTime.Now;
+            int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
+
+            //将xml格式的结果转换为对象以返回
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+
+            ReportCostTime(url, timeCost, result);//测速上报
+
+            return result;
+        }
+
+
+        /**
+        * 下载对账单
+        * @param WxPayData inputObj 提交给下载对账单API的参数
+        * @param int timeOut 接口超时时间
+        * @throws WxPayException
+        * @return 成功时返回,其他抛异常
+        */
+        public static WxPayData DownloadBill(WxPayData inputObj, int timeOut = 6)
+        {
+            string url = "https://api.mch.weixin.qq.com/pay/downloadbill";
+            //检测必填参数
+            if (!inputObj.IsSet("bill_date"))
+            {
+                throw new WxPayException("对账单接口中,缺少必填参数bill_date!");
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+
+            string xml = inputObj.ToXml();
+
+            NlogHelper.quartzLogger.Debug("WxPayApi", "DownloadBill request : " + xml);
+            string response = HttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
+            NlogHelper.quartzLogger.Debug("WxPayApi", "DownloadBill result : " + response);
+
+            WxPayData result = new WxPayData();
+            //若接口调用失败会返回xml格式的结果
+            if (response.Substring(0, 5) == "<xml>")
+            {
+                result.FromXml(response);
+            }
+            //接口调用成功则返回非xml格式的数据
+            else
+                result.SetValue("result", response);
+
+            return result;
+        }
+
+
+        /**
+	    * 
+	    * 转换短链接
+	    * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),
+	    * 减小二维码数据量,提升扫描速度和精确度。
+	    * @param WxPayData inputObj 提交给转换短连接API的参数
+	    * @param int timeOut 接口超时时间
+	    * @throws WxPayException
+	    * @return 成功时返回,其他抛异常
+	    */
+        public static WxPayData ShortUrl(WxPayData inputObj, int timeOut = 6)
+        {
+            string url = "https://api.mch.weixin.qq.com/tools/shorturl";
+            //检测必填参数
+            if (!inputObj.IsSet("long_url"))
+            {
+                throw new WxPayException("需要转换的URL,签名用原串,传输需URL encode!");
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串	
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+            string xml = inputObj.ToXml();
+
+            var start = DateTime.Now;//请求开始时间
+
+            NlogHelper.quartzLogger.Debug("WxPayApi", "ShortUrl request : " + xml);
+            string response = HttpService.Post(xml, url, false, timeOut);
+            NlogHelper.quartzLogger.Debug("WxPayApi", "ShortUrl response : " + response);
+
+            var end = DateTime.Now;
+            int timeCost = (int)((end - start).TotalMilliseconds);
+
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+            ReportCostTime(url, timeCost, result);//测速上报
+
+            return result;
+        }
+
+
+        /**
+        * 
+        * 统一下单
+        * @param WxPaydata inputObj 提交给统一下单API的参数
+        * @param int timeOut 超时时间
+        * @throws WxPayException
+        * @return 成功时返回,其他抛异常
+        */
+        public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 6)
+        {
+            string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
+            //检测必填参数
+            if (!inputObj.IsSet("out_trade_no"))
+            {
+                throw new WxPayException("缺少统一支付接口必填参数out_trade_no!");
+            }
+            else if (!inputObj.IsSet("body"))
+            {
+                throw new WxPayException("缺少统一支付接口必填参数body!");
+            }
+            else if (!inputObj.IsSet("total_fee"))
+            {
+                throw new WxPayException("缺少统一支付接口必填参数total_fee!");
+            }
+            else if (!inputObj.IsSet("trade_type"))
+            {
+                throw new WxPayException("缺少统一支付接口必填参数trade_type!");
+            }
+
+            //关联参数
+            if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
+            {
+                throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
+            }
+            if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
+            {
+                throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为NATIVE时,product_id为必填参数!");
+            }
+
+            //异步通知url未设置,则使用配置文件中的url
+            if (!inputObj.IsSet("notify_url"))
+            {
+                inputObj.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//异步通知url
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip	  	    
+            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
+
+            //签名
+            inputObj.SetValue("sign", inputObj.MakeSign());
+            //Log.Info("sign:", inputObj.MakeSign());
+
+            string xml = inputObj.ToXml();
+            var start = DateTime.Now;
+
+            NlogHelper.quartzLogger.Debug("WxPayApi", "UnfiedOrder request : " + xml);
+            string response = HttpService.Post(xml, url, false, timeOut);
+            NlogHelper.quartzLogger.Debug("WxPayApi", "UnfiedOrder response : " + response);
+
+            var end = DateTime.Now;
+            int timeCost = (int)((end - start).TotalMilliseconds);
+
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+
+            ReportCostTime(url, timeCost, result);//测速上报
+
+            return result;
+        }
+
+
+        /**
+	    * 
+	    * 关闭订单
+	    * @param WxPayData inputObj 提交给关闭订单API的参数
+	    * @param int timeOut 接口超时时间
+	    * @throws WxPayException
+	    * @return 成功时返回,其他抛异常
+	    */
+        public static WxPayData CloseOrder(WxPayData inputObj, int timeOut = 6)
+        {
+            string url = "https://api.mch.weixin.qq.com/pay/closeorder";
+            //检测必填参数
+            if (!inputObj.IsSet("out_trade_no"))
+            {
+                throw new WxPayException("关闭订单接口中,out_trade_no必填!");
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串		
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+            string xml = inputObj.ToXml();
+
+            var start = DateTime.Now;//请求开始时间
+
+            string response = HttpService.Post(xml, url, false, timeOut);
+
+            var end = DateTime.Now;
+            int timeCost = (int)((end - start).TotalMilliseconds);
+
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+
+            ReportCostTime(url, timeCost, result);//测速上报
+
+            return result;
+        }
+
+
+        /**
+	    * 
+	    * 测速上报
+	    * @param string interface_url 接口URL
+	    * @param int timeCost 接口耗时
+	    * @param WxPayData inputObj参数数组
+	    */
+        private static void ReportCostTime(string interface_url, int timeCost, WxPayData inputObj)
+        {
+            //如果不需要进行上报
+            if (WxPayConfig.REPORT_LEVENL == 0)
+            {
+                return;
+            }
+
+            //如果仅失败上报
+            if (WxPayConfig.REPORT_LEVENL == 1 && inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" &&
+             inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS")
+            {
+                return;
+            }
+
+            //上报逻辑
+            WxPayData data = new WxPayData();
+            data.SetValue("interface_url", interface_url);
+            data.SetValue("execute_time_", timeCost);
+            //返回状态码
+            if (inputObj.IsSet("return_code"))
+            {
+                data.SetValue("return_code", inputObj.GetValue("return_code"));
+            }
+            //返回信息
+            if (inputObj.IsSet("return_msg"))
+            {
+                data.SetValue("return_msg", inputObj.GetValue("return_msg"));
+            }
+            //业务结果
+            if (inputObj.IsSet("result_code"))
+            {
+                data.SetValue("result_code", inputObj.GetValue("result_code"));
+            }
+            //错误代码
+            if (inputObj.IsSet("err_code"))
+            {
+                data.SetValue("err_code", inputObj.GetValue("err_code"));
+            }
+            //错误代码描述
+            if (inputObj.IsSet("err_code_des"))
+            {
+                data.SetValue("err_code_des", inputObj.GetValue("err_code_des"));
+            }
+            //商户订单号
+            if (inputObj.IsSet("out_trade_no"))
+            {
+                data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no"));
+            }
+            //设备号
+            if (inputObj.IsSet("device_info"))
+            {
+                data.SetValue("device_info", inputObj.GetValue("device_info"));
+            }
+
+            try
+            {
+                Report(data);
+            }
+            catch (WxPayException ex)
+            {
+                //不做任何处理
+            }
+        }
+
+
+        /**
+	    * 
+	    * 测速上报接口实现
+	    * @param WxPayData inputObj 提交给测速上报接口的参数
+	    * @param int timeOut 测速上报接口超时时间
+	    * @throws WxPayException
+	    * @return 成功时返回测速上报接口返回的结果,其他抛异常
+	    */
+        public static WxPayData Report(WxPayData inputObj, int timeOut = 1)
+        {
+            string url = "https://api.mch.weixin.qq.com/payitil/report";
+            //检测必填参数
+            if (!inputObj.IsSet("interface_url"))
+            {
+                throw new WxPayException("接口URL,缺少必填参数interface_url!");
+            }
+            if (!inputObj.IsSet("return_code"))
+            {
+                throw new WxPayException("返回状态码,缺少必填参数return_code!");
+            }
+            if (!inputObj.IsSet("result_code"))
+            {
+                throw new WxPayException("业务结果,缺少必填参数result_code!");
+            }
+            if (!inputObj.IsSet("user_ip"))
+            {
+                throw new WxPayException("访问接口IP,缺少必填参数user_ip!");
+            }
+            if (!inputObj.IsSet("execute_time_"))
+            {
+                throw new WxPayException("接口耗时,缺少必填参数execute_time_!");
+            }
+
+            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
+            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
+            inputObj.SetValue("user_ip", WxPayConfig.IP);//终端ip
+            inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商户上报时间	 
+            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
+            inputObj.SetValue("sign", inputObj.MakeSign());//签名
+            string xml = inputObj.ToXml();
+
+            NlogHelper.quartzLogger.Info("WxPayApi", "Report request : " + xml);
+
+            string response = HttpService.Post(xml, url, false, timeOut);
+
+            NlogHelper.quartzLogger.Info("WxPayApi", "Report response : " + response);
+
+            WxPayData result = new WxPayData();
+            result.FromXml(response);
+            return result;
+        }
+
+        /**
+        * 根据当前系统时间加随机序列来生成订单号
+         * @return 订单号
+        */
+        public static string GenerateOutTradeNo()
+        {
+            var ran = new Random();
+            return string.Format("{0}{1}{2}", WxPayConfig.MCHID, DateTime.Now.ToString("yyyyMMddHHmmss"), ran.Next(999));
+        }
+
+        /**
+        * 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
+         * @return 时间戳
+        */
+        public static string GenerateTimeStamp()
+        {
+            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
+            return Convert.ToInt64(ts.TotalSeconds).ToString();
+        }
+
+        /**
+        * 生成随机串,随机串包含字母或数字
+        * @return 随机串
+        */
+        public static string GenerateNonceStr()
+        {
+            return Guid.NewGuid().ToString().Replace("-", "");
+        }
+    }
+}

+ 60 - 0
PMS.NetCore/WxPayAPI/WxPayConfig.cs

@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Utils;
+
+namespace WxPayAPI
+{
+    /**
+   * 	配置账号信息
+   */
+    public class WxPayConfig
+    {
+        //=======【基本信息设置】=====================================
+        /* 微信公众号信息配置
+        * APPID:绑定支付的APPID(必须配置)
+        * MCHID:商户号(必须配置)
+        * KEY:商户支付密钥,参考开户邮件设置(必须配置)
+        * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置)
+        */
+        public static string APPID = AppSettingsHelper.Configuration["WxPay:APPID"] ?? "wx44b7856041918e01";
+        public static string MCHID = AppSettingsHelper.Configuration["WxPay:MCHID"] ?? "";
+        public static string KEY = AppSettingsHelper.Configuration["WxPay:KEY"] ?? "";
+        public static string APPSECRET = AppSettingsHelper.Configuration["WxPay:APPSECRET"] ?? "7f40e071886104b4ea01538db4402e1a";
+
+        //=======【证书路径设置】===================================== 
+        /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要)
+        */
+        public const string SSLCERT_PATH = "cert/apiclient_cert.p12";
+        public const string SSLCERT_PASSWORD = "1233410002";
+
+
+
+        //=======【支付结果通知url】===================================== 
+        /* 支付结果通知回调url,用于商户接收支付结果
+        */
+        public static string NOTIFY_URL = AppSettingsHelper.Configuration["WxPay:NOTIFY_URL"] ?? "http://localhost/wxpay/getpayback.aspx";
+
+
+        //=======【商户系统后台机器IP】===================================== 
+        /* 此参数可手动配置也可在程序中自动获取
+        */
+        public const string IP = "8.8.8.8";
+
+
+        //=======【代理服务器设置】===================================
+        /* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)
+        */
+        public const string PROXY_URL = "http://10.152.18.220:8080";
+
+        //=======【上报信息配置】===================================
+        /* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报
+        */
+        public const int REPORT_LEVENL = 1;
+
+        //=======【日志级别】===================================
+        /* 日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息
+        */
+        public const int LOG_LEVENL = 3;
+    }
+}

+ 268 - 0
PMS.NetCore/WxPayAPI/WxPayData.cs

@@ -0,0 +1,268 @@
+using System;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.Text;
+using System.Xml;
+using Newtonsoft.Json;
+
+namespace WxPayAPI
+{
+    /// <summary>
+    /// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
+    /// 在调用接口之前先填充各个字段的值,然后进行接口通信,
+    /// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
+    /// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
+    /// </summary>
+    public class WxPayData
+    {
+        public WxPayData()
+        {
+
+        }
+
+        //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
+        private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>();
+
+        /**
+        * 设置某个字段的值
+        * @param key 字段名
+         * @param value 字段值
+        */
+        public void SetValue(string key, object value)
+        {
+            m_values[key] = value;
+        }
+
+        /**
+        * 根据字段名获取某个字段的值
+        * @param key 字段名
+         * @return key对应的字段值
+        */
+        public object GetValue(string key)
+        {
+            object o = null;
+            m_values.TryGetValue(key, out o);
+            return o;
+        }
+
+        /**
+         * 判断某个字段是否已设置
+         * @param key 字段名
+         * @return 若字段key已被设置,则返回true,否则返回false
+         */
+        public bool IsSet(string key)
+        {
+            object o = null;
+            m_values.TryGetValue(key, out o);
+            if (null != o)
+                return true;
+            else
+                return false;
+        }
+
+        /**
+        * @将Dictionary转成xml
+        * @return 经转换得到的xml串
+        * @throws WxPayException
+        **/
+        public string ToXml()
+        {
+            //数据为空时不能转化为xml格式
+            if (0 == m_values.Count)
+            {
+                //Log.Error(this.GetType().ToString(), "WxPayData数据为空!");
+                throw new WxPayException("WxPayData数据为空!");
+            }
+
+            string xml = "<xml>";
+            foreach (KeyValuePair<string, object> pair in m_values)
+            {
+                //字段值不能为null,会影响后续流程
+                if (pair.Value == null)
+                {
+                    //Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!");
+                    throw new WxPayException("WxPayData内部含有值为null的字段!");
+                }
+
+                if (pair.Value.GetType() == typeof(int))
+                {
+                    xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
+                }
+                else if (pair.Value.GetType() == typeof(string))
+                {
+                    xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">";
+                }
+                else//除了string和int类型不能含有其他数据类型
+                {
+                    //Log.Error(this.GetType().ToString(), "WxPayData字段数据类型错误!");
+                    throw new WxPayException("WxPayData字段数据类型错误!");
+                }
+            }
+            xml += "</xml>";
+            return xml;
+        }
+
+        /**
+        * @将xml转为WxPayData对象并返回对象内部的数据
+        * @param string 待转换的xml串
+        * @return 经转换得到的Dictionary
+        * @throws WxPayException
+        */
+        public SortedDictionary<string, object> FromXml(string xml)
+        {
+            if (string.IsNullOrEmpty(xml))
+            {
+                //Log.Error(this.GetType().ToString(), "将空的xml串转换为WxPayData不合法!");
+                throw new WxPayException("将空的xml串转换为WxPayData不合法!");
+            }
+
+            XmlDocument xmlDoc = new XmlDocument();
+            xmlDoc.LoadXml(xml);
+            XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
+            XmlNodeList nodes = xmlNode.ChildNodes;
+            foreach (XmlNode xn in nodes)
+            {
+                XmlElement xe = (XmlElement)xn;
+                m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
+            }
+
+            try
+            {
+                //2015-06-29 错误是没有签名
+                if (m_values["return_code"] != "SUCCESS")
+                {
+                    return m_values;
+                }
+                CheckSign();//验证签名,不通过会抛异常
+            }
+            catch (WxPayException ex)
+            {
+                throw new WxPayException(ex.Message);
+            }
+
+            return m_values;
+        }
+
+        /**
+        * @Dictionary格式转化成url参数格式
+        * @ return url格式串, 该串不包含sign字段值
+        */
+        public string ToUrl()
+        {
+            string buff = "";
+            foreach (KeyValuePair<string, object> pair in m_values)
+            {
+                if (pair.Value == null)
+                {
+                    //Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!");
+                    throw new WxPayException("WxPayData内部含有值为null的字段!");
+                }
+
+                if (pair.Key != "sign" && pair.Value.ToString() != "")
+                {
+                    buff += pair.Key + "=" + pair.Value + "&";
+                }
+            }
+            buff = buff.Trim('&');
+            return buff;
+        }
+
+
+        /**
+        * @Dictionary格式化成Json
+         * @return json串数据
+        */
+        public string ToJson()
+        {
+            string jsonStr = JsonConvert.SerializeObject(m_values);
+            return jsonStr;
+        }
+
+        /**
+        * @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
+        */
+        public string ToPrintStr()
+        {
+            string str = "";
+            foreach (KeyValuePair<string, object> pair in m_values)
+            {
+                if (pair.Value == null)
+                {
+                    //Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!");
+                    throw new WxPayException("WxPayData内部含有值为null的字段!");
+                }
+
+                str += string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString());
+            }
+            //Log.Debug(this.GetType().ToString(), "Print in Web Page : " + str);
+            return str;
+        }
+
+        /**
+        * @生成签名,详见签名生成算法
+        * @return 签名, sign字段不参加签名
+        */
+        public string MakeSign()
+        {
+            //转url格式
+            string str = ToUrl();
+            //在string后加入API KEY
+            str += "&key=" + WxPayConfig.KEY;
+            //Log.Info("thiskey", str);
+            //MD5加密
+            var md5 = MD5.Create();
+            var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
+            var sb = new StringBuilder();
+            foreach (byte b in bs)
+            {
+                sb.Append(b.ToString("x2"));
+            }
+            //所有字符转为大写
+            return sb.ToString().ToUpper();
+        }
+
+        /**
+        * 
+        * 检测签名是否正确
+        * 正确返回true,错误抛异常
+        */
+        public bool CheckSign()
+        {
+            //如果没有设置签名,则跳过检测
+            if (!IsSet("sign"))
+            {
+                //Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!");
+                throw new WxPayException("WxPayData签名存在但不合法!");
+            }
+            //如果设置了签名但是签名为空,则抛异常
+            else if (GetValue("sign") == null || GetValue("sign").ToString() == "")
+            {
+                //Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!");
+                throw new WxPayException("WxPayData签名存在但不合法!");
+            }
+
+            //获取接收到的签名
+            string return_sign = GetValue("sign").ToString();
+
+            //在本地计算新的签名
+            string cal_sign = MakeSign();
+            //Log.Info("sign:", return_sign);
+            //Log.Info("cal_sign:", cal_sign);
+            if (cal_sign == return_sign)
+            {
+                return true;
+            }
+
+            //Log.Error(this.GetType().ToString(), "WxPayData签名验证错误!");
+            throw new WxPayException("WxPayData签名验证错误!");
+        }
+
+        /**
+        * @获取Dictionary
+        */
+        public SortedDictionary<string, object> GetValues()
+        {
+            return m_values;
+        }
+    }
+}

+ 14 - 0
PMS.NetCore/WxPayAPI/WxPayException.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WxPayAPI
+{
+    public class WxPayException : Exception
+    {
+        public WxPayException(string msg) : base(msg)
+        {
+
+        }
+    }
+}

+ 32 - 0
PMS.NetCore/需求/Order.sql

@@ -0,0 +1,32 @@
+CREATE TABLE [dbo].[Order]
+(
+[OrderNo] [varchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL CONSTRAINT [DF_Order_OrderNo] DEFAULT (''),
+[Farerowid] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL CONSTRAINT [DF_Order_farwarid] DEFAULT (''),
+[Revfarecode] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL CONSTRAINT [DF_Order_revfarecode] DEFAULT (''),
+[Money] [decimal] (18, 2) NULL CONSTRAINT [DF_Order_Money] DEFAULT ((0)),
+[Ownername] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL CONSTRAINT [DF_Order_Ownername] DEFAULT (''),
+[Hname] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL CONSTRAINT [DF_Order_Hname] DEFAULT (''),
+[Status] [int] NULL CONSTRAINT [DF_Order_Status] DEFAULT ((0)),
+[AddTime] [datetime] NULL CONSTRAINT [DF_Order_AddTime] DEFAULT (getdate())
+) ON [PRIMARY]
+GO
+ALTER TABLE [dbo].[Order] ADD CONSTRAINT [PK_Order] PRIMARY KEY CLUSTERED ([OrderNo]) ON [PRIMARY]
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'订单表', 'SCHEMA', N'dbo', 'TABLE', N'Order', NULL, NULL
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'订单号', 'SCHEMA', N'dbo', 'TABLE', N'Order', 'COLUMN', N'OrderNo'
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'缴费表fdc_pm_fare_b主键', 'SCHEMA', N'dbo', 'TABLE', N'Order', 'COLUMN', N'Farerowid'
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'已缴费表fdc_pm_revfare主键', 'SCHEMA', N'dbo', 'TABLE', N'Order', 'COLUMN', N'Revfarecode'
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'应缴费用', 'SCHEMA', N'dbo', 'TABLE', N'Order', 'COLUMN', N'Money'
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'业主名称', 'SCHEMA', N'dbo', 'TABLE', N'Order', 'COLUMN', N'Ownername'
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'房屋名称', 'SCHEMA', N'dbo', 'TABLE', N'Order', 'COLUMN', N'Hname'
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'状态  0:未支付  1:已支付  2:支付失败', 'SCHEMA', N'dbo', 'TABLE', N'Order', 'COLUMN', N'Status'
+GO
+EXEC sp_addextendedproperty N'MS_Description', N'添加时间', 'SCHEMA', N'dbo', 'TABLE', N'Order', 'COLUMN', N'AddTime'
+GO