From 208c93bc8f5136791653ddd7c18f11570c96ca30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=A9=99=E5=AD=90?= <454313500@qq.com>
Date: Sat, 14 May 2022 17:45:17 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BE=AE=E4=BF=A1=E6=94=AF?=
=?UTF-8?q?=E4=BB=98=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Core/Config/IPayConfig.cs | 66 ++++
.../Core/Config/PayConfig.cs | 108 ++++++
.../Core/Helper/PayApi.cs | 138 ++++++++
.../Core/Helper/PayData.cs | 322 ++++++++++++++++++
.../Core/Helper/PayHelper.cs | 178 ++++++++++
.../Core/Helper/PayHttpService.cs | 196 +++++++++++
.../Core/IPayInvoker.cs | 29 ++
.../Core/Notify/AbstractNotify.cs | 72 ++++
.../Core/Notify/ResultNotify.cs | 101 ++++++
.../Yi.Framework.WeChatPay/Core/PayInvoker.cs | 55 +++
.../Exceptions/PayException.cs | 25 ++
.../Options/PayOptions.cs | 14 +
.../PayServiceExtensions.cs | 51 +++
.../Yi.Framework.WeChatPay.csproj | 18 +
Yi.Framework.Net6/Yi.Framework.sln | 11 +-
15 files changed, 1382 insertions(+), 2 deletions(-)
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Config/IPayConfig.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Config/PayConfig.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayApi.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayData.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayHelper.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayHttpService.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/IPayInvoker.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Notify/AbstractNotify.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Notify/ResultNotify.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/PayInvoker.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Exceptions/PayException.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Options/PayOptions.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/PayServiceExtensions.cs
create mode 100644 Yi.Framework.Net6/Yi.Framework.WeChatPay/Yi.Framework.WeChatPay.csproj
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Config/IPayConfig.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Config/IPayConfig.cs
new file mode 100644
index 00000000..7e9eb90d
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Config/IPayConfig.cs
@@ -0,0 +1,66 @@
+using System;
+namespace Yi.Framework.WeChatPay.Core
+{
+ ///
+ /// IConfig
+ ///
+ public interface IPayConfig
+ {
+
+ //=======【基本信息设置】=====================================
+ /* 微信公众号信息配置
+ * APPID:绑定支付的APPID(必须配置)
+ * MCHID:商户号(必须配置)
+ * KEY:商户支付密钥,参考开户邮件设置(必须配置),请妥善保管,避免密钥泄露
+ * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置),请妥善保管,避免密钥泄露
+ */
+
+ string GetAppID();
+ string GetMchID();
+ string GetKey();
+ string GetAppSecret();
+
+
+
+ //=======【证书路径设置】=====================================
+ /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要)
+ * 1.证书文件不能放在web服务器虚拟目录,应放在有访问权限控制的目录中,防止被他人下载;
+ * 2.建议将证书文件名改为复杂且不容易猜测的文件
+ * 3.商户服务器要做好病毒和木马防护工作,不被非法侵入者窃取证书文件。
+ */
+ string GetSSlCertPath();
+ string GetSSlCertPassword();
+
+
+
+ //=======【支付结果通知url】=====================================
+ /* 支付结果通知回调url,用于商户接收支付结果
+ */
+ string GetNotifyUrl();
+
+ //=======【商户系统后台机器IP】=====================================
+ /* 此参数可手动配置也可在程序中自动获取
+ */
+ string GetIp();
+
+
+ //=======【代理服务器设置】===================================
+ /* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)
+ */
+ string GetProxyUrl();
+
+
+ //=======【上报信息配置】===================================
+ /* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报
+ */
+ int GetReportLevel();
+
+
+ //=======【日志级别】===================================
+ /* 日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息
+ */
+ int GetLogLevel();
+
+
+ }
+}
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Config/PayConfig.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Config/PayConfig.cs
new file mode 100644
index 00000000..73600b69
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Config/PayConfig.cs
@@ -0,0 +1,108 @@
+using Newtonsoft.Json;
+using System;
+using System.IO;
+using Microsoft.Extensions.Configuration;
+using Yi.Framework.WeChatPay.Options;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ public class PayConfig : IPayConfig
+ {
+ public static PayConfig Current = null;
+
+ public PayConfig(PayOptions payOptions)
+ {
+ Current = this;
+ _BasicConfig = payOptions;
+ }
+
+ private static PayOptions _BasicConfig = null;
+
+
+ //=======【基本信息设置】=====================================
+ /* 微信公众号信息配置
+ * APPID:绑定支付的APPID(必须配置)
+ * MCHID:商户号(必须配置)
+ * KEY:商户支付密钥,参考开户邮件设置(必须配置),请妥善保管,避免密钥泄露
+ * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置),请妥善保管,避免密钥泄露
+ */
+
+ public string GetAppID()
+ {
+ return _BasicConfig.AppID;
+ }
+ public string GetMchID()
+ {
+ return _BasicConfig.MchID;
+ }
+ public string GetKey()
+ {
+ return _BasicConfig.Key;
+ }
+ public string GetAppSecret()
+ {
+ return "";
+ }
+
+
+
+ //=======【证书路径设置】=====================================
+ /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要)
+ * 1.证书文件不能放在web服务器虚拟目录,应放在有访问权限控制的目录中,防止被他人下载;
+ * 2.建议将证书文件名改为复杂且不容易猜测的文件
+ * 3.商户服务器要做好病毒和木马防护工作,不被非法侵入者窃取证书文件。
+ */
+ public string GetSSlCertPath()
+ {
+ return "";
+ }
+ public string GetSSlCertPassword()
+ {
+ return "";
+ }
+
+
+
+ //=======【支付结果通知url】=====================================
+ /* 支付结果通知回调url,用于商户接收支付结果
+ */
+ public string GetNotifyUrl()
+ {
+ return _BasicConfig.NotifyUrl;
+ }
+
+ //=======【商户系统后台机器IP】=====================================
+ /* 此参数可手动配置也可在程序中自动获取
+ */
+ public string GetIp()
+ {
+ return "0.0.0.0";
+ }
+
+ //=======【代理服务器设置】===================================
+ /* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)
+ */
+ public string GetProxyUrl()
+ {
+ return "";
+ }
+
+
+ //=======【上报信息配置】===================================
+ /* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报
+ */
+ public int GetReportLevel()
+ {
+ return 1;
+ }
+
+
+ //=======【日志级别】===================================
+ /* 日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息
+ */
+ public int GetLogLevel()
+ {
+ return 1;
+ }
+ }
+}
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayApi.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayApi.cs
new file mode 100644
index 00000000..db6e41f3
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayApi.cs
@@ -0,0 +1,138 @@
+using FizzWare.NBuilder;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Yi.Framework.WeChatPay.Exceptions;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ public class PayApi
+ {
+ private readonly IPayConfig _IPayConfig = null;
+ private readonly PayHttpService _PayHttpService = null;
+ public PayApi(IPayConfig payConfig, PayHttpService payHttpService)
+ {
+ this._IPayConfig = payConfig;
+ this._PayHttpService = payHttpService;
+ }
+
+ /**
+ *
+ * 统一下单
+ * @param WxPaydata inputObj 提交给统一下单API的参数
+ * @param int timeOut 超时时间
+ * @throws WxPayException
+ * @return 成功时返回,其他抛异常
+ */
+ public PayData UnifiedOrder(PayData inputObj, int timeOut = 6)
+ {
+ string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
+ //检测必填参数
+ if (!inputObj.IsSet("out_trade_no"))
+ {
+ throw new PayException("缺少统一支付接口必填参数out_trade_no!");
+ }
+ else if (!inputObj.IsSet("body"))
+ {
+ throw new PayException("缺少统一支付接口必填参数body!");
+ }
+ else if (!inputObj.IsSet("total_fee"))
+ {
+ throw new PayException("缺少统一支付接口必填参数total_fee!");
+ }
+ else if (!inputObj.IsSet("trade_type"))
+ {
+ throw new PayException("缺少统一支付接口必填参数trade_type!");
+ }
+
+ //关联参数
+ if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
+ {
+ throw new PayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
+ }
+ if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
+ {
+ throw new PayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
+ }
+
+ //异步通知url未设置,则使用配置文件中的url
+ /*if (!inputObj.IsSet("notify_url"))
+ {
+ inputObj.SetValue("notify_url", this._IWxPayConfig().GetNotifyUrl());//异步通知url
+ }*/
+
+ inputObj.SetValue("appid", this._IPayConfig.GetAppID());//公众账号ID
+ inputObj.SetValue("mch_id", this._IPayConfig.GetMchID());//商户号
+ inputObj.SetValue("spbill_create_ip", this._IPayConfig.GetIp());//终端ip
+ inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
+ inputObj.SetValue("sign_type", PayData.SIGN_TYPE_HMAC_SHA256);//签名类型
+
+ //签名
+ inputObj.SetValue("sign", inputObj.MakeSign());
+ string xml = inputObj.ToXml();
+ // 发起http请求
+ string response = this._PayHttpService.Post(xml, url, false, timeOut);
+ PayData result = new PayData();
+ result.FromXml(response);
+
+ return result;
+ }
+
+ /**
+ *
+ * 查询订单
+ * @param WxPayData inputObj 提交给查询订单API的参数
+ * @param int timeOut 超时时间
+ * @throws WxPayException
+ * @return 成功时返回订单查询结果,其他抛异常
+ */
+ public PayData OrderQuery(PayData inputObj, HttpContext httpContext, 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 PayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
+ }
+
+ inputObj.SetValue("appid", this._IPayConfig.GetAppID());//公众账号ID
+ inputObj.SetValue("mch_id", this._IPayConfig.GetMchID());//商户号
+ inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
+ inputObj.SetValue("sign_type", PayData.SIGN_TYPE_HMAC_SHA256);//签名类型
+ inputObj.SetValue("sign", inputObj.MakeSign());//签名
+ string xml = inputObj.ToXml();
+ //Log.Debug("WxPayApi", "OrderQuery request : " + xml);
+ string response = this._PayHttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口提交数据
+ //Log.Debug("WxPayApi", "OrderQuery response : " + response);
+
+ //将xml格式的数据转化为对象以返回
+ PayData result = new PayData();
+ result.FromXml(response);
+
+ return result;
+ }
+
+ /**
+ * 生成时间戳,标准北京时间,时区为东八区,自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()
+ {
+ RandomGenerator randomGenerator = new RandomGenerator();
+ return randomGenerator.Int().ToString();
+ }
+ }
+}
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayData.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayData.cs
new file mode 100644
index 00000000..61dff76a
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayData.cs
@@ -0,0 +1,322 @@
+using System;
+using System.Collections.Generic;
+using System.Web;
+using System.Xml;
+using System.Security.Cryptography;
+using System.Text;
+using System.Linq;
+using Newtonsoft.Json;
+using Yi.Framework.WeChatPay.Core;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ ///
+ /// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
+ /// 在调用接口之前先填充各个字段的值,然后进行接口通信,
+ /// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
+ /// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
+ ///
+ public class PayData
+ {
+ public const string SIGN_TYPE_MD5 = "MD5";
+ public const string SIGN_TYPE_HMAC_SHA256 = "HMAC-SHA256";
+
+
+ //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
+ private SortedDictionary m_values = new SortedDictionary();
+
+ /**
+ * 设置某个字段的值
+ * @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 && !o.Equals(""))
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * @将Dictionary转成xml
+ * @return 经转换得到的xml串
+ * @throws WxPayException
+ **/
+ public string ToXml()
+ {
+ //数据为空时不能转化为xml格式
+ if (0 == m_values.Count)
+ {
+ throw new Exception("WxPayData数据为空!");
+ }
+
+ string xml = "";
+ foreach (KeyValuePair pair in m_values)
+ {
+ //字段值不能为null,会影响后续流程
+ if (pair.Value == null)
+ {
+ throw new Exception("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 + ">" + "" + pair.Key + ">";
+ }
+ else//除了string和int类型不能含有其他数据类型
+ {
+ throw new Exception("WxPayData字段数据类型错误!");
+ }
+ }
+ xml += "";
+ return xml;
+ }
+
+ /**
+ * @将xml转为WxPayData对象并返回对象内部的数据
+ * @param string 待转换的xml串
+ * @return 经转换得到的Dictionary
+ * @throws WxPayException
+ */
+ public SortedDictionary FromXml(string xml)
+ {
+ if (string.IsNullOrEmpty(xml))
+ {
+ throw new Exception("将空的xml串转换为WxPayData不合法!");
+ }
+
+
+ SafeXmlDocument xmlDoc = new SafeXmlDocument();
+ xmlDoc.LoadXml(xml);
+ XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点
+ 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"].ToString() != "SUCCESS")
+ {
+ return m_values;
+ }
+ //验证签名,不通过会抛异常
+ CheckSign();
+ }
+ catch (Exception ex)
+ {
+ throw new Exception(ex.Message);
+ }
+
+ return m_values;
+ }
+
+ /**
+ * @Dictionary格式转化成url参数格式
+ * @ return url格式串, 该串不包含sign字段值
+ */
+ public string ToUrl()
+ {
+ string buff = "";
+ foreach (KeyValuePair pair in m_values)
+ {
+ if (pair.Value == null)
+ {
+ throw new Exception("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 pair in m_values)
+ {
+ if (pair.Value == null)
+ {
+ throw new Exception("WxPayData内部含有值为null的字段!");
+ }
+
+
+ str += string.Format("{0}={1}\n", pair.Key, pair.Value.ToString());
+ }
+ str = HttpUtility.HtmlEncode(str);
+ return str;
+ }
+
+
+ /**
+ * @生成签名,详见签名生成算法
+ * @return 签名, sign字段不参加签名
+ */
+ public string MakeSign(string signType)
+ {
+ //转url格式
+ string str = ToUrl();
+ //在string后加入API KEY
+ string apiKey = PayConfig.Current.GetKey();
+ str += "&key=" + apiKey;
+ if (signType == SIGN_TYPE_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();
+ }
+ else if (signType == SIGN_TYPE_HMAC_SHA256)
+ {
+ return CalcHMACSHA256Hash(str, apiKey);
+ }
+ else
+ {
+ throw new Exception("sign_type 不合法");
+ }
+ }
+
+ /**
+ * @生成签名,详见签名生成算法
+ * @return 签名, sign字段不参加签名 SHA256
+ */
+ public string MakeSign()
+ {
+ return MakeSign(SIGN_TYPE_HMAC_SHA256);
+ }
+
+
+
+ /**
+ *
+ * 检测签名是否正确
+ * 正确返回true,错误抛异常
+ */
+ public bool CheckSign(string signType)
+ {
+ //如果没有设置签名,则跳过检测
+ if (!IsSet("sign"))
+ {
+ throw new Exception("WxPayData签名存在但不合法!");
+ }
+ //如果设置了签名但是签名为空,则抛异常
+ else if (GetValue("sign") == null || GetValue("sign").ToString() == "")
+ {
+ throw new Exception("WxPayData签名存在但不合法!");
+ }
+
+ //获取接收到的签名
+ string return_sign = GetValue("sign").ToString();
+
+ //在本地计算新的签名
+ string cal_sign = MakeSign(signType);
+
+ if (cal_sign == return_sign)
+ {
+ return true;
+ }
+
+ throw new Exception("WxPayData签名验证错误!");
+ }
+
+
+
+ /**
+ *
+ * 检测签名是否正确
+ * 正确返回true,错误抛异常
+ */
+ public bool CheckSign()
+ {
+ return CheckSign(SIGN_TYPE_HMAC_SHA256);
+ }
+
+ /**
+ * @获取Dictionary
+ */
+ public SortedDictionary GetValues()
+ {
+ return m_values;
+ }
+
+
+ private string CalcHMACSHA256Hash(string plaintext, string salt)
+ {
+ string result = "";
+ var enc = Encoding.Default;
+ byte[]
+ baText2BeHashed = enc.GetBytes(plaintext),
+ baSalt = enc.GetBytes(salt);
+ HMACSHA256 hasher = new HMACSHA256(baSalt);
+ byte[] baHashedText = hasher.ComputeHash(baText2BeHashed);
+ result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x2")).ToArray());
+ return result.ToUpper();
+ }
+
+
+ private class SafeXmlDocument : XmlDocument
+ {
+ public SafeXmlDocument()
+ {
+ this.XmlResolver = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayHelper.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayHelper.cs
new file mode 100644
index 00000000..6eae84c2
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayHelper.cs
@@ -0,0 +1,178 @@
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using Yi.Framework.WeChatPay.Options;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ public class PayHelper
+ {
+ public static readonly string KEY_PAY_PREFIX = "order:pay:url:";
+ private readonly IPayConfig _IPayConfig = null;
+ private readonly PayApi _PayApi = null;
+
+ private HttpContext _httpContext;
+ public PayHelper(IPayConfig PayConfig, PayApi PayApi, IHttpContextAccessor httpContextAccessor)
+ {
+ this._IPayConfig = PayConfig;
+ this._PayApi = PayApi;
+ this._httpContext = httpContextAccessor.HttpContext;
+ }
+
+ ///
+ /// 创建支付连接
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public string CreatePayUrl(long orderId, string description, long totalPay)
+ {
+ // 定义返回的支付连接
+ string url;
+ try
+ {
+ // 构建支付需要的参数对象
+ PayData data = new PayData();
+ //描述
+ data.SetValue("body", description);
+ //订单号
+ data.SetValue("out_trade_no", orderId.ToString());
+ data.SetValue("product_id", orderId.ToString());
+ //货币(默认就是人民币)
+ data.SetValue("fee_type", "CNY");
+ //TODO 总金额 (模拟1分钱, 线上环境换成真实价格)
+ data.SetValue("total_fee", /*totalPay.ToString()*/ 1); // 单位是分
+ //调用微信支付的终端ip
+ data.SetValue("spbill_create_ip", "0.0.0.0");
+ //回调地址
+ data.SetValue("notify_url", this._IPayConfig.GetNotifyUrl());
+ //交易类型为扫码支付
+ data.SetValue("trade_type", "NATIVE");
+
+ PayData result = this._PayApi.UnifiedOrder(data);//调用统一下单接口
+ url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
+ }
+ catch (Exception e)
+ {
+ throw new Exception("生成支付链接连接失败", e);
+ }
+
+ return url;
+ }
+
+
+ ///
+ /// (调用微信API)根据订单ID查询订单信息,全部信息
+ ///
+ ///
+ ///
+ public PayData QueryOrderById(long orderId)
+ {
+ PayData req = new PayData();
+ req.SetValue("out_trade_no", orderId.ToString());
+ PayData res = this._PayApi.OrderQuery(req, _httpContext);
+ return res;// 返回查询数据
+ }
+
+
+
+
+ public static PayOptions GetPayOptions(string path)
+ {
+ string config = File.ReadAllText(path);
+ var option = JsonConvert.DeserializeObject(config);
+ Console.WriteLine($"configPath={path} AppID={option.AppID}");
+ return option;
+ }
+
+ ///
+ /// 生成二维码方法
+ ///
+ /// 输入的字符串
+ /// 二维码宽度
+ /// 二维码高度
+ ///
+ public static string QRcode(string text, int width = 360, int height = 360)
+ {
+
+ //这里要感谢一下http://old.wwei.cn/
+ Dictionary dic = new()
+ {
+ { "qrid", "0" },
+ { "data[type]", "index" },
+ { "data[text]", text },
+ { "moban_id", "0" },
+ { "size", "300" },
+ { "level", "M" },
+ { "moban_type", "qrcpu" },
+ { "style_setting[protype]", "1" },
+ { "style_setting[ptcolor]", "#000000" },
+ { "style_setting[inptcolor]", "#000000" },
+ { "style_setting[fcolor]", "#000000" },
+ { "style_setting[bcolor]", "#ffffff" },
+ { "style_setting[mbtype_hb]", "0" },
+ { "style_setting[logo_id]", "" },
+ { "style_setting[logo_width]", "46" },
+ { "style_setting[logo_height]", "46" },
+ { "style_setting[logo_border]", "0" }
+
+ };
+
+
+ StringBuilder builder = new StringBuilder();
+ int i = 0;
+ if (dic.Count > 0)
+ {
+ foreach (var item in dic)
+ {
+ if (i > 0)
+ builder.Append("&");
+ builder.AppendFormat("{0}={1}", item.Key, item.Value);
+ i++;
+ }
+ }
+
+ string postDataStr = builder.ToString();
+
+
+#pragma warning disable SYSLIB0014 // 类型或成员已过时
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://old.wwei.cn/qrcode-wwei_create.html");
+#pragma warning restore SYSLIB0014 // 类型或成员已过时
+
+ request.Method = "POST";
+ request.ContentType = "application/x-www-form-urlencoded";
+ request.ContentLength = Encoding.UTF8.GetByteCount(postDataStr);
+ request.Headers.Add("Host", "old.wwei.cn");
+ request.Headers.Add("User-Agent", "PostmanRuntime/6.66.6");
+ request.Headers.Add("Origin", "http://old.wwei.cn");
+ request.Headers.Add("Referer", "http://old.wwei.cn/");
+
+ Stream myRequestStream = request.GetRequestStream();
+ StreamWriter myStreamWriter = new StreamWriter(myRequestStream, Encoding.GetEncoding("utf-8"));
+ myStreamWriter.Write(postDataStr);
+ myStreamWriter.Close();
+
+ HttpWebResponse response = (HttpWebResponse)request.GetResponse();
+
+ Stream myResponseStream = response.GetResponseStream();
+ StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
+ string retString = myStreamReader.ReadToEnd();
+ myStreamReader.Close();
+ myResponseStream.Close();
+
+ var json = Newtonsoft.Json.JsonConvert.DeserializeObject(retString);
+ var data = json["data"].ToString();
+
+ return data;
+
+ }
+ }
+}
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayHttpService.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayHttpService.cs
new file mode 100644
index 00000000..432bfd53
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Helper/PayHttpService.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Collections.Generic;
+using System.Web;
+using System.Net;
+using System.IO;
+using System.Text;
+using System.Net.Security;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.AspNetCore.Http;
+using Yi.Framework.WeChatPay.Exceptions;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ ///
+ /// http连接基础类,负责底层的http通信
+ ///
+ public class PayHttpService
+ {
+ private readonly IPayConfig _IPayConfig = null;
+ private readonly HttpContext _httpContext = null;
+ public PayHttpService(IPayConfig PayConfig, IHttpContextAccessor httpContextAccessor)
+ {
+ this._IPayConfig = PayConfig;
+ this._httpContext = httpContextAccessor.HttpContext;
+ }
+
+ public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
+ {
+ //直接确认,否则打不开
+ return true;
+ }
+
+ public 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的相关属性
+ * ************************************************************/
+#pragma warning disable SYSLIB0014 // 类型或成员已过时
+ request = (HttpWebRequest)WebRequest.Create(url);
+#pragma warning restore SYSLIB0014 // 类型或成员已过时
+ request.UserAgent = string.Format("WXPaySDK/{3} ({0}) .net/{1} {2}", Environment.OSVersion, Environment.Version, this._IPayConfig.GetMchID(), typeof(PayHttpService).Assembly.GetName().Version);
+ request.Method = "POST";
+ request.Timeout = timeout * 1000;
+
+ //设置POST的数据类型和长度
+ request.ContentType = "text/xml";
+ byte[] data = Encoding.UTF8.GetBytes(xml);
+ request.ContentLength = data.Length;
+
+
+ if (isUseCert)//是否使用证书--没用证书
+ {
+ string path = _httpContext.Request.Path;
+ X509Certificate2 cert = new X509Certificate2(path + this._IPayConfig.GetSSlCertPath(), this._IPayConfig.GetSSlCertPassword());
+ request.ClientCertificates.Add(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)
+ {
+#pragma warning disable SYSLIB0006 // 类型或成员已过时
+ Thread.ResetAbort();
+#pragma warning restore SYSLIB0006 // 类型或成员已过时
+ }
+ catch (WebException e)
+ {
+ if (e.Status == WebExceptionStatus.ProtocolError)
+ {
+ throw new PayException(e.ToString());
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new PayException(e.ToString());
+ }
+ finally
+ {
+ //关闭连接和流
+ if (response != null)
+ {
+ response.Close();
+ }
+ if (request != null)
+ {
+ request.Abort();
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// 处理http GET请求,返回数据
+ ///
+ /// 请求的url地址
+ /// http GET成功后返回的数据,失败抛WebException异常
+ public 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的相关属性
+ * ************************************************************/
+#pragma warning disable SYSLIB0014 // 类型或成员已过时
+ request = (HttpWebRequest)WebRequest.Create(url);
+#pragma warning restore SYSLIB0014 // 类型或成员已过时
+ request.UserAgent = string.Format("WXPaySDK/{3} ({0}) .net/{1} {2}", Environment.OSVersion, Environment.Version, this._IPayConfig.GetMchID(), typeof(PayHttpService).Assembly.GetName().Version);
+ request.Method = "GET";
+
+ //获取服务器返回
+ response = (HttpWebResponse)request.GetResponse();
+
+ //获取HTTP返回数据
+ StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
+ result = sr.ReadToEnd().Trim();
+ sr.Close();
+ }
+ catch (System.Threading.ThreadAbortException)
+ {
+#pragma warning disable SYSLIB0006 // 类型或成员已过时
+ Thread.ResetAbort();
+#pragma warning restore SYSLIB0006 // 类型或成员已过时
+ }
+ catch (WebException e)
+ {
+ throw new PayException(e.ToString());
+ }
+ catch (Exception e)
+ {
+ throw new PayException(e.ToString());
+ }
+ finally
+ {
+ //关闭连接和流
+ if (response != null)
+ {
+ response.Close();
+ }
+ if (request != null)
+ {
+ request.Abort();
+ }
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/IPayInvoker.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/IPayInvoker.cs
new file mode 100644
index 00000000..5d1fbe9b
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/IPayInvoker.cs
@@ -0,0 +1,29 @@
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ public interface IPayInvoker
+ {
+ ///
+ /// 获取WX支付链接的方法
+ /// 然后发布定时同步状态任务
+ ///
+ /// 订单ID
+ /// 用户信息
+ /// 请求上下文
+ /// 返回生成的支持链接
+ string GenerateUrl(long orderId, long totalPay);
+ ///
+ /// 处理微信支付回调
+ ///
+ ///
+ ///
+ PayData HandleNotify();
+
+ }
+}
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Notify/AbstractNotify.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Notify/AbstractNotify.cs
new file mode 100644
index 00000000..0034e46b
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Notify/AbstractNotify.cs
@@ -0,0 +1,72 @@
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Yi.Framework.WeChatPay.Exceptions;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ public abstract class AbstractNotify
+ {
+ protected HttpContext _httpContext;
+
+ public AbstractNotify(IHttpContextAccessor httpContextAccessor)
+ {
+ this._httpContext = httpContextAccessor.HttpContext;
+ }
+
+ ///
+ /// 从请求对象中获取信息
+ ///
+ ///
+ ///
+ ///
+ private async Task GetRawBodyStringAsync(HttpRequest request, Encoding encoding = null)
+ {
+ if (encoding is null)
+ {
+ encoding = Encoding.UTF8;
+ }
+ var stream = new MemoryStream();
+ await request.Body.CopyToAsync(stream);
+ stream.Seek(0, 0);
+ using (var reader = new StreamReader(stream, encoding))
+ {
+ var result = await reader.ReadToEndAsync();
+ return result;
+ }
+ }
+
+ ///
+ /// 接收从微信支付后台发送过来的数据并验证签名
+ ///
+ /// 微信支付后台返回的数据
+ public PayData GetNotifyData()
+ {
+ //接收从微信后台POST过来的数据
+ string content = GetRawBodyStringAsync(_httpContext.Request).Result;
+ Console.WriteLine(this.GetType().ToString(), "Receive data from WeChat : " + content);
+
+ //转换数据格式并验证签名
+ PayData data = new PayData();
+ try
+ {
+ data.FromXml(content);
+ }
+ catch (PayException ex)
+ {
+ throw new Exception("验签失败", ex);
+ }
+
+ Console.WriteLine(this.GetType().ToString(), "Check sign success");
+ return data;
+ }
+
+ //派生类需要重写这个方法,进行不同的回调处理
+ public abstract PayData ProcessNotify();
+
+ }
+}
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Notify/ResultNotify.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Notify/ResultNotify.cs
new file mode 100644
index 00000000..562e04ca
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/Notify/ResultNotify.cs
@@ -0,0 +1,101 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ public class ResultNotify : AbstractNotify
+ {
+ private readonly PayApi _PayApi;
+ private readonly ILogger _logger;
+ public ResultNotify(PayApi PayApi,ILogger logger, IHttpContextAccessor httpContextAccessor) :base(httpContextAccessor)
+ {
+ this._PayApi = PayApi;
+ this._logger = logger;
+ }
+
+ ///
+ /// 处理支付回调
+ ///
+ ///
+ public override PayData ProcessNotify()
+ {
+ PayData notifyData = GetNotifyData();
+ //解析数据
+ string totalFee = notifyData.GetValue("total_fee").ToString(); //订单金额
+ string outTradeNo = notifyData.GetValue("out_trade_no").ToString(); //订单编号
+ string transactionId = notifyData.GetValue("transaction_id").ToString(); //商户订单号
+ string bankType = notifyData.GetValue("bank_type").ToString(); //银行类型
+
+ _logger.LogInformation($"======支付回调参数:{totalFee}=={outTradeNo}=={transactionId}=={bankType}");
+ if (totalFee.Equals("") || outTradeNo.Equals("") || transactionId.Equals("") || bankType.Equals(""))
+ {
+ PayData res = new();
+ res.SetValue("return_code", "FAIL");
+ res.SetValue("return_msg", "支付回调返回数据不正确");
+ _logger.LogInformation("支付错误结果 : " + res.ToXml());
+ return res;
+ }
+
+ //检查支付结果中transaction_id是否存在--流水号
+ if (!notifyData.IsSet("transaction_id"))
+ {
+ //若transaction_id不存在,则立即返回结果给微信支付后台
+ PayData res = new();
+ res.SetValue("return_code", "FAIL");
+ res.SetValue("return_msg", "支付结果中微信订单号不存在");
+ _logger.LogInformation("支付错误结果 : " + res.ToXml());
+
+ return res;
+ }
+
+ //查询订单,判断订单真实性
+ if (!QueryOrder(transactionId))
+ {
+ //若订单查询失败,则立即返回结果给微信支付后台
+ PayData res = new();
+ res.SetValue("return_code", "FAIL");
+ res.SetValue("return_msg", "订单查询失败");
+ _logger.LogInformation("订单查询失败 : " + res.ToXml());
+
+ return res;
+ }
+ //查询订单成功
+ else
+ {
+ // 打印结果 (仅打印)
+ PayData res = new PayData();
+ res.SetValue("return_code", "SUCCESS");
+ res.SetValue("return_msg", "OK");
+ res.SetValue("transaction_id", transactionId);
+ _logger.LogInformation("订单查成功: " + res.ToXml());
+
+ // 设置返回响应结果
+ notifyData.SetValue("return_code", "SUCCESS");
+ notifyData.SetValue("return_msg", "OK");
+
+ return notifyData;
+ }
+ }
+
+ ///
+ /// 根据流水号查询订单信息
+ ///
+ ///
+ ///
+ private bool QueryOrder(string transaction_id)
+ {
+ PayData req = new PayData();
+ req.SetValue("transaction_id", transaction_id);
+ PayData res = this._PayApi.OrderQuery(req, _httpContext);
+ if (res.GetValue("return_code").ToString() == "SUCCESS" &&
+ res.GetValue("result_code").ToString() == "SUCCESS")
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/PayInvoker.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/PayInvoker.cs
new file mode 100644
index 00000000..e1817ef8
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Core/PayInvoker.cs
@@ -0,0 +1,55 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Yi.Framework.WeChatPay.Options;
+
+namespace Yi.Framework.WeChatPay.Core
+{
+ public class PayInvoker:IPayInvoker
+ {
+ private readonly PayHelper _payHelper;
+ private readonly ILogger _logger;
+ private readonly AbstractNotify _notify;
+
+ public PayInvoker(PayHelper payHelper, ILogger logger, AbstractNotify notify)
+ {
+ this._payHelper = payHelper;
+ this._logger = logger;
+ this._notify = notify;
+ }
+
+ ///
+ /// 获取WX支付链接的方法
+ /// 然后发布定时同步状态任务
+ ///
+ /// 订单ID
+ /// 用户信息
+ /// 请求上下文
+ /// 返回生成的支持链接
+ public string GenerateUrl(long orderId, long totalPay)
+ {
+ string payUrl = null;
+ _logger.LogInformation("准备生成支付链接.....");
+ payUrl = _payHelper.CreatePayUrl(orderId, "微信支付链接", totalPay);
+ _logger.LogInformation("生成支付链接为:{payUrl}", payUrl);
+ return payUrl;
+ }
+
+ ///
+ /// 处理微信支付回调
+ ///
+ ///
+ ///
+ public PayData HandleNotify()
+ {
+ PayData wxPayData = this._notify.ProcessNotify();
+
+ return wxPayData;
+ }
+
+ }
+}
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Exceptions/PayException.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Exceptions/PayException.cs
new file mode 100644
index 00000000..2aafe37d
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Exceptions/PayException.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Yi.Framework.WeChatPay.Exceptions
+{
+ [Serializable]
+ public class PayException : Exception
+ {
+ public PayException()
+ {
+ }
+
+ public PayException(string message) : base(message)
+ {
+ }
+
+ public PayException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ protected PayException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Options/PayOptions.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Options/PayOptions.cs
new file mode 100644
index 00000000..861266da
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Options/PayOptions.cs
@@ -0,0 +1,14 @@
+namespace Yi.Framework.WeChatPay.Options
+{
+ public class PayOptions
+ {
+ public string AppID { get; set; }
+ public string MchID { get; set; }
+ public string Key { get; set; }
+ public string NotifyUrl { get; set; }
+
+ public bool IsFileConfig { get; set; } = false;
+
+ public string ConfigPath { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/PayServiceExtensions.cs b/Yi.Framework.Net6/Yi.Framework.WeChatPay/PayServiceExtensions.cs
new file mode 100644
index 00000000..883c4eb9
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/PayServiceExtensions.cs
@@ -0,0 +1,51 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Yi.Framework.WeChatPay.Core;
+using Yi.Framework.WeChatPay.Exceptions;
+using Yi.Framework.WeChatPay.Options;
+
+namespace Yi.Framework.WeChatPay.WebCore
+{
+ public static class PayServiceExtensions
+ {
+ public static IServiceCollection AddWeChatPayService(this IServiceCollection services,Action configure)
+ {
+ var option= new PayOptions();
+ configure(option);
+ if (option.IsFileConfig)
+ {
+ option=PayHelper.GetPayOptions(option.ConfigPath);
+ }
+ if (option.AppID == null)
+ {
+ throw new PayException("AppId为空值");
+ }
+ if (option.MchID == null)
+ {
+ throw new PayException("MchID为空值");
+ }
+ if (option.Key == null)
+ {
+ throw new PayException("Key为空值");
+ }
+ if (option.NotifyUrl == null)
+ {
+ throw new PayException("NotifyUrl为空值");
+ }
+ services.AddSingleton(option);
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddHttpContextAccessor();
+ return services;
+ }
+ }
+}
diff --git a/Yi.Framework.Net6/Yi.Framework.WeChatPay/Yi.Framework.WeChatPay.csproj b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Yi.Framework.WeChatPay.csproj
new file mode 100644
index 00000000..dc80f313
--- /dev/null
+++ b/Yi.Framework.Net6/Yi.Framework.WeChatPay/Yi.Framework.WeChatPay.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0
+ enable
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Yi.Framework.Net6/Yi.Framework.sln b/Yi.Framework.Net6/Yi.Framework.sln
index f169cc5a..47d08aa4 100644
--- a/Yi.Framework.Net6/Yi.Framework.sln
+++ b/Yi.Framework.Net6/Yi.Framework.sln
@@ -41,9 +41,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Job", "Yi.Fram
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SMSProcessor", "Yi.Framework.SMSProcessor\Yi.Framework.SMSProcessor.csproj", "{7C58FB7C-9AB5-47CF-ACEB-B784CF820E7E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.Repository", "Yi.Framework.Repository\Yi.Framework.Repository.csproj", "{DA96B62F-2D4C-4AFB-937C-5AEA1119A129}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Repository", "Yi.Framework.Repository\Yi.Framework.Repository.csproj", "{DA96B62F-2D4C-4AFB-937C-5AEA1119A129}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.Language", "Yi.Framework.Language\Yi.Framework.Language.csproj", "{3047069B-4084-461F-BC9F-023BC60401D1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Language", "Yi.Framework.Language\Yi.Framework.Language.csproj", "{3047069B-4084-461F-BC9F-023BC60401D1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.WeChatPay", "Yi.Framework.WeChatPay\Yi.Framework.WeChatPay.csproj", "{C307189D-C42C-4C09-BB65-5A386C9F182B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -115,6 +117,10 @@ Global
{3047069B-4084-461F-BC9F-023BC60401D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3047069B-4084-461F-BC9F-023BC60401D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3047069B-4084-461F-BC9F-023BC60401D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C307189D-C42C-4C09-BB65-5A386C9F182B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C307189D-C42C-4C09-BB65-5A386C9F182B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C307189D-C42C-4C09-BB65-5A386C9F182B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C307189D-C42C-4C09-BB65-5A386C9F182B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -136,6 +142,7 @@ Global
{7C58FB7C-9AB5-47CF-ACEB-B784CF820E7E} = {D6B44435-EAFA-4D55-90D0-3AF80485FB83}
{DA96B62F-2D4C-4AFB-937C-5AEA1119A129} = {DB2506F5-05FD-4E76-940E-41D7AA148550}
{3047069B-4084-461F-BC9F-023BC60401D1} = {9ABAF6B1-6C02-498A-90A2-ABC1140CF89A}
+ {C307189D-C42C-4C09-BB65-5A386C9F182B} = {9ABAF6B1-6C02-498A-90A2-ABC1140CF89A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1ED77A6E-377F-4EEF-A3D0-D65C94657DAF}