Ver código fonte

Alipay 实现(#26)

Roc 6 anos atrás
pai
commit
b98549920b

+ 45 - 25
samples/WebApplicationSample/Controllers/AlipayController.cs

@@ -1,3 +1,4 @@
+using System.Collections.Generic;
 using System.Text;
 using System.Threading.Tasks;
 using Essensoft.AspNetCore.Payment.Alipay;
@@ -5,6 +6,7 @@ using Essensoft.AspNetCore.Payment.Alipay.Domain;
 using Essensoft.AspNetCore.Payment.Alipay.Notify;
 using Essensoft.AspNetCore.Payment.Alipay.Request;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
 using WebApplicationSample.Models;
 
 namespace WebApplicationSample.Controllers
@@ -13,11 +15,13 @@ namespace WebApplicationSample.Controllers
     {
         private readonly IAlipayClient _client;
         private readonly IAlipayNotifyClient _notifyClient;
+        private readonly IOptions<AlipayOptions> _optionsAccessor;
 
-        public AlipayController(IAlipayClient client, IAlipayNotifyClient notifyClient)
+        public AlipayController(IAlipayClient client, IAlipayNotifyClient notifyClient, IOptions<AlipayOptions> optionsAccessor)
         {
             _client = client;
             _notifyClient = notifyClient;
+            _optionsAccessor = optionsAccessor;
         }
 
         /// <summary>
@@ -58,9 +62,9 @@ namespace WebApplicationSample.Controllers
             req.SetBizModel(model);
             req.SetNotifyUrl(viewModel.NotifyUrl);
 
-            var response = await _client.ExecuteAsync(req);
+            var response = await _client.ExecuteAsync(req, _optionsAccessor.Value);
             ViewData["qrcode"] = response.QrCode;
-            ViewData["response"] = response.Body;
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -93,8 +97,8 @@ namespace WebApplicationSample.Controllers
             var req = new AlipayTradePayRequest();
             req.SetBizModel(model);
 
-            var response = await _client.ExecuteAsync(req);
-            ViewData["response"] = response.Body;
+            var response = await _client.ExecuteAsync(req, _optionsAccessor.Value);
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -127,9 +131,9 @@ namespace WebApplicationSample.Controllers
             req.SetBizModel(model);
             req.SetNotifyUrl(viewModel.NotifyUrl);
 
-            var response = await _client.SdkExecuteAsync(req);
+            var response = await _client.SdkExecuteAsync(req, _optionsAccessor.Value);
             //将response.Body给 ios/android端 由其去调起支付宝APP(https://docs.open.alipay.com/204/105296/ https://docs.open.alipay.com/204/105295/)
-            ViewData["response"] = response.Body;
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -165,8 +169,8 @@ namespace WebApplicationSample.Controllers
             req.SetNotifyUrl(viewModel.NotifyUrl);
             req.SetReturnUrl(viewModel.ReturnUrl);
 
-            var response = await _client.PageExecuteAsync(req);
-            return Content(response.Body, "text/html", Encoding.UTF8);
+            var response = await _client.PageExecuteAsync(req, _optionsAccessor.Value);
+            return Content(response.ResponseBody, "text/html", Encoding.UTF8);
         }
 
         /// <summary>
@@ -199,8 +203,8 @@ namespace WebApplicationSample.Controllers
             req.SetNotifyUrl(viewMode.NotifyUrl);
             req.SetReturnUrl(viewMode.ReturnUrl);
 
-            var response = await _client.PageExecuteAsync(req);
-            return Content(response.Body, "text/html", Encoding.UTF8);
+            var response = await _client.PageExecuteAsync(req, _optionsAccessor.Value);
+            return Content(response.ResponseBody, "text/html", Encoding.UTF8);
         }
 
         /// <summary>
@@ -229,8 +233,8 @@ namespace WebApplicationSample.Controllers
             var req = new AlipayTradeQueryRequest();
             req.SetBizModel(model);
 
-            var response = await _client.ExecuteAsync(req);
-            ViewData["response"] = response.Body;
+            var response = await _client.ExecuteAsync(req, _optionsAccessor.Value);
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -263,8 +267,8 @@ namespace WebApplicationSample.Controllers
             var req = new AlipayTradeRefundRequest();
             req.SetBizModel(model);
 
-            var response = await _client.ExecuteAsync(req);
-            ViewData["response"] = response.Body;
+            var response = await _client.ExecuteAsync(req, _optionsAccessor.Value);
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -295,8 +299,8 @@ namespace WebApplicationSample.Controllers
             var req = new AlipayTradeFastpayRefundQueryRequest();
             req.SetBizModel(model);
 
-            var response = await _client.ExecuteAsync(req);
-            ViewData["response"] = response.Body;
+            var response = await _client.ExecuteAsync(req, _optionsAccessor.Value);
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -327,8 +331,8 @@ namespace WebApplicationSample.Controllers
             };
             var req = new AlipayFundTransToaccountTransferRequest();
             req.SetBizModel(model);
-            var response = await _client.ExecuteAsync(req);
-            ViewData["response"] = response.Body;
+            var response = await _client.ExecuteAsync(req, _optionsAccessor.Value);
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -357,8 +361,8 @@ namespace WebApplicationSample.Controllers
 
             var req = new AlipayFundTransOrderQueryRequest();
             req.SetBizModel(model);
-            var response = await _client.ExecuteAsync(req);
-            ViewData["response"] = response.Body;
+            var response = await _client.ExecuteAsync(req, _optionsAccessor.Value);
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -387,8 +391,8 @@ namespace WebApplicationSample.Controllers
 
             var req = new AlipayDataDataserviceBillDownloadurlQueryRequest();
             req.SetBizModel(model);
-            var response = await _client.ExecuteAsync(req);
-            ViewData["response"] = response.Body;
+            var response = await _client.ExecuteAsync(req, _optionsAccessor.Value);
+            ViewData["response"] = response.ResponseBody;
             return View();
         }
 
@@ -401,7 +405,15 @@ namespace WebApplicationSample.Controllers
         {
             try
             {
-                var notify = await _notifyClient.ExecuteAsync<AlipayTradePagePayReturn>(Request);
+                var parameters = new Dictionary<string, string>();
+                {
+                    foreach (var iter in Request.Query)
+                    {
+                        parameters.Add(iter.Key, iter.Value);
+                    }
+                }
+
+                var notify = await _notifyClient.ExecuteAsync<AlipayTradePagePayReturn>(parameters, _optionsAccessor.Value);
                 ViewData["response"] = "支付成功";
                 return View();
             }
@@ -421,7 +433,15 @@ namespace WebApplicationSample.Controllers
         {
             try
             {
-                var notify = await _notifyClient.ExecuteAsync<AlipayTradeWapPayReturn>(Request);
+                var parameters = new Dictionary<string, string>();
+                {
+                    foreach (var iter in Request.Query)
+                    {
+                        parameters.Add(iter.Key, iter.Value);
+                    }
+                }
+
+                var notify = await _notifyClient.ExecuteAsync<AlipayTradeWapPayReturn>(parameters, _optionsAccessor.Value);
                 ViewData["response"] = "支付成功";
                 return View();
             }

+ 33 - 5
samples/WebApplicationSample/Controllers/NotifyController.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.Threading.Tasks;
 using Essensoft.AspNetCore.Payment.Alipay;
 using Essensoft.AspNetCore.Payment.Alipay.Notify;
@@ -13,6 +14,7 @@ using Essensoft.AspNetCore.Payment.UnionPay.Notify;
 using Essensoft.AspNetCore.Payment.WeChatPay;
 using Essensoft.AspNetCore.Payment.WeChatPay.Notify;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
 
 namespace WebApplicationSample.Controllers
 {
@@ -22,10 +24,12 @@ namespace WebApplicationSample.Controllers
     public class AlipayNotifyController : Controller
     {
         private readonly IAlipayNotifyClient _client;
+        private readonly IOptions<AlipayOptions> _optionsAccessor;
 
-        public AlipayNotifyController(IAlipayNotifyClient client)
+        public AlipayNotifyController(IAlipayNotifyClient client, IOptions<AlipayOptions> optionsAccessor)
         {
             _client = client;
+            _optionsAccessor = optionsAccessor;
         }
 
         /// <summary>
@@ -38,7 +42,13 @@ namespace WebApplicationSample.Controllers
         {
             try
             {
-                var notify = await _client.ExecuteAsync<AlipayTradePrecreateNotify>(Request);
+                var parameters = new Dictionary<string, string>();
+                foreach (var iter in Request.Form)
+                {
+                    parameters.Add(iter.Key, iter.Value);
+                }
+
+                var notify = await _client.ExecuteAsync<AlipayTradePrecreateNotify>(parameters, _optionsAccessor.Value);
                 if ("TRADE_SUCCESS" == notify.TradeStatus)
                 {
                     Console.WriteLine("OutTradeNo: " + notify.OutTradeNo);
@@ -63,7 +73,13 @@ namespace WebApplicationSample.Controllers
         {
             try
             {
-                var notify = await _client.ExecuteAsync<AlipayTradeAppPayNotify>(Request);
+                var parameters = new Dictionary<string, string>();
+                foreach (var iter in Request.Form)
+                {
+                    parameters.Add(iter.Key, iter.Value);
+                }
+
+                var notify = await _client.ExecuteAsync<AlipayTradeAppPayNotify>(parameters, _optionsAccessor.Value);
                 if ("TRADE_SUCCESS" == notify.TradeStatus)
                 {
                     Console.WriteLine("OutTradeNo: " + notify.OutTradeNo);
@@ -88,7 +104,13 @@ namespace WebApplicationSample.Controllers
         {
             try
             {
-                var notify = await _client.ExecuteAsync<AlipayTradePagePayNotify>(Request);
+                var parameters = new Dictionary<string, string>();
+                foreach (var iter in Request.Form)
+                {
+                    parameters.Add(iter.Key, iter.Value);
+                }
+
+                var notify = await _client.ExecuteAsync<AlipayTradePagePayNotify>(parameters, _optionsAccessor.Value);
                 if ("TRADE_SUCCESS" == notify.TradeStatus)
                 {
                     Console.WriteLine("OutTradeNo: " + notify.OutTradeNo);
@@ -113,7 +135,13 @@ namespace WebApplicationSample.Controllers
         {
             try
             {
-                var notify = await _client.ExecuteAsync<AlipayTradeWapPayNotify>(Request);
+                var parameters = new Dictionary<string, string>();
+                foreach (var iter in Request.Form)
+                {
+                    parameters.Add(iter.Key, iter.Value);
+                }
+
+                var notify = await _client.ExecuteAsync<AlipayTradeWapPayNotify>(parameters, _optionsAccessor.Value);
                 if ("TRADE_SUCCESS" == notify.TradeStatus)
                 {
                     Console.WriteLine("OutTradeNo: " + notify.OutTradeNo);

+ 0 - 20
samples/WebApplicationSample/Startup.cs

@@ -35,26 +35,6 @@ namespace WebApplicationSample
                 options.MinimumSameSitePolicy = SameSiteMode.None;
             });
 
-            // 引入HttpClient
-            services.AddHttpClient();
-
-            //引入HttpClient API证书的使用(仅QPay / WeChatPay的部分API使用到)。
-            //services.AddHttpClient("qpayCertificateName").ConfigurePrimaryHttpMessageHandler(() =>
-            //{
-            //    var certificate = new X509Certificate2("", "", X509KeyStorageFlags.MachineKeySet);
-            //    var handler = new HttpClientHandler();
-            //    handler.ClientCertificates.Add(certificate);
-            //    return handler;
-            //});
-
-            //services.AddHttpClient("wechatpayCertificateName").ConfigurePrimaryHttpMessageHandler(() =>
-            //{
-            //    var certificate = new X509Certificate2("", "", X509KeyStorageFlags.MachineKeySet);
-            //    var handler = new HttpClientHandler();
-            //    handler.ClientCertificates.Add(certificate);
-            //    return handler;
-            //});
-
             // 引入Payment 依赖注入
             services.AddAlipay();
             services.AddJDPay();

+ 133 - 134
src/Essensoft.AspNetCore.Payment.Alipay/AlipayClient.cs

@@ -1,14 +1,10 @@
 using System;
 using System.Collections.Generic;
 using System.Net.Http;
-using System.Security.Cryptography;
 using System.Text;
 using System.Threading.Tasks;
 using Essensoft.AspNetCore.Payment.Alipay.Parser;
 using Essensoft.AspNetCore.Payment.Alipay.Utility;
-using Essensoft.AspNetCore.Payment.Security;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
 using Newtonsoft.Json;
 
 namespace Essensoft.AspNetCore.Payment.Alipay
@@ -36,45 +32,51 @@ namespace Essensoft.AspNetCore.Payment.Alipay
         private const string APP_AUTH_TOKEN = "app_auth_token";
         private const string RETURN_URL = "return_url";
 
-        private readonly ILogger _logger;
-        private readonly IHttpClientFactory _clientFactory;
-        private readonly IOptionsSnapshot<AlipayOptions> _optionsSnapshotAccessor;
+        private readonly IHttpClientFactory _httpClientFactory;
 
         #region AlipayClient Constructors
 
-        public AlipayClient(
-            ILogger<AlipayClient> logger,
-            IHttpClientFactory clientFactory,
-            IOptionsSnapshot<AlipayOptions> optionsAccessor)
+        public AlipayClient(IHttpClientFactory httpClientFactory)
         {
-            _logger = logger;
-            _clientFactory = clientFactory;
-            _optionsSnapshotAccessor = optionsAccessor;
+            _httpClientFactory = httpClientFactory;
         }
 
         #endregion
 
-
         #region IAlipayClient Members
 
-        public async Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request) where T : AlipayResponse
+        public async Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options) where T : AlipayResponse
         {
-            return await PageExecuteAsync(request, null, "POST");
+            return await PageExecuteAsync(request, options, null, "POST");
         }
 
-        public async Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, string optionsName) where T : AlipayResponse
+        public async Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options, string accessToken, string reqMethod) where T : AlipayResponse
         {
-            return await PageExecuteAsync(request, null, "POST");
-        }
+            if (options == null)
+            {
+                throw new ArgumentNullException(nameof(options));
+            }
 
-        public async Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, string accessToken, string reqMethod) where T : AlipayResponse
-        {
-            return await PageExecuteAsync(request, null, accessToken, reqMethod);
-        }
+            if (string.IsNullOrEmpty(options.AppId))
+            {
+                throw new ArgumentNullException(nameof(options.AppId));
+            }
+
+            if (string.IsNullOrEmpty(options.SignType))
+            {
+                throw new ArgumentNullException(nameof(options.SignType));
+            }
+
+            if (string.IsNullOrEmpty(options.AppPrivateKey))
+            {
+                throw new ArgumentNullException(nameof(options.AppPrivateKey));
+            }
+
+            if (string.IsNullOrEmpty(options.ServerUrl))
+            {
+                throw new ArgumentNullException(nameof(options.ServerUrl));
+            }
 
-        public async Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, string optionsName, string accessToken, string reqMethod) where T : AlipayResponse
-        {
-            var options = _optionsSnapshotAccessor.Get(optionsName);
             var apiVersion = string.IsNullOrEmpty(request.GetApiVersion()) ? options.Version : request.GetApiVersion();
 
             // 添加协议级请求参数
@@ -100,60 +102,45 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
             // 添加签名参数
             var signContent = AlipaySignature.GetSignContent(txtParams);
-            txtParams.Add(SIGN, AlipaySignature.RSASignContent(signContent, options.PrivateRSAParameters, options.SignType));
+            txtParams.Add(SIGN, AlipaySignature.RSASignContent(signContent, options.AppPrivateKey, options.SignType));
 
-            // 是否需要上传文件
-            var body = string.Empty;
+            string body;
 
-            if (request is IAlipayUploadRequest<T> uRequest)
+            // 是否需要上传文件
+            if (request is IAlipayUploadRequest<T> uploadRequest)
             {
-                var fileParams = AlipayUtility.CleanupDictionary(uRequest.GetFileParameters());
+                var fileParams = AlipayUtility.CleanupDictionary(uploadRequest.GetFileParameters());
 
-                using (var client = _clientFactory.CreateClient())
-                {
-                    body = await client.DoPostAsync(options.ServerUrl, txtParams, fileParams);
-                }
+                var client = _httpClientFactory.CreateClient(nameof(AlipayClient));
+                body = await client.PostAsync(options.ServerUrl, txtParams, fileParams);
             }
             else
             {
-                if (reqMethod.ToUpper() == "GET")
+                if (reqMethod.ToUpperInvariant() == "GET")
                 {
-                    //拼接get请求的url
-                    var tmpUrl = options.ServerUrl;
+                    var url = options.ServerUrl;
                     if (txtParams != null && txtParams.Count > 0)
                     {
-                        if (tmpUrl.Contains("?"))
+                        if (url.Contains("?"))
                         {
-                            tmpUrl = tmpUrl + "&" + AlipayUtility.BuildQuery(txtParams);
+                            url += "&" + AlipayUtility.BuildQuery(txtParams);
                         }
                         else
                         {
-                            tmpUrl = tmpUrl + "?" + AlipayUtility.BuildQuery(txtParams);
+                            url += "?" + AlipayUtility.BuildQuery(txtParams);
                         }
                     }
-                    body = tmpUrl;
-                    _logger.Log(options.LogLevel, "Request Url:{body}", body);
+                    body = url;
                 }
                 else
                 {
-                    //输出post表单
-                    body = BuildHtmlRequest(txtParams, reqMethod, options);
-                    _logger.Log(options.LogLevel, "Request Html:{body}", body);
+                    body = BuildHtmlRequest(txtParams, options.ServerUrl, options.Charset, reqMethod);
                 }
             }
 
-            T rsp = null;
-            IAlipayParser<T> parser = null;
-            if ("xml".Equals(options.Format))
-            {
-                parser = new AlipayXmlParser<T>();
-                rsp = parser.Parse(body);
-            }
-            else
-            {
-                parser = new AlipayJsonParser<T>();
-                rsp = parser.Parse(body);
-            }
+            var parser = new AlipayJsonParser<T>();
+            var rsp = parser.Parse(body);
+
             return rsp;
         }
 
@@ -161,24 +148,48 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
         #region IAlipayClient Members
 
-        public async Task<T> ExecuteAsync<T>(IAlipayRequest<T> request) where T : AlipayResponse
+        public async Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options) where T : AlipayResponse
         {
-            return await ExecuteAsync(request, null);
+            return await ExecuteAsync(request, options, null, null);
         }
 
-        public async Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, string optionsName) where T : AlipayResponse
+        public async Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options, string accessToken) where T : AlipayResponse
         {
-            return await ExecuteAsync(request, optionsName, null, null);
+            return await ExecuteAsync(request, options, accessToken, null);
         }
 
-        public async Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, string optionsName, string accessToken) where T : AlipayResponse
+        public async Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options, string accessToken, string appAuthToken) where T : AlipayResponse
         {
-            return await ExecuteAsync(request, optionsName, accessToken, null);
-        }
+            if (options == null)
+            {
+                throw new ArgumentNullException(nameof(options));
+            }
+
+            if (string.IsNullOrEmpty(options.AppId))
+            {
+                throw new ArgumentNullException(nameof(options.AppId));
+            }
+
+            if (string.IsNullOrEmpty(options.SignType))
+            {
+                throw new ArgumentNullException(nameof(options.SignType));
+            }
+
+            if (string.IsNullOrEmpty(options.AlipayPublicKey))
+            {
+                throw new ArgumentNullException(nameof(options.AlipayPublicKey));
+            }
+
+            if (string.IsNullOrEmpty(options.AppPrivateKey))
+            {
+                throw new ArgumentNullException(nameof(options.AppPrivateKey));
+            }
+
+            if (string.IsNullOrEmpty(options.ServerUrl))
+            {
+                throw new ArgumentNullException(nameof(options.ServerUrl));
+            }
 
-        public async Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, string optionsName, string accessToken, string appAuthToken) where T : AlipayResponse
-        {
-            var options = _optionsSnapshotAccessor.Get(optionsName);
             var apiVersion = string.IsNullOrEmpty(request.GetApiVersion()) ? options.Version : request.GetApiVersion();
 
             // 添加协议级请求参数
@@ -212,7 +223,6 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
             if (request.GetNeedEncrypt())
             {
-
                 if (string.IsNullOrEmpty(txtParams[BIZ_CONTENT]))
                 {
                     throw new AlipayException("api request Fail ! The reason: encrypt request is not supported!");
@@ -226,10 +236,9 @@ namespace Essensoft.AspNetCore.Payment.Alipay
                 if (!"AES".Equals(options.EncyptType))
                 {
                     throw new AlipayException("api only support Aes!");
-
                 }
 
-                var encryptContent = AES.Encrypt(txtParams[BIZ_CONTENT], options.EncyptKey, AlipaySignature.AES_IV, CipherMode.CBC, PaddingMode.PKCS7);
+                var encryptContent = AlipaySignature.AESEncrypt(txtParams[BIZ_CONTENT], options.EncyptKey);
                 txtParams.Remove(BIZ_CONTENT);
                 txtParams.Add(BIZ_CONTENT, encryptContent);
                 txtParams.Add(ENCRYPT_TYPE, options.EncyptType);
@@ -237,44 +246,26 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
             // 添加签名参数
             var signContent = AlipaySignature.GetSignContent(txtParams);
-            txtParams.Add(SIGN, AlipaySignature.RSASignContent(signContent, options.PrivateRSAParameters, options.SignType));
+            txtParams.Add(SIGN, AlipaySignature.RSASignContent(signContent, options.AppPrivateKey, options.SignType));
 
-            var query = AlipayUtility.BuildQuery(txtParams);
-            _logger.Log(options.LogLevel, "Request:{query}", query);
+            string body;
+            var client = _httpClientFactory.CreateClient(nameof(AlipayClient));
 
             // 是否需要上传文件
-            var body = string.Empty;
-            using (var client = _clientFactory.CreateClient())
+            if (request is IAlipayUploadRequest<T> uRequest)
             {
-                if (request is IAlipayUploadRequest<T> uRequest)
-                {
-                    var fileParams = AlipayUtility.CleanupDictionary(uRequest.GetFileParameters());
-
-                    body = await client.DoPostAsync(options.ServerUrl, txtParams, fileParams);
-                }
-                else
-                {
-                    body = await client.DoPostAsync(options.ServerUrl, query);
-                }
-            }
-
-            _logger.Log(options.LogLevel, "Response:{body}", body);
+                var fileParams = AlipayUtility.CleanupDictionary(uRequest.GetFileParameters());
 
-            T rsp = null;
-            IAlipayParser<T> parser = null;
-            if ("xml".Equals(options.Format))
-            {
-                parser = new AlipayXmlParser<T>();
-                rsp = parser.Parse(body);
+                body = await client.PostAsync(options.ServerUrl, txtParams, fileParams);
             }
             else
             {
-                parser = new AlipayJsonParser<T>();
-                rsp = parser.Parse(body);
+                body = await client.PostAsync(options.ServerUrl, txtParams);
             }
 
+            var parser = new AlipayJsonParser<T>();
             var item = ParseRespItem(request, body, parser, options.EncyptKey, options.EncyptType);
-            rsp = parser.Parse(item.RealContent);
+            var rsp = parser.Parse(item.RealContent);
 
             CheckResponseSign(request, item.RespContent, rsp.IsError, parser, options);
 
@@ -283,8 +274,7 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
         private ResponseParseItem ParseRespItem<T>(IAlipayRequest<T> request, string respBody, IAlipayParser<T> parser, string encryptKey, string encryptType) where T : AlipayResponse
         {
-            string realContent = null;
-
+            string realContent;
             if (request.GetNeedEncrypt())
             {
                 realContent = parser.EncryptSourceData(request, respBody, encryptType, encryptKey);
@@ -294,13 +284,11 @@ namespace Essensoft.AspNetCore.Payment.Alipay
                 realContent = respBody;
             }
 
-            var item = new ResponseParseItem
+            return new ResponseParseItem
             {
                 RealContent = realContent,
                 RespContent = respBody
             };
-            return item;
-
         }
 
         private void CheckResponseSign<T>(IAlipayRequest<T> request, string responseBody, bool isError, IAlipayParser<T> parser, AlipayOptions options) where T : AlipayResponse
@@ -313,13 +301,13 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
             if (!isError || isError && !string.IsNullOrEmpty(signItem.Sign))
             {
-                var rsaCheckContent = AlipaySignature.RSACheckContent(signItem.SignSourceDate, signItem.Sign, options.PublicRSAParameters, options.SignType);
+                var rsaCheckContent = AlipaySignature.RSACheckContent(signItem.SignSourceDate, signItem.Sign, options.AlipayPublicKey, options.SignType);
                 if (!rsaCheckContent)
                 {
                     if (!string.IsNullOrEmpty(signItem.SignSourceDate) && signItem.SignSourceDate.Contains("\\/"))
                     {
                         var srouceData = signItem.SignSourceDate.Replace("\\/", "/");
-                        var jsonCheck = AlipaySignature.RSACheckContent(srouceData, signItem.Sign, options.PublicRSAParameters, options.SignType);
+                        var jsonCheck = AlipaySignature.RSACheckContent(srouceData, signItem.Sign, options.AlipayPublicKey, options.SignType);
                         if (!jsonCheck)
                         {
                             throw new AlipayException("sign check fail: check Sign and Data Fail JSON also");
@@ -337,23 +325,17 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
         #region Common Method
 
-        private string BuildHtmlRequest(IDictionary<string, string> dictionary, string strMethod, AlipayOptions options)
+        private string BuildHtmlRequest(IDictionary<string, string> dictionary, string serverUrl, string charset, string strMethod)
         {
-            var sbHtml = new StringBuilder();
-            sbHtml.Append("<form id='submit' name='submit' action='" + options.ServerUrl + "?charset=" + options.Charset +
-                 "' method='" + strMethod + "' style='display:none;'>");
-
+            var sb = new StringBuilder();
+            sb.Append($"<form id='submit' name='submit' action='{serverUrl}?charset={charset}' method='{strMethod}' style='display:none;'>");
             foreach (var iter in dictionary)
             {
-                sbHtml.Append("<input  name='" + iter.Key + "' value='" + iter.Value + "'/>");
+                sb.Append("<input  name='" + iter.Key + "' value='" + iter.Value + "'/>");
             }
-
-            sbHtml.Append("<input type='submit' style='display:none;'></form>");
-
-            //表单实现自动提交
-            sbHtml.Append("<script>document.forms['submit'].submit();</script>");
-
-            return sbHtml.ToString();
+            sb.Append("<input type='submit' style='display:none;'></form>");
+            sb.Append("<script>document.forms['submit'].submit();</script>");
+            return sb.ToString();
         }
 
         private AlipayDictionary BuildRequestParams<T>(IAlipayRequest<T> request, string accessToken, string appAuthToken, AlipayOptions options) where T : AlipayResponse
@@ -399,7 +381,7 @@ namespace Essensoft.AspNetCore.Payment.Alipay
                     throw new AlipayException("api only support Aes!");
                 }
 
-                var encryptContent = AES.Encrypt(result[BIZ_CONTENT], options.EncyptKey, AlipaySignature.AES_IV, CipherMode.CBC, PaddingMode.PKCS7);
+                var encryptContent = AlipaySignature.AESEncrypt(result[BIZ_CONTENT], options.EncyptKey);
                 result.Remove(BIZ_CONTENT);
                 result.Add(BIZ_CONTENT, encryptContent);
                 result.Add(ENCRYPT_TYPE, options.EncyptType);
@@ -412,35 +394,53 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
         #region SDK Execute
 
-        public Task<T> SdkExecuteAsync<T>(IAlipayRequest<T> request) where T : AlipayResponse
+        public Task<T> SdkExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options) where T : AlipayResponse
         {
-            return SdkExecuteAsync(request, null);
-        }
+            if (options == null)
+            {
+                throw new ArgumentNullException(nameof(options));
+            }
 
-        public Task<T> SdkExecuteAsync<T>(IAlipayRequest<T> request, string optionsName) where T : AlipayResponse
-        {
-            var options = _optionsSnapshotAccessor.Get(optionsName);
+            if (string.IsNullOrEmpty(options.AppId))
+            {
+                throw new ArgumentNullException(nameof(options.AppId));
+            }
+
+            if (string.IsNullOrEmpty(options.SignType))
+            {
+                throw new ArgumentNullException(nameof(options.SignType));
+            }
+
+            if (string.IsNullOrEmpty(options.AppPrivateKey))
+            {
+                throw new ArgumentNullException(nameof(options.AppPrivateKey));
+            }
+
+            if (string.IsNullOrEmpty(options.ServerUrl))
+            {
+                throw new ArgumentNullException(nameof(options.ServerUrl));
+            }
 
             // 构造请求参数
             var requestParams = BuildRequestParams(request, null, null, options);
 
             // 字典排序
             var sortedParams = new SortedDictionary<string, string>(requestParams);
-            var sortedAlipayDic = new AlipayDictionary(sortedParams);
+            var sortedDic = new AlipayDictionary(sortedParams);
 
             // 参数签名
-            var signContent = AlipaySignature.GetSignContent(sortedAlipayDic);
-            var signResult = AlipaySignature.RSASignContent(signContent, options.PrivateRSAParameters, options.SignType);
+            var signContent = AlipaySignature.GetSignContent(sortedDic);
+            var signResult = AlipaySignature.RSASignContent(signContent, options.AppPrivateKey, options.SignType);
 
             // 添加签名结果参数
-            sortedAlipayDic.Add(SIGN, signResult);
+            sortedDic.Add(SIGN, signResult);
 
             // 参数拼接
-            var signedResult = AlipayUtility.BuildQuery(sortedAlipayDic);
+            var signedResult = AlipayUtility.BuildQuery(sortedDic);
 
             // 构造结果
             var rsp = Activator.CreateInstance<T>();
-            rsp.Body = signedResult;
+            rsp.ResponseBody = signedResult;
             return Task.FromResult(rsp);
         }
 
@@ -455,7 +455,6 @@ namespace Essensoft.AspNetCore.Payment.Alipay
             var result = requestParams;
             var isBizContentEmpty = !requestParams.ContainsKey(BIZ_CONTENT) || string.IsNullOrEmpty(requestParams[BIZ_CONTENT]);
             var bizModel = request.GetBizModel();
-
             if (isBizContentEmpty && bizModel != null)
             {
                 var content = JsonConvert.SerializeObject(bizModel, jsonSerializerSettings);
@@ -467,4 +466,4 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
         #endregion
     }
-}
+}

+ 20 - 48
src/Essensoft.AspNetCore.Payment.Alipay/AlipayNotifyClient.cs

@@ -1,81 +1,52 @@
 using System;
 using System.Collections.Generic;
-using System.Security.Cryptography;
 using System.Text;
 using System.Threading.Tasks;
 using Essensoft.AspNetCore.Payment.Alipay.Parser;
 using Essensoft.AspNetCore.Payment.Alipay.Utility;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
 
 namespace Essensoft.AspNetCore.Payment.Alipay
 {
-    /// <summary>
-    /// Alipay 通知解析客户端。
-    /// </summary>
     public class AlipayNotifyClient : IAlipayNotifyClient
     {
-        private readonly ILogger _logger;
-        private readonly IOptionsSnapshot<AlipayOptions> _optionsSnapshotAccessor;
-
         #region AlipayNotifyClient Constructors
 
-        public AlipayNotifyClient(
-            ILogger<AlipayNotifyClient> logger,
-            IOptionsSnapshot<AlipayOptions> optionsAccessor)
+        public AlipayNotifyClient()
         {
-            _logger = logger;
-            _optionsSnapshotAccessor = optionsAccessor;
+
         }
 
         #endregion
 
         #region IAlipayNotifyClient Members
 
-        public async Task<T> ExecuteAsync<T>(HttpRequest request) where T : AlipayNotify
+        public Task<T> ExecuteAsync<T>(IDictionary<string, string> parameters, AlipayOptions options) where T : AlipayNotify
         {
-            return await ExecuteAsync<T>(request, null);
-        }
+            if (options == null)
+            {
+                throw new ArgumentNullException(nameof(options));
+            }
 
-        public async Task<T> ExecuteAsync<T>(HttpRequest request, string optionsName) where T : AlipayNotify
-        {
-            var options = _optionsSnapshotAccessor.Get(optionsName);
-            var parameters = await GetParametersAsync(request);
-            var query = AlipayUtility.BuildQuery(parameters);
-            _logger.Log(options.LogLevel, "Request:{query}", query);
+            if (string.IsNullOrEmpty(options.SignType))
+            {
+                throw new ArgumentNullException(nameof(options.SignType));
+            }
+
+            if (string.IsNullOrEmpty(options.AppPrivateKey))
+            {
+                throw new ArgumentNullException(nameof(options.AppPrivateKey));
+            }
 
             var parser = new AlipayDictionaryParser<T>();
             var rsp = parser.Parse(parameters);
             CheckNotifySign(parameters, options);
-            return rsp;
+            return Task.FromResult(rsp);
         }
 
         #endregion
 
         #region Common Method
 
-        private async Task<SortedDictionary<string, string>> GetParametersAsync(HttpRequest request)
-        {
-            var parameters = new SortedDictionary<string, string>();
-            if (request.Method == "POST")
-            {
-                var form = await request.ReadFormAsync();
-                foreach (var iter in form)
-                {
-                    parameters.Add(iter.Key, iter.Value);
-                }
-            }
-            else
-            {
-                foreach (var iter in request.Query)
-                {
-                    parameters.Add(iter.Key, iter.Value);
-                }
-            }
-            return parameters;
-        }
-
         private void CheckNotifySign(IDictionary<string, string> dictionary, AlipayOptions options)
         {
             if (dictionary == null || dictionary.Count == 0)
@@ -89,7 +60,7 @@ namespace Essensoft.AspNetCore.Payment.Alipay
             }
 
             var prestr = GetSignContent(dictionary);
-            if (!AlipaySignature.RSACheckContent(prestr, sign, options.PublicRSAParameters, options.SignType))
+            if (!AlipaySignature.RSACheckContent(prestr, sign, options.AlipayPublicKey, options.SignType))
             {
                 throw new AlipayException("sign check fail: check Sign Data Fail!");
             }
@@ -102,8 +73,9 @@ namespace Essensoft.AspNetCore.Payment.Alipay
                 throw new ArgumentNullException(nameof(dictionary));
             }
 
+            var sortPara = new SortedDictionary<string, string>(dictionary);
             var sb = new StringBuilder();
-            foreach (var iter in dictionary)
+            foreach (var iter in sortPara)
             {
                 if (!string.IsNullOrEmpty(iter.Value) && iter.Key != "sign" && iter.Key != "sign_type")
                 {

+ 13 - 44
src/Essensoft.AspNetCore.Payment.Alipay/AlipayOptions.cs

@@ -1,84 +1,58 @@
-using System.Security.Cryptography;
-using Essensoft.AspNetCore.Payment.Security;
-using Microsoft.Extensions.Logging;
-
-namespace Essensoft.AspNetCore.Payment.Alipay
+namespace Essensoft.AspNetCore.Payment.Alipay
 {
     /// <summary>
     /// Alipay 选项。
     /// </summary>
     public class AlipayOptions
     {
-        internal RSAParameters PrivateRSAParameters;
-        internal RSAParameters PublicRSAParameters;
-
-        private string rsaPrivateKey;
-        private string rsaPublicKey;
-
         /// <summary>
-        /// 应用ID
+        /// 蚂蚁金服开放平台 应用ID
         /// </summary>
         public string AppId { get; set; }
 
         /// <summary>
-        /// 支付宝公钥
+        /// RSA 支付宝公钥
         /// </summary>
-        public string RsaPublicKey
-        {
-            get => rsaPublicKey;
-            set
-            {
-                rsaPublicKey = value;
-                if (!string.IsNullOrEmpty(rsaPublicKey))
-                {
-                    PublicRSAParameters = RSAUtilities.GetRSAParametersFormPublicKey(RsaPublicKey);
-                }
-            }
-        }
+        public string AlipayPublicKey { get; set; }
 
         /// <summary>
-        /// 应用私钥
+        /// RSA 应用私钥
         /// </summary>
-        public string RsaPrivateKey
-        {
-            get => rsaPrivateKey;
-            set
-            {
-                rsaPrivateKey = value;
-                if (!string.IsNullOrEmpty(rsaPrivateKey))
-                {
-                    PrivateRSAParameters = RSAUtilities.GetRSAParametersFormPrivateKey(rsaPrivateKey);
-                }
-            }
-        }
+        public string AppPrivateKey { get; set; }
 
         /// <summary>
         /// 服务地址
+        /// "https://openapi.alipay.com/gateway.do"
         /// </summary>
         public string ServerUrl { get; set; } = "https://openapi.alipay.com/gateway.do";
 
         /// <summary>
         /// 数据格式
+        /// "json"
         /// </summary>
-        public string Format { get; set; } = "json";
+        public string Format { get; } = "json";
 
         /// <summary>
         /// 接口版本
+        /// "1.0"
         /// </summary>
         public string Version { get; set; } = "1.0";
 
         /// <summary>
         /// 签名方式
+        /// "RSA2"
         /// </summary>
         public string SignType { get; set; } = "RSA2";
 
         /// <summary>
         /// 编码格式
+        /// "utf-8"
         /// </summary>
         public string Charset { get; } = "utf-8";
 
         /// <summary>
         /// 加密方式
+        /// "AES"
         /// </summary>
         public string EncyptType { get; } = "AES";
 
@@ -86,10 +60,5 @@ namespace Essensoft.AspNetCore.Payment.Alipay
         /// 加密秘钥
         /// </summary>
         public string EncyptKey { get; set; }
-
-        /// <summary>
-        /// 日志等级
-        /// </summary>
-        public LogLevel LogLevel { get; set; } = LogLevel.Information;
     }
 }

+ 6 - 13
src/Essensoft.AspNetCore.Payment.Alipay/AlipayResponse.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Xml.Serialization;
 using Newtonsoft.Json;
 
 namespace Essensoft.AspNetCore.Payment.Alipay
@@ -15,45 +14,39 @@ namespace Essensoft.AspNetCore.Payment.Alipay
         /// 对应 ErrCode
         /// </summary>
         [JsonProperty("code")]
-        [XmlElement("code")]
-        public string Code { get; set; }
+        public virtual string Code { get; set; }
 
         /// <summary>
         /// 错误信息
         /// 对应 ErrMsg
         /// </summary>
         [JsonProperty("msg")]
-        [XmlElement("msg")]
-        public string Msg { get; set; }
+        public virtual string Msg { get; set; }
 
         /// <summary>
         /// 子错误码
         /// 对应 SubErrCode
         /// </summary>
         [JsonProperty("sub_code")]
-        [XmlElement("sub_code")]
-        public string SubCode { get; set; }
+        public virtual string SubCode { get; set; }
 
         /// <summary>
         /// 子错误信息
         /// 对应 SubErrMsg
         /// </summary>
         [JsonProperty("sub_msg")]
-        [XmlElement("sub_msg")]
-        public string SubMsg { get; set; }
+        public virtual string SubMsg { get; set; }
 
         /// <summary>
         /// 响应原始内容
         /// </summary>
         [JsonIgnore]
-        [XmlIgnore]
-        public string Body { get; set; }
+        public virtual string ResponseBody { get; set; }
 
         /// <summary>
         /// 响应结果是否错误
         /// </summary>
         [JsonIgnore]
-        [XmlIgnore]
-        public bool IsError => !string.IsNullOrEmpty(SubCode);
+        public virtual bool IsError => !string.IsNullOrEmpty(SubCode);
     }
 }

+ 17 - 69
src/Essensoft.AspNetCore.Payment.Alipay/IAlipayClient.cs

@@ -2,102 +2,50 @@
 
 namespace Essensoft.AspNetCore.Payment.Alipay
 {
-    /// <summary>
-    /// Alipay 客户端。
-    /// </summary>
     public interface IAlipayClient
     {
         /// <summary>
-        /// 执行Alipay公开API请求。
+        /// 执行 Alipay API请求。
         /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
         /// <param name="request">具体的Alipay API请求</param>
+        /// <param name="options">配置选项</param>
         /// <returns>领域对象</returns>
-        Task<T> ExecuteAsync<T>(IAlipayRequest<T> request) where T : AlipayResponse;
+        Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options) where T : AlipayResponse;
 
         /// <summary>
-        /// 执行Alipay公开API请求。
+        /// 执行 Alipay API请求。
         /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
         /// <param name="request">具体的Alipay API请求</param>
-        /// <param name="optionsName">配置选项名称</param>
-        /// <returns>领域对象</returns>
-        Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, string optionsName) where T : AlipayResponse;
-
-        /// <summary>
-        /// 执行Alipay公开API请求。
-        /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
-        /// <param name="request">具体的Alipay API请求</param>
-        /// <param name="session">用户会话码</param>
-        /// <param name="appAuthToken">应用授权码</param>
-        /// <returns>领域对象</returns>
-        Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, string session, string appAuthToken) where T : AlipayResponse;
-
-        /// <summary>
-        /// 执行Alipay公开API请求。
-        /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
-        /// <param name="request">具体的Alipay API请求</param>
-        /// <param name="optionsName">配置选项名称</param>
-        /// <param name="session">用户会话码</param>
+        /// <param name="options">配置选项</param>
+        /// <param name="accessToken">用户会话码</param>
         /// <param name="appAuthToken">应用授权码</param>
         /// <returns>领域对象</returns>
-        Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, string optionsName, string session, string appAuthToken) where T : AlipayResponse;
+        Task<T> ExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options, string accessToken, string appAuthToken) where T : AlipayResponse;
 
         /// <summary>
-        /// 执行Alipay公开API请求。
+        /// 执行 Alipay API请求。
         /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
         /// <param name="request">具体的Alipay API请求</param>
+        /// <param name="options">配置选项</param>
         /// <returns>领域对象</returns>
-        Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request) where T : AlipayResponse;
+        Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options) where T : AlipayResponse;
 
         /// <summary>
-        /// 执行Alipay公开API请求。
+        /// 执行 Alipay API请求。
         /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
         /// <param name="request">具体的Alipay API请求</param>
-        /// <param name="optionsName">配置选项名称</param>
-        /// <returns>领域对象</returns>
-        Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, string optionsName) where T : AlipayResponse;
-
-        /// <summary>
-        /// 执行Alipay公开API请求。
-        /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
-        /// <param name="request">具体的Alipay API请求</param>
-        /// <param name="session">用户会话码</param>
+        /// <param name="options">配置选项</param>
+        /// <param name="accessToken">用户会话码</param>
         /// <param name="reqMethod">请求访问方法</param>
         /// <returns>领域对象</returns>
-        Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, string session, string reqMethod) where T : AlipayResponse;
-
-        /// <summary>
-        /// 执行Alipay公开API请求。
-        /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
-        /// <param name="request">具体的Alipay API请求</param>
-        /// <param name="optionsName">配置选项名称</param>
-        /// <param name="session">用户会话码</param>
-        /// <param name="reqMethod">请求访问方法</param>
-        /// <returns>领域对象</returns>
-        Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, string optionsName, string session, string reqMethod) where T : AlipayResponse;
-
-        /// <summary>
-        /// 执行Alipay公开API请求。
-        /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
-        /// <param name="request">具体的Alipay API请求</param>
-        /// <returns>领域对象</returns>
-        Task<T> SdkExecuteAsync<T>(IAlipayRequest<T> request) where T : AlipayResponse;
+        Task<T> PageExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options, string accessToken, string reqMethod) where T : AlipayResponse;
 
         /// <summary>
-        /// 执行Alipay公开API请求。
+        /// 执行 Alipay API请求。
         /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
         /// <param name="request">具体的Alipay API请求</param>
-        /// <param name="optionsName">配置选项名称</param>
+        /// <param name="options">配置选项</param>
         /// <returns>领域对象</returns>
-        Task<T> SdkExecuteAsync<T>(IAlipayRequest<T> request, string optionsName) where T : AlipayResponse;
+        Task<T> SdkExecuteAsync<T>(IAlipayRequest<T> request, AlipayOptions options) where T : AlipayResponse;
     }
 }

+ 6 - 14
src/Essensoft.AspNetCore.Payment.Alipay/IAlipayNotifyClient.cs

@@ -1,5 +1,5 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
+using System.Collections.Generic;
+using System.Threading.Tasks;
 
 namespace Essensoft.AspNetCore.Payment.Alipay
 {
@@ -9,20 +9,12 @@ namespace Essensoft.AspNetCore.Payment.Alipay
     public interface IAlipayNotifyClient
     {
         /// <summary>
-        /// 执行Alipay通知请求解析。
+        /// 执行 Alipay 通知请求解析。
         /// </summary>
         /// <typeparam name="T">领域对象</typeparam>
-        /// <param name="request">控制器的请求</param>
+        /// <param name="parameters">通知参数</param>
+        /// <param name="options">配置选项</param>
         /// <returns>领域对象</returns>
-        Task<T> ExecuteAsync<T>(HttpRequest request) where T : AlipayNotify;
-
-        /// <summary>
-        /// 执行Alipay通知请求解析。
-        /// </summary>
-        /// <typeparam name="T">领域对象</typeparam>
-        /// <param name="request">控制器的请求</param>
-        /// <param name="optionsName">配置选项名称</param>
-        /// <returns>领域对象</returns>
-        Task<T> ExecuteAsync<T>(HttpRequest request, string optionsName) where T : AlipayNotify;
+        Task<T> ExecuteAsync<T>(IDictionary<string, string> parameters, AlipayOptions options) where T : AlipayNotify;
     }
 }

+ 7 - 7
src/Essensoft.AspNetCore.Payment.Alipay/Parser/AlipayDictionaryParser.cs

@@ -1,33 +1,33 @@
 using System;
-using System.Collections;
+using System.Collections.Generic;
 using Newtonsoft.Json;
 
 namespace Essensoft.AspNetCore.Payment.Alipay.Parser
 {
     public class AlipayDictionaryParser<T> where T : AlipayObject
     {
-        public T Parse(IDictionary dictionary)
+        public T Parse(IDictionary<string, string> dictionary)
         {
             if (dictionary == null || dictionary.Count == 0)
             {
                 throw new ArgumentNullException(nameof(dictionary));
             }
 
-            T rsp = null;
+            T result = null;
 
             try
             {
                 var jsonText = JsonConvert.SerializeObject(dictionary);
-                rsp = JsonConvert.DeserializeObject<T>(jsonText);
+                result = JsonConvert.DeserializeObject<T>(jsonText);
             }
             catch { }
 
-            if (rsp == null)
+            if (result == null)
             {
-                rsp = Activator.CreateInstance<T>();
+                result = Activator.CreateInstance<T>();
             }
 
-            return rsp;
+            return result;
         }
     }
 }

+ 1 - 1
src/Essensoft.AspNetCore.Payment.Alipay/Parser/AlipayJsonParser.cs

@@ -162,7 +162,7 @@ namespace Essensoft.AspNetCore.Payment.Alipay.Parser
 
             if (rsp != null)
             {
-                rsp.Body = body;
+                rsp.ResponseBody = body;
             }
 
             return rsp;

+ 0 - 225
src/Essensoft.AspNetCore.Payment.Alipay/Parser/AlipayXmlParser.cs

@@ -1,225 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Security.Cryptography;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Xml.Serialization;
-using Essensoft.AspNetCore.Payment.Alipay.Utility;
-using Essensoft.AspNetCore.Payment.Security;
-
-namespace Essensoft.AspNetCore.Payment.Alipay.Parser
-{
-    /// <summary>
-    /// Alipay XML响应通用解释器。
-    /// </summary>
-    public class AlipayXmlParser<T> : IAlipayParser<T> where T : AlipayResponse
-    {
-        private static readonly Regex regex = new Regex("<(\\w+?)[ >]", RegexOptions.Compiled);
-        private static readonly Dictionary<string, XmlSerializer> parsers = new Dictionary<string, XmlSerializer>();
-
-        public string EncryptSourceData(IAlipayRequest<T> request, string body, string encryptType, string encryptKey)
-        {
-            var item = ParseEncryptData(request, body);
-
-            var bodyIndexContent = body.Substring(0, item.startIndex);
-            var bodyEndContent = body.Substring(item.endIndex);
-            var encryptContent = AES.Decrypt(item.encryptContent, encryptKey, AlipaySignature.AES_IV, CipherMode.CBC, PaddingMode.PKCS7);
-
-            return bodyIndexContent + encryptContent + bodyEndContent;
-        }
-
-        /// <summary>
-        /// 获取XML响应的根节点名称
-        /// </summary>
-        private string GetRootElement(string body)
-        {
-            var match = regex.Match(body);
-            if (match.Success)
-            {
-                return match.Groups[1].ToString();
-            }
-
-            throw new AlipayException("Invalid XML response format!");
-        }
-
-        private static string GetSign(string body)
-        {
-            var signNodeName = "<" + AlipayConstants.SIGN + ">";
-            var signEndNodeName = "</" + AlipayConstants.SIGN + ">";
-
-            var indexOfSignNode = body.IndexOf(signNodeName);
-            var indexOfSignEndNode = body.IndexOf(signEndNodeName);
-
-            if (indexOfSignNode < 0 || indexOfSignEndNode < 0)
-            {
-                return null;
-            }
-
-            //  签名
-            var startPos = indexOfSignNode + signNodeName.Length;
-            return body.Substring(startPos, indexOfSignEndNode - startPos);
-        }
-
-        private static string GetSignSourceData(IAlipayRequest<T> request, string body)
-        {
-            var rootNode = request.GetApiName().Replace(".", "_") + AlipayConstants.RESPONSE_SUFFIX;
-            var errorRootNode = AlipayConstants.ERROR_RESPONSE;
-
-            var indexOfRootNode = body.IndexOf(rootNode);
-            var indexOfErrorRoot = body.IndexOf(errorRootNode);
-
-            string result = null;
-            if (indexOfRootNode > 0)
-            {
-                result = ParseSignSourceData(body, rootNode, indexOfRootNode);
-            }
-            else if (indexOfErrorRoot > 0)
-            {
-                result = ParseSignSourceData(body, errorRootNode, indexOfErrorRoot);
-            }
-
-            return result;
-        }
-
-        private static string ParseSignSourceData(string body, string rootNode, int indexOfRootNode)
-        {
-
-            //  第一个字母+长度+>
-            var signDataStartIndex = indexOfRootNode + rootNode.Length + 1;
-            var indexOfSign = body.IndexOf("<" + AlipayConstants.SIGN);
-            if (indexOfSign < 0)
-            {
-                return null;
-            }
-
-            // 签名前减去
-            var signDataEndIndex = indexOfSign;
-
-            return body.Substring(signDataStartIndex, signDataEndIndex - signDataStartIndex);
-        }
-
-        private static EncryptParseItem ParseEncryptData(IAlipayRequest<T> request, string body)
-        {
-            var rootNode = request.GetApiName().Replace(".", "_") + AlipayConstants.RESPONSE_SUFFIX;
-            var errorRootNode = AlipayConstants.ERROR_RESPONSE;
-
-            var indexOfRootNode = body.IndexOf(rootNode);
-            var indexOfErrorRoot = body.IndexOf(errorRootNode);
-
-            EncryptParseItem result = null;
-            if (indexOfRootNode > 0)
-            {
-                result = ParseEncryptItem(body, rootNode, indexOfRootNode);
-            }
-            else if (indexOfErrorRoot > 0)
-            {
-                result = ParseEncryptItem(body, errorRootNode, indexOfErrorRoot);
-            }
-
-            return result;
-        }
-
-        private static EncryptParseItem ParseEncryptItem(string body, string rootNode, int indexOfRootNode)
-        {
-            //  第一个字母+长度+>
-            var signDataStartIndex = indexOfRootNode + rootNode.Length + 1;
-
-            var xmlStartNode = "<" + AlipayConstants.ENCRYPT_NODE_NAME + ">";
-            var xmlEndNode = "</" + AlipayConstants.ENCRYPT_NODE_NAME + ">";
-            var indexOfEncryptNode = body.IndexOf(xmlEndNode);
-
-            if (indexOfEncryptNode < 0)
-            {
-                var item = new EncryptParseItem
-                {
-                    encryptContent = null,
-                    startIndex = 0,
-                    endIndex = 0
-                };
-                return item;
-            }
-
-            var startIndex = signDataStartIndex + xmlStartNode.Length;
-            var bizLen = indexOfEncryptNode - startIndex;
-
-            var encryptBizContent = body.Substring(startIndex, bizLen);
-
-            var item2 = new EncryptParseItem
-            {
-                encryptContent = encryptBizContent,
-                startIndex = signDataStartIndex,
-                endIndex = indexOfEncryptNode + xmlEndNode.Length
-            };
-            return item2;
-        }
-
-        #region IAlipayParser<T> Members
-
-        public T Parse(string body)
-        {
-            if (string.IsNullOrEmpty(body))
-            {
-                throw new ArgumentNullException(nameof(body));
-            }
-
-            T rsp = null;
-
-            try
-            {
-                var rootTagName = GetRootElement(body);
-
-                var inc = parsers.TryGetValue(rootTagName, out var serializer);
-                if (!inc || serializer == null)
-                {
-                    var rootAttrs = new XmlAttributes
-                    {
-                        XmlRoot = new XmlRootAttribute(rootTagName)
-                    };
-                    var attrOvrs = new XmlAttributeOverrides();
-                    attrOvrs.Add(typeof(T), rootAttrs);
-
-                    serializer = new XmlSerializer(typeof(T), attrOvrs);
-                    parsers[rootTagName] = serializer;
-                }
-
-                using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(body)))
-                {
-                    rsp = (T)serializer.Deserialize(stream);
-                }
-            }
-            catch
-            { }
-
-            if (rsp == null)
-            {
-                rsp = Activator.CreateInstance<T>();
-            }
-
-            if (rsp != null)
-            {
-                rsp.Body = body;
-            }
-
-            return rsp;
-        }
-
-        public SignItem GetSignItem(IAlipayRequest<T> request, string reponseBody)
-        {
-            if (string.IsNullOrEmpty(reponseBody))
-            {
-                return null;
-            }
-
-            var signItem = new SignItem();
-            var sign = GetSign(reponseBody);
-            signItem.Sign = sign;
-
-            var signSourceData = GetSignSourceData(request, reponseBody);
-            signItem.SignSourceDate = signSourceData;
-            return signItem;
-        }
-
-        #endregion
-    }
-}

+ 5 - 2
src/Essensoft.AspNetCore.Payment.Alipay/ServiceCollectionExtensions.cs

@@ -15,8 +15,11 @@ namespace Essensoft.AspNetCore.Payment.Alipay
             this IServiceCollection services,
             Action<AlipayOptions> setupAction)
         {
-            services.AddScoped<IAlipayClient, AlipayClient>();
-            services.AddScoped<IAlipayNotifyClient, AlipayNotifyClient>();
+            services.AddHttpClient(nameof(AlipayClient));
+
+            services.AddSingleton<IAlipayClient, AlipayClient>();
+            services.AddSingleton<IAlipayNotifyClient, AlipayNotifyClient>();
+
             if (setupAction != null)
             {
                 services.Configure(setupAction);

+ 20 - 14
src/Essensoft.AspNetCore.Payment.Alipay/Utility/AlipaySignature.cs

@@ -1,13 +1,10 @@
-using System;
 using System.Collections.Generic;
 using System.Security.Cryptography;
 using System.Text;
+using Essensoft.AspNetCore.Payment.Security;
 
 namespace Essensoft.AspNetCore.Payment.Alipay.Utility
 {
-    /// <summary>
-    /// Alipay Ç©ÃûÀà¡£
-    /// </summary>
     public class AlipaySignature
     {
         public static readonly byte[] AES_IV = InitIv(16);
@@ -32,26 +29,35 @@ namespace Essensoft.AspNetCore.Payment.Alipay.Utility
             return sb.Remove(sb.Length - 1, 1).ToString();
         }
 
-        public static string RSASignContent(string data, RSAParameters parameters, string signType)
+        public static string RSASignContent(string data, string privateKey, string signType)
         {
-            using (var rsa = RSA.Create())
+            var key = RSAUtilities.GetRSAParametersFormRsaPrivateKey(privateKey);
+            switch (signType)
             {
-                rsa.ImportParameters(parameters);
-                var type = "RSA2" == signType ? HashAlgorithmName.SHA256 : HashAlgorithmName.SHA1;
-                return Convert.ToBase64String(rsa.SignData(Encoding.UTF8.GetBytes(data), type, RSASignaturePadding.Pkcs1));
+                case "RSA2":
+                    return SHA256WithRSA.Sign(data, key);
+                default:
+                    return SHA1WithRSA.Sign(data, key);
             }
         }
 
-        public static bool RSACheckContent(string data, string sign, RSAParameters parameters, string signType)
+        public static bool RSACheckContent(string data, string sign, string publicKey, string signType)
         {
-            using (var rsa = RSA.Create())
+            var key = RSAUtilities.GetRSAParametersFormPublicKey(publicKey);
+            switch (signType)
             {
-                var type = "RSA2" == signType ? HashAlgorithmName.SHA256 : HashAlgorithmName.SHA1;
-                rsa.ImportParameters(parameters);
-                return rsa.VerifyData(Encoding.UTF8.GetBytes(data), Convert.FromBase64String(sign), type, RSASignaturePadding.Pkcs1);
+                case "RSA2":
+                    return SHA256WithRSA.Verify(data, sign, key);
+                default:
+                    return SHA1WithRSA.Verify(data, sign, key);
             }
         }
 
+        public static string AESEncrypt(string data, string encyptKey)
+        {
+            return AES.Encrypt(data, encyptKey, AES_IV, CipherMode.CBC, PaddingMode.PKCS7);
+        }
+
         private static byte[] InitIv(int blockSize)
         {
             var iv = new byte[blockSize];

+ 18 - 63
src/Essensoft.AspNetCore.Payment.Alipay/Utility/HttpClientExtensions.cs

@@ -7,95 +7,50 @@ using System.Threading.Tasks;
 
 namespace Essensoft.AspNetCore.Payment.Alipay.Utility
 {
-    /// <summary>
-    /// HTTP客户端扩展。
-    /// </summary>
     public static class HttpClientExtensions
     {
         /// <summary>
         /// 执行HTTP POST请求。
         /// </summary>
-        /// <param name="client"></param>
+        /// <param name="client">HttpClient</param>
         /// <param name="url">请求地址</param>
-        /// <param name="dictionary">请求参数</param>
-        /// <returns>HTTP响应</returns>
-        public static async Task<string> DoPostAsync(this HttpClient client, string url, IDictionary<string, string> dictionary)
+        /// <param name="textParams">请求参数</param>
+        /// <returns>响应内容</returns>
+        public static async Task<string> PostAsync(this HttpClient client, string url, IDictionary<string, string> textParams)
         {
-            using (var requestContent = new StringContent(AlipayUtility.BuildQuery(dictionary), Encoding.UTF8, "application/x-www-form-urlencoded"))
-            using (var response = await client.PostAsync(url, requestContent))
-            using (var responseContent = response.Content)
+            using (var reqContent = new StringContent(AlipayUtility.BuildQuery(textParams), Encoding.UTF8, "application/x-www-form-urlencoded"))
+            using (var resp = await client.PostAsync(url, reqContent))
+            using (var respContent = resp.Content)
             {
-                return await responseContent.ReadAsStringAsync();
-            }
-        }
-
-        /// <summary>
-        /// 执行HTTP POST请求。
-        /// </summary>
-        /// <param name="client"></param>
-        /// <param name="url">请求地址</param>
-        /// <param name="content">请求内容</param>
-        /// <returns>HTTP响应</returns>
-        public static async Task<string> DoPostAsync(this HttpClient client, string url, string content)
-        {
-            using (var requestContent = new StringContent(content, Encoding.UTF8, "application/x-www-form-urlencoded"))
-            using (var response = await client.PostAsync(url, requestContent))
-            using (var responseContent = response.Content)
-            {
-                return await responseContent.ReadAsStringAsync();
-            }
-        }
-
-        /// <summary>
-        /// 执行HTTP GET请求。
-        /// </summary>
-        /// <param name="client"></param>
-        /// <param name="url">请求地址</param>
-        /// <param name="dictionary">请求参数</param>
-        /// <returns>HTTP响应</returns>
-        public static async Task<string> DoGetAsync(this HttpClient client, string url, IDictionary<string, string> dictionary)
-        {
-            if (url.Contains("?"))
-            {
-                url = url + "&" + AlipayUtility.BuildQuery(dictionary);
-            }
-            else
-            {
-                url = url + "?" + AlipayUtility.BuildQuery(dictionary);
-            }
-
-            using (var response = await client.GetAsync(url))
-            using (var responseContent = response.Content)
-            {
-                return await responseContent.ReadAsStringAsync();
+                return await respContent.ReadAsStringAsync();
             }
         }
 
         /// <summary>
         /// 执行带文件上传的HTTP POST请求。
         /// </summary>
-        /// <param name="client"></param>
+        /// <param name="client">HttpClient</param>
         /// <param name="url">请求地址</param>
         /// <param name="textParams">请求文本参数</param>
         /// <param name="fileParams">请求文件参数</param>
-        /// <returns>HTTP响应</returns>
-        public static async Task<string> DoPostAsync(this HttpClient client, string url, IDictionary<string, string> textParams, IDictionary<string, FileItem> fileParams)
+        /// <returns>响应内容</returns>
+        public static async Task<string> PostAsync(this HttpClient client, string url, IDictionary<string, string> textParams, IDictionary<string, FileItem> fileParams)
         {
             // 如果没有文件参数,则走普通POST请求
             if (fileParams == null || fileParams.Count == 0)
             {
-                return await DoPostAsync(client, url, textParams);
+                return await PostAsync(client, url, textParams);
             }
 
             // 随机分隔线
             var boundary = DateTime.Now.Ticks.ToString("X");
-            using (var requestContent = new MultipartFormDataContent(boundary))
+            using (var reqContent = new MultipartFormDataContent(boundary))
             {
                 // 组装文本请求参数
                 foreach (var iter in textParams)
                 {
                     var streamContent = new StringContent(iter.Value, Encoding.UTF8, "text/plain");
-                    requestContent.Add(streamContent, iter.Key);
+                    reqContent.Add(streamContent, iter.Key);
                 }
 
                 // 组装文件请求参数
@@ -104,13 +59,13 @@ namespace Essensoft.AspNetCore.Payment.Alipay.Utility
                     var fileItem = iter.Value;
                     var byteArrayContent = new ByteArrayContent(fileItem.GetContent());
                     byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue(fileItem.GetMimeType());
-                    requestContent.Add(byteArrayContent, iter.Key, fileItem.GetFileName());
+                    reqContent.Add(byteArrayContent, iter.Key, fileItem.GetFileName());
                 }
 
-                using (var response = await client.PostAsync(url, requestContent))
-                using (var responseContent = response.Content)
+                using (var resp = await client.PostAsync(url, reqContent))
+                using (var respContent = resp.Content)
                 {
-                    return await responseContent.ReadAsStringAsync();
+                    return await respContent.ReadAsStringAsync();
                 }
             }
         }