├── .gitattributes ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── OmniPay.sln ├── README.md ├── azure-pipelines.yml ├── samples └── OmniPay.Pay │ ├── Certs │ ├── apiclient_cert.p12 │ ├── certs.rar │ └── unioncerts │ │ ├── acp_test_enc.cer │ │ ├── acp_test_middle.cer │ │ └── acp_test_root.cer │ ├── Controllers │ ├── AliPayController.cs │ ├── HomeController.cs │ ├── UnionPayController.cs │ └── WeChatPayController.cs │ ├── OmniPay.Pay.csproj │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Startup.cs │ ├── Views │ ├── Home │ │ └── Index.cshtml │ ├── Shared │ │ └── _Layout.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── WeatherForecast.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── htmlpage.html │ ├── index.html │ └── wwwroot │ ├── demo.css │ ├── demo.js │ ├── favicon.ico │ ├── jquery-ui.min.css │ ├── jquery-ui.min.js │ └── jquery.min.js ├── src ├── OmniPay.Alipay │ ├── AliPayClient.cs │ ├── AlipayOptions.cs │ ├── Domain │ │ ├── AppPayModel.cs │ │ ├── BarCodePayModel.cs │ │ ├── BasePayModel.cs │ │ ├── Goods.cs │ │ ├── Payee.cs │ │ ├── ScanPayModel.cs │ │ └── WebPayModel.cs │ ├── Endpoints │ │ ├── AliScanPayEndpoint.cs │ │ └── Result │ │ │ └── ScanPayResult.cs │ ├── Enums │ │ ├── BillType.cs │ │ ├── TransBizScene.cs │ │ ├── TransIdentityType.cs │ │ └── TransProductCode.cs │ ├── Extensions │ │ ├── AliPayApplicationBuilderExtensions.cs │ │ └── AlipayServiceCollectionExtensions.cs │ ├── IAliPayClient.cs │ ├── Middleware │ │ └── AliPayMiddleware.cs │ ├── OmniPay.Alipay.csproj │ ├── Request │ │ ├── AppPayRequest.cs │ │ ├── BarCodePayRequest.cs │ │ ├── BillDownloadRequest.cs │ │ ├── RefundQueryRequest.cs │ │ ├── ScanPayRequest.cs │ │ ├── TradeCancelRequest.cs │ │ ├── TradeCloseRequest.cs │ │ ├── TradeQueryRequest.cs │ │ ├── TradeRefundRequest.cs │ │ ├── TransferQueryRequest.cs │ │ ├── TransferRequest.cs │ │ ├── WapPayRequest.cs │ │ └── WebPagePayRequest.cs │ └── Response │ │ ├── AppPayResponse.cs │ │ ├── BaseResponse.cs │ │ ├── ScanPayResponse.cs │ │ └── WebPayResponse.cs ├── OmniPay.Core │ ├── Configuration │ │ └── DependencyInjection │ │ │ ├── IOmniPayBuilder.cs │ │ │ ├── OmniPayBuilder.cs │ │ │ └── OmniPayServiceCollectionExtensions.cs │ ├── Endpoints │ │ ├── DiscoveryEndpoint.cs │ │ ├── DiscoveryResponseGenerator.cs │ │ └── Results │ │ │ └── DiscoveryDocumentResult.cs │ ├── Exceptions │ │ └── OmniPayException.cs │ ├── Hosting │ │ ├── Endpoint.cs │ │ ├── EndpointRouter.cs │ │ ├── IEndpointHandler.cs │ │ ├── IEndpointResult.cs │ │ └── IEndpointRouter.cs │ ├── OmniPay.Core.csproj │ ├── Request │ │ └── BaseRequest.cs │ ├── Results │ │ ├── ProccessErrorResult.cs │ │ └── StatusCodeResult.cs │ └── Utils │ │ ├── CertUtil.cs │ │ ├── EncryptUtil.cs │ │ ├── Extensions.Json.cs │ │ ├── Extensions.String.cs │ │ ├── Extensions.cs │ │ ├── HttpHandler.cs │ │ ├── HttpUtil.cs │ │ ├── IHttpHandler.cs │ │ ├── IReadableStringCollectionExtensions.cs │ │ ├── SecurityUtil.cs │ │ └── UnionPayUntil.cs ├── OmniPay.Unionpay │ ├── Domain │ │ ├── BackTransModel.cs │ │ ├── BasePayModel.cs │ │ └── FrontTransModel.cs │ ├── Extensions │ │ ├── UnionPayApplicationBuilderExtensions.cs │ │ └── UnionpayServiceCollectionExtensions.cs │ ├── IUnionPayClient.cs │ ├── Middleware │ │ └── UnionPayMiddleware.cs │ ├── OmniPay.Unionpay.csproj │ ├── Request │ │ ├── BackTransRequest.cs │ │ └── FrontTransRequest.cs │ ├── Response │ │ ├── BackTransResponse.cs │ │ ├── BaseResponse.cs │ │ └── FrontTransResponse.cs │ ├── UnionPayClient.cs │ └── UnionPayOptions.cs └── OmniPay.Wechatpay │ ├── Domain │ ├── AppPayModel.cs │ ├── AppletPayModel.cs │ ├── BasePayModel.cs │ ├── CloseModel.cs │ ├── PublicPayModel.cs │ ├── QueryModel.cs │ ├── RefundModel.cs │ ├── ScanPayModel.cs │ └── WapPayModel.cs │ ├── Endpoints │ ├── Result │ │ ├── AppPayResult.cs │ │ ├── AppletPayResult.cs │ │ ├── CloseResult.cs │ │ ├── PublicPayResult.cs │ │ ├── QueryResult.cs │ │ ├── ScanPayResult.cs │ │ └── WapPayResult.cs │ ├── WechatAppPayEndpoint.cs │ ├── WechatAppletPayEndpoint.cs │ ├── WechatCloseEndpoint.cs │ ├── WechatPublicPayEndpoint.cs │ ├── WechatQueryEndpoint.cs │ ├── WechatScanPayEndpoint.cs │ └── WechatWapPayEndpoint.cs │ ├── Extensions │ ├── WeChatPayApplicationBuilderExtensions.cs │ └── WeChatServiceCollectionExtensions.cs │ ├── IWeChatPayClient.cs │ ├── Middleware │ ├── ApiEndpointMiddleware.cs │ └── WechatPayMiddleware.cs │ ├── OmniPay.Wechatpay.csproj │ ├── Request │ ├── AppPayRequest.cs │ ├── AppletPayRequest.cs │ ├── CloseRequest.cs │ ├── PublicPayRequest.cs │ ├── QueryRequest.cs │ ├── RefundRequest.cs │ ├── ScanPayRequest.cs │ └── WapPayRequest.cs │ ├── Response │ ├── AppPayResponse.cs │ ├── AppletPayResponse.cs │ ├── BaseResponse.cs │ ├── CloseResponse.cs │ ├── PublicPayResponse.cs │ ├── QueryResponse.cs │ ├── RefundResponse.cs │ ├── ScanPayResponse.cs │ └── WapPayResponse.cs │ ├── Results │ └── TestWxResult.cs │ ├── TestPay.cs │ ├── Validation │ ├── Default │ │ └── ScanPayValidator.cs │ ├── IScanPayValidator.cs │ ├── Models │ │ └── ScanPayRequestValidationResult.cs │ └── ValidationResult.cs │ ├── WeChatPayClient.cs │ └── WeChatPayOptions.cs └── test ├── OmniPay.TestBase ├── BaseTest.cs └── OmniPay.TestBase.csproj └── Wechatpay.Test ├── Core └── WechatBaseTest.cs ├── Endpoints ├── WechatAppPayEndpointTest.cs ├── WechatAppletPayEndpointTest.cs ├── WechatCloseEndpointTest.cs ├── WechatPublicPayEndpointTest.cs ├── WechatQueryEndpointTest.cs ├── WechatScanPayEndpointTest.cs └── WechatWapPayEndpointTest.cs ├── WeChatPays └── WeChatPayTest.cs └── Wechatpay.Test.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/test/Wechatpay.Test/bin/Debug/netcoreapp3.1/Wechatpay.Test.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/test/Wechatpay.Test", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/test/Wechatpay.Test/Wechatpay.Test.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/test/Wechatpay.Test/Wechatpay.Test.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/test/Wechatpay.Test/Wechatpay.Test.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Huei Feng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OmniPay.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29519.181 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3642F616-626D-4205-8F66-68615D3F7D6C}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniPay.Wechatpay", "src\OmniPay.Wechatpay\OmniPay.Wechatpay.csproj", "{4F57892F-25E8-4244-A8F8-7C84C7BE1A5C}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{719D3223-895D-4B19-B7EF-B37BE5F8017C}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DB6BE635-1CE8-4DB5-AE37-C8FB10A6EA26}" 13 | ProjectSection(SolutionItems) = preProject 14 | .gitattributes = .gitattributes 15 | .gitignore = .gitignore 16 | azure-pipelines.yml = azure-pipelines.yml 17 | LICENSE = LICENSE 18 | README.md = README.md 19 | EndProjectSection 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniPay.Core", "src\OmniPay.Core\OmniPay.Core.csproj", "{4EE2E97F-7070-4C78-A382-D7C4F7CCA3F7}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wechatpay.Test", "test\Wechatpay.Test\Wechatpay.Test.csproj", "{5CC0C897-2325-4851-958E-E00F8A28550E}" 24 | EndProject 25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniPay.Alipay", "src\OmniPay.Alipay\OmniPay.Alipay.csproj", "{8DAD9EA3-4A61-4843-8697-BC4C5C9BFE9C}" 26 | EndProject 27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniPay.Unionpay", "src\OmniPay.Unionpay\OmniPay.Unionpay.csproj", "{94AEF135-EBB4-49FC-90A1-D27DAB205876}" 28 | EndProject 29 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{9907DC21-7353-4DFD-A298-54A30C6784E7}" 30 | EndProject 31 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniPay.Pay", "samples\OmniPay.Pay\OmniPay.Pay.csproj", "{C3416D37-F867-448F-8528-4DE352A4B928}" 32 | EndProject 33 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OmniPay.TestBase", "test\OmniPay.TestBase\OmniPay.TestBase.csproj", "{E46A48C1-99FE-4AEF-8A9B-B2E43FA616E9}" 34 | EndProject 35 | Global 36 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 37 | Debug|Any CPU = Debug|Any CPU 38 | Release|Any CPU = Release|Any CPU 39 | EndGlobalSection 40 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 41 | {4F57892F-25E8-4244-A8F8-7C84C7BE1A5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {4F57892F-25E8-4244-A8F8-7C84C7BE1A5C}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {4F57892F-25E8-4244-A8F8-7C84C7BE1A5C}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {4F57892F-25E8-4244-A8F8-7C84C7BE1A5C}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {4EE2E97F-7070-4C78-A382-D7C4F7CCA3F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {4EE2E97F-7070-4C78-A382-D7C4F7CCA3F7}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {4EE2E97F-7070-4C78-A382-D7C4F7CCA3F7}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {4EE2E97F-7070-4C78-A382-D7C4F7CCA3F7}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {5CC0C897-2325-4851-958E-E00F8A28550E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {5CC0C897-2325-4851-958E-E00F8A28550E}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {5CC0C897-2325-4851-958E-E00F8A28550E}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {5CC0C897-2325-4851-958E-E00F8A28550E}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {8DAD9EA3-4A61-4843-8697-BC4C5C9BFE9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {8DAD9EA3-4A61-4843-8697-BC4C5C9BFE9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {8DAD9EA3-4A61-4843-8697-BC4C5C9BFE9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {8DAD9EA3-4A61-4843-8697-BC4C5C9BFE9C}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {94AEF135-EBB4-49FC-90A1-D27DAB205876}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {94AEF135-EBB4-49FC-90A1-D27DAB205876}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {94AEF135-EBB4-49FC-90A1-D27DAB205876}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {94AEF135-EBB4-49FC-90A1-D27DAB205876}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {C3416D37-F867-448F-8528-4DE352A4B928}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 62 | {C3416D37-F867-448F-8528-4DE352A4B928}.Debug|Any CPU.Build.0 = Debug|Any CPU 63 | {C3416D37-F867-448F-8528-4DE352A4B928}.Release|Any CPU.ActiveCfg = Release|Any CPU 64 | {C3416D37-F867-448F-8528-4DE352A4B928}.Release|Any CPU.Build.0 = Release|Any CPU 65 | {E46A48C1-99FE-4AEF-8A9B-B2E43FA616E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {E46A48C1-99FE-4AEF-8A9B-B2E43FA616E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {E46A48C1-99FE-4AEF-8A9B-B2E43FA616E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 68 | {E46A48C1-99FE-4AEF-8A9B-B2E43FA616E9}.Release|Any CPU.Build.0 = Release|Any CPU 69 | EndGlobalSection 70 | GlobalSection(SolutionProperties) = preSolution 71 | HideSolutionNode = FALSE 72 | EndGlobalSection 73 | GlobalSection(NestedProjects) = preSolution 74 | {4F57892F-25E8-4244-A8F8-7C84C7BE1A5C} = {3642F616-626D-4205-8F66-68615D3F7D6C} 75 | {4EE2E97F-7070-4C78-A382-D7C4F7CCA3F7} = {3642F616-626D-4205-8F66-68615D3F7D6C} 76 | {5CC0C897-2325-4851-958E-E00F8A28550E} = {719D3223-895D-4B19-B7EF-B37BE5F8017C} 77 | {8DAD9EA3-4A61-4843-8697-BC4C5C9BFE9C} = {3642F616-626D-4205-8F66-68615D3F7D6C} 78 | {94AEF135-EBB4-49FC-90A1-D27DAB205876} = {3642F616-626D-4205-8F66-68615D3F7D6C} 79 | {C3416D37-F867-448F-8528-4DE352A4B928} = {9907DC21-7353-4DFD-A298-54A30C6784E7} 80 | {E46A48C1-99FE-4AEF-8A9B-B2E43FA616E9} = {719D3223-895D-4B19-B7EF-B37BE5F8017C} 81 | EndGlobalSection 82 | GlobalSection(ExtensibilityGlobals) = postSolution 83 | SolutionGuid = {7E7D1B2E-48E6-4A24-8815-2CF293AA888E} 84 | EndGlobalSection 85 | EndGlobal 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OmniPay 2 | =============== 3 | 4 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/hueifeng/OmniPay/blob/master/LICENSE) 5 | [![Build Status](https://dev.azure.com/dotnetserverprojects/OmniPay/_apis/build/status/omnipaypay20200709180728%20-%20CI?branchName=master)](https://dev.azure.com/dotnetserverprojects/OmniPay/_build/latest?definitionId=1&branchName=master) 6 | 7 | OmniPay支付SDK,主要打造微信支付,支付宝支付,以及银联支付等,使用OmniPay可以简化订单的创建、查询、退款和回调通知等操作 8 | 9 | 10 | # 代码贡献 11 | 12 | 如果您有其它支付网关的需求,或者发现本项目中需要改进的代码,欢迎 Fork 并提交 PR! 13 | 14 | # Wiki 15 | 16 | 支付宝支付文档: 17 | 18 | https://openhome.alipay.com/developmentDocument.htm 19 | 20 | 微信支付文档: 21 | 22 | https://pay.weixin.qq.com/wiki/doc/api/index.html 23 | 24 | 银联支付文档: 25 | 26 | https://open.unionpay.com/ajweb/product 27 | 28 | # LICENSE 29 | 30 | [MIT](https://github.com/hueifeng/OmniPay/blob/master/LICENSE) 31 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET Core (.NET Framework) 2 | # Build and test ASP.NET Core projects targeting the full .NET Framework. 3 | # Add steps that publish symbols, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - task: NuGetToolInstaller@1 19 | 20 | - task: NuGetCommand@2 21 | inputs: 22 | restoreSolution: '$(solution)' 23 | 24 | - task: VSBuild@1 25 | inputs: 26 | solution: '$(solution)' 27 | msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"' 28 | platform: '$(buildPlatform)' 29 | configuration: '$(buildConfiguration)' 30 | 31 | - task: VSTest@2 32 | inputs: 33 | platform: '$(buildPlatform)' 34 | configuration: '$(buildConfiguration)' 35 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Certs/apiclient_cert.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hueifeng/OmniPay/3761886d6c1106e4018a22fd43726ff5e21e435f/samples/OmniPay.Pay/Certs/apiclient_cert.p12 -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Certs/certs.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hueifeng/OmniPay/3761886d6c1106e4018a22fd43726ff5e21e435f/samples/OmniPay.Pay/Certs/certs.rar -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Certs/unioncerts/acp_test_enc.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEQzCCAyugAwIBAgIFEAJkkicwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMC 3 | Q04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhv 4 | cml0eTEXMBUGA1UEAxMOQ0ZDQSBURVNUIE9DQTEwHhcNMTUxMjE1MDkxMTM1WhcN 5 | MTcxMjE1MDkxMTM1WjCBgTELMAkGA1UEBhMCY24xFzAVBgNVBAoTDkNGQ0EgVEVT 6 | VCBPQ0ExMRIwEAYDVQQLEwlDRkNBIFRFU1QxFDASBgNVBAsTC0VudGVycHJpc2Vz 7 | MS8wLQYDVQQDFCYwNDFAWjIwMTQtMTEtMTFAMDAwNDAwMDA6U0lHTkAwMDAwMDAw 8 | NTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHnoPx0JZKZmFjIURxN 9 | AbLlWAw2jiFFWBnDF2MIGkya2r0fGiR0knq8zkKUnoIyC+tzEiOavniQaSu0ucuv 10 | /V4ugz66PSRxw1gaPcR2dDVdgojF00TcewxlJEA65fK3eKhUYfC3NbRaVQOMMdwv 11 | 7nNEvzxvdExE47ceMya7FmsUPyLFu9X++chFQiYfr8nH+wdDeYo8w8vCX+Jd2vRu 12 | qDOah29CQfkAmXsx3D68zg0q4AjlLI1t5gLKiU5YoG6yWrigPyreEHh716rV8HkT 13 | jGWx3cxF/HsLZ/E4SgIr5yIZA6qw8RFqaSXuyw3iDrNf6aSJGO0GKlvxnvD20oGR 14 | JokCAwEAAaOB6TCB5jAfBgNVHSMEGDAWgBTPcJ1h6518Lrj3ywJA9wmd/jN0gDBI 15 | BgNVHSAEQTA/MD0GCGCBHIbvKgEBMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cu 16 | Y2ZjYS5jb20uY24vdXMvdXMtMTQuaHRtMDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6 17 | Ly91Y3JsLmNmY2EuY29tLmNuL1JTQS9jcmw0NTE3LmNybDALBgNVHQ8EBAMCA+gw 18 | HQYDVR0OBBYEFGjriHHUE1MnYX7H6GrFi+8R2zmUMBMGA1UdJQQMMAoGCCsGAQUF 19 | BwMCMA0GCSqGSIb3DQEBBQUAA4IBAQAjsN0fyDqcxS9YKMpY3CIdlarCjvnus+wS 20 | ExjNnPv7n2urqhz2Jf3yJuhxVVPzdgKT51C2UiR+/i1OJPWFx0IUos/v8js/TM5j 21 | mTdPkBsRSxSDieHHiuE1nPUwGXUEO7mlOVkkzmLI75bJ86foxNflbQCF0+VvpMe7 22 | KwQoNOR8DxIBxHdlsjSxE2RKM/ftXLhptrK4GK3K4FAcSiqBMEn5PF/5V9mHp5N6 23 | 3LdkMYqBj4pRcy8vrclucq99b2glmMLw7CI6Kxu22WVoRnZESjcgXiMVLLe+qy55 24 | 0pWcZ2BChS7Ln19tj49LnS3vFp6xf4qNSqxEBaQuNLEx0ObjI6pz 25 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Certs/unioncerts/acp_test_middle.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDzjCCAragAwIBAgIKGNDz/H99Hd/CxjANBgkqhkiG9w0BAQUFADBZMQswCQYD 3 | VQQGEwJDTjEwMC4GA1UEChMnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24g 4 | QXV0aG9yaXR5MRgwFgYDVQQDEw9DRkNBIFRFU1QgQ1MgQ0EwHhcNMTIwODMwMDMx 5 | NDMzWhcNMzEwNTExMDMxNDMzWjBYMQswCQYDVQQGEwJDTjEwMC4GA1UEChMnQ2hp 6 | bmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRcwFQYDVQQDEw5D 7 | RkNBIFRFU1QgT0NBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiL 8 | J/BrdvHSbXNfLIMTwUg9tDtVjMRGXOl6aZnu9IpxjI5SMUJ4hVwgJnmbTokxs6GF 9 | IXKsCLSm5H1jHLI22ysc/ltByEybLWj5jjJuC9+Uknbl3/Ls1RBG6MogUCqZckuo 10 | hKrf5DmlV3C/jVLxGn3pUeanvmqVUi4TKpXxgm5QqKSPF8VtQY4qCpNcQwwZqbMr 11 | D+IfJtfpGAeVrP+Kg6i1t65seeEnVSaLhqpRUDU0PTblOuUv3OhiKJWA3cYWxUrg 12 | 7U7SIHNJLSEUWmjy4mKty+g7Cnjzt29F9qXFb6oB2mR8yt4GHCilw1Rc5RBXY63H 13 | eTuOwdtGE3M2p7Q++OECAwEAAaOBmDCBlTAfBgNVHSMEGDAWgBR03sWNCn0QGqpp 14 | g1tNIc6Gm8xxODAMBgNVHRMEBTADAQH/MDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6 15 | Ly8yMTAuNzQuNDIuMy90ZXN0cmNhL1JTQS9jcmwxLmNybDALBgNVHQ8EBAMCAQYw 16 | HQYDVR0OBBYEFM9wnWHrnXwuuPfLAkD3CZ3+M3SAMA0GCSqGSIb3DQEBBQUAA4IB 17 | AQC0JOazrbkk0XMxMMeBCc3lgBId1RjQLgWUZ7zaUISpPstGIrE5A9aB6Ppq0Sxl 18 | pt2gkFhPEKUqgOFN1CzCDEbP3n4H0chqK1DOMrgTCD8ID5UW+ECTYNe35rZ+1JiF 19 | lOPEhFL3pv6XSkiKTfDnjum8+wFwUBGlfoWK1Hcx0P2Hk1jcZZKwGTx1IAkesF83 20 | pufhxHE2Ur7W4d4tfp+eC7XXcA91pdd+VUrAfkj9eKHcDEYZz66HvHzmt6rtJVBa 21 | pwrtCi9pW3rcm8c/1jSnEETZIaokai0fD7260h/LkD/GrNCibSWxFj1CqyP9Y5Yv 22 | cj6aA5LnUcJYeNkrQ3V4XvVc 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Certs/unioncerts/acp_test_root.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIKUhN+zB19hbc65jANBgkqhkiG9w0BAQUFADBZMQswCQYD 3 | VQQGEwJDTjEwMC4GA1UEChMnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24g 4 | QXV0aG9yaXR5MRgwFgYDVQQDEw9DRkNBIFRFU1QgQ1MgQ0EwHhcNMTIwODI5MDUw 5 | MTI5WhcNMzIwODI5MDUwMTI5WjBZMQswCQYDVQQGEwJDTjEwMC4GA1UEChMnQ2hp 6 | bmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRgwFgYDVQQDEw9D 7 | RkNBIFRFU1QgQ1MgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa 8 | rMJGruH6rOBPFxUI7T1ybydSRRtOM1xvkVjQNX0qmYir8feE6Tb0ctgtKR7a20DI 9 | YCj9kZ5ANBQqjRcj3Soq9XH3cirqhYHJ723OKyTpS0RPQ0N6vtVt3P5JQ+ztjWHd 10 | qIbbTOQ6O024TGTiqi6uHgMuz9/OVur81X3a5YVkK7jFeZ9o8cTcvQxD853/1sgZ 11 | QcmR9aUSw0RXH4XFLTrn7n4QSfWKiNotlD8Ag5gS1pH9ONUb6nGkMn3gh1xfJqjm 12 | ONMSknPXTGiNpXtqvYi8oIvByVCbUDO59IwPP1r1SYyE3P8Nr7DdQRu0KQSdXLoG 13 | iugSR3fn+toObVAQmplDAgMBAAGjXTBbMB8GA1UdIwQYMBaAFHTexY0KfRAaqmmD 14 | W00hzoabzHE4MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBR0 15 | 3sWNCn0QGqppg1tNIc6Gm8xxODANBgkqhkiG9w0BAQUFAAOCAQEAM0eTkM35D4hj 16 | RlGC63wY0h++wVPUvOrObqAVBbzEEQ7ScBienmeY8Q6lWMUTXM9ALibZklpJPcJv 17 | 3ntht7LL6ztd4wdX7E9RzZCQnRvbL9A/BU3NxWdeSpCg/OyPod5oCKP+6Uc7kApi 18 | F9OtYNWnt3l2Zp/NiedzEQD8H4qEWQLAq+0dFo5BkfVhb/jPcktndpfPOuH1IMhP 19 | tVcvo6jpFHw4U/nP2Jv59osIE97KJz/SPt2JAYnZOlIDqWwp9/Afvt0/MDr8y0PK 20 | Q9c6eqIzBx7a9LpUTUl5u1jS+xSDZ/KF2lXnjwaFp7jICLWEMlBstCoogi7KwH9A 21 | LpJP7/dj9g== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace OmniPay.Pay.Controllers 4 | { 5 | public class HomeController : Controller 6 | { 7 | public IActionResult Index() 8 | { 9 | return View(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Controllers/UnionPayController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using OmniPay.Core.Utils; 3 | using OmniPay.Unionpay; 4 | using OmniPay.Unionpay.Domain; 5 | using OmniPay.Unionpay.Request; 6 | using System.Threading.Tasks; 7 | 8 | namespace OmniPay.Pay.Controllers 9 | { 10 | public class UnionPayController : ControllerBase 11 | { 12 | private readonly IUnionPayClient _client; 13 | 14 | public UnionPayController(IUnionPayClient client) 15 | { 16 | this._client = client; 17 | } 18 | 19 | /// 20 | /// 跳转网关页面支付 21 | /// 22 | /// 23 | [HttpPost] 24 | public async Task FrontTrans(string txnAmt, string orderId) 25 | { 26 | var request = new FrontTransRequest(); 27 | request.AddParameters(new FrontTransModel 28 | { 29 | TxnAmt = txnAmt, 30 | OrderId = orderId 31 | }, StringCase.Camel); 32 | return Ok(await _client.ExecuteAsync(request)); 33 | } 34 | 35 | /// 36 | /// 消费撤销接口 37 | /// 38 | /// 39 | [HttpPost] 40 | public async Task BackTrans(string txnAmt, string orderId) 41 | { 42 | var request = new BackTransRequest(); 43 | request.AddParameters(new BackTransModel 44 | { 45 | TxnAmt = txnAmt, 46 | OrderId = orderId, 47 | }, StringCase.Camel); 48 | return Ok(await _client.ExecuteAsync(request)); 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Controllers/WeChatPayController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Mvc; 3 | using OmniPay.Wechatpay; 4 | using OmniPay.Wechatpay.Domain; 5 | using OmniPay.Wechatpay.Request; 6 | 7 | namespace OmniPay.Pay.Controllers 8 | { 9 | 10 | public class WeChatPayController : ControllerBase 11 | { 12 | private readonly IWeChatPayClient _client; 13 | 14 | public WeChatPayController(IWeChatPayClient client) 15 | { 16 | _client = client; 17 | } 18 | /// 19 | /// 扫码支付 20 | /// 21 | /// 22 | [HttpPost] 23 | public async Task ScanPay(string Body,string Out_Trade_No,int Total_Amount) 24 | { 25 | var request = new ScanPayRequest(); 26 | request.AddParameters(new ScanPayModel() 27 | { 28 | Body = Body, 29 | OutTradeNo = Out_Trade_No, 30 | TotalFee = Total_Amount 31 | }); 32 | return Ok(await _client.ExecuteAsync(request)); 33 | } 34 | /// 35 | /// H5支付 36 | /// 37 | /// 38 | /// 39 | /// 40 | /// 41 | [HttpPost] 42 | public async Task WapPay(string Body, string Out_Trade_No, int Total_Amount) 43 | { 44 | var request = new WapPayRequest(); 45 | request.AddParameters(new WapPayModel() 46 | { 47 | Body = Body, 48 | OutTradeNo = Out_Trade_No, 49 | TotalFee = Total_Amount 50 | }); 51 | return Ok(await _client.ExecuteAsync(request)); 52 | } 53 | /// 54 | /// App支付 55 | /// 56 | /// 57 | /// 58 | /// 59 | /// 60 | [HttpPost] 61 | public async Task AppPay(string Body,string Out_Trade_No,int Total_Amount) 62 | { 63 | var request=new AppPayRequest(); 64 | request.AddParameters(new AppPayModel() 65 | { 66 | Body = Body, 67 | OutTradeNo = Out_Trade_No, 68 | TotalFee = Total_Amount 69 | }); 70 | return Ok(await _client.ExecuteAsync(request)); 71 | } 72 | 73 | /// 74 | /// 公众号支付 75 | /// 76 | /// 77 | /// 78 | /// 79 | /// 80 | /// 81 | [HttpPost] 82 | public async Task PublicPay(string Body,string OutTradeNo,int TotalAmount,string OpenId) 83 | { 84 | var request=new PublicPayRequest(); 85 | request.AddParameters(new PublicPayModel() 86 | { 87 | Body = Body, 88 | OutTradeNo = OutTradeNo, 89 | TotalFee = TotalAmount, 90 | OpenId = OpenId 91 | }); 92 | return Ok(await _client.ExecuteAsync(request)); 93 | } 94 | 95 | /// 96 | /// 小程序支付 97 | /// 98 | /// 99 | [HttpPost] 100 | public async Task AppletPay(string Body,string OutTradeNo,int TotalAmount,string OpenId) 101 | { 102 | var request=new AppletPayRequest(); 103 | request.AddParameters(new AppletPayModel() 104 | { 105 | Body = Body, 106 | OutTradeNo = OutTradeNo, 107 | TotalFee = TotalAmount, 108 | OpenId = OpenId 109 | }); 110 | return Ok(await _client.ExecuteAsync(request)); 111 | } 112 | 113 | /// 114 | /// 查询订单 115 | /// 116 | /// 117 | /// 118 | /// 119 | [HttpPost] 120 | public async Task Query(string OuttradeNo, string TradeNo) 121 | { 122 | var request = new QueryRequest(); 123 | request.AddParameters(new QueryModel() 124 | { 125 | TransactionId = TradeNo, 126 | OutTradeNo = OuttradeNo 127 | }); 128 | return Ok(await _client.ExecuteAsync(request)); 129 | } 130 | 131 | /// 132 | /// 关闭订单 133 | /// 134 | /// 135 | /// 136 | [HttpPost] 137 | public async Task Close(string OutTradeNo) 138 | { 139 | var request = new CloseRequest(); 140 | request.AddParameters(new CloseModel() 141 | { 142 | OutTradeNo = OutTradeNo 143 | }); 144 | return Ok(await _client.ExecuteAsync(request)); 145 | } 146 | 147 | /// 148 | /// 申请退款 149 | /// 150 | /// 151 | /// 152 | /// 153 | /// 154 | /// 155 | /// 156 | [HttpPost] 157 | public async Task Refund(string transactionId,string outTradeNo,string outRefundNo,int refundFee, 158 | string refundDesc) 159 | { 160 | var request = new RefundRequest(); 161 | request.AddParameters(new RefundModel() 162 | { 163 | OutTradeNo = outTradeNo, 164 | RefundFee = refundFee, 165 | TransactionId = transactionId, 166 | OutRefundNo = outRefundNo, 167 | RefundDesc = refundDesc 168 | }); 169 | return Ok(await _client.ExecuteAsync(request)); 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /samples/OmniPay.Pay/OmniPay.Pay.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | OmniPay.Pay 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Always 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace OmniPay.Pay 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:6113", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "AspNetCore.Free.Pay": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using OmniPay.Alipay.Extensions; 7 | using OmniPay.Core.Configuration.DependencyInjection; 8 | using OmniPay.Unionpay.Extensions; 9 | using OmniPay.Wechatpay.Extensions; 10 | using System.Net.Http; 11 | using System.Security.Cryptography.X509Certificates; 12 | using System.Text; 13 | 14 | namespace OmniPay.Pay 15 | { 16 | public class Startup 17 | { 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | } 22 | 23 | public IConfiguration Configuration { get; } 24 | 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | public void ConfigureServices(IServiceCollection services) 27 | { 28 | //微信支付中有些接口需要对证书配置 29 | var clientCertificate = 30 | new X509Certificate2( 31 | "Certs/apiclient_cert.p12", "1233410002"); 32 | var handler = new HttpClientHandler(); 33 | handler.ClientCertificates.Add(clientCertificate); 34 | //以命名式客户端处理,可延伸相关特性 35 | services.AddHttpClient("WeChatPaysHttpClientName", c => 36 | { 37 | }).ConfigurePrimaryHttpMessageHandler(() => handler); 38 | 39 | services.AddOmniPayService(builder => 40 | { 41 | builder.Services.AddWeChatPay(options => 42 | { 43 | Configuration.GetSection("WeChatPays").Bind(options); 44 | }).AddValidators(); 45 | builder.Services.AddAliPay(options => 46 | { 47 | Configuration.GetSection("AliPays").Bind(options); 48 | }); 49 | builder.Services.AddUnionPay(options => 50 | { 51 | Configuration.GetSection("Unionpays").Bind(options); 52 | }); 53 | }); 54 | 55 | services.AddControllersWithViews(); 56 | } 57 | 58 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 59 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 60 | { 61 | if (env.IsDevelopment()) 62 | { 63 | app.UseDeveloperExceptionPage(); 64 | } 65 | 66 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 67 | app.UseStaticFiles(); 68 | app.UseRouting(); 69 | 70 | app.UseAuthorization(); 71 | app.UseEndpoints(endpoints => 72 | { 73 | endpoints.MapControllerRoute( 74 | name: "default", 75 | pattern: "{controller=Home}/{action=Index}"); 76 | }); 77 | 78 | // app.UseOmniPay(); 79 | // app.UseAliOmniPay(); 80 | // app.UseUnionPay(); 81 | 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewData["Title"] 7 | 8 | @RenderSection("Styles", required: false) 9 | 10 | 11 | @RenderBody() 12 | 13 | 14 | @RenderSection("Scripts", required: false) 15 | 16 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -------------------------------------------------------------------------------- /samples/OmniPay.Pay/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } -------------------------------------------------------------------------------- /samples/OmniPay.Pay/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OmniPay.Pay 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string Summary { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "WeChatPays": { 11 | "AppId": "wxdace645e0bc2c424", 12 | "AppSecret": "4693afc6b2084885ca9fbc2355b97827", 13 | "BaseUrl": "https://api.mch.weixin.qq.com", 14 | "MchId": "1900009641", 15 | "NotifyUrl": "http://localhost:6113", 16 | "Key": "b7c996fbda5a9633ee4feb6b991c3919", 17 | "HttpClientName": "WeChatPaysHttpClientName" 18 | }, 19 | "Alipays": { 20 | "AppId": "2021000116668110", 21 | "NotifyUrl": "http://localhost:6113/Notify", 22 | "ReturnUrl": "http://localhost:6113/Notify", 23 | "AlipayPublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmx3bj8uy6tN1LtrouRoe0BMCRVtfgKQDRN9mYbueSnd0M5//SiJuvkRkxVfuUIylb/5xTjPGT5VdPKDiyfv3d+9UsqcQnotp/oQm3UF8SLBishvXH02ufvg2qxXiMp2sORYLCSpp0Ycq5gcyOwJdv/4nBGDhD6FWmtbqbSPmu2NZDpKpGS1IoM3PF1heZIoXx7LtBL9MKh6g1TStwRCo/QXrZAn5A4o9/TojwDob3yfdC3acnnJcfEsjGAsJ5Ivxj2Ls20LWI+DHnQVMrByDXnH5qrRyAg6LBvcidAAusUwbQZdsoJkGLu7whh3xMixLCr1Qhjr9hILcskuUoqxK4QIDAQAB", 24 | "Privatekey": "MIIEowIBAAKCAQEA2HLTSTfuFFhRDmb9xc4DVz/e8o/SCXknvPwjQuRIC+MFr+ptj4t3uF4w/j8rqYx2V0ttyGI+MA4byMI/9RYoeX2p9JDcrQHYPIrcmgYIeP5cAl1n1oRhMVjAhtQeI2hYfbelV51SdhGPfIrcUdRNjXMxErrRABeK7vjjdq69ksYdncOOJWZ5O7104Xx98+cOkgJGYABU9V11Gz+/X4P5x90g9iL2pfOmBoMzedfPVLcD4w513emjG3IhHeEdi8vVrmPw7AecF8JPYIpRJBsgM9K+LycwSXW0tMpEjmF28ioisTHvbZpEa+DSEFBIVECry6xXw/utzv7IYLmNwQM3qwIDAQABAoIBAQCWu0gGOivTATnZRMG/KVtMPE9/LcbAEB4rTo8juyOtG3jPa/rGNwK1YurNc6JtXULgQcf+/uN9xaV1pkix3a9sA6YCiYsT9C/o4W8E1+S4lbHvd6qjSecBXWQdwMQINldBnU1IeWd+j3YT7gPF/InRUoG/IFgBr2NyTeLhuIiOF56wEqLa3m1KChGjpMMkJWObBTQqoAAiWe327XxYETRz38x1qc8sfGGleyCo2QqRS6o7A7BdsrkSgMq7HAoo9gZsQBOIEKEsi0gdFq47/ybHDw43TXhMeL0PM7ezKJ4ky/bg/N9PVGP3mKlD2snXk+lUZikkmNH56mVNSAq3Wi3hAoGBAP2FF3+RODmPoNqtcfk6luMaMerZOdcgnmsys0ry/ji1VYeTysfvXwWXT6kcoWG6OFPvPKUgTtAvy2X4GV7KCnmpkM2YL309ha6HYc3dAJHrYObwLG6mPIVoOQsgxYqrNeDAL0PmcMfy9F/ergRjcQfxwwajpDIF+sAMVIfujCW7AoGBANqQ5KBjXKRPhg1eP5PTD+xIhJNffMD7nL9m1R71XjNWdMXOhdCFb1di1CBmb5V9KEHZudKSnAaL8n5jjOtoCNX3vcBOjcY7LcEpOFWrvKWu/PQ3OUSaNvjEM82TDffhXsdhPz+FXmhf4WWKytmU9KIsHK16akKSjbc17z1vfZ7RAoGAZ1LU7uLqvVryPe2uo9rjIA/PBF4gGrNqnVn+hK9gORB+mVD8tluyqH6wssW+aCwTRPIeD1aJiIPSK+7fuCgz+L3JDGHYCP0H/MekbtiBoPcDeVutYDNUOzLs/MIQgKGixcTN/qhukq9MNb51wcgdixVXXN9YziJtvdPIp9XrPH0CgYADfQiJHszdun8zO2vcWiQI62diSsXc9qcbzvJb2iK0ww5+EbvFBjwust8b3Uauph68XlM+7yQaXqVyKviW0URC1f9rUFWm8k7apGPHykPqiQ50f2UkmSmDcu44u74fVLOEjyLJSsGk/NLGIh72tg/pfra+dhO4GEq2v9+fpWXl4QKBgE4UzbW/VBN7mFF2sDuN7W9WGl0r0ikCP+2UBMCBsNPORQlHRkFbTB0wdKwbCmvrG3iRmk947KzdZKJSmkMlLjTCrKddAwog/yPzEvzIV5eUctARXyox7WTihEo/JIqwmbzTiAcXqVYhzDKUMP4k42J/Ugc0JMZXu0hXwPsV77kn", 25 | "BaseUrl": "https://openapi.alipaydev.com" 26 | }, 27 | "Unionpays": { 28 | "Version": "5.1.0", 29 | "SignMethod": "01", 30 | "MerId": "777290058110048", 31 | "SignCertPath": "Certs/unioncerts/acp_test_sign.pfx", 32 | "SignCertPwd": "000000", 33 | "EncryptCertPath": "Certs/unioncerts/acp_test_enc.cer", 34 | "MiddleCertPath": "Certs/unioncerts/acp_test_middle.cer", 35 | "RootCertPath": "Certs/unioncerts/acp_test_root.cer", 36 | "BaseUrl": "https://gateway.test.95516.com/gateway/api", 37 | "BackUrl": "http://localhost:6113/", 38 | "FrontUrl": "http://localhost:6113/" 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/htmlpage.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/wwwroot/demo.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | /*! index页面 */ 3 | .ui-tabs-vertical { 4 | width: 51.5em; 5 | } 6 | 7 | .ui-tabs-vertical .ui-tabs-nav { 8 | padding: .2em .1em .2em .2em; 9 | float: left; 10 | width: 11em; 11 | } 12 | 13 | .ui-tabs-vertical .ui-tabs-nav li { 14 | clear: left; 15 | width: 100%; 16 | border-bottom-width: 1px !important; 17 | border-right-width: 0 !important; 18 | margin: 0 -1px .2em 0; 19 | } 20 | 21 | .ui-tabs-vertical .ui-tabs-nav li a { 22 | display: block; 23 | } 24 | 25 | .ui-tabs-vertical .ui-tabs-nav li.ui-tabs-active { 26 | padding-bottom: 0; 27 | padding-right: .1em; 28 | border-right-width: 1px; 29 | border-right-width: 1px; 30 | } 31 | 32 | .ui-tabs-vertical .ui-tabs-panel { 33 | padding: 1.2em; 34 | float: right; 35 | width: 37.5em; 36 | } 37 | 38 | .ui-tabs { 39 | font-family: 微软雅黑; 40 | } 41 | 42 | div#header { 43 | width: inherit; 44 | height: 3em; 45 | } 46 | 47 | div#wrapper { 48 | font-family: 微软雅黑; 49 | width: 62em; 50 | margin: 0 auto; 51 | font-size: 16px; 52 | color: #5E5E5E; 53 | } 54 | 55 | h1, h2, h3, h4, h5, h6, h7, .ui-tooltip { 56 | font-family: 微软雅黑 !important; 57 | color: #555 !important; 58 | } 59 | 60 | 61 | /*! 各个接口的页面 */ 62 | hr { 63 | height: 1px; 64 | border: none; 65 | border-top: 1px dashed #9DC45F; 66 | } 67 | 68 | .api-form { 69 | margin-left: auto; 70 | margin-right: auto; 71 | background: #F8F8F8; 72 | padding: 30px 30px 20px 30px; 73 | color: #666; 74 | border-radius: 5px; 75 | -webkit-border-radius: 5px; 76 | -moz-border-radius: 5px; 77 | } 78 | 79 | .api-form p { 80 | display: block; 81 | margin: 0px 0px 5px; 82 | } 83 | 84 | .api-form p > label { 85 | float: left; 86 | width: 10em; 87 | text-align: right; 88 | padding-right: 0.5em; 89 | margin-top: .1em; 90 | color: #5E5E5E; 91 | } 92 | 93 | .api-form input[type="text"] { 94 | border: 1px solid #E5E5E5; 95 | ; 96 | color: #555; 97 | height: 20px; 98 | line-height: 15px; 99 | margin-bottom: 16px; 100 | margin-right: 6px; 101 | margin-top: 2px; 102 | outline: 0 none; 103 | padding: 5px 0px 5px 5px; 104 | width: 65%; 105 | border-radius: 4px; 106 | -webkit-border-radius: 4px; 107 | -moz-border-radius: 4px; 108 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 109 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 110 | -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 111 | } 112 | 113 | .api-form input.button { 114 | font-family: 微软雅黑; 115 | background-color: #9DC45F; 116 | border-radius: 5px; 117 | -webkit-border-radius: 5px; 118 | -moz-border-border-radius: 5px; 119 | border: none; 120 | padding: 10px 25px 10px 25px; 121 | color: #FFF; 122 | text-shadow: 1px 1px 1px #949494; 123 | } 124 | 125 | .api-form input.button:hover { 126 | font-family: 微软雅黑; 127 | background-color: #80A24A; 128 | } 129 | 130 | .api-form .showFaqBtn { 131 | font-family: 微软雅黑; 132 | float: right; 133 | background-color: transparent; 134 | color: inherit; 135 | border: none; 136 | padding: 10px 25px 10px 25px; 137 | } 138 | 139 | .faq { 140 | font-size: 14px; 141 | font-weight: normal; 142 | } 143 | -------------------------------------------------------------------------------- /samples/OmniPay.Pay/wwwroot/demo.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $("#tabs-api").tabs(); 3 | setApiDemoTabs("#tabs-purchase-alipay"); 4 | setApiDemoTabs("#tabs-purchase-wechatpay"); 5 | setApiDemoTabs("#tabs-purchase-qpay"); 6 | setApiDemoTabs("#tabs-purchase-jdpay"); 7 | setApiDemoTabs("#tabs-purchase-unionpay"); 8 | $(document).tooltip(); 9 | }); 10 | 11 | function setApiDemoTabs(selector) { 12 | $(selector).tabs({ 13 | beforeLoad: function (event, ui) { 14 | ui.jqXHR.error(function () { ui.panel.html("加载中"); }); 15 | }, load: function (event, ui) { 16 | $(".question").hide(); 17 | $(".showFaqBtn").click( 18 | function () { $(".question").toggle(); }); 19 | } 20 | }).addClass("ui-tabs-vertical ui-helper-clearfix"); 21 | $(selector + " li").removeClass("ui-corner-top").addClass("ui-corner-left"); 22 | } -------------------------------------------------------------------------------- /samples/OmniPay.Pay/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hueifeng/OmniPay/3761886d6c1106e4018a22fd43726ff5e21e435f/samples/OmniPay.Pay/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/OmniPay.Alipay/AliPayClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Options; 3 | using OmniPay.Alipay.Response; 4 | using OmniPay.Core.Exceptions; 5 | using OmniPay.Core.Request; 6 | using OmniPay.Core.Utils; 7 | using System; 8 | using System.Threading.Tasks; 9 | using Newtonsoft.Json; 10 | using Newtonsoft.Json.Linq; 11 | using OmniPay.Alipay.Request; 12 | 13 | namespace OmniPay.Alipay 14 | { 15 | public class AliPayClient : IAliPayClient 16 | { 17 | private readonly AlipayOptions _alipayOptions; 18 | private readonly ILogger _logger; 19 | private readonly IHttpHandler _httpHandler; 20 | 21 | public AliPayClient(IOptions alioptions, ILogger logger, 22 | IHttpHandler httpHandler) 23 | { 24 | this._alipayOptions = alioptions.Value; 25 | this._logger = logger; 26 | this._httpHandler = httpHandler; 27 | } 28 | 29 | /// 30 | /// Execute AliPay API request implementation 31 | /// 32 | /// 33 | /// 34 | /// 35 | /// 36 | public async Task ExecuteAsync(BaseRequest request) 37 | { 38 | BuildParams(request); 39 | if (request is AppPayRequest || request is WapPayRequest || request is WebPagePayRequest) 40 | { 41 | return (TResponse)Activator.CreateInstance(typeof(TResponse), request); 42 | } 43 | string result = await _httpHandler.PostAsync(request.RequestUrl, request.ToUrl()); 44 | var jObject = JObject.Parse(result); 45 | var jToken = jObject.First.First; 46 | var sign = jObject.Value("sign"); 47 | if (!string.IsNullOrEmpty(sign) && 48 | !CheckSign(jToken.ToString(Formatting.None), sign, _alipayOptions.AlipayPublicKey, _alipayOptions.SignType)) 49 | { 50 | _logger.LogError("Signature verification failed:{0}", result); 51 | throw new OmniPayException("Signature verification failed."); 52 | } 53 | 54 | var baseResponse = (BaseResponse)(object)jToken.ToObject(); 55 | baseResponse.Raw = result; 56 | baseResponse.Sign = sign; 57 | return (TResponse)(object)baseResponse; 58 | } 59 | 60 | private void BuildParams(BaseRequest request) 61 | { 62 | if (string.IsNullOrEmpty(_alipayOptions.AppId)) 63 | { 64 | throw new OmniPayException(nameof(_alipayOptions.AppId)); 65 | } 66 | request.Add("app_id", _alipayOptions.AppId); 67 | request.Add("format", _alipayOptions.Format); 68 | request.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); 69 | request.Add("sign_type", _alipayOptions.SignType); 70 | request.Add("charset", _alipayOptions.Charset); 71 | request.Add("version", "1.0"); 72 | request.Add("biz_content", request.ToStringCaseObj(request).ToJson()); 73 | request.Add("sign", EncryptUtil.RSA(request.ToUrl(false), _alipayOptions.PrivateKey, _alipayOptions.SignType)); 74 | request.RequestUrl = _alipayOptions.BaseUrl + request.RequestUrl; 75 | } 76 | 77 | internal static bool CheckSign(string data, string sign, string alipayPublicKey, string signType) 78 | { 79 | var result = EncryptUtil.RSAVerifyData(data, sign, alipayPublicKey, signType); 80 | if (!result) 81 | { 82 | data = data.Replace("/", "\\/"); 83 | result = EncryptUtil.RSAVerifyData(data, sign, alipayPublicKey, signType); 84 | } 85 | 86 | return result; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/AlipayOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OmniPay.Alipay 4 | { 5 | public class AlipayOptions 6 | { 7 | 8 | /// 9 | /// 应用ID 10 | /// 11 | public string AppId { get; set; } 12 | 13 | /// 14 | /// 签名类型 15 | /// 16 | public string SignType { get; set; } = "RSA2"; 17 | 18 | /// 19 | /// 格式 20 | /// 21 | public string Format => "JSON"; 22 | 23 | /// 24 | /// 时间戳 25 | /// 26 | public string Timestamp => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); 27 | 28 | /// 29 | /// 版本 30 | /// 31 | public string Version => "1.0"; 32 | 33 | /// 34 | /// 编码格式 35 | /// 36 | public string Charset => "UTF-8"; 37 | 38 | /// 39 | /// 商户私钥 40 | /// 41 | public string PrivateKey { get; set; } 42 | 43 | /// 44 | /// 支付宝公钥 45 | /// 46 | public string AlipayPublicKey { get; set; } 47 | 48 | /// 49 | /// 返回地址 50 | /// 51 | public string ReturnUrl { get; set; } 52 | 53 | /// 54 | /// 网关回发通知URL 55 | /// 56 | public string NotifyUrl { get; set; } 57 | 58 | public string BaseUrl { get; set; } 59 | } 60 | } -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Domain/AppPayModel.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Alipay.Domain 2 | { 3 | /// 4 | /// app支付 5 | /// 6 | public class AppPayModel : BasePayModel 7 | { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Domain/BarCodePayModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Alipay.Domain 6 | { 7 | /// 8 | /// 条码支付 9 | /// 10 | public class BarCodePayModel : BasePayModel 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Domain/BasePayModel.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Alipay.Domain 2 | { 3 | /// 4 | /// 支付宝支付基类 5 | /// 6 | public class BasePayModel 7 | { 8 | /// 9 | /// 订单金额,单位为元 10 | /// 11 | public double TotalAmount { get; set; } 12 | 13 | /// 14 | /// 对一笔交易的具体描述信息。 15 | /// 16 | public string Body { get; set; } 17 | 18 | /// 19 | /// 商户网站唯一订单号 20 | /// 21 | public string OutTradeNo { get; set; } 22 | 23 | /// 24 | /// 商品的标题/交易标题/订单标题/订单关键字等。 25 | /// 26 | public string Subject { get; set; } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Domain/Goods.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace OmniPay.Alipay.Domain 4 | { 5 | public class Goods 6 | { 7 | /// 8 | /// 商品编码 9 | /// 10 | public string Id { get; set; } 11 | 12 | /// 13 | /// 支付宝统一商品编号 14 | /// 15 | public string AlipayGoodsId { get; set; } 16 | 17 | /// 18 | /// 商品名称 19 | /// 20 | [JsonProperty("goods_name")] 21 | public string Name { get; set; } 22 | 23 | /// 24 | /// 商品数量 25 | /// 26 | public int Quantity { get; set; } 27 | 28 | /// 29 | /// 商品单价,单位为元 30 | /// 31 | public double Price { get; set; } 32 | 33 | /// 34 | /// 商品类目 35 | /// 36 | public string Category { get; set; } 37 | 38 | /// 39 | /// 商品描述信息 40 | /// 41 | public string Body { get; set; } 42 | 43 | /// 44 | /// 展品展示地址 45 | /// 46 | public string ShowUrl { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Domain/Payee.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Alipay.Domain 6 | { 7 | public class Payee 8 | { 9 | /// 10 | /// 收款方的唯一标识 11 | /// 12 | public string identity { get; set; } 13 | 14 | /// 15 | /// 收款方的唯一标识 16 | /// 17 | public string identity_type { get; set; } 18 | 19 | /// 20 | /// 参与方真实姓名,如果非空,将校验收款支付宝账号姓名一致性。当identity_type=ALIPAY_LOGON_ID时,本字段必填。 21 | /// 22 | public string name { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Domain/ScanPayModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace OmniPay.Alipay.Domain 4 | { 5 | public class ScanPayModel 6 | { 7 | /// 8 | /// 商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复 9 | /// 10 | public string OutTradeNo { get; set; } 11 | 12 | /// 13 | /// 卖家编码,如果该值为空,则默认为商户签约账号对应的支付宝用户ID 14 | /// 15 | public string SellerId { get; set; } 16 | 17 | /// 18 | /// 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] 19 | /// 20 | public double TotalAmount { get; set; } 21 | 22 | /// 23 | /// 参与优惠计算的金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]。 24 | /// 25 | public double DiscountableAmount { get; set; } 26 | 27 | /// 28 | /// 订单标题 29 | /// 30 | public string Subject { get; set; } 31 | 32 | /// 33 | /// 订单包含的商品列表信息 34 | /// 35 | public List GoodsDetail { get; set; } 36 | 37 | /// 38 | /// 订单描述 39 | /// 40 | public string Body { get; set; } 41 | 42 | /// 43 | /// 卖家端自定义的的操作员编号 44 | /// 45 | public string OperatorId { get; set; } 46 | 47 | /// 48 | /// 商户门店编号 49 | /// 50 | public string StoreId { get; set; } 51 | 52 | /// 53 | /// 禁用渠道,用户不可用指定渠道支付,当有多个渠道时用“,”分隔 54 | /// 55 | public string DisablePayChannels { get; set; } 56 | 57 | /// 58 | /// 可用渠道,用户只能在指定渠道范围内支付,当有多个渠道时用“,”分隔 59 | /// 60 | public string EnablePayChannels { get; set; } 61 | 62 | /// 63 | /// 商户的终端编号 64 | /// 65 | public string TerminalId { get; set; } 66 | 67 | /// 68 | /// 该笔订单允许的最晚付款时间,逾期将关闭交易。 69 | /// 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 70 | /// 该参数数值不接受小数点, 如 1.5h,可转换为 90m 71 | /// 72 | public string TimeoutExpress { get; set; } 73 | 74 | /// 75 | /// 商户传入业务信息,具体值要和支付宝约定 76 | /// 将商户传入信息分发给相应系统,应用于安全,营销等参数直传场景 77 | /// 格式为json格式 78 | /// 79 | public string BusinessParams { get; set; } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Domain/WebPayModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace OmniPay.Alipay.Domain 4 | { 5 | /// 6 | /// 电脑网站支付 7 | /// 8 | public class WebPayModel : BasePayModel 9 | { 10 | /// 11 | /// 订单包含的商品列表信息 12 | /// 13 | public List GoodsDetail { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Endpoints/AliScanPayEndpoint.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Extensions.Logging; 3 | using OmniPay.Alipay.Endpoints.Result; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Results; 6 | using System.Net; 7 | 8 | namespace OmniPay.Alipay.Endpoints 9 | { 10 | public class AliScanPayEndpoint : IEndpointHandler 11 | { 12 | private readonly ILogger _logger; 13 | private readonly IAliPayClient _client; 14 | 15 | public AliScanPayEndpoint(ILogger logger, IAliPayClient client) 16 | { 17 | this._logger = logger; 18 | this._client = client; 19 | } 20 | 21 | public IEndpointResult Process(HttpContext context) 22 | { 23 | _logger.LogDebug("Start WechatScanPay request"); 24 | 25 | if (!HttpMethods.IsPost(context.Request.Method)) 26 | { 27 | _logger.LogWarning("Invalid HTTP method for ScanPay endpoint."); 28 | return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); 29 | } 30 | _logger.LogTrace("End WechatScanPay request. result type: {0}", this?.GetType().ToString() ?? "-none-"); 31 | return new ScanPayResult(_client); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Endpoints/Result/ScanPayResult.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using OmniPay.Core.Hosting; 3 | using System; 4 | using OmniPay.Core.Utils; 5 | using System.Threading.Tasks; 6 | using OmniPay.Alipay.Request; 7 | using OmniPay.Alipay.Domain; 8 | 9 | namespace OmniPay.Alipay.Endpoints.Result 10 | { 11 | public class ScanPayResult : IEndpointResult 12 | { 13 | private readonly IAliPayClient _client; 14 | 15 | public ScanPayResult(IAliPayClient client) 16 | { 17 | this._client = client; 18 | } 19 | 20 | public async Task ExecuteAsync(HttpContext context) 21 | { 22 | try 23 | { 24 | var body = (await context.Request.ReadFormAsync()).AsNameValueCollection(); 25 | if (body?.Count == 0) 26 | { 27 | throw new ArgumentNullException(nameof(body)); 28 | } 29 | var request = new ScanPayRequest(); 30 | request.AddParameters(new ScanPayModel() 31 | { 32 | Body = body.Get("Body"), 33 | OutTradeNo = body.Get("Out_Trade_No"), 34 | TotalAmount = double.Parse(body.Get("Total_Amount")), 35 | Subject = body.Get("Subject") 36 | }); 37 | await context.Response.WriteAsync((await _client.ExecuteAsync(request)).ToJson()); 38 | } 39 | catch (Exception ex) 40 | { 41 | context.Response.StatusCode = StatusCodes.Status500InternalServerError; 42 | await context.Response.WriteAsync(ex.Message); 43 | } 44 | context.Response.ContentType = "application/json; charset=UTF-8"; 45 | await context.Response.Body.FlushAsync(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Enums/BillType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Alipay.Enums 6 | { 7 | public enum BillType 8 | { 9 | /// 10 | /// 商户基于支付宝交易收单的业务账单 11 | /// 只有当面付接口可以下载trade类型的账单 12 | /// 13 | Trade, 14 | 15 | /// 16 | /// 于商户支付宝余额收入及支出等资金变动的帐务账单 17 | /// 18 | SignCustomer 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Enums/TransBizScene.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Alipay.Enums 6 | { 7 | public enum TransBizScene 8 | { 9 | /// 10 | /// 单笔无密转账到支付宝/银行卡, B2C现金红包 11 | /// 12 | DIRECT_TRANSFER, 13 | 14 | /// 15 | /// C2C现金红包-领红包 16 | /// 17 | PERSONAL_COLLECTION 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Enums/TransIdentityType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Alipay.Enums 6 | { 7 | public enum TransIdentityType 8 | { 9 | /// 10 | /// 支付宝的会员ID 11 | /// 12 | ALIPAY_USER_ID, 13 | 14 | /// 15 | /// 支付宝登录号,支持邮箱和手机号格式 16 | /// 17 | ALIPAY_LOGON_ID 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Enums/TransProductCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Text; 5 | 6 | namespace OmniPay.Alipay.Enums 7 | { 8 | /// 9 | /// 转账业务产品代码 10 | /// 11 | public enum TransProductCode 12 | { 13 | /// 14 | /// 单笔无密转账到支付宝账户 15 | /// 16 | TRANS_ACCOUNT_NO_PWD, 17 | 18 | /// 19 | /// 单笔无密转账到银行卡 20 | /// 21 | TRANS_BANKCARD_NO_PWD, 22 | 23 | /// 24 | /// 收发现金红包 25 | /// 26 | STD_RED_PACKET 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Extensions/AliPayApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using OmniPay.Alipay.Middleware; 5 | using OmniPay.Core.Utils; 6 | 7 | namespace OmniPay.Alipay.Extensions 8 | { 9 | public static class AliPayApplicationBuilderExtensions 10 | { 11 | /// 12 | /// 使用OmniPay 13 | /// 14 | /// 15 | /// 16 | public static IApplicationBuilder UseAliOmniPay(this IApplicationBuilder app) 17 | { 18 | app.UseMiddleware(); 19 | //var httpContextAccessor = app.ApplicationServices.GetRequiredService(); 20 | //HttpUtil.Configure(httpContextAccessor); 21 | return app; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Extensions/AlipayServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | 4 | namespace OmniPay.Alipay.Extensions 5 | { 6 | public static class AlipayServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddAliPay(this IServiceCollection services, Action action) 9 | { 10 | //services.AddTransient(); 11 | //services.AddSingleton(); 12 | //services.AddSingleton(new Endpoint("aliScanPay", "/pay-api/alipay/ScanPay", typeof(AliScanPayEndpoint))); 13 | return services.AddAliServices(action); 14 | } 15 | 16 | private static IServiceCollection AddAliServices(this IServiceCollection services, Action action) 17 | { 18 | if (action != null) 19 | { 20 | services.Configure(action.Invoke); 21 | } 22 | services.AddSingleton(); 23 | return services; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/IAliPayClient.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using System.Threading.Tasks; 3 | 4 | namespace OmniPay.Alipay 5 | { 6 | public interface IAliPayClient 7 | { 8 | /// 9 | /// Execute AliPay API request 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | Task ExecuteAsync(BaseRequest request); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Middleware/AliPayMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Extensions.Logging; 3 | using OmniPay.Core.Hosting; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace OmniPay.Alipay.Middleware 8 | { 9 | public class AliPayMiddleware 10 | { 11 | private readonly ILogger _logger; 12 | private readonly RequestDelegate _next; 13 | 14 | public AliPayMiddleware(ILogger logger,RequestDelegate next) 15 | { 16 | this._logger = logger; 17 | this._next = next; 18 | } 19 | 20 | public async Task InvokeAsync(HttpContext context, IEndpointRouter router) 21 | { 22 | try 23 | { 24 | var endpoint = router.Find(context); 25 | if (endpoint != null) 26 | { 27 | _logger.LogInformation("Invoking WechatPay endpoint: {endpointType} for {url}", endpoint.GetType().FullName, context.Request.Path.ToString()); 28 | 29 | var result = endpoint.Process(context); 30 | 31 | if (result != null) 32 | { 33 | _logger.LogTrace("Invoking result: {type}", result.GetType().FullName); 34 | await result.ExecuteAsync(context); 35 | } 36 | } 37 | else 38 | { 39 | context.Response.StatusCode = StatusCodes.Status404NotFound; 40 | } 41 | } 42 | catch (Exception ex) 43 | { 44 | _logger.LogCritical(ex, "Unhandled exception: {exception}", ex.Message); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/OmniPay.Alipay.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | OmniPay.Alipay 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/AppPayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | namespace OmniPay.Alipay.Request 6 | { 7 | public class AppPayRequest : BaseRequest 8 | { 9 | public AppPayRequest() 10 | { 11 | RequestUrl = "/gateway.do?charset=UTF-8"; 12 | Add("method", "alipay.trade.app.pay"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/BarCodePayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | 6 | namespace OmniPay.Alipay.Request 7 | { 8 | public class BarCodePayRequest : BaseRequest 9 | { 10 | public BarCodePayRequest() 11 | { 12 | RequestUrl = "/gateway.do?charset=UTF-8"; 13 | Add("method", "alipay.trade.pay"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/BillDownloadRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | namespace OmniPay.Alipay.Request 6 | { 7 | public class BillDownloadRequest : BaseRequest 8 | { 9 | public BillDownloadRequest() 10 | { 11 | RequestUrl = "/gateway.do?charset=UTF-8"; 12 | Add("method", "alipay.data.dataservice.bill.downloadurl.query"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/RefundQueryRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | 6 | namespace OmniPay.Alipay.Request 7 | { 8 | public class RefundQueryRequest : BaseRequest 9 | { 10 | public RefundQueryRequest() 11 | { 12 | RequestUrl = "/gateway.do?charset=UTF-8"; 13 | Add("method", "alipay.trade.fastpay.refund.query"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/ScanPayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | namespace OmniPay.Alipay.Request 6 | { 7 | public class ScanPayRequest : BaseRequest 8 | { 9 | public ScanPayRequest() 10 | { 11 | RequestUrl = "/gateway.do?charset=UTF-8"; 12 | Add("method", "alipay.trade.precreate"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/TradeCancelRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | namespace OmniPay.Alipay.Request 6 | { 7 | public class TradeCancelRequest : BaseRequest 8 | { 9 | public TradeCancelRequest() 10 | { 11 | RequestUrl = "/gateway.do?charset=UTF-8"; 12 | Add("method", "alipay.trade.cancel"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/TradeCloseRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | namespace OmniPay.Alipay.Request 6 | { 7 | public class TradeCloseRequest : BaseRequest 8 | { 9 | public TradeCloseRequest() 10 | { 11 | RequestUrl = "/gateway.do?charset=UTF-8"; 12 | Add("method", "alipay.trade.close"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/TradeQueryRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | namespace OmniPay.Alipay.Request 6 | { 7 | public class TradeQueryRequest : BaseRequest 8 | { 9 | public TradeQueryRequest() 10 | { 11 | RequestUrl = "/gateway.do?charset=UTF-8"; 12 | Add("method", "alipay.trade.query"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/TradeRefundRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | namespace OmniPay.Alipay.Request 6 | { 7 | public class TradeRefundRequest : BaseRequest 8 | { 9 | public TradeRefundRequest() 10 | { 11 | RequestUrl = "/gateway.do?charset=UTF-8"; 12 | Add("method", "alipay.trade.refund"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/TransferQueryRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | 6 | namespace OmniPay.Alipay.Request 7 | { 8 | public class TransferQueryRequest : BaseRequest 9 | { 10 | public TransferQueryRequest() 11 | { 12 | RequestUrl = "/gateway.do?charset=UTF-8"; 13 | Add("method", "alipay.fund.trans.order.query"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/TransferRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | 6 | namespace OmniPay.Alipay.Request 7 | { 8 | public class TransferRequest : BaseRequest 9 | { 10 | public TransferRequest() 11 | { 12 | RequestUrl = "/gateway.do?charset=UTF-8"; 13 | Add("method", "alipay.fund.trans.uni.transfer"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/WapPayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Response; 2 | using OmniPay.Core.Request; 3 | 4 | namespace OmniPay.Alipay.Request 5 | { 6 | public class WapPayRequest : BaseRequest 7 | { 8 | public WapPayRequest() 9 | { 10 | RequestUrl = "/gateway.do?charset=UTF-8"; 11 | Add("method", "alipay.trade.wap.pay"); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Request/WebPagePayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Domain; 2 | using OmniPay.Alipay.Response; 3 | using OmniPay.Core.Request; 4 | 5 | namespace OmniPay.Alipay.Request 6 | { 7 | public class WebPagePayRequest : BaseRequest 8 | { 9 | public WebPagePayRequest() 10 | { 11 | RequestUrl = "/gateway.do"; 12 | Add("method", "alipay.trade.page.pay"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Response/AppPayResponse.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Request; 2 | 3 | namespace OmniPay.Alipay.Response 4 | { 5 | public class AppPayResponse 6 | { 7 | public AppPayResponse(AppPayRequest request) 8 | { 9 | OrderInfo = request.ToUrl(); 10 | } 11 | 12 | /// 13 | /// 用于唤起App的订单参数 14 | /// 15 | public string OrderInfo { get; set; } 16 | 17 | public string Raw { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Response/BaseResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace OmniPay.Alipay.Response 4 | { 5 | public abstract class BaseResponse 6 | { 7 | 8 | /// 9 | /// 返回状态码 10 | /// 11 | public string Code { get; set; } 12 | 13 | /// 14 | /// 返回码描述 15 | /// 16 | [JsonProperty("msg")] 17 | public string Message { get; set; } 18 | 19 | /// 20 | /// 明细返回码 21 | /// 22 | public string SubCode { get; set; } 23 | 24 | /// 25 | /// 明细返回码描述 26 | /// 27 | [JsonProperty("sub_msg")] 28 | public string SubMessage { get; set; } 29 | 30 | /// 31 | /// 签名 32 | /// 33 | public string Sign { get; set; } 34 | 35 | /// 36 | /// 原始值 37 | /// 38 | public string Raw { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Response/ScanPayResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace OmniPay.Alipay.Response 4 | { 5 | public class ScanPayResponse : BaseResponse 6 | { 7 | /// 8 | /// 商户订单号 9 | /// 10 | [JsonProperty("out_trade_no")] 11 | public string OutTradeNo { get; set; } 12 | 13 | /// 14 | /// 二维码链接 15 | /// 16 | [JsonProperty("qr_code")] 17 | public string QrCode { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/OmniPay.Alipay/Response/WebPayResponse.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Alipay.Request; 2 | 3 | namespace OmniPay.Alipay.Response 4 | { 5 | public class WebPayResponse 6 | { 7 | public WebPayResponse(WapPayRequest request) 8 | { 9 | Html = request.ToForm(request.RequestUrl); 10 | } 11 | 12 | public WebPayResponse(WebPagePayRequest request) 13 | { 14 | Html = request.ToForm(request.RequestUrl); 15 | } 16 | 17 | /// 18 | /// 生成的Html网页 19 | /// 20 | public string Html { get; set; } 21 | 22 | public string Raw { get; set; } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Configuration/DependencyInjection/IOmniPayBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace OmniPay.Core.Configuration.DependencyInjection 4 | { 5 | public interface IOmniPayBuilder 6 | { 7 | IServiceCollection Services { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Configuration/DependencyInjection/OmniPayBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace OmniPay.Core.Configuration.DependencyInjection 4 | { 5 | public class OmniPayBuilder 6 | { 7 | public IServiceCollection Services { get; set; } 8 | 9 | public OmniPayBuilder(IServiceCollection services) 10 | { 11 | this.Services = services; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Configuration/DependencyInjection/OmniPayServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Utils; 5 | 6 | namespace OmniPay.Core.Configuration.DependencyInjection 7 | { 8 | public static class OmniPayServiceCollectionExtensions 9 | { 10 | public static IServiceCollection AddOmniPayService(this IServiceCollection services, Action builder) 11 | { 12 | //自动API将放到下一个里程碑计划中 13 | //services.AddTransient(); 14 | //services.AddSingleton(); 15 | //services.AddSingleton(new Endpoint("OmniPay", "/.well-known/openpay-configuration", typeof(DiscoveryEndpoint))); 16 | //var httpContextAccessor = services.ApplicationServices.GetRequiredService(); 17 | //HttpUtil.Configure(httpContextAccessor); 18 | services.AddTransient(); 19 | var omniPayBuilder = new OmniPayBuilder(services); 20 | builder(omniPayBuilder); 21 | return services; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Endpoints/DiscoveryEndpoint.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using OmniPay.Core.Endpoints.Results; 3 | using OmniPay.Core.Hosting; 4 | 5 | namespace OmniPay.Core.Endpoints 6 | { 7 | public class DiscoveryEndpoint : IEndpointHandler 8 | { 9 | public IEndpointResult Process(HttpContext context) 10 | { 11 | return new DiscoveryDocumentResult(DiscoveryResponseGenerator.DiscoveryDictionary); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Endpoints/DiscoveryResponseGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace OmniPay.Core.Endpoints 4 | { 5 | public class DiscoveryResponseGenerator 6 | { 7 | public static Dictionary DiscoveryDictionary { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Endpoints/Results/DiscoveryDocumentResult.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using OmniPay.Core.Hosting; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using OmniPay.Core.Utils; 7 | 8 | namespace OmniPay.Core.Endpoints.Results 9 | { 10 | /// 11 | /// Result for a discovery document 12 | /// 13 | /// 14 | public class DiscoveryDocumentResult : IEndpointResult 15 | { 16 | /// 17 | /// Gets the entries. 18 | /// 19 | /// 20 | /// The entries. 21 | /// 22 | public Dictionary Entries { get; } 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | /// The entries. 28 | /// entries 29 | public DiscoveryDocumentResult(Dictionary entries) 30 | { 31 | Entries = entries ?? throw new ArgumentNullException(nameof(entries)); 32 | } 33 | 34 | /// 35 | /// Executes the result. 36 | /// 37 | /// The HTTP context. 38 | /// 39 | public Task ExecuteAsync(HttpContext context) 40 | { 41 | return context.Response.WriteAsync(Entries.ToJson()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Exceptions/OmniPayException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OmniPay.Core.Exceptions 4 | { 5 | public class OmniPayException : Exception 6 | { 7 | public OmniPayException(string message) 8 | : base(message) 9 | { 10 | } 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Hosting/Endpoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace OmniPay.Core.Hosting 5 | { 6 | public class Endpoint 7 | { 8 | public Endpoint(string name, string path, Type handlerType) 9 | { 10 | Name = name; 11 | Path = path; 12 | Handler = handlerType; 13 | } 14 | 15 | /// 16 | /// Gets or sets the name 17 | /// 18 | /// 19 | /// The name 20 | /// 21 | public string Name { get; set; } 22 | /// 23 | /// Gets or sets the path 24 | /// 25 | /// 26 | /// The path 27 | /// 28 | public PathString Path { get; set; } 29 | /// 30 | /// Gets or sets the handler 31 | /// 32 | /// 33 | /// The handler 34 | /// 35 | public Type Handler { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Hosting/EndpointRouter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace OmniPay.Core.Hosting 7 | { 8 | public class EndpointRouter : IEndpointRouter 9 | { 10 | 11 | private readonly IEnumerable _endpoints; 12 | private readonly ILogger _logger; 13 | 14 | public EndpointRouter(IEnumerable endpoints, ILogger logger) 15 | { 16 | _endpoints = endpoints; 17 | _logger = logger; 18 | } 19 | public IEndpointHandler Find(HttpContext context) 20 | { 21 | if (context == null) throw new ArgumentNullException(nameof(context)); 22 | foreach (var endpoint in _endpoints) 23 | { 24 | var path = endpoint.Path; 25 | if (context.Request.Path.Equals(path, StringComparison.OrdinalIgnoreCase)) 26 | { 27 | var endpointName = endpoint.Name; 28 | _logger.LogDebug("Request path {path} matched to endpoint type {endpoint}", context.Request.Path, endpointName); 29 | 30 | return GetEndpointHandler(endpoint, context); 31 | } 32 | } 33 | _logger.LogTrace("No endpoint entry found for request path: {path}", context.Request.Path); 34 | 35 | return null; 36 | } 37 | 38 | private IEndpointHandler GetEndpointHandler(Endpoint endpoint, HttpContext context) 39 | { 40 | if (context.RequestServices.GetService(endpoint.Handler) is IEndpointHandler handler) 41 | { 42 | _logger.LogDebug("Endpoint enabled: {endpoint}, successfully created handler: {endpointHandler}", endpoint.Name, endpoint.Handler.FullName); 43 | return handler; 44 | } 45 | else 46 | { 47 | _logger.LogDebug("Endpoint enabled: {endpoint}, failed to create handler: {endpointHandler}", endpoint.Name, endpoint.Handler.FullName); 48 | } 49 | 50 | return null; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Hosting/IEndpointHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace OmniPay.Core.Hosting 5 | { 6 | public interface IEndpointHandler 7 | { 8 | /// 9 | /// Processes the request 10 | /// 11 | /// The HTTP context. 12 | /// 13 | IEndpointResult Process(HttpContext context); 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Hosting/IEndpointResult.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace OmniPay.Core.Hosting 5 | { 6 | /// 7 | /// Endpoint result 8 | /// 9 | public interface IEndpointResult 10 | { 11 | /// 12 | /// Executes the result 13 | /// 14 | /// The HTTP context. 15 | /// 16 | Task ExecuteAsync(HttpContext context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Hosting/IEndpointRouter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | 3 | namespace OmniPay.Core.Hosting 4 | { 5 | /// 6 | /// The endpoint router 7 | /// 8 | public interface IEndpointRouter 9 | { 10 | /// 11 | /// Finds a matching endpoint. 12 | /// 13 | /// The HTTP context. 14 | /// 15 | IEndpointHandler Find(HttpContext context); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/OmniPay.Core/OmniPay.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | OmniPay.Core 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Results/ProccessErrorResult.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using OmniPay.Core.Hosting; 3 | using System.Threading.Tasks; 4 | 5 | namespace OmniPay.Core.Results 6 | { 7 | public class ProccessErrorResult : IEndpointResult 8 | { 9 | public string Error; 10 | public string ErrorDescription; 11 | 12 | public ProccessErrorResult(string error, string errorDescription = null) 13 | { 14 | Error = error; 15 | ErrorDescription = errorDescription; 16 | } 17 | 18 | public Task ExecuteAsync(HttpContext context) 19 | { 20 | context.Response.ContentType = "application/json; charset=UTF-8"; 21 | context.Response.WriteAsync("{'test':'测试'}"); 22 | context.Response.Body.FlushAsync(); 23 | return Task.CompletedTask; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Results/StatusCodeResult.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Hosting; 5 | 6 | namespace OmniPay.Core.Results 7 | { 8 | /// 9 | /// Result for a raw HTTP status code 10 | /// 11 | /// 12 | public class StatusCodeResult : IEndpointResult 13 | { 14 | /// 15 | /// Gets the status code. 16 | /// 17 | /// 18 | /// The status code. 19 | /// 20 | public int StatusCode { get; } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The status code. 26 | public StatusCodeResult(HttpStatusCode statusCode) 27 | { 28 | StatusCode = (int)statusCode; 29 | } 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// The status code. 35 | public StatusCodeResult(int statusCode) 36 | { 37 | StatusCode = statusCode; 38 | } 39 | 40 | /// 41 | /// Executes the result. 42 | /// 43 | /// The HTTP context. 44 | /// 45 | public Task ExecuteAsync(HttpContext context) 46 | { 47 | context.Response.StatusCode = StatusCode; 48 | 49 | return Task.CompletedTask; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Utils/CertUtil.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Org.BouncyCastle.Crypto; 3 | using Org.BouncyCastle.Pkcs; 4 | using Org.BouncyCastle.X509; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Text; 9 | 10 | namespace OmniPay.Core.Utils 11 | { 12 | public class Cert 13 | { 14 | public AsymmetricKeyParameter key; 15 | public X509Certificate cert; 16 | public string certId; 17 | } 18 | 19 | public class CertUtil 20 | { 21 | private static Dictionary signCerts = new Dictionary(); 22 | 23 | 24 | private static void initSignCert(string certPath, string certPwd) 25 | { 26 | 27 | FileStream fileStream = null; 28 | try 29 | { 30 | fileStream = new FileStream(certPath, FileMode.Open, FileAccess.Read); 31 | 32 | Pkcs12Store store = new Pkcs12Store(fileStream, certPwd.ToCharArray()); 33 | 34 | string pName = null; 35 | foreach (string n in store.Aliases) 36 | { 37 | if (store.IsKeyEntry(n)) 38 | { 39 | pName = n; 40 | //break; 41 | } 42 | } 43 | 44 | Cert signCert = new Cert(); 45 | signCert.key = store.GetKey(pName).Key; 46 | X509CertificateEntry[] chain = store.GetCertificateChain(pName); 47 | signCert.cert = chain[0].Certificate; 48 | signCert.certId = signCert.cert.SerialNumber.ToString(); 49 | 50 | signCerts[certPath] = signCert; 51 | 52 | } 53 | catch (Exception e) 54 | { 55 | //_logger.LogError("签名证书读取失败,异常:" + e); 56 | } 57 | finally 58 | { 59 | if (fileStream != null) 60 | fileStream.Close(); 61 | } 62 | 63 | } 64 | 65 | /// 66 | /// 获取签名证书的证书序列号 67 | /// 68 | /// 69 | public static string GetSignCertId(string certPath, string certPwd) 70 | { 71 | if (!signCerts.ContainsKey(certPath)) 72 | { 73 | initSignCert(certPath, certPwd); 74 | } 75 | return signCerts[certPath].certId; 76 | } 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Utils/Extensions.Json.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Converters; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace OmniPay.Core.Utils { 8 | public static partial class Extensions { 9 | /// 10 | /// 反序列化 11 | /// 12 | /// 13 | /// 14 | public static object ToJson (this string Json) { 15 | return Json == null ? null : JsonConvert.DeserializeObject (Json); 16 | } 17 | /// 18 | /// 序列化 19 | /// 20 | /// 21 | /// 22 | public static string ToJson (this object obj) { 23 | var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }; 24 | return JsonConvert.SerializeObject (obj, timeConverter); 25 | } 26 | public static string ToJson (this object obj, string datetimeformats) { 27 | var timeConverter = new IsoDateTimeConverter { DateTimeFormat = datetimeformats }; 28 | return JsonConvert.SerializeObject (obj, timeConverter); 29 | } 30 | public static T ToObject (this string Json) { 31 | return Json == null ? default (T) : JsonConvert.DeserializeObject (Json); 32 | } 33 | public static List ToList (this string Json) { 34 | return Json == null ? null : JsonConvert.DeserializeObject> (Json); 35 | } 36 | public static DataTable ToTable (this string Json) { 37 | return Json == null ? null : JsonConvert.DeserializeObject (Json); 38 | } 39 | public static JObject ToJObject (this string Json) { 40 | return Json == null ? JObject.Parse ("{}") : JObject.Parse (Json.Replace (" :", "")); 41 | } 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /src/OmniPay.Core/Utils/Extensions.String.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace OmniPay.Core.Utils 4 | { 5 | public enum StringCase 6 | { 7 | /// 8 | /// 蛇形策略 9 | /// 10 | Snake, 11 | /// 12 | /// 驼峰策略 13 | /// 14 | Camel, 15 | /// 16 | /// 默认 17 | /// 18 | None 19 | } 20 | 21 | internal enum SnakeCaseState 22 | { 23 | Start, 24 | Lower, 25 | Upper, 26 | NewWord 27 | } 28 | 29 | public static partial class Extensions 30 | { 31 | /// 32 | /// 将字符串转换为蛇形策略 33 | /// 34 | /// 字符串 35 | /// 36 | public static string ToSnakeCase(this string s) 37 | { 38 | if (string.IsNullOrEmpty(s)) 39 | { 40 | return s; 41 | } 42 | 43 | var sb = new StringBuilder(); 44 | var state = SnakeCaseState.Start; 45 | 46 | for (var i = 0; i < s.Length; i++) 47 | { 48 | if (s[i] == ' ') 49 | { 50 | if (state != SnakeCaseState.Start) 51 | { 52 | state = SnakeCaseState.NewWord; 53 | } 54 | } 55 | else if (char.IsUpper(s[i])) 56 | { 57 | switch (state) 58 | { 59 | case SnakeCaseState.Upper: 60 | var hasNext = i + 1 < s.Length; 61 | if (i > 0 && hasNext) 62 | { 63 | var nextChar = s[i + 1]; 64 | if (!char.IsUpper(nextChar) && nextChar != '_') 65 | { 66 | sb.Append('_'); 67 | } 68 | } 69 | break; 70 | case SnakeCaseState.Lower: 71 | case SnakeCaseState.NewWord: 72 | sb.Append('_'); 73 | break; 74 | } 75 | 76 | sb.Append(char.ToLowerInvariant(s[i])); 77 | 78 | state = SnakeCaseState.Upper; 79 | } 80 | else if (s[i] == '_') 81 | { 82 | sb.Append('_'); 83 | state = SnakeCaseState.Start; 84 | } 85 | else 86 | { 87 | if (state == SnakeCaseState.NewWord) 88 | { 89 | sb.Append('_'); 90 | } 91 | 92 | sb.Append(s[i]); 93 | state = SnakeCaseState.Lower; 94 | } 95 | } 96 | 97 | return sb.ToString(); 98 | } 99 | 100 | /// 101 | /// 将字符串转换为骆驼策略 102 | /// 103 | /// 字符串 104 | /// 105 | public static string ToCamelCase(this string s) 106 | { 107 | if (string.IsNullOrEmpty(s) || !char.IsUpper(s[0])) 108 | { 109 | return s; 110 | } 111 | 112 | var chars = s.ToCharArray(); 113 | 114 | for (var i = 0; i < chars.Length; i++) 115 | { 116 | if (i == 1 && !char.IsUpper(chars[i])) 117 | { 118 | break; 119 | } 120 | 121 | var hasNext = i + 1 < chars.Length; 122 | if (i > 0 && hasNext && !char.IsUpper(chars[i + 1])) 123 | { 124 | if (char.IsSeparator(chars[i + 1])) 125 | { 126 | chars[i] = char.ToLowerInvariant(chars[i]); 127 | } 128 | 129 | break; 130 | } 131 | 132 | chars[i] = char.ToLowerInvariant(chars[i]); 133 | } 134 | 135 | return new string(chars); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Utils/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OmniPay.Core.Utils 4 | { 5 | public static partial class Extensions 6 | { 7 | public static string GetNonceStr() 8 | { 9 | return Guid.NewGuid().ToString("N"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Utils/HttpHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Net.Http.Headers; 5 | using System.Text; 6 | using System.Text.Json; 7 | using System.Threading.Tasks; 8 | 9 | namespace OmniPay.Core.Utils 10 | { 11 | public class HttpHandler : IHttpHandler 12 | { 13 | private readonly IHttpClientFactory _httpClientFactory; 14 | 15 | public HttpHandler(IHttpClientFactory httpClientFactory) 16 | { 17 | _httpClientFactory = httpClientFactory; 18 | } 19 | 20 | public HttpClient CreateHttpClient(string? name = null) 21 | { 22 | _ = _httpClientFactory ?? throw new NullReferenceException("No IHttpClientFactory provided, please add AddHttpClient() in configure services!"); 23 | return _httpClientFactory.CreateClient(name); 24 | } 25 | 26 | public async Task GetAsync(string url, JsonSerializerOptions options = null) 27 | { 28 | _ = _httpClientFactory ?? throw new NullReferenceException("No IHttpClientFactory provided, please add AddHttpClient() in configure services!"); 29 | var httpClient = _httpClientFactory.CreateClient(); 30 | 31 | var streamTask = httpClient.GetStreamAsync(url); 32 | 33 | return await JsonSerializer.DeserializeAsync(await streamTask.ConfigureAwait(false), options); 34 | } 35 | 36 | public async Task PostAsync(string url, string request, JsonSerializerOptions options = null) 37 | { 38 | _ = _httpClientFactory ?? throw new NullReferenceException("No IHttpClientFactory provided, please add AddHttpClient() in configure services!"); 39 | var httpClient = _httpClientFactory.CreateClient(); 40 | 41 | var reqContent = new StringContent(request, Encoding.UTF8, "application/x-www-form-urlencoded"); 42 | var response = await httpClient.PostAsync(url, reqContent); 43 | var streamTask = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 44 | return streamTask; 45 | } 46 | 47 | public async Task PostAsync(string url, string request, string logicalName, JsonSerializerOptions options = null) 48 | { 49 | _ = _httpClientFactory ?? throw new NullReferenceException("No IHttpClientFactory provided, please add AddHttpClient() in configure services!"); 50 | var httpClient = _httpClientFactory.CreateClient(logicalName); 51 | if (logicalName == null) 52 | { 53 | httpClient = _httpClientFactory.CreateClient(); 54 | } 55 | 56 | var reqContent = new StringContent(request, Encoding.UTF8, "application/x-www-form-urlencoded"); 57 | var resp = await httpClient.PostAsync(url, reqContent); 58 | var streamTask = await resp.Content.ReadAsStringAsync().ConfigureAwait(false); 59 | return streamTask; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Utils/IHttpHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Text.Json; 3 | using System.Threading.Tasks; 4 | 5 | namespace OmniPay.Core.Utils 6 | { 7 | public interface IHttpHandler 8 | { 9 | /// 10 | /// 返回HttpClient 11 | /// 12 | /// 13 | /// 14 | HttpClient CreateHttpClient(string? name = null); 15 | 16 | /// 17 | /// 异步GET请求 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | Task GetAsync(string url, JsonSerializerOptions? options = null); 25 | 26 | /// 27 | /// 异步POST请求 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | Task PostAsync(string url, string request, JsonSerializerOptions options = null); 35 | 36 | 37 | /// 38 | /// 异步POST请求 39 | /// 40 | /// 41 | /// 42 | /// 43 | /// 44 | /// 45 | /// 46 | Task PostAsync(string url, string request, string logicalName, JsonSerializerOptions options = null); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/OmniPay.Core/Utils/IReadableStringCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Specialized; 3 | using System.Linq; 4 | using Microsoft.Extensions.Primitives; 5 | 6 | namespace OmniPay.Core.Utils 7 | { 8 | public static class IReadableStringCollectionExtensions 9 | { 10 | public static NameValueCollection AsNameValueCollection(this IEnumerable> collection){ 11 | var nv=new NameValueCollection(); 12 | foreach (var field in collection) 13 | { 14 | nv.Add(field.Key,field.Value.First()); 15 | } 16 | return nv; 17 | } 18 | 19 | public static NameValueCollection AsNameValueCollection(this IDictionary collection){ 20 | var nv=new NameValueCollection(); 21 | foreach (var field in collection) 22 | { 23 | nv.Add(field.Key,field.Value.First()); 24 | } 25 | return nv; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Domain/BackTransModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Unionpay.Domain 6 | { 7 | public class BackTransModel: BasePayModel 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Domain/BasePayModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Unionpay.Domain 6 | { 7 | public class BasePayModel 8 | { 9 | /// 10 | /// 商户订单号 商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易 11 | /// 12 | public string OrderId { get; set; } 13 | 14 | /// 15 | /// 交易金额 16 | /// 17 | public string TxnAmt { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Domain/FrontTransModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Unionpay.Domain 6 | { 7 | public class FrontTransModel : BasePayModel 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Extensions/UnionPayApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using OmniPay.Core.Utils; 5 | using OmniPay.Unionpay.Middleware; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Security.Cryptography.X509Certificates; 9 | using System.Text; 10 | 11 | namespace OmniPay.Unionpay.Extensions 12 | { 13 | public static class UnionPayApplicationBuilderExtensions 14 | { 15 | public static IApplicationBuilder UseUnionPay(this IApplicationBuilder app) 16 | { 17 | app.UseMiddleware(); 18 | var httpContextAccessor = app.ApplicationServices.GetRequiredService(); 19 | HttpUtil.Configure(httpContextAccessor); 20 | return app; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Extensions/UnionpayServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using OmniPay.Core.Hosting; 4 | using System; 5 | 6 | namespace OmniPay.Unionpay.Extensions 7 | { 8 | public static class UnionpayServiceCollectionExtensions 9 | { 10 | public static IServiceCollection AddUnionPay(this IServiceCollection services, Action action) 11 | { 12 | //services.AddTransient(); 13 | return services.AddUnionPayServices(action); 14 | } 15 | 16 | private static IServiceCollection AddUnionPayServices(this IServiceCollection services, Action action) 17 | { 18 | if (action != null) 19 | { 20 | services.Configure(action.Invoke); 21 | } 22 | services.AddSingleton(); 23 | return services; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/IUnionPayClient.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace OmniPay.Unionpay 8 | { 9 | public interface IUnionPayClient 10 | { 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | Task ExecuteAsync(BaseRequest request); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Middleware/UnionPayMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Extensions.Logging; 3 | using OmniPay.Core.Hosting; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace OmniPay.Unionpay.Middleware 10 | { 11 | public class UnionPayMiddleware 12 | { 13 | private readonly ILogger _logger; 14 | private readonly RequestDelegate _next; 15 | 16 | public UnionPayMiddleware(ILogger logger, RequestDelegate next) 17 | { 18 | this._logger = logger; 19 | this._next = next; 20 | } 21 | 22 | public async Task InvokeAsync(HttpContext context, IEndpointRouter router) 23 | { 24 | try 25 | { 26 | var endpoint = router.Find(context); 27 | if (endpoint != null) 28 | { 29 | _logger.LogInformation("Invoking WechatPay endpoint: {endpointType} for {url}", endpoint.GetType().FullName, context.Request.Path.ToString()); 30 | 31 | var result = endpoint.Process(context); 32 | 33 | if (result != null) 34 | { 35 | _logger.LogTrace("Invoking result: {type}", result.GetType().FullName); 36 | await result.ExecuteAsync(context); 37 | } 38 | } 39 | else 40 | { 41 | context.Response.StatusCode = StatusCodes.Status404NotFound; 42 | } 43 | } 44 | catch (Exception ex) 45 | { 46 | _logger.LogCritical(ex, "Unhandled exception: {exception}", ex.Message); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/OmniPay.Unionpay.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Request/BackTransRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Unionpay.Domain; 3 | using OmniPay.Unionpay.Response; 4 | 5 | namespace OmniPay.Unionpay.Request 6 | { 7 | public class BackTransRequest : BaseRequest 8 | { 9 | public BackTransRequest() 10 | { 11 | RequestUrl = "/backTransReq.do"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Request/FrontTransRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Unionpay.Domain; 3 | using OmniPay.Unionpay.Response; 4 | 5 | namespace OmniPay.Unionpay.Request 6 | { 7 | public class FrontTransRequest : BaseRequest 8 | { 9 | public FrontTransRequest() 10 | { 11 | RequestUrl = "/frontTransReq.do"; 12 | } 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Response/BackTransResponse.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Unionpay.Request; 2 | 3 | namespace OmniPay.Unionpay.Response 4 | { 5 | public class BackTransResponse 6 | { 7 | public BackTransResponse(BackTransRequest request) 8 | { 9 | Html = request.ToForm(request.RequestUrl); 10 | } 11 | 12 | /// 13 | /// 生成的Html网页 14 | /// 15 | public string Html { get; set; } 16 | 17 | public string Raw { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Response/BaseResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace OmniPay.Unionpay.Response 7 | { 8 | public abstract class BaseResponse 9 | { 10 | /// 11 | /// 返回状态码 12 | /// 13 | public string Code { get; set; } 14 | 15 | /// 16 | /// 返回码描述 17 | /// 18 | [JsonProperty("msg")] 19 | public string Message { get; set; } 20 | 21 | /// 22 | /// 明细返回码 23 | /// 24 | public string SubCode { get; set; } 25 | 26 | /// 27 | /// 明细返回码描述 28 | /// 29 | [JsonProperty("sub_msg")] 30 | public string SubMessage { get; set; } 31 | 32 | /// 33 | /// 签名 34 | /// 35 | public string Sign { get; set; } 36 | 37 | /// 38 | /// 原始值 39 | /// 40 | public string Raw { get; set; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/Response/FrontTransResponse.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Unionpay.Request; 2 | 3 | namespace OmniPay.Unionpay.Response 4 | { 5 | public class FrontTransResponse 6 | { 7 | public FrontTransResponse(FrontTransRequest request) 8 | { 9 | Html = request.ToForm(request.RequestUrl); 10 | } 11 | 12 | /// 13 | /// 生成的Html网页 14 | /// 15 | public string Html { get; set; } 16 | 17 | public string Raw { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/UnionPayClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Options; 3 | using Newtonsoft.Json.Linq; 4 | using OmniPay.Core.Exceptions; 5 | using OmniPay.Core.Request; 6 | using OmniPay.Core.Utils; 7 | using OmniPay.Unionpay.Request; 8 | using OmniPay.Unionpay.Response; 9 | using System; 10 | using System.Threading.Tasks; 11 | using static OmniPay.Core.Utils.UnionPayUntil; 12 | 13 | namespace OmniPay.Unionpay 14 | { 15 | public class UnionPayClient : IUnionPayClient 16 | { 17 | 18 | private readonly UnionPayOptions _unionPayOptions; 19 | private readonly ILogger _logger; 20 | 21 | private UnionPayCertificate SignCertificate; 22 | private UnionPayCertificate EncryptCertificate; 23 | private UnionPayCertificate MiddleCertificate; 24 | private UnionPayCertificate RootCertificate; 25 | 26 | 27 | public UnionPayClient(IOptions unionPayOptions, ILogger logger) 28 | { 29 | this._unionPayOptions = unionPayOptions.Value; 30 | this._logger = logger; 31 | 32 | if (string.IsNullOrWhiteSpace(_unionPayOptions.Version)) 33 | { 34 | throw new OmniPayException(nameof(_unionPayOptions.Version)); 35 | } 36 | if (string.IsNullOrWhiteSpace(_unionPayOptions.SignCertPath)) 37 | { 38 | throw new OmniPayException(nameof(_unionPayOptions.SignCertPath)); 39 | } 40 | if (string.IsNullOrWhiteSpace(_unionPayOptions.SignCertPwd)) 41 | { 42 | throw new OmniPayException(nameof(_unionPayOptions.SignCertPwd)); 43 | } 44 | 45 | SignCertificate = UnionPayUntil.GetSignCertificate(_unionPayOptions.SignCertPath, _unionPayOptions.SignCertPwd); 46 | EncryptCertificate = UnionPayUntil.GetCertificate(_unionPayOptions.EncryptCertPath); 47 | MiddleCertificate = UnionPayUntil.GetCertificate(_unionPayOptions.MiddleCertPath); 48 | RootCertificate = UnionPayUntil.GetCertificate(_unionPayOptions.RootCertPath); 49 | } 50 | 51 | 52 | 53 | 54 | /// 55 | /// 56 | /// 57 | /// 58 | /// 59 | /// 60 | /// 61 | public async Task ExecuteAsync(BaseRequest request) 62 | { 63 | BuildParams(request); 64 | if (request is FrontTransRequest || request is BackTransRequest) 65 | { 66 | return (TResponse)Activator.CreateInstance(typeof(TResponse), request); 67 | } 68 | 69 | string result = await HttpUtil.PostAsync(request.RequestUrl, request.ToUrl()); 70 | var jObject = JObject.Parse(result); 71 | var jToken = jObject.First.First; 72 | var baseResponse = (BaseResponse)(object)jToken.ToObject(); 73 | return (TResponse)(object)baseResponse; 74 | } 75 | 76 | private void BuildParams(BaseRequest request) 77 | { 78 | //request.Add("version", _unionPayOptions.Version); 79 | //request.Add("encoding", _unionPayOptions.Encoding); 80 | //request.Add("bizType", _unionPayOptions.BizType); 81 | //request.Add("txnTime", _unionPayOptions.TxnTime); 82 | //request.Add("backUrl", _unionPayOptions.BackUrl); 83 | //request.Add("currencyCode", _unionPayOptions.CurrencyCode); 84 | //request.Add("txnType", _unionPayOptions.TxnType); 85 | //request.Add("txnSubType", _unionPayOptions.TxnSubType); 86 | //request.Add("accessType", _unionPayOptions.AccessType); 87 | //request.Add("frontUrl", _unionPayOptions.FrontUrl); 88 | //request.Add("signMethod", _unionPayOptions.SignMethod); 89 | //request.Add("channelType", _unionPayOptions.ChannelType); 90 | //request.Add("merId", _unionPayOptions.MerId); 91 | //request.Add("certId", SignCertificate.certId); 92 | 93 | request.Add("version", "5.1.0"); 94 | request.Add("encoding", "UTF-8"); 95 | request.Add("txnType", "01"); 96 | request.Add("txnSubType", "01"); 97 | request.Add("bizType", "000201"); 98 | request.Add("signMethod", "01"); 99 | request.Add("channelType", "08"); 100 | request.Add("accessType", "0"); 101 | request.Add("frontUrl", "http://localhost:8080/demo/api_02_b2b/FrontRcvResponse.aspx"); 102 | request.Add("backUrl", "http://222.222.222.222:8080/demo/api_02_b2b/BackRcvResponse.aspx"); 103 | request.Add("currencyCode", "156"); 104 | request.Add("payTimeout", "20201106095402"); 105 | request.Add("merId", "777290058110048"); 106 | request.Add("orderId", "20201106093901185"); 107 | request.Add("txnTime", "20201106093901"); 108 | request.Add("txnAmt", "1000"); 109 | request.Add("riskRateInfo", "{commodityName=测试商品名称}"); 110 | request.Add("certId", "69629715588"); 111 | var strData = request.ToUrl(); 112 | 113 | 114 | //UnionPayUntil.Sign(); 115 | //var signDigest = SecurityUtil.Sha256(strData, System.Text.Encoding.UTF8); 116 | //var stringSignDigest = BitConverter.ToString(signDigest).Replace("-", "").ToLower(); 117 | 118 | //string stringSign = Convert.ToBase64String(byteSign); 119 | 120 | var stringSignDigest = SHA256.Compute(strData); 121 | var strSign = UnionPayUntil.SignSha256WithRsa(stringSignDigest, SignCertificate.key); 122 | 123 | request.Add("signature", strSign); 124 | 125 | request.RequestUrl = _unionPayOptions.BaseUrl + request.RequestUrl; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/OmniPay.Unionpay/UnionPayOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OmniPay.Unionpay 6 | { 7 | public class UnionPayOptions 8 | { 9 | /// 10 | /// 版本号 11 | /// 12 | public string Version { get; set; } 13 | 14 | /// 15 | /// 编码方式 16 | /// 17 | public string Encoding => "UTF-8"; 18 | 19 | /// 20 | /// 产品类型 21 | /// 22 | public string BizType => "000201"; 23 | 24 | /// 25 | /// 订单发送时间 26 | /// 27 | public string TxnTime => DateTime.Now.ToString("yyyyMMddHHmmss"); 28 | 29 | /// 30 | /// 后台通知地址 31 | /// 32 | public string BackUrl; 33 | 34 | /// 35 | /// 交易币种 36 | /// 37 | public string CurrencyCode => "156"; 38 | 39 | /// 40 | /// 交易类型 41 | /// 42 | public string TxnType => "01"; 43 | 44 | /// 45 | /// 交易子类 46 | /// 47 | public string TxnSubType => "01"; 48 | 49 | /// 50 | /// 接入类型 51 | /// 52 | public string AccessType => "0"; 53 | 54 | /// 55 | /// 签名 56 | /// 57 | public string Signature; 58 | 59 | /// 60 | /// 签名方法 61 | /// 62 | public string SignMethod { get; set; } 63 | 64 | /// 65 | /// 渠道类型 66 | /// 67 | public string ChannelType => "08"; 68 | 69 | /// 70 | /// 商户代码 71 | /// 72 | public string MerId { get; set; } 73 | 74 | public string BaseUrl { get; set; } 75 | 76 | /// 77 | /// 前台通知地址 78 | /// 79 | public string FrontUrl { get; set; } 80 | 81 | /// 82 | /// 签名证书路径 83 | /// 84 | public string SignCertPath { get; set; } 85 | 86 | /// 87 | /// 签名证书密码 88 | /// 89 | public string SignCertPwd { get; set; } 90 | 91 | /// 92 | /// 加密证书 93 | /// 94 | public string EncryptCertPath { get; set; } 95 | 96 | /// 97 | /// 验签中级证书 98 | /// 99 | public string MiddleCertPath { get; set; } 100 | 101 | /// 102 | /// 验签根证书 103 | /// 104 | public string RootCertPath { get; set; } 105 | 106 | /// 107 | /// 散列方式签名密钥 108 | /// 109 | public string SecureKey { get; set; } 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/AppPayModel.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Utils; 2 | 3 | namespace OmniPay.Wechatpay.Domain 4 | { 5 | public class AppPayModel:BasePayModel 6 | { 7 | public AppPayModel() 8 | { 9 | TradeType = "APP"; 10 | } 11 | /// 12 | /// 交易类型 13 | /// 14 | public string TradeType { get; set; } 15 | 16 | /// 17 | /// 机器IP 18 | /// 19 | public string SpbillCreateIp { get; set; } 20 | /// 21 | /// 用户标识 22 | /// 23 | public string OpenId { get; set; } 24 | /// 25 | /// 场景信息 26 | /// 27 | public string SceneInfo { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/AppletPayModel.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Domain 2 | { 3 | public class AppletPayModel:PublicPayModel 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/BasePayModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OmniPay.Wechatpay.Domain 4 | { 5 | /// 6 | /// Pay base class 7 | /// 8 | public class BasePayModel 9 | { 10 | /// 11 | /// 随机字符串 12 | /// 13 | public string NonceStr { get; set; }= Core.Utils.Extensions.GetNonceStr(); 14 | /// 15 | /// 设备号 16 | /// 17 | public string DeviceInfo { get; set; } 18 | /// 19 | /// 商品描述 20 | /// 21 | public string Body { get; set; } 22 | /// 23 | /// 商品详细描述 24 | /// 25 | public string Detail { get; set; } 26 | /// 27 | /// 附加数据 28 | /// 29 | public string Attach { get; set; } 30 | /// 31 | /// 商户系统内部订单号 32 | /// 33 | public string OutTradeNo { get; set; } 34 | 35 | /// 36 | /// 标价金额,订单总金额,单位为分 37 | /// 38 | public int TotalFee { get; set; } 39 | 40 | /// 41 | /// 订单生成时间,格式为yyyyMMddHHmmss 42 | /// 43 | public string TimeStart { get; set; }=DateTime.Now.ToString("yyyyMMddHHmmss"); 44 | 45 | /// 46 | /// 订单失效时间,格式为yyyyMMddHHmmss 47 | /// 注意:最短失效时间间隔必须大于5分钟 48 | /// 49 | public string TimeExpire { get; set; } 50 | 51 | /// 52 | /// 订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠 53 | /// 54 | public string GoodsTag { get; set; } 55 | 56 | /// 57 | /// 指定支付方式,上传此参数no_credit--可限制用户不能使用信用卡支付 58 | /// 59 | public string LimitPay { get; set; } 60 | /// 61 | /// 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 62 | /// 63 | public string SignType { get; set; } = "HMAC-SHA256"; 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/CloseModel.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Domain 2 | { 3 | public class CloseModel 4 | { 5 | /// 6 | /// 商户订单 7 | /// 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。 8 | /// 9 | public string OutTradeNo { get; set; } 10 | /// 11 | /// 随机字符串 12 | /// 13 | public string NonceStr { get; } = Core.Utils.Extensions.GetNonceStr(); 14 | /// 15 | /// 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 16 | /// 17 | public string SignType { get; set; } = "HMAC-SHA256"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/PublicPayModel.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Utils; 2 | 3 | namespace OmniPay.Wechatpay.Domain 4 | { 5 | public class PublicPayModel:BasePayModel 6 | { 7 | public PublicPayModel() 8 | { 9 | TradeType = "JSAPI"; 10 | } 11 | /// 12 | /// 交易类型 13 | /// 14 | public string TradeType { get; set; } 15 | 16 | /// 17 | /// 机器IP 18 | /// 19 | public string SpbillCreateIp { get; set; } 20 | /// 21 | /// 用户标识 22 | /// 23 | public string OpenId { get; set; } 24 | /// 25 | /// 场景信息 26 | /// 27 | public string SceneInfo { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/QueryModel.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Domain 2 | { 3 | public class QueryModel 4 | { 5 | /// 6 | /// 商户系统内部订单号,该订单号和微信订单号二选一即可,如果二者都存在则优先微信订单号 7 | /// 8 | public string OutTradeNo { get; set; } 9 | /// 10 | /// 微信订单号,和商户订单号不能同时为空 11 | /// 12 | public string TransactionId { get; set; } 13 | /// 14 | /// 随机字符串 15 | /// 16 | public string NonceStr { get; } = Core.Utils.Extensions.GetNonceStr(); 17 | /// 18 | /// 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 19 | /// 20 | public string SignType { get; set; } = "HMAC-SHA256"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/RefundModel.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Domain 2 | { 3 | public class RefundModel 4 | { 5 | /// 6 | /// 随机字符串 7 | /// 8 | public string NonceStr { get; } = Core.Utils.Extensions.GetNonceStr(); 9 | /// 10 | /// 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 11 | /// 12 | public string SignType { get; set; } = "HMAC-SHA256"; 13 | 14 | /// 15 | /// 微信订单号 16 | /// 17 | public string TransactionId { get; set; } 18 | 19 | /// 20 | /// 商户订单 21 | /// 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。 22 | /// 23 | public string OutTradeNo { get; set; } 24 | 25 | /// 26 | /// 商户退款单号 27 | /// 28 | public string OutRefundNo { get; set; } 29 | 30 | /// 31 | /// 订单金额 32 | /// 33 | public int TotalFee { get; set; } 34 | 35 | /// 36 | /// 退款金额 37 | /// 38 | public int RefundFee { get; set; } 39 | 40 | /// 41 | /// 退款货币种类 42 | /// 43 | public string RefundFeeType { get; set; } 44 | 45 | /// 46 | /// 退款原因 47 | /// 48 | public string RefundDesc { get; set; } 49 | 50 | /// 51 | /// 退款资金来源 52 | /// 53 | public string RefundAccount { get; set; } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/ScanPayModel.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Utils; 2 | 3 | namespace OmniPay.Wechatpay.Domain 4 | { 5 | public class ScanPayModel : BasePayModel 6 | { 7 | public ScanPayModel() 8 | { 9 | TradeType = "NATIVE"; 10 | } 11 | 12 | /// 13 | /// 交易类型 14 | /// 15 | public string TradeType { get; set; } 16 | 17 | /// 18 | /// 机器IP 19 | /// 20 | public string SpbillCreateIp { get; set; } 21 | /// 22 | /// 商品ID 23 | /// 24 | public string ProductId { get; set; } 25 | /// 26 | /// 用户标识 27 | /// 28 | public string OpenId { get; set; } 29 | /// 30 | /// 场景信息 31 | /// 32 | public string SceneInfo { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Domain/WapPayModel.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Utils; 2 | 3 | namespace OmniPay.Wechatpay.Domain 4 | { 5 | public class WapPayModel: BasePayModel 6 | { 7 | public WapPayModel() 8 | { 9 | TradeType = "MWEB"; 10 | } 11 | /// 12 | /// 交易类型 13 | /// 14 | public string TradeType { get; set; } 15 | 16 | /// 17 | /// 机器IP 18 | /// 19 | public string SpbillCreateIp { get; set; } 20 | /// 21 | /// 商品ID 22 | /// 23 | public string ProductId { get; set; } 24 | /// 25 | /// 用户标识 26 | /// 27 | public string OpenId { get; set; } 28 | /// 29 | /// 场景信息 30 | /// 31 | public string SceneInfo { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/Result/AppPayResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay.Domain; 7 | using OmniPay.Wechatpay.Request; 8 | 9 | namespace OmniPay.Wechatpay.Endpoints.Result 10 | { 11 | public class AppPayResult:IEndpointResult 12 | { 13 | private readonly IWeChatPayClient _client; 14 | public AppPayResult(IWeChatPayClient client){ 15 | this._client=client; 16 | } 17 | public async Task ExecuteAsync(HttpContext context) 18 | { 19 | try 20 | { 21 | var body = (await context.Request.ReadFormAsync()).AsNameValueCollection(); 22 | if (body?.Count == 0) { 23 | throw new ArgumentNullException (nameof (body)); 24 | } 25 | var request=new AppPayRequest(); 26 | request.AddParameters(new AppPayModel() 27 | { 28 | Body = body.Get("Body"), 29 | OutTradeNo = body.Get("Out_Trade_No"), 30 | TotalFee =int.Parse(body.Get("Total_Amount")) 31 | }); 32 | await context.Response.WriteAsync ((await _client.ExecuteAsync (request)).ToJson()); 33 | } 34 | catch (System.Exception ex) 35 | { 36 | context.Response.StatusCode = StatusCodes.Status500InternalServerError; 37 | await context.Response.WriteAsync (ex.Message); 38 | } 39 | context.Response.ContentType = "application/json; charset=UTF-8"; 40 | await context.Response.Body.FlushAsync (); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/Result/AppletPayResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay.Domain; 7 | using OmniPay.Wechatpay.Request; 8 | 9 | namespace OmniPay.Wechatpay.Endpoints.Result 10 | { 11 | public class AppletPayResult : IEndpointResult 12 | { 13 | private readonly IWeChatPayClient _client; 14 | public AppletPayResult(IWeChatPayClient client) 15 | { 16 | this._client = client; 17 | } 18 | 19 | public async Task ExecuteAsync(HttpContext context) 20 | { 21 | try 22 | { 23 | var body = (await context.Request.ReadFormAsync()).AsNameValueCollection(); 24 | if (body?.Count == 0) 25 | { 26 | throw new ArgumentNullException(nameof(body)); 27 | } 28 | var request = new AppletPayRequest(); 29 | request.AddParameters(new AppletPayModel() 30 | { 31 | Body = body.Get("Body"), 32 | OutTradeNo = body.Get("Out_Trade_No"), 33 | TotalFee = int.Parse(body.Get("Total_Amount")), 34 | OpenId = body.Get("OpenId") 35 | }); 36 | await context.Response.WriteAsync((await _client.ExecuteAsync(request)).ToJson()); 37 | } 38 | catch (System.Exception ex) 39 | { 40 | context.Response.StatusCode = StatusCodes.Status500InternalServerError; 41 | await context.Response.WriteAsync(ex.Message); 42 | } 43 | context.Response.ContentType = "application/json; charset=UTF-8"; 44 | await context.Response.Body.FlushAsync(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/Result/CloseResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay.Domain; 7 | using OmniPay.Wechatpay.Request; 8 | 9 | namespace OmniPay.Wechatpay.Endpoints.Result 10 | { 11 | public class CloseResult : IEndpointResult 12 | { 13 | private readonly IWeChatPayClient _client; 14 | public CloseResult(IWeChatPayClient client){ 15 | this._client=client; 16 | } 17 | public async Task ExecuteAsync(HttpContext context) 18 | { 19 | try 20 | { 21 | var body = (await context.Request.ReadFormAsync()).AsNameValueCollection(); 22 | if (body?.Count == 0) { 23 | throw new ArgumentNullException (nameof (body)); 24 | } 25 | var request = new CloseRequest(); 26 | request.AddParameters(new CloseModel() 27 | { 28 | OutTradeNo = body.Get("Out_Trade_No") 29 | }); 30 | await context.Response.WriteAsync ((await _client.ExecuteAsync (request)).ToJson()); 31 | } 32 | catch (System.Exception ex) 33 | { 34 | context.Response.StatusCode = StatusCodes.Status500InternalServerError; 35 | await context.Response.WriteAsync (ex.Message); 36 | } 37 | context.Response.ContentType = "application/json; charset=UTF-8"; 38 | await context.Response.Body.FlushAsync (); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/Result/PublicPayResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay.Domain; 7 | using OmniPay.Wechatpay.Request; 8 | 9 | namespace OmniPay.Wechatpay.Endpoints.Result 10 | { 11 | public class PublicPayResult : IEndpointResult 12 | { 13 | private readonly IWeChatPayClient _client; 14 | public PublicPayResult(IWeChatPayClient client){ 15 | this._client=client; 16 | } 17 | 18 | public async Task ExecuteAsync(HttpContext context) 19 | { 20 | try 21 | { 22 | var body = (await context.Request.ReadFormAsync()).AsNameValueCollection(); 23 | if (body?.Count == 0) { 24 | throw new ArgumentNullException (nameof (body)); 25 | } 26 | var request=new PublicPayRequest(); 27 | request.AddParameters(new PublicPayModel() 28 | { 29 | Body =body.Get("Body"), 30 | OutTradeNo = body.Get("Out_Trade_No"), 31 | TotalFee =int.Parse(body.Get("Total_Amount")), 32 | OpenId = body.Get("OpenId") 33 | }); 34 | await context.Response.WriteAsync ((await _client.ExecuteAsync (request)).ToJson()); 35 | 36 | } 37 | catch (System.Exception ex) 38 | { 39 | context.Response.StatusCode = StatusCodes.Status500InternalServerError; 40 | await context.Response.WriteAsync (ex.Message); 41 | } 42 | context.Response.ContentType = "application/json; charset=UTF-8"; 43 | await context.Response.Body.FlushAsync (); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/Result/QueryResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay.Domain; 7 | using OmniPay.Wechatpay.Request; 8 | 9 | namespace OmniPay.Wechatpay.Endpoints.Result 10 | { 11 | public class QueryResult : IEndpointResult 12 | { 13 | private readonly IWeChatPayClient _client; 14 | public QueryResult(IWeChatPayClient client){ 15 | this._client=client; 16 | } 17 | public async Task ExecuteAsync(HttpContext context) 18 | { 19 | try 20 | { 21 | var body = (await context.Request.ReadFormAsync()).AsNameValueCollection(); 22 | if (body?.Count == 0) { 23 | throw new ArgumentNullException (nameof (body)); 24 | } 25 | var request = new QueryRequest(); 26 | request.AddParameters(new QueryModel() 27 | { 28 | TransactionId = body.Get("TradeNo"), 29 | OutTradeNo = body.Get("Out_Trade_No") 30 | }); 31 | await context.Response.WriteAsync ((await _client.ExecuteAsync (request)).ToJson()); 32 | } 33 | catch (System.Exception ex) 34 | { 35 | context.Response.StatusCode = StatusCodes.Status500InternalServerError; 36 | await context.Response.WriteAsync (ex.Message); 37 | } 38 | context.Response.ContentType = "application/json; charset=UTF-8"; 39 | await context.Response.Body.FlushAsync (); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/Result/ScanPayResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay.Domain; 7 | using OmniPay.Wechatpay.Request; 8 | 9 | namespace OmniPay.Wechatpay.Endpoints.Result 10 | { 11 | public class ScanPayResult : IEndpointResult 12 | { 13 | private readonly IWeChatPayClient _client; 14 | 15 | public ScanPayResult(IWeChatPayClient client) 16 | { 17 | this._client = client; 18 | } 19 | 20 | public async Task ExecuteAsync(HttpContext context) 21 | { 22 | try 23 | { 24 | var body = (await context.Request.ReadFormAsync()).AsNameValueCollection(); 25 | if (body?.Count == 0) 26 | { 27 | throw new ArgumentNullException(nameof(body)); 28 | } 29 | var request = new ScanPayRequest(); 30 | request.AddParameters(new ScanPayModel() 31 | { 32 | Body = body.Get("Body"), 33 | OutTradeNo = body.Get("Out_Trade_No"), 34 | TotalFee = int.Parse(body.Get("Total_Amount")) 35 | }); 36 | await context.Response.WriteAsync((await _client.ExecuteAsync(request)).ToJson()); 37 | } 38 | catch (Exception ex) 39 | { 40 | context.Response.StatusCode = StatusCodes.Status500InternalServerError; 41 | await context.Response.WriteAsync(ex.Message); 42 | } 43 | context.Response.ContentType = "application/json; charset=UTF-8"; 44 | await context.Response.Body.FlushAsync(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/Result/WapPayResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay.Domain; 7 | using OmniPay.Wechatpay.Request; 8 | 9 | namespace OmniPay.Wechatpay.Endpoints.Result 10 | { 11 | public class WapPayResult : IEndpointResult 12 | { 13 | private readonly IWeChatPayClient _client; 14 | public WapPayResult(IWeChatPayClient client){ 15 | this._client=client; 16 | } 17 | public async Task ExecuteAsync(HttpContext context) 18 | { 19 | try 20 | { 21 | var body = (await context.Request.ReadFormAsync()).AsNameValueCollection(); 22 | if (body?.Count==0) 23 | { 24 | throw new ArgumentNullException(nameof(body)); 25 | } 26 | var request = new WapPayRequest(); 27 | request.AddParameters(new WapPayModel() 28 | { 29 | Body =body.Get("Body"), 30 | OutTradeNo = body.Get("Out_Trade_No"), 31 | TotalFee =int.Parse(body.Get("Total_Amount")) 32 | }); 33 | await context.Response.WriteAsync ((await _client.ExecuteAsync (request)).ToJson()); 34 | } 35 | catch (Exception ex) 36 | { 37 | context.Response.StatusCode = StatusCodes.Status500InternalServerError; 38 | await context.Response.WriteAsync (ex.Message); 39 | } 40 | context.Response.ContentType = "application/json; charset=UTF-8"; 41 | await context.Response.Body.FlushAsync (); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/WechatAppPayEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay.Endpoints.Result; 7 | 8 | namespace OmniPay.Wechatpay.Endpoints 9 | { 10 | public class WechatAppPayEndpoint : IEndpointHandler 11 | { 12 | private readonly ILogger _logger; 13 | private readonly IWeChatPayClient _client; 14 | 15 | public WechatAppPayEndpoint(ILogger logger,IWeChatPayClient client){ 16 | this._logger=logger; 17 | this._client=client; 18 | } 19 | public IEndpointResult Process(HttpContext context) 20 | { 21 | _logger.LogDebug("Start WechatAppPay request"); 22 | 23 | if (!HttpMethods.IsPost(context.Request.Method)) 24 | { 25 | _logger.LogWarning("Invalid HTTP method for ScanPay endpoint."); 26 | return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); 27 | } 28 | _logger.LogTrace("End WechatAppPay request. result type: {0}", this?.GetType().ToString() ?? "-none-"); 29 | return new AppPayResult(_client); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/WechatAppletPayEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay.Endpoints.Result; 7 | 8 | namespace OmniPay.Wechatpay.Endpoints 9 | { 10 | public class WechatAppletPayEndpoint : IEndpointHandler 11 | { 12 | private readonly ILogger _logger; 13 | private readonly IWeChatPayClient _client; 14 | 15 | public WechatAppletPayEndpoint(ILogger logger,IWeChatPayClient client){ 16 | this._logger=logger; 17 | this._client=client; 18 | } 19 | public IEndpointResult Process(HttpContext context) 20 | { 21 | _logger.LogDebug("Start WechatAppletPay request"); 22 | 23 | if (!HttpMethods.IsPost(context.Request.Method)) 24 | { 25 | _logger.LogWarning("Invalid HTTP method for AppletPay endpoint."); 26 | return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); 27 | } 28 | _logger.LogTrace("End WechatAppletPay request. result type: {0}", this?.GetType().ToString() ?? "-none-"); 29 | return new AppletPayResult(_client); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/WechatCloseEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay.Endpoints.Result; 7 | 8 | namespace OmniPay.Wechatpay.Endpoints 9 | { 10 | public class WechatCloseEndpoint:IEndpointHandler 11 | { 12 | private readonly ILogger _logger; 13 | private readonly IWeChatPayClient _client; 14 | 15 | public WechatCloseEndpoint(ILogger logger,IWeChatPayClient client){ 16 | this._logger=logger; 17 | this._client=client; 18 | } 19 | public IEndpointResult Process(HttpContext context) 20 | { 21 | _logger.LogDebug("Start WechatClose request"); 22 | 23 | if (!HttpMethods.IsPost(context.Request.Method)) 24 | { 25 | _logger.LogWarning("Invalid HTTP method for Close endpoint."); 26 | return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); 27 | } 28 | _logger.LogTrace("End WechatClose request. result type: {0}", this?.GetType().ToString() ?? "-none-"); 29 | return new CloseResult(_client); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/WechatPublicPayEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay.Endpoints.Result; 7 | 8 | namespace OmniPay.Wechatpay.Endpoints 9 | { 10 | public class WechatPublicPayEndpoint:IEndpointHandler 11 | { 12 | private readonly ILogger _logger; 13 | private readonly IWeChatPayClient _client; 14 | 15 | public WechatPublicPayEndpoint(ILogger logger,IWeChatPayClient client){ 16 | this._logger=logger; 17 | this._client=client; 18 | } 19 | public IEndpointResult Process(HttpContext context) 20 | { 21 | _logger.LogDebug("Start WechatPublicPay request"); 22 | 23 | if (!HttpMethods.IsPost(context.Request.Method)) 24 | { 25 | _logger.LogWarning("Invalid HTTP method for PublicPay endpoint."); 26 | return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); 27 | } 28 | _logger.LogTrace("End WechatPublicPay request. result type: {0}", this?.GetType().ToString() ?? "-none-"); 29 | return new PublicPayResult(_client); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/WechatQueryEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay.Endpoints.Result; 7 | 8 | namespace OmniPay.Wechatpay.Endpoints 9 | { 10 | public class WechatQueryEndpoint:IEndpointHandler 11 | { 12 | private readonly ILogger _logger; 13 | private readonly IWeChatPayClient _client; 14 | 15 | public WechatQueryEndpoint(ILogger logger,IWeChatPayClient client){ 16 | this._logger=logger; 17 | this._client=client; 18 | } 19 | public IEndpointResult Process(HttpContext context) 20 | { 21 | _logger.LogDebug("Start WechatQuery request"); 22 | 23 | if (!HttpMethods.IsPost(context.Request.Method)) 24 | { 25 | _logger.LogWarning("Invalid HTTP method for PublicPay endpoint."); 26 | return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); 27 | } 28 | _logger.LogTrace("End WechatQuery request. result type: {0}", this?.GetType().ToString() ?? "-none-"); 29 | return new QueryResult(_client); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/WechatScanPayEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay.Endpoints.Result; 7 | using OmniPay.Wechatpay.Validation; 8 | 9 | namespace OmniPay.Wechatpay.Endpoints 10 | { 11 | public class WechatScanPayEndpoint : IEndpointHandler 12 | { 13 | private readonly ILogger _logger; 14 | private readonly IWeChatPayClient _client; 15 | 16 | public WechatScanPayEndpoint(ILogger logger, IWeChatPayClient client) 17 | { 18 | this._logger = logger; 19 | this._client = client; 20 | } 21 | 22 | public IEndpointResult Process(HttpContext context) 23 | { 24 | _logger.LogDebug("Start WechatScanPay request"); 25 | 26 | if (!HttpMethods.IsPost(context.Request.Method)) 27 | { 28 | _logger.LogWarning("Invalid HTTP method for ScanPay endpoint."); 29 | return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); 30 | } 31 | 32 | //var validateResult = _validator.ValidateAsync(context).GetAwaiter().GetResult(); 33 | //if (validateResult.IsError) 34 | //{ 35 | // return Error(validateResult.Error); 36 | //} 37 | _logger.LogTrace("End WechatScanPay request. result type: {0}", this?.GetType().ToString() ?? "-none-"); 38 | return new ScanPayResult(_client); 39 | } 40 | 41 | private IEndpointResult Error(string error, string description = null) 42 | { 43 | return new ProccessErrorResult(error, description); 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Endpoints/WechatWapPayEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using OmniPay.Core.Hosting; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay.Endpoints.Result; 7 | 8 | namespace OmniPay.Wechatpay.Endpoints 9 | { 10 | public class WechatWapPayEndpoint : IEndpointHandler 11 | { 12 | private readonly ILogger _logger; 13 | private readonly IWeChatPayClient _client; 14 | public WechatWapPayEndpoint(ILogger logger,IWeChatPayClient client){ 15 | this._logger=logger; 16 | this._client=client; 17 | } 18 | public IEndpointResult Process(HttpContext context) 19 | { 20 | _logger.LogDebug("Start WechatWapPay request"); 21 | if (!HttpMethods.IsPost(context.Request.Method)) 22 | { 23 | _logger.LogWarning("Invalid HTTP method for ScanPay endpoint."); 24 | return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); 25 | } 26 | _logger.LogTrace("End WechatWapPay request. result type: {0}", this?.GetType().ToString() ?? "-none-"); 27 | return new WapPayResult(_client); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Extensions/WeChatPayApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using OmniPay.Core.Utils; 5 | using OmniPay.Wechatpay.Middleware; 6 | 7 | namespace OmniPay.Wechatpay.Extensions 8 | { 9 | public static class WeChatPayApplicationBuilderExtensions 10 | { 11 | //public static IEndpointConventionBuilder UseWeChatPayEndpoints(this IEndpointRouteBuilder endpoints,string pattern) { 12 | // var pipeline = endpoints.CreateApplicationBuilder() 13 | // .UseMiddleware().Build(); 14 | // return endpoints.Map(pattern, pipeline).WithDisplayName("wechatpay") ; 15 | //} 16 | 17 | /// 18 | /// 使用OmniPay 19 | /// 20 | /// 21 | /// 22 | public static IApplicationBuilder UseOmniPay(this IApplicationBuilder app) 23 | { 24 | app.UseMiddleware(); 25 | //var httpContextAccessor = app.ApplicationServices.GetRequiredService(); 26 | //HttpUtil.Configure(httpContextAccessor); 27 | return app; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Extensions/WeChatServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.DependencyInjection.Extensions; 4 | using OmniPay.Wechatpay.Validation; 5 | using OmniPay.Wechatpay.Validation.Default; 6 | using System; 7 | 8 | namespace OmniPay.Wechatpay.Extensions 9 | { 10 | public static class WeChatServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddWeChatPay(this IServiceCollection services, Action action) 13 | { 14 | //services.AddSingleton(); 15 | //services.AddSingleton(); 16 | //services.AddSingleton(); 17 | //services.AddSingleton(); 18 | //services.AddSingleton(); 19 | //services.AddSingleton(new Endpoint("wechatScanPay", "/pay-api/Wechatpay/ScanPay", typeof(WechatScanPayEndpoint))); 20 | //services.AddSingleton(new Endpoint("wechatWapPay", "/pay-api/Wechatpay/WapPay", typeof(WechatWapPayEndpoint))); 21 | //services.AddSingleton(new Endpoint("wechatAppPay", "/pay-api/Wechatpay/AppPay", typeof(WechatAppPayEndpoint))); 22 | //services.AddSingleton(new Endpoint("wechatPublicPay", "/pay-api/Wechatpay/PublicPay", typeof(WechatPublicPayEndpoint))); 23 | //services.AddSingleton(new Endpoint("wechatAppletPay", "/pay-api/Wechatpay/AppletPay", typeof(WechatAppletPayEndpoint))); 24 | return services.AddWeChatPayServices(action); 25 | } 26 | 27 | /// 28 | /// 验证 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 该验证设计目的主要用于自动api控制层,当然他的当前局限是不可以按照指定模型输出内容 34 | /// 在后期的模块设计中可以考虑该模块的自定义模型输出 35 | /// 36 | public static IServiceCollection AddValidators(this IServiceCollection services) 37 | { 38 | services.TryAddTransient(); 39 | return services; 40 | } 41 | 42 | private static IServiceCollection AddWeChatPayServices(this IServiceCollection services, Action action) 43 | { 44 | if (action != null) 45 | { 46 | services.Configure(action.Invoke); 47 | } 48 | services.TryAddSingleton(); 49 | services.AddSingleton(); 50 | return services; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/IWeChatPayClient.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using OmniPay.Core.Request; 3 | 4 | namespace OmniPay.Wechatpay 5 | { 6 | public interface IWeChatPayClient 7 | { 8 | /// 9 | /// Execute WeChatPay API request 10 | /// 11 | /// 12 | /// 13 | /// 14 | Task ExecuteAsync(BaseRequest request); 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Middleware/ApiEndpointMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace OmniPay.Wechatpay.Middleware 7 | { 8 | /// 9 | /// WeChat Pay middleware 10 | /// 11 | public class ApiEndpointMiddleware 12 | { 13 | private readonly RequestDelegate _next; 14 | 15 | public ApiEndpointMiddleware(RequestDelegate next) 16 | { 17 | _next = next; 18 | } 19 | 20 | public async Task InvokeAsync(HttpContext context) 21 | { 22 | //Access request method 23 | var responseContent = context.Request.Path.Value.Replace("/pay-api",""); 24 | responseContent = responseContent.TrimStart('/'); 25 | 26 | context.Response.ContentType = "application/json"; 27 | //Reflective access methods 28 | Type t = typeof(TestPay); 29 | var tuple= GetObjectMethodTuple(t, responseContent); 30 | if (tuple.Item1 == null) throw new NotImplementedException("not implement the stack"); 31 | 32 | string str = tuple.Item1.Invoke(tuple.Item2, null).ToString(); 33 | 34 | await context.Response.WriteAsync(str); 35 | } 36 | 37 | 38 | public static Tuple GetObjectMethodTuple(Type type,string methodname) { 39 | object obj = Activator.CreateInstance(type); 40 | MethodInfo mt = type.GetMethod(methodname); 41 | return new Tuple(mt,obj); 42 | } 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Middleware/WechatPayMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.Logging; 5 | using OmniPay.Core.Hosting; 6 | 7 | namespace OmniPay.Wechatpay.Middleware 8 | { 9 | public class WechatPayMiddleware 10 | { 11 | private readonly ILogger _logger; 12 | private readonly RequestDelegate _next; 13 | 14 | public WechatPayMiddleware(RequestDelegate next, ILogger logger) 15 | { 16 | _logger = logger; 17 | _next = next; 18 | } 19 | 20 | public async Task InvokeAsync(HttpContext context, IEndpointRouter router) 21 | { 22 | try 23 | { 24 | var endpoint = router.Find(context); 25 | if (endpoint != null) 26 | { 27 | _logger.LogInformation("Invoking WechatPay endpoint: {endpointType} for {url}", endpoint.GetType().FullName, context.Request.Path.ToString()); 28 | 29 | var result =endpoint.Process(context); 30 | 31 | if (result != null) 32 | { 33 | _logger.LogTrace("Invoking result: {type}", result.GetType().FullName); 34 | await result.ExecuteAsync(context); 35 | } 36 | }else{ 37 | context.Response.StatusCode = StatusCodes.Status404NotFound; 38 | } 39 | 40 | } 41 | catch (Exception ex) 42 | { 43 | _logger.LogCritical(ex, "Unhandled exception: {exception}", ex.Message); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/OmniPay.Wechatpay.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | OmniPay.Wechatpay 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Request/AppPayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Wechatpay.Domain; 3 | using OmniPay.Wechatpay.Response; 4 | 5 | namespace OmniPay.Wechatpay.Request 6 | { 7 | public class AppPayRequest : BaseRequest 8 | { 9 | public AppPayRequest() 10 | { 11 | RequestUrl = "/pay/unifiedorder"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Request/AppletPayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Wechatpay.Domain; 3 | using OmniPay.Wechatpay.Response; 4 | 5 | namespace OmniPay.Wechatpay.Request 6 | { 7 | public class AppletPayRequest : BaseRequest 8 | { 9 | public AppletPayRequest() 10 | { 11 | RequestUrl = "/pay/unifiedorder"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Request/CloseRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Wechatpay.Domain; 3 | using OmniPay.Wechatpay.Response; 4 | 5 | namespace OmniPay.Wechatpay.Request 6 | { 7 | public class CloseRequest : BaseRequest 8 | { 9 | public CloseRequest() 10 | { 11 | RequestUrl = "/pay/closeorder"; 12 | } 13 | public override void Execute() 14 | { 15 | Remove("notify_url"); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Request/PublicPayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Wechatpay.Domain; 3 | using OmniPay.Wechatpay.Response; 4 | 5 | namespace OmniPay.Wechatpay.Request 6 | { 7 | public class PublicPayRequest:BaseRequest 8 | { 9 | public PublicPayRequest() 10 | { 11 | RequestUrl = "/pay/unifiedorder"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Request/QueryRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Wechatpay.Domain; 3 | using OmniPay.Wechatpay.Response; 4 | 5 | namespace OmniPay.Wechatpay.Request 6 | { 7 | public class QueryRequest:BaseRequest 8 | { 9 | public QueryRequest() 10 | { 11 | RequestUrl = "/pay/orderquery"; 12 | } 13 | public override void Execute() 14 | { 15 | Remove("notify_url"); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Request/RefundRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Wechatpay.Domain; 3 | using OmniPay.Wechatpay.Response; 4 | 5 | namespace OmniPay.Wechatpay.Request 6 | { 7 | public class RefundRequest : BaseRequest 8 | { 9 | public RefundRequest() 10 | { 11 | RequestUrl = "/secapi/pay/refund"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Request/ScanPayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Wechatpay.Domain; 3 | using OmniPay.Wechatpay.Response; 4 | 5 | namespace OmniPay.Wechatpay.Request 6 | { 7 | public class ScanPayRequest:BaseRequest 8 | { 9 | public ScanPayRequest() 10 | { 11 | RequestUrl = "/pay/unifiedorder"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Request/WapPayRequest.cs: -------------------------------------------------------------------------------- 1 | using OmniPay.Core.Request; 2 | using OmniPay.Wechatpay.Domain; 3 | using OmniPay.Wechatpay.Response; 4 | 5 | namespace OmniPay.Wechatpay.Request 6 | { 7 | public class WapPayRequest : BaseRequest 8 | { 9 | public WapPayRequest() 10 | { 11 | RequestUrl = "/pay/unifiedorder"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/AppPayResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public class AppPayResponse:BaseResponse 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/AppletPayResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public class AppletPayResponse:PublicPayResponse 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/BaseResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public abstract class BaseResponse 4 | { 5 | /// 6 | /// 应用ID 7 | /// 8 | public string Appid { get; set; } 9 | 10 | /// 11 | /// 商户号 12 | /// 13 | public string MchId { get; set; } 14 | 15 | /// 16 | /// 设备号 17 | /// 18 | public string DeviceInfo { get; set; } 19 | 20 | /// 21 | /// 随机字符串 22 | /// 23 | public string NonceStr { get; set; } 24 | 25 | /// 26 | /// 签名 27 | /// 28 | public string Sign { get; set; } 29 | 30 | /// 31 | /// 业务结果 32 | /// 33 | public string ReturnCode { get; set; } 34 | 35 | /// 36 | /// 错误代码 37 | /// 38 | public string ErrCode { get; set; } 39 | 40 | /// 41 | /// 错误代码描述 42 | /// 43 | public string ErrCodeDes { get; set; } 44 | 45 | /// 46 | /// 返回状态码 47 | /// 48 | public string ResultCode { get; set; } 49 | 50 | /// 51 | /// 返回信息 52 | /// 53 | public string ReturnMsg { get; set; } 54 | 55 | /// 56 | /// 原始值 57 | /// 58 | public string Raw { get; set; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/CloseResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public class CloseResponse: BaseResponse 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/PublicPayResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public class PublicPayResponse:BaseResponse 4 | { 5 | /// 6 | /// 交易类型 7 | /// 8 | public string TradeType { get; set; } 9 | 10 | /// 11 | /// 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 12 | /// 13 | public string PrepayId { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/QueryResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public class QueryResponse:BaseResponse 4 | { 5 | /// 6 | /// 用户标识 7 | /// 8 | public string OpenId { get; set; } 9 | /// 10 | /// 是否关注公众账号 11 | /// 用户是否关注公众账号,Y-关注,N-未关注 12 | /// 13 | public string IsSubscribe { get; set; } 14 | /// 15 | /// 交易类型 16 | /// 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP,MICROPAY 17 | /// 18 | public string TradeType { get; set; } 19 | /// 20 | /// 交易状态 21 | /// SUCCESS-支付成功 22 | /// REFUND-转入退款 23 | /// NOTPAY-未支付 24 | /// CLOSED-已关闭 25 | /// REVOKED-已撤销(刷卡支付) 26 | /// USERPAYING-用户支付中 27 | /// PAYERROR-支付失败(其他原因,如银行返回失败) 28 | /// 支付状态机请见下单API页面 29 | /// 30 | public string TradeState { get; set; } 31 | /// 32 | /// 银行类型,采用字符串类型的银行标识,详见银行类型 33 | /// 34 | public string BankType { get; set; } 35 | /// 36 | /// 订单金额 37 | /// 订单总金额,单位为分 38 | /// 39 | public int TotalFree { get; set; } 40 | /// 41 | /// 现金支付货币类型 42 | /// 货币类型,符合ISO 4217标准的三位字符代码,默认人民币:CNY,其他值列表详见 43 | /// 44 | public string CashFreeType { get; set; } 45 | /// 46 | /// 代金券金额 47 | /// "代金券"金额≤订单金额,订单金额-"代金券"金额=现金支付金额 48 | /// 49 | public int CouponFree { get; set; } 50 | /// 51 | /// 代金券使用数量 52 | /// 53 | public string CouponCount { get; set; } 54 | /// 55 | /// 微信支付订单号 56 | /// 57 | public string TransactionId { get; set; } 58 | /// 59 | /// 商户订单号 60 | /// 61 | public string OutTradeNo { get; set; } 62 | /// 63 | /// 商户附加数据包 64 | /// 65 | public string Attach { get; set; } 66 | /// 67 | /// 支付完成时间 68 | /// 69 | public string TimeEnd { get; set; } 70 | /// 71 | /// 交易状态描述 72 | /// 73 | public string TradeStateDesc { get; set; } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/RefundResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public class RefundResponse : BaseResponse 4 | { 5 | /// 6 | /// 微信订单号 7 | /// 8 | public string TransactionId { get; set; } 9 | 10 | /// 11 | /// 商户订单号 12 | /// 13 | public string OutTradeNo { get; set; } 14 | 15 | /// 16 | /// 商户退款单号 17 | /// 18 | public string OutRefundNo { get; set; } 19 | 20 | /// 21 | /// 微信退款单号 22 | /// 23 | public string RefundId { get; set; } 24 | 25 | /// 26 | /// 退款金额 27 | /// 28 | public int RefundFee { get; set; } 29 | 30 | /// 31 | /// 应结退款金额 32 | /// 33 | public int SettlementRefundFee { get; set; } 34 | 35 | /// 36 | /// 标价金额 37 | /// 38 | public int TotalFee { get; set; } 39 | 40 | /// 41 | /// 现金支付金额 42 | /// 43 | public int CashFee { get; set; } 44 | 45 | /// 46 | /// 现金支付币种 47 | /// 48 | public string CashFeeType { get; set; } 49 | 50 | /// 51 | /// 代金券退款总金额 52 | /// 53 | public int CouponRefundFee { get; set; } 54 | 55 | /// 56 | /// 退款代金券使用数量 57 | /// 58 | public int CouponRefundCount { get; set; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/ScanPayResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public class ScanPayResponse:BaseResponse 4 | { 5 | /// 6 | /// 交易类型 7 | /// 8 | public string TradeType { get; set; } 9 | 10 | /// 11 | /// 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 12 | /// 13 | public string PrepayId { get; set; } 14 | 15 | /// 16 | /// 二维码链接 17 | /// 18 | public string CodeUrl { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Response/WapPayResponse.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Response 2 | { 3 | public class WapPayResponse:BaseResponse 4 | { 5 | /// 6 | /// 交易类型 7 | /// 8 | public string TradeType { get; set; } 9 | /// 10 | /// 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 11 | /// 12 | public string PrepayId { get; set; } 13 | 14 | /// 15 | /// 支付跳转链接 16 | /// mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。 17 | /// 18 | public string MwebUrl { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Results/TestWxResult.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Http; 3 | using OmniPay.Core.Hosting; 4 | 5 | namespace OmniPay.Wechatpay.Results 6 | { 7 | public class TestWxResult : IEndpointResult 8 | { 9 | public async Task ExecuteAsync(HttpContext context) 10 | { 11 | context.Response.ContentType = "application/json; charset=UTF-8"; 12 | await context.Response.WriteAsync("微信支付成功!"); 13 | await context.Response.Body.FlushAsync(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/TestPay.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay 2 | { 3 | public class TestPay 4 | { 5 | public string TestWxPay() { 6 | 7 | return "微信支付成功"; 8 | } 9 | 10 | 11 | public string TestaliPay() 12 | { 13 | return "支付宝支付成功"; 14 | } 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Validation/Default/ScanPayValidator.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hueifeng/OmniPay/3761886d6c1106e4018a22fd43726ff5e21e435f/src/OmniPay.Wechatpay/Validation/Default/ScanPayValidator.cs -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Validation/IScanPayValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace OmniPay.Wechatpay.Validation 5 | { 6 | public interface IScanPayValidator 7 | { 8 | Task ValidateAsync(HttpContext context); 9 | } 10 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Validation/Models/ScanPayRequestValidationResult.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Validation.Models 2 | { 3 | public class ScanPayRequestValidationResult 4 | { 5 | public string Result { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/Validation/ValidationResult.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay.Validation 2 | { 3 | public class ValidationResult 4 | { 5 | /// 6 | /// Gets or sets a value indicating whether the validation was successful. 7 | /// 8 | public bool IsError { get; set; } = true; 9 | 10 | /// 11 | /// Gets or sets the error. 12 | /// 13 | /// 14 | /// The error. 15 | /// 16 | public string Error { get; set; } 17 | 18 | /// 19 | /// Gets or sets the error description. 20 | /// 21 | /// 22 | /// The error description. 23 | /// 24 | public string ErrorDescription { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/WeChatPayClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Options; 3 | using OmniPay.Core.Exceptions; 4 | using OmniPay.Core.Request; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay.Response; 7 | using System.Threading.Tasks; 8 | 9 | namespace OmniPay.Wechatpay 10 | { 11 | public class WeChatPayClient : IWeChatPayClient 12 | { 13 | private readonly WeChatPayOptions _weChatPayOptions; 14 | private readonly ILogger _logger; 15 | private readonly IHttpHandler _httpHandler; 16 | 17 | public WeChatPayClient(IOptions weChatPayOptions, ILogger logger, 18 | IHttpHandler httpHandler) 19 | { 20 | _weChatPayOptions = weChatPayOptions.Value; 21 | _logger = logger; 22 | _httpHandler = httpHandler; 23 | } 24 | 25 | /// 26 | /// Execute WeChatPay API request implementation 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | public async Task ExecuteAsync(BaseRequest request) 33 | { 34 | BuildParams(request); 35 | var result = await _httpHandler.PostAsync(request.RequestUrl, request.ToXml(), _weChatPayOptions.HttpClientName, null); 36 | request.FromXml(result); 37 | var baseResponse = (BaseResponse)(object)request.ToObject(); 38 | baseResponse.Raw = result; 39 | var repSign = request.GetStringValue("sign"); 40 | if (string.IsNullOrEmpty(repSign) && !CheckSign(request, repSign, _weChatPayOptions.Key)) 41 | { 42 | _logger.LogError("Signature verification failed:{0}", result); 43 | throw new OmniPayException("Signature verification failed."); 44 | } 45 | return (TResponse)(object)baseResponse; 46 | } 47 | 48 | private void BuildParams(BaseRequest request) 49 | { 50 | if (string.IsNullOrEmpty(_weChatPayOptions.AppId)) 51 | { 52 | throw new OmniPayException(nameof(_weChatPayOptions.AppId)); 53 | } 54 | request.Add("appid", _weChatPayOptions.AppId); 55 | request.Add("mch_id", _weChatPayOptions.MchId); 56 | request.Add("notify_url", _weChatPayOptions.NotifyUrl); 57 | request.Execute(); 58 | request.Add("sign", BuildSign(request, _weChatPayOptions.Key, request.GetStringValue("sign_type") == "HMAC-SHA256")); 59 | request.RequestUrl = _weChatPayOptions.BaseUrl + request.RequestUrl; 60 | } 61 | 62 | internal static string BuildSign(BaseRequest request, string key, bool isHMACSHA256 = false) 63 | { 64 | var data = string.Join("&", $"{request.ToUrl(false)}&key={key}"); 65 | return isHMACSHA256 ? EncryptUtil.HMACSHA256(data, key) : EncryptUtil.MD5(data); 66 | } 67 | 68 | internal static bool CheckSign(BaseRequest request, string sign, string key) 69 | { 70 | return BuildSign(request, key) == sign; 71 | } 72 | 73 | } 74 | } -------------------------------------------------------------------------------- /src/OmniPay.Wechatpay/WeChatPayOptions.cs: -------------------------------------------------------------------------------- 1 | namespace OmniPay.Wechatpay 2 | { 3 | public class WeChatPayOptions 4 | { 5 | /// 6 | /// Application ID 7 | /// 8 | public string AppId { get; set; } 9 | 10 | /// 11 | /// Signature type 12 | /// 13 | public string SignType=>"MD5"; 14 | /// 15 | /// WeChat pay API keys 16 | /// 17 | public string Key { get; set; } 18 | /// 19 | /// Merchant Id 20 | /// 21 | public string MchId { get; set; } 22 | 23 | /// 24 | /// App Secret 25 | /// 26 | public string AppSecret { get; set; } 27 | 28 | /// 29 | /// Notice the URL 30 | /// 31 | public string NotifyUrl { get; set; } 32 | 33 | /// 34 | /// Merchants public key 35 | /// 36 | public string PublicKey { get; set; } 37 | 38 | /// 39 | /// BaseUrl 40 | /// 41 | public string BaseUrl { get; set; } 42 | 43 | /// 44 | /// The logical name of the to configure. 45 | /// 46 | public string HttpClientName { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/OmniPay.TestBase/BaseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Moq; 7 | using OmniPay.Core.Utils; 8 | using OmniPay.Wechatpay; 9 | 10 | namespace OmniPay.TestBase 11 | { 12 | public class BaseTest 13 | { 14 | public IServiceCollection CreateServiceCollection() 15 | { 16 | return new ServiceCollection(); 17 | } 18 | 19 | public IServiceCollection Services { get; set; } 20 | 21 | public IServiceProvider ServiceProvider { get; set; } 22 | 23 | public T GetService() 24 | { 25 | return ServiceProvider.GetService(); 26 | } 27 | 28 | public T GetRequiredService() 29 | { 30 | return ServiceProvider.GetRequiredService(); 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/OmniPay.TestBase/OmniPay.TestBase.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/Wechatpay.Test/Core/WechatBaseTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using Moq; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.TestBase; 7 | using OmniPay.Wechatpay; 8 | using System.Net.Http; 9 | 10 | namespace Wechatpay.Test.Core 11 | { 12 | public class WechatBaseTest : BaseTest 13 | { 14 | public WechatBaseTest() 15 | { 16 | //添加ILogger 17 | ILogger wechatpaylogger = 18 | new LoggerFactory().CreateLogger(); 19 | //添加配置参数 20 | var someOptions = Options.Create(new WeChatPayOptions 21 | { 22 | AppId = "wxdace645e0bc2c424", 23 | AppSecret = "4693afc6b2084885ca9fbc2355b97827", 24 | Key = "b7c996fbda5a9633ee4feb6b991c3919", 25 | BaseUrl = "https://api.mch.weixin.qq.com", 26 | MchId = "1900009641", 27 | NotifyUrl = "http://localhost:6113" 28 | }); 29 | //mock IHttpClientFactory 30 | var mock = new Mock(); 31 | mock.Setup(_ => _.CreateClient(It.IsAny())).Returns(new HttpClient()); 32 | IHttpClientFactory factory = mock.Object; 33 | var client = new WeChatPayClient(someOptions, wechatpaylogger, new HttpHandler(factory)); 34 | 35 | Services = CreateServiceCollection(); 36 | Services.AddSingleton(client); 37 | ServiceProvider = Services.BuildServiceProvider(); 38 | 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/Wechatpay.Test/Endpoints/WechatAppPayEndpointTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay; 7 | using OmniPay.Wechatpay.Endpoints; 8 | using OmniPay.Wechatpay.Endpoints.Result; 9 | using Xunit; 10 | 11 | namespace Wechatpay.Test.Endpoints 12 | { 13 | public class WechatAppPayEndpointTest 14 | { 15 | HttpContext _context = new DefaultHttpContext (); 16 | private ILogger wechatpaylogger = new LoggerFactory ().CreateLogger (); 17 | private ILogger logger = new LoggerFactory ().CreateLogger (); 18 | IWeChatPayClient _client; 19 | private WechatAppPayEndpoint _subject; 20 | 21 | private void init () { 22 | var someOptions = Options.Create (new WeChatPayOptions { 23 | AppId = "wxdace645e0bc2c424", 24 | AppSecret = "4693afc6b2084885ca9fbc2355b97827", 25 | BaseUrl = "https://api.mch.weixin.qq.com", 26 | MchId = "1900009641" 27 | }); 28 | _client = new WeChatPayClient (someOptions, wechatpaylogger,null); 29 | _subject = new WechatAppPayEndpoint (logger, _client); 30 | } 31 | public WechatAppPayEndpointTest () { 32 | this.init (); 33 | } 34 | 35 | [Fact] 36 | public void Process_get_entry_point_shoud_return_405 () { 37 | _context.Request.Method = "GET"; 38 | var result = _subject.Process (_context); 39 | var statusCode = result as StatusCodeResult; 40 | statusCode.Should ().NotBeNull (); 41 | statusCode.StatusCode.Should ().Be (405); 42 | } 43 | 44 | [Fact] 45 | public void Process_apppay_path_should_return_apppay_result () { 46 | _context.Request.Method = "POST"; 47 | var result = _subject.Process (_context); 48 | result.Should ().BeOfType (); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /test/Wechatpay.Test/Endpoints/WechatAppletPayEndpointTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay; 7 | using OmniPay.Wechatpay.Endpoints; 8 | using OmniPay.Wechatpay.Endpoints.Result; 9 | using Xunit; 10 | 11 | namespace Wechatpay.Test.Endpoints 12 | { 13 | public class WechatAppletPayEndpointTest 14 | { 15 | HttpContext _context = new DefaultHttpContext (); 16 | private ILogger wechatpaylogger = new LoggerFactory ().CreateLogger (); 17 | private ILogger logger = new LoggerFactory ().CreateLogger (); 18 | IWeChatPayClient _client; 19 | private WechatAppletPayEndpoint _subject; 20 | 21 | private void init () { 22 | var someOptions = Options.Create (new WeChatPayOptions { 23 | AppId = "wxdace645e0bc2c424", 24 | AppSecret = "4693afc6b2084885ca9fbc2355b97827", 25 | BaseUrl = "https://api.mch.weixin.qq.com", 26 | MchId = "1900009641" 27 | }); 28 | _client = new WeChatPayClient (someOptions, wechatpaylogger,null); 29 | _subject = new WechatAppletPayEndpoint (logger, _client); 30 | } 31 | public WechatAppletPayEndpointTest () { 32 | this.init (); 33 | } 34 | 35 | [Fact] 36 | public void Process_get_entry_point_shoud_return_405 () { 37 | _context.Request.Method = "GET"; 38 | var result = _subject.Process (_context); 39 | var statusCode = result as StatusCodeResult; 40 | statusCode.Should ().NotBeNull (); 41 | statusCode.StatusCode.Should ().Be (405); 42 | } 43 | 44 | [Fact] 45 | public void Process_appletpay_path_should_return_appletpay_result () { 46 | _context.Request.Method = "POST"; 47 | var result = _subject.Process (_context); 48 | result.Should ().BeOfType (); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /test/Wechatpay.Test/Endpoints/WechatCloseEndpointTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay; 7 | using OmniPay.Wechatpay.Endpoints; 8 | using OmniPay.Wechatpay.Endpoints.Result; 9 | using Xunit; 10 | 11 | namespace Wechatpay.Test.Endpoints 12 | { 13 | public class WechatCloseEndpointTest 14 | { 15 | HttpContext _context = new DefaultHttpContext (); 16 | private ILogger wechatpaylogger = new LoggerFactory ().CreateLogger (); 17 | private ILogger logger = new LoggerFactory ().CreateLogger (); 18 | IWeChatPayClient _client; 19 | private WechatCloseEndpoint _subject; 20 | 21 | private void init () { 22 | var someOptions = Options.Create (new WeChatPayOptions { 23 | AppId = "wxdace645e0bc2c424", 24 | AppSecret = "4693afc6b2084885ca9fbc2355b97827", 25 | BaseUrl = "https://api.mch.weixin.qq.com", 26 | MchId = "1900009641" 27 | }); 28 | _client = new WeChatPayClient (someOptions, wechatpaylogger,null); 29 | _subject = new WechatCloseEndpoint (logger, _client); 30 | } 31 | public WechatCloseEndpointTest () { 32 | this.init (); 33 | } 34 | 35 | [Fact] 36 | public void Process_get_entry_point_shoud_return_405 () { 37 | _context.Request.Method = "GET"; 38 | var result = _subject.Process (_context); 39 | var statusCode = result as StatusCodeResult; 40 | statusCode.Should ().NotBeNull (); 41 | statusCode.StatusCode.Should ().Be (405); 42 | } 43 | 44 | [Fact] 45 | public void Process_close_path_should_return_close_result () { 46 | _context.Request.Method = "POST"; 47 | var result = _subject.Process (_context); 48 | result.Should ().BeOfType (); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /test/Wechatpay.Test/Endpoints/WechatPublicPayEndpointTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Http; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using OmniPay.Core.Results; 7 | using OmniPay.Core.Utils; 8 | using OmniPay.Wechatpay; 9 | using OmniPay.Wechatpay.Endpoints; 10 | using OmniPay.Wechatpay.Endpoints.Result; 11 | using Xunit; 12 | 13 | namespace Wechatpay.Test.Endpoints 14 | { 15 | public class WechatPublicPayEndpointTest 16 | { 17 | 18 | HttpContext _context = new DefaultHttpContext (); 19 | private ILogger wechatpaylogger = new LoggerFactory ().CreateLogger (); 20 | private ILogger logger = new LoggerFactory ().CreateLogger (); 21 | IWeChatPayClient _client; 22 | private WechatPublicPayEndpoint _subject; 23 | 24 | 25 | private void init () { 26 | var someOptions = Options.Create (new WeChatPayOptions { 27 | AppId = "wxdace645e0bc2c424", 28 | AppSecret = "4693afc6b2084885ca9fbc2355b97827", 29 | BaseUrl = "https://api.mch.weixin.qq.com", 30 | MchId = "1900009641" 31 | }); 32 | _client = new WeChatPayClient (someOptions, wechatpaylogger,null); 33 | _subject = new WechatPublicPayEndpoint (logger, _client); 34 | } 35 | public WechatPublicPayEndpointTest () { 36 | this.init (); 37 | } 38 | 39 | [Fact] 40 | public void Process_get_entry_point_shoud_return_405 () { 41 | _context.Request.Method = "GET"; 42 | var result = _subject.Process (_context); 43 | var statusCode = result as StatusCodeResult; 44 | statusCode.Should ().NotBeNull (); 45 | statusCode.StatusCode.Should ().Be (405); 46 | } 47 | 48 | [Fact] 49 | public void Process_publicpay_path_should_return_publicpay_result () { 50 | _context.Request.Method = "POST"; 51 | var result = _subject.Process (_context); 52 | result.Should ().BeOfType (); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /test/Wechatpay.Test/Endpoints/WechatQueryEndpointTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay; 7 | using OmniPay.Wechatpay.Endpoints; 8 | using OmniPay.Wechatpay.Endpoints.Result; 9 | using Xunit; 10 | 11 | namespace Wechatpay.Test.Endpoints 12 | { 13 | public class WechatQueryEndpointTest 14 | { 15 | HttpContext _context = new DefaultHttpContext (); 16 | private ILogger wechatpaylogger = new LoggerFactory ().CreateLogger (); 17 | private ILogger logger = new LoggerFactory ().CreateLogger (); 18 | IWeChatPayClient _client; 19 | private WechatQueryEndpoint _subject; 20 | 21 | private void init () { 22 | var someOptions = Options.Create (new WeChatPayOptions { 23 | AppId = "wxdace645e0bc2c424", 24 | AppSecret = "4693afc6b2084885ca9fbc2355b97827", 25 | BaseUrl = "https://api.mch.weixin.qq.com", 26 | MchId = "1900009641" 27 | }); 28 | _client = new WeChatPayClient (someOptions, wechatpaylogger,null); 29 | _subject = new WechatQueryEndpoint (logger, _client); 30 | } 31 | public WechatQueryEndpointTest () { 32 | this.init (); 33 | } 34 | 35 | [Fact] 36 | public void Process_get_entry_point_shoud_return_405 () { 37 | _context.Request.Method = "GET"; 38 | var result = _subject.Process (_context); 39 | var statusCode = result as StatusCodeResult; 40 | statusCode.Should ().NotBeNull (); 41 | statusCode.StatusCode.Should ().Be (405); 42 | } 43 | 44 | [Fact] 45 | public void Process_query_path_should_return_query_result () { 46 | _context.Request.Method = "POST"; 47 | var result = _subject.Process (_context); 48 | result.Should ().BeOfType (); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /test/Wechatpay.Test/Endpoints/WechatScanPayEndpointTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using OmniPay.Core.Results; 7 | using OmniPay.Wechatpay; 8 | using OmniPay.Wechatpay.Endpoints; 9 | using OmniPay.Wechatpay.Endpoints.Result; 10 | using OmniPay.Wechatpay.Validation; 11 | using Xunit; 12 | 13 | namespace Wechatpay.Test.Endpoints 14 | { 15 | public class WechatScanPayEndpointTest { 16 | HttpContext _context = new DefaultHttpContext (); 17 | private ILogger wechatpaylogger = new LoggerFactory ().CreateLogger (); 18 | private ILogger logger = new LoggerFactory ().CreateLogger (); 19 | IWeChatPayClient _client; 20 | private WechatScanPayEndpoint _subject; 21 | private IScanPayValidator _validator; 22 | 23 | private void init () { 24 | var someOptions = Options.Create (new WeChatPayOptions { 25 | AppId = "wxdace645e0bc2c424", 26 | AppSecret = "4693afc6b2084885ca9fbc2355b97827", 27 | BaseUrl = "https://api.mch.weixin.qq.com", 28 | MchId = "1900009641" 29 | }); 30 | _client = new WeChatPayClient (someOptions, wechatpaylogger,null); 31 | _subject = new WechatScanPayEndpoint (logger, _client); 32 | } 33 | public WechatScanPayEndpointTest () { 34 | this.init (); 35 | } 36 | 37 | [Fact] 38 | public void Process_get_entry_point_shoud_return_405 () { 39 | _context.Request.Method = "GET"; 40 | var result = _subject.Process (_context); 41 | var statusCode = result as StatusCodeResult; 42 | statusCode.Should ().NotBeNull (); 43 | statusCode.StatusCode.Should ().Be (405); 44 | } 45 | 46 | [Fact] 47 | public void Process_scanpay_path_should_return_sanpay_result () { 48 | _context.Request.Method = "POST"; 49 | var result = _subject.Process (_context); 50 | result.Should ().BeOfType (); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /test/Wechatpay.Test/Endpoints/WechatWapPayEndpointTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using OmniPay.Core.Results; 6 | using OmniPay.Wechatpay; 7 | using OmniPay.Wechatpay.Endpoints; 8 | using OmniPay.Wechatpay.Endpoints.Result; 9 | using Xunit; 10 | 11 | namespace Wechatpay.Test.Endpoints 12 | { 13 | public class WechatWapPayEndpointTest 14 | { 15 | HttpContext _context = new DefaultHttpContext(); 16 | private ILogger wechatpaylogger = new LoggerFactory().CreateLogger(); 17 | private ILogger logger = new LoggerFactory().CreateLogger(); 18 | IWeChatPayClient _client; 19 | private WechatWapPayEndpoint _subject; 20 | 21 | private void init() 22 | { 23 | var someOptions = Options.Create(new WeChatPayOptions 24 | { 25 | AppId = "wxdace645e0bc2c424", 26 | AppSecret = "4693afc6b2084885ca9fbc2355b97827", 27 | BaseUrl = "https://api.mch.weixin.qq.com", 28 | MchId = "1900009641" 29 | }); 30 | _client = new WeChatPayClient(someOptions, wechatpaylogger, null); 31 | _subject = new WechatWapPayEndpoint(logger, _client); 32 | } 33 | public WechatWapPayEndpointTest() 34 | { 35 | this.init(); 36 | } 37 | 38 | [Fact] 39 | public void Process_get_entry_point_shoud_return_405() 40 | { 41 | _context.Request.Method = "GET"; 42 | var result = _subject.Process(_context); 43 | var statusCode = result as StatusCodeResult; 44 | statusCode.Should().NotBeNull(); 45 | statusCode.StatusCode.Should().Be(405); 46 | } 47 | 48 | [Fact] 49 | public void Process_apppay_path_should_return_wappay_result() 50 | { 51 | _context.Request.Method = "POST"; 52 | var result = _subject.Process(_context); 53 | result.Should().BeOfType(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /test/Wechatpay.Test/WeChatPays/WeChatPayTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using Moq; 5 | using OmniPay.Core.Utils; 6 | using OmniPay.Wechatpay; 7 | using OmniPay.Wechatpay.Domain; 8 | using OmniPay.Wechatpay.Request; 9 | using System; 10 | using System.Net.Http; 11 | using System.Threading.Tasks; 12 | using Wechatpay.Test.Core; 13 | using Xunit; 14 | 15 | namespace Wechatpay.Test.WeChatPays 16 | { 17 | public class WeChatPayTest : WechatBaseTest 18 | { 19 | private readonly IWeChatPayClient _client; 20 | 21 | public WeChatPayTest() 22 | { 23 | _client = GetRequiredService(); 24 | } 25 | 26 | [Fact(DisplayName = "微信扫码支付是否返回成功")] 27 | public async Task ScanPay_shoud_return_success() 28 | { 29 | var request = new ScanPayRequest(); 30 | request.AddParameters(new ScanPayModel 31 | { 32 | Body = "扫码支付", 33 | OutTradeNo = DateTime.Now.ToString("yyyyMMddHHmmssfff"), 34 | TotalFee = 1 35 | }); 36 | var result = await _client.ExecuteAsync(request); 37 | result.ResultCode.Should().Be("SUCCESS", "判断其是否返回SUCCESS"); 38 | 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/Wechatpay.Test/Wechatpay.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------