Ver código fonte

重构并更名为 `Paylinks` (#184)

* 重构并更名为 `Paylinks`

* Update WeChatPayBackgroundService.cs
Roc 9 meses atrás
pai
commit
c4376321e8
100 arquivos alterados com 4606 adições e 345 exclusões
  1. 49 42
      .editorconfig
  2. 16 0
      .github/FUNDING.yml
  3. 9 7
      .github/workflows/build.yml
  4. 0 5
      .gitignore
  5. 22 33
      Directory.Build.props
  6. 0 59
      FUNDING.md
  7. 0 64
      Paylink.sln
  8. BIN
      Paylink.snk
  9. 137 0
      Paylinks.sln
  10. 15 106
      README.md
  11. 0 20
      delete-bin-obj-folders.bat
  12. 1 1
      global.json
  13. BIN
      logo.png
  14. 0 8
      nuget.config
  15. BIN
      qrcode-dingtalk.png
  16. BIN
      qrcode-qq.png
  17. 23 0
      samples/Essensoft.Paylinks.Sample.Web/Essensoft.Paylinks.Sample.Web.csproj
  18. 22 0
      samples/Essensoft.Paylinks.Sample.Web/Extensions/HtmlHelpersExtensions.cs
  19. 20 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Index.cshtml
  20. 10 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Index.cshtml.cs
  21. 57 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/App.cshtml
  22. 34 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/App.cshtml.cs
  23. 49 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/BillDownloadUrlQuery.cshtml
  24. 32 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/BillDownloadUrlQuery.cshtml.cs
  25. 49 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Cancel.cshtml
  26. 28 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Cancel.cshtml.cs
  27. 53 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Close.cshtml
  28. 32 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Close.cshtml.cs
  29. 69 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Create.cshtml
  30. 35 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Create.cshtml.cs
  31. 364 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Index.cshtml
  32. 10 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Index.cshtml.cs
  33. 5 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Notify/TradeResult.cshtml
  34. 37 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Notify/TradeResult.cshtml.cs
  35. 65 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Pay.cshtml
  36. 36 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Pay.cshtml.cs
  37. 68 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/PreCreate.cshtml
  38. 36 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/PreCreate.cshtml.cs
  39. 49 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Query.cshtml
  40. 28 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Query.cshtml.cs
  41. 61 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Refund.cshtml
  42. 33 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Refund.cshtml.cs
  43. 53 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/RefundQuery.cshtml
  44. 28 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/RefundQuery.cshtml.cs
  45. 62 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Wap.cshtml
  46. 46 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Wap.cshtml.cs
  47. 62 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Web.cshtml
  48. 46 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Web.cshtml.cs
  49. 45 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/ConvertCertPrivateKey.cshtml
  50. 29 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/ConvertCertPrivateKey.cshtml.cs
  51. 45 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetCertPublicKey.cshtml
  52. 29 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetCertPublicKey.cshtml.cs
  53. 45 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetCertSN.cshtml
  54. 29 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetCertSN.cshtml.cs
  55. 45 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetRootCertSN.cshtml
  56. 29 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetRootCertSN.cshtml.cs
  57. 24 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/Index.cshtml
  58. 10 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/Index.cshtml.cs
  59. 26 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Error.cshtml
  60. 26 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Error.cshtml.cs
  61. 14 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Index.cshtml
  62. 10 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Index.cshtml.cs
  63. 114 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Shared/_Layout.cshtml
  64. 2 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Shared/_Layout.cshtml.css
  65. 2 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/Shared/_ValidationScriptsPartial.cshtml
  66. 20 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Index.cshtml
  67. 10 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Index.cshtml.cs
  68. 65 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/AbnormalRefund.cshtml
  69. 31 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/AbnormalRefund.cshtml.cs
  70. 72 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/AppPrepay.cshtml
  71. 46 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/AppPrepay.cshtml.cs
  72. 49 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Close.cshtml
  73. 32 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Close.cshtml.cs
  74. 69 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/CodePay.cshtml
  75. 39 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/CodePay.cshtml.cs
  76. 45 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/DownloadBill.cshtml
  77. 26 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/DownloadBill.cshtml.cs
  78. 45 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/GetFundFlowBill.cshtml
  79. 29 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/GetFundFlowBill.cshtml.cs
  80. 45 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/GetTradeBill.cshtml
  81. 29 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/GetTradeBill.cshtml.cs
  82. 80 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/H5Prepay.cshtml
  83. 39 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/H5Prepay.cshtml.cs
  84. 796 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Index.cshtml
  85. 10 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Index.cshtml.cs
  86. 76 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/JsapiPrepay.cshtml
  87. 47 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/JsapiPrepay.cshtml.cs
  88. 76 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/NativePrepay.cshtml
  89. 39 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/NativePrepay.cshtml.cs
  90. 5 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Notify/RefundResult.cshtml
  91. 57 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Notify/RefundResult.cshtml.cs
  92. 5 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Notify/TransactionSuccess.cshtml
  93. 38 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Notify/TransactionSuccess.cshtml.cs
  94. 49 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/QueryByOutTradeNo.cshtml
  95. 32 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/QueryByOutTradeNo.cshtml.cs
  96. 49 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/QueryByTransactionId.cshtml
  97. 32 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/QueryByTransactionId.cshtml.cs
  98. 69 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Refund.cshtml
  99. 35 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Refund.cshtml.cs
  100. 45 0
      samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/RefundQueryByOutRefundNo.cshtml

+ 49 - 42
.editorconfig

@@ -1,33 +1,34 @@
 # 如果要从更高级别的目录继承 .editorconfig 设置,请删除以下行
 root = true
 
-# 不使用制表符进行缩进。 
 [*]
-indent_style = space
-
-# XML 项目文件
-[*.{csproj,props}]
-indent_size = 2
+charset = utf-8
+end_of_line = crlf
+insert_final_newline = true
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_property = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_event = false:silent
 
-# JSON 文件
-[*.json]
+[*.{xaml,props,targets,proj,csproj,shproj,ttproj,vcxproj}]
+indent_style = space
 indent_size = 2
 
-# c# 文件
 [*.cs]
+indent_style = space
+indent_size = 4
 
-# 字符集
-charset = utf-8-bom
+# 最大行长度
+max_line_length = 160
 
 #### Core EditorConfig 选项 ####
 
-# 缩进和间距
-indent_size = 4
-indent_style = space
-tab_width = 4
+# Resharper
+resharper_csharp_space_within_single_line_array_initializer_braces = true
+resharper_csharp_place_accessorholder_attribute_on_same_line = false
+resharper_space_within_single_line_array_initializer_braces = true
 
 # 新行首选项
-end_of_line = crlf
 insert_final_newline = true
 trim_trailing_whitespace = true
 
@@ -84,26 +85,26 @@ dotnet_code_quality_unused_parameters = all
 # 禁止显示首选项
 dotnet_remove_unnecessary_suppression_exclusions = none
 
-# New line preferences
+# 新行首选项
 dotnet_style_allow_multiple_blank_lines_experimental = true
 dotnet_style_allow_statement_immediately_after_block_experimental = true
 
 #### c# 编码约定 ####
 
 # var 首选项
-csharp_style_var_elsewhere = false
-csharp_style_var_for_built_in_types = false
-csharp_style_var_when_type_is_apparent = false
+csharp_style_var_elsewhere = true:silent
+csharp_style_var_for_built_in_types = true:silent
+csharp_style_var_when_type_is_apparent = true:silent
 
 # Expression-bodied 成员
-csharp_style_expression_bodied_accessors = true
-csharp_style_expression_bodied_constructors = false
-csharp_style_expression_bodied_indexers = true
-csharp_style_expression_bodied_lambdas = true
-csharp_style_expression_bodied_local_functions = false
-csharp_style_expression_bodied_methods = false
-csharp_style_expression_bodied_operators = false
-csharp_style_expression_bodied_properties = true
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
 
 # 模式匹配首选项
 csharp_style_pattern_matching_over_as_with_null_check = true
@@ -113,15 +114,16 @@ csharp_style_prefer_pattern_matching = true
 csharp_style_prefer_switch_expression = true
 
 # Null 检查首选项
-csharp_style_conditional_delegate_call = true
+csharp_style_conditional_delegate_call = true:suggestion
 
 # 修饰符首选项
 csharp_prefer_static_local_function = true
-csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
+csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
 
 # 代码块首选项
-csharp_prefer_braces = true
-csharp_prefer_simple_using_statement = false
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_style_namespace_declarations = file_scoped:silent
 
 # 表达式级首选项
 csharp_prefer_simple_default_expression = true
@@ -130,15 +132,16 @@ csharp_style_implicit_object_creation_when_type_is_apparent = true
 csharp_style_inlined_variable_declaration = true
 csharp_style_pattern_local_over_anonymous_function = true
 csharp_style_prefer_index_operator = true
+csharp_style_prefer_null_check_over_type_check = true
 csharp_style_prefer_range_operator = true
 csharp_style_throw_expression = true
 csharp_style_unused_value_assignment_preference = discard_variable
 csharp_style_unused_value_expression_statement_preference = discard_variable
 
 # "using" 指令首选项
-csharp_using_directive_placement = outside_namespace
+csharp_using_directive_placement = outside_namespace:silent
 
-# New line preferences
+# 新行首选项
 csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
 csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
 csharp_style_allow_embedded_statements_on_same_line_experimental = true
@@ -210,24 +213,28 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
 
 dotnet_naming_symbols.interface.applicable_kinds = interface
 dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.interface.required_modifiers = 
+dotnet_naming_symbols.interface.required_modifiers =
 
 dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
 dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.types.required_modifiers = 
+dotnet_naming_symbols.types.required_modifiers =
 
 dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
 dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.non_field_members.required_modifiers = 
+dotnet_naming_symbols.non_field_members.required_modifiers =
 
 # 命名样式
 
-dotnet_naming_style.pascal_case.required_prefix = 
-dotnet_naming_style.pascal_case.required_suffix = 
-dotnet_naming_style.pascal_case.word_separator = 
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
 dotnet_naming_style.pascal_case.capitalization = pascal_case
 
 dotnet_naming_style.begins_with_i.required_prefix = I
-dotnet_naming_style.begins_with_i.required_suffix = 
-dotnet_naming_style.begins_with_i.word_separator = 
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
 dotnet_naming_style.begins_with_i.capitalization = pascal_case
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_prefer_system_threading_lock = true:suggestion

+ 16 - 0
.github/FUNDING.yml

@@ -0,0 +1,16 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+polar: # Replace with a single Polar username
+buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
+thanks_dev: # Replace with a single thanks.dev username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+custom: https://paylinks.cn/sponsors

+ 9 - 7
.github/workflows/build.yml

@@ -5,19 +5,21 @@ on:
     branches:
       - dev
     paths:
-      - '**/*.props'
       - 'global.json'
-      - 'src/**/*.cs'
-      - 'src/**/*.csproj'
+      - '**/*.props'
+      - '**/*.cs'
+      - '**/*.cshtml'
+      - '**/*.csproj'
 
   pull_request:
     branches:
       - dev
     paths:
-      - '**/*.props'
       - 'global.json'
-      - 'src/**/*.cs'
-      - 'src/**/*.csproj'
+      - '**/*.props'
+      - '**/*.cs'
+      - '**/*.cshtml'
+      - '**/*.csproj'
 
 permissions:
   contents: read
@@ -27,7 +29,7 @@ jobs:
 
     runs-on: 'ubuntu-latest'
     timeout-minutes: 10
-
+    
     steps:
     - uses: actions/checkout@v3
     - name: Setup .NET

+ 0 - 5
.gitignore

@@ -1,8 +1,3 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
-
 # User-specific files
 *.rsuser
 *.suo

+ 22 - 33
Directory.Build.props

@@ -1,38 +1,27 @@
 <Project>
 
-  <PropertyGroup>
-    <Copyright>Copyright (c) Essensoft and Contributors</Copyright>
-    <Authors>Essensoft</Authors>
-    <Product>Paylink</Product>
-    <Version>4.1.9</Version>
-    <NoWarn>$(NoWarn);CS1591;CS1570</NoWarn>
-    <PackageIcon>logo.png</PackageIcon>
-    <PackageProjectUrl>https://github.com/essensoft/paylink</PackageProjectUrl>
-    <RepositoryType>git</RepositoryType>
-    <RepositoryUrl>https://github.com/essensoft/paylink</RepositoryUrl>
-    <PackageLicenseFile>LICENSE.md</PackageLicenseFile>
-    <PackageReadmeFile>README.md</PackageReadmeFile>
-    <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
-    <PublishRepositoryUrl>true</PublishRepositoryUrl>
-    <EmbedUntrackedSources>true</EmbedUntrackedSources>
-    <SignAssembly>true</SignAssembly>
-    <!-- Include symbol files (*.pdb) in the built .nupkg -->
-    <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
-    <GenerateDocumentationFile Condition="'$(Configuration)' == 'Release'">true</GenerateDocumentationFile>
-    <GeneratePackageOnBuild Condition="'$(Configuration)' == 'Release'">true</GeneratePackageOnBuild>
-    <LangVersion>latest</LangVersion>
-    <SystemTextJsonPackageVersion>9.0.0</SystemTextJsonPackageVersion>
-    <MicrosoftExtensionsHttpPackageVersion>9.0.0</MicrosoftExtensionsHttpPackageVersion>
-  </PropertyGroup>
+	<PropertyGroup>
+		<Copyright>Copyright (c) Essensoft and Contributors</Copyright>
+		<Authors>Essensoft</Authors>
+		<Product>Paylinks</Product>
+		<Version>5.0.0</Version>
+		<NoWarn>$(NoWarn);CS8618</NoWarn>
+		<PackageIcon>logo.png</PackageIcon>
+		<PackageProjectUrl>https://github.com/essensoft/paylinks</PackageProjectUrl>
+		<RepositoryType>git</RepositoryType>
+		<RepositoryUrl>https://github.com/essensoft/paylinks</RepositoryUrl>
+		<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
+		<PackageReadmeFile>README.md</PackageReadmeFile>
+		<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
+		<PublishRepositoryUrl>true</PublishRepositoryUrl>
+		<EmbedUntrackedSources>true</EmbedUntrackedSources>
+		<LangVersion>latest</LangVersion>
+	</PropertyGroup>
 
-  <ItemGroup>
-    <None Include="$(MSBuildThisFileDirectory)logo.png" Pack="true" Visible="false" PackagePath="" />
-    <None Include="$(MSBuildThisFileDirectory)LICENSE.md" Pack="true" Visible="false" PackagePath="" />
-    <None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" Visible="false" PackagePath="" />
-  </ItemGroup>
-
-  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0'">
-    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
-  </ItemGroup>
+	<ItemGroup>
+		<None Include="$(MSBuildThisFileDirectory)logo.png" Pack="true" Visible="false" PackagePath=""/>
+		<None Include="$(MSBuildThisFileDirectory)LICENSE.md" Pack="true" Visible="false" PackagePath=""/>
+		<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" Visible="false" PackagePath=""/>
+	</ItemGroup>
 
 </Project>

+ 0 - 59
FUNDING.md

@@ -1,59 +0,0 @@
-# 捐赠名单
-
-* 不定期更新名单。
-* 每一笔捐赠都是对Paylink项目的肯定和鼓励!
-* 非常感谢大家!
-
----
-
-日期 | 名称 | 渠道 | 金额 | 留言
---- | --- | --- | --- | ---
-2022-02-19 | *菜 | 微信 | 100.00 | 
-2021-11-10 | Y*o | 微信 | 20.00 | 
-2021-11-05 | *乖 | 微信 | 100.00 | 微信分账
-2021-10-22 | *浩 | 微信 | 5.00 |
-2021-10-07 | J*K | 微信 | 200.00 | 感谢你的付出与贡献
-2021-09-02 | 天堂亦或是地狱 | 微信 | 30.00 |
-2021-08-07 | *亮 | 微信 | 300.00 | 感谢分享
-2021-07-29 | *鸣 | 微信 | 18.80 |
-2021-05-12 | P*n | 微信 | 66.66 |
-2021-05-12 | 沛森 | QQ | 2.88 |
-2021-04-02 | *作 | 微信 | 1.00 |
-2021-03-19 | @*e | 微信 | 11.70 |
-2020-12-30 | 天佑 | 微信 | 66.00 | 感谢大佬开源
-2020-12-24 | 艺智软件-酒店PMS | QQ | 40.00 | 
-2020-12-07 | d*g | 微信 | 10.00 |
-2020-07-29 | *客 | 微信 | 105.86 | 易易工作室 赞助5CNE
-2020-06-11 | *道 | 微信 | 30.00 | 技术赞助
-2020-06-11 | slobber | QQ | 35.00 | 
-2020-05-20 | 연료유 | QQ | 10.00 |
-2020-05-14 | zph | 微信 | 20.00 | 
-2020-04-23 | 西湖渔夫 | 微信 | 10.00 | 给大佬加个鸡腿
-2020-04-21 | 和太阳肩并肩 | 微信 | 100.00 |
-2020-04-04 | 深情*不离 | QQ | 9.90 |
-2020-03-30 | 安徽艺智软件🐤 | 微信 | 200.00 | 感谢大佬开源
-2020-03-02 | *牟 | 支付宝 | 3.00 | payment加油
-2019-11-21 | **龙 | 支付宝 | 18.88 |
-2019-11-11 | too | 微信 | 10.00 | payment很好用 谢谢
-2019-09-27 | Anduin | 微信 | 1.00 |
-2019-08-17 | 行走在发际线的猿人 | 微信 | 10.00 |
-2019-05-10 | *天 | 支付宝 | 5.00 | 5元也是爱,感谢开源,超好用
-2019-01-28 | Mike Cheers | 微信 | 10.24 | 支持,加油
-2018-12-11 | betry | 微信 | 6.66 |
-2018-08-28 | 丁* 极思灵创 *** | 微信 | 10.00 |
-2018-08-12 | 邓* | 微信 | 10.00 | 谢谢你的payment代码
-2018-07-07 | * | 微信 | 200.00 |
-2018-06-23 | Zf | 微信 | 10.00 |
-2018-05-29 | 清欢 | 微信 | 88.88 |
-2018-05-14 | 微风 | 微信 | 10.00 | 支付代码很好 很不错
-2018-03-28 | 🤖 | 微信 | 6.66 |
-2018-03-27 | 那份、小倔强 | 微信 | 10.00 |
-2018-03-27 | 毒药 | 微信 | 10.00 |
-2018-03-22 | Mr. Li | 微信 | 10.00 |
-2018-03-21 | Friest | 微信 | 100.00 | 体面
-2018-03-20 | 陽光微笑 | 微信 | 100.00 |
-2018-03-20 | 社会主义好 | 微信 | 10.00 |
-2018-03-20 | Niu Ke🐮 | 微信 | 10.00 |
-2018-03-19 | 方向 | 微信 | 10.00 |
-2018-03-19 | Gpwei💨 | 微信 | 10.00 |
-2018-03-19 | 拒绝者 | 微信 | 10.00 |

+ 0 - 64
Paylink.sln

@@ -1,64 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29009.5
-MinimumVisualStudioVersion = 15.0.26124.0
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0-solution-items", "0-solution-items", "{E2D9A588-E676-446B-BE7B-B3AAAD9FC9B6}"
-	ProjectSection(SolutionItems) = preProject
-		.editorconfig = .editorconfig
-		Directory.Build.props = Directory.Build.props
-		FUNDING.md = FUNDING.md
-		global.json = global.json
-		LICENSE.md = LICENSE.md
-		nuget.config = nuget.config
-		README.md = README.md
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1-src", "1-src", "{D3871E61-CA47-4BD0-8BF9-B64A42B2200D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylink.Alipay", "src\Essensoft.Paylink.Alipay\Essensoft.Paylink.Alipay.csproj", "{167FAA95-300E-49D5-A29B-EFCFF90A0584}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylink.Security", "src\Essensoft.Paylink.Security\Essensoft.Paylink.Security.csproj", "{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylink.WeChatPay", "src\Essensoft.Paylink.WeChatPay\Essensoft.Paylink.WeChatPay.csproj", "{66655C56-A98C-4F67-9F41-FECF0AFFA28E}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2-samples", "2-samples", "{DD8CC860-F3E3-40F4-8A8F-20BF66EA051C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApplicationSample", "samples\WebApplicationSample\WebApplicationSample.csproj", "{6C90BFFA-2F73-4961-922A-588B589C444C}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{167FAA95-300E-49D5-A29B-EFCFF90A0584}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{167FAA95-300E-49D5-A29B-EFCFF90A0584}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{167FAA95-300E-49D5-A29B-EFCFF90A0584}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{167FAA95-300E-49D5-A29B-EFCFF90A0584}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}.Release|Any CPU.Build.0 = Release|Any CPU
-		{66655C56-A98C-4F67-9F41-FECF0AFFA28E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{66655C56-A98C-4F67-9F41-FECF0AFFA28E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{66655C56-A98C-4F67-9F41-FECF0AFFA28E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{66655C56-A98C-4F67-9F41-FECF0AFFA28E}.Release|Any CPU.Build.0 = Release|Any CPU
-		{6C90BFFA-2F73-4961-922A-588B589C444C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{6C90BFFA-2F73-4961-922A-588B589C444C}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6C90BFFA-2F73-4961-922A-588B589C444C}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{6C90BFFA-2F73-4961-922A-588B589C444C}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-	GlobalSection(NestedProjects) = preSolution
-		{167FAA95-300E-49D5-A29B-EFCFF90A0584} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
-		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
-		{66655C56-A98C-4F67-9F41-FECF0AFFA28E} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
-		{6C90BFFA-2F73-4961-922A-588B589C444C} = {DD8CC860-F3E3-40F4-8A8F-20BF66EA051C}
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {C5BE9B18-CD29-4490-84EA-68EDEEF75428}
-	EndGlobalSection
-EndGlobal

BIN
Paylink.snk


+ 137 - 0
Paylinks.sln

@@ -0,0 +1,137 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.12.35514.174
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution-items", "solution-items", "{A97F4A9F-F750-4E72-847A-D3078746C4A2}"
+	ProjectSection(SolutionItems) = preProject
+		.editorconfig = .editorconfig
+		.gitattributes = .gitattributes
+		.gitignore = .gitignore
+		Directory.Build.props = Directory.Build.props
+		global.json = global.json
+		LICENSE.md = LICENSE.md
+		logo.png = logo.png
+		README.md = README.md
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D3871E61-CA47-4BD0-8BF9-B64A42B2200D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.Security", "src\Essensoft.Paylinks.Security\Essensoft.Paylinks.Security.csproj", "{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.WeChatPay.Client", "src\Essensoft.Paylinks.WeChatPay.Client\Essensoft.Paylinks.WeChatPay.Client.csproj", "{66655C56-A98C-4F67-9F41-FECF0AFFA28E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.WeChatPay.Core", "src\Essensoft.Paylinks.WeChatPay.Core\Essensoft.Paylinks.WeChatPay.Core.csproj", "{8DF20961-5CE8-4475-9827-667A8168B6B9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.WeChatPay.Mvc", "src\Essensoft.Paylinks.WeChatPay.Mvc\Essensoft.Paylinks.WeChatPay.Mvc.csproj", "{4A71E5E5-214C-4693-8C4A-39550A93B53A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.WeChatPay.Payments", "src\Essensoft.Paylinks.WeChatPay.Payments\Essensoft.Paylinks.WeChatPay.Payments.csproj", "{43DDFFF8-C08C-44F4-B7DB-E8AB23E1F1E7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.WeChatPay.Certificates", "src\Essensoft.Paylinks.WeChatPay.Certificates\Essensoft.Paylinks.WeChatPay.Certificates.csproj", "{F9C473E4-7EB5-484E-B2DF-0F7E17AB89B9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.Alipay.Client", "src\Essensoft.Paylinks.Alipay.Client\Essensoft.Paylinks.Alipay.Client.csproj", "{167FAA95-300E-49D5-A29B-EFCFF90A0584}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.Alipay.Core", "src\Essensoft.Paylinks.Alipay.Core\Essensoft.Paylinks.Alipay.Core.csproj", "{D2F94E9E-4396-4584-8E22-CB17796A2677}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.Alipay.Mvc", "src\Essensoft.Paylinks.Alipay.Mvc\Essensoft.Paylinks.Alipay.Mvc.csproj", "{F1465B3F-BF77-44B9-A85E-66A1B054388A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.Alipay.Payments", "src\Essensoft.Paylinks.Alipay.Payments\Essensoft.Paylinks.Alipay.Payments.csproj", "{6ABD7368-BEDC-4357-85CB-35168553D9A2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{45CFA2CF-2657-44D8-BE32-694318537385}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.Sample.Web", "samples\Essensoft.Paylinks.Sample.Web\Essensoft.Paylinks.Sample.Web.csproj", "{6CF2DF33-5C3C-4ABF-8A92-8E45C3ACAFB7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7C8578D4-64B9-46BC-B1B0-1452D925711B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.Security.Tests", "test\Essensoft.Paylinks.Security.Tests\Essensoft.Paylinks.Security.Tests.csproj", "{0109448B-BB12-48CB-8820-B1670899DDA7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Essensoft.Paylinks.WeChatPay.Client.Tests", "test\Essensoft.Paylinks.WeChatPay.Client.Tests\Essensoft.Paylinks.WeChatPay.Client.Tests.csproj", "{1FD54CA7-5937-4118-BD75-2C64582EF553}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Essensoft.Paylinks.WeChatPay.Core.Tests", "test\Essensoft.Paylinks.WeChatPay.Core.Tests\Essensoft.Paylinks.WeChatPay.Core.Tests.csproj", "{56F84702-19F4-464F-8F67-226E0036EE35}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66}.Release|Any CPU.Build.0 = Release|Any CPU
+		{66655C56-A98C-4F67-9F41-FECF0AFFA28E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{66655C56-A98C-4F67-9F41-FECF0AFFA28E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{66655C56-A98C-4F67-9F41-FECF0AFFA28E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{66655C56-A98C-4F67-9F41-FECF0AFFA28E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8DF20961-5CE8-4475-9827-667A8168B6B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8DF20961-5CE8-4475-9827-667A8168B6B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8DF20961-5CE8-4475-9827-667A8168B6B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8DF20961-5CE8-4475-9827-667A8168B6B9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4A71E5E5-214C-4693-8C4A-39550A93B53A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4A71E5E5-214C-4693-8C4A-39550A93B53A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4A71E5E5-214C-4693-8C4A-39550A93B53A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4A71E5E5-214C-4693-8C4A-39550A93B53A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{43DDFFF8-C08C-44F4-B7DB-E8AB23E1F1E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{43DDFFF8-C08C-44F4-B7DB-E8AB23E1F1E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{43DDFFF8-C08C-44F4-B7DB-E8AB23E1F1E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{43DDFFF8-C08C-44F4-B7DB-E8AB23E1F1E7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F9C473E4-7EB5-484E-B2DF-0F7E17AB89B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F9C473E4-7EB5-484E-B2DF-0F7E17AB89B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F9C473E4-7EB5-484E-B2DF-0F7E17AB89B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F9C473E4-7EB5-484E-B2DF-0F7E17AB89B9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{167FAA95-300E-49D5-A29B-EFCFF90A0584}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{167FAA95-300E-49D5-A29B-EFCFF90A0584}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{167FAA95-300E-49D5-A29B-EFCFF90A0584}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{167FAA95-300E-49D5-A29B-EFCFF90A0584}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D2F94E9E-4396-4584-8E22-CB17796A2677}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D2F94E9E-4396-4584-8E22-CB17796A2677}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D2F94E9E-4396-4584-8E22-CB17796A2677}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D2F94E9E-4396-4584-8E22-CB17796A2677}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F1465B3F-BF77-44B9-A85E-66A1B054388A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F1465B3F-BF77-44B9-A85E-66A1B054388A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F1465B3F-BF77-44B9-A85E-66A1B054388A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F1465B3F-BF77-44B9-A85E-66A1B054388A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6ABD7368-BEDC-4357-85CB-35168553D9A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6ABD7368-BEDC-4357-85CB-35168553D9A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6ABD7368-BEDC-4357-85CB-35168553D9A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6ABD7368-BEDC-4357-85CB-35168553D9A2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6CF2DF33-5C3C-4ABF-8A92-8E45C3ACAFB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6CF2DF33-5C3C-4ABF-8A92-8E45C3ACAFB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6CF2DF33-5C3C-4ABF-8A92-8E45C3ACAFB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6CF2DF33-5C3C-4ABF-8A92-8E45C3ACAFB7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{0109448B-BB12-48CB-8820-B1670899DDA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0109448B-BB12-48CB-8820-B1670899DDA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0109448B-BB12-48CB-8820-B1670899DDA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0109448B-BB12-48CB-8820-B1670899DDA7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{1FD54CA7-5937-4118-BD75-2C64582EF553}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{1FD54CA7-5937-4118-BD75-2C64582EF553}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{1FD54CA7-5937-4118-BD75-2C64582EF553}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{1FD54CA7-5937-4118-BD75-2C64582EF553}.Release|Any CPU.Build.0 = Release|Any CPU
+		{56F84702-19F4-464F-8F67-226E0036EE35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{56F84702-19F4-464F-8F67-226E0036EE35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{56F84702-19F4-464F-8F67-226E0036EE35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{56F84702-19F4-464F-8F67-226E0036EE35}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{3578E08D-A08E-4C4A-A5DE-8D31C21F7D66} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{66655C56-A98C-4F67-9F41-FECF0AFFA28E} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{8DF20961-5CE8-4475-9827-667A8168B6B9} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{4A71E5E5-214C-4693-8C4A-39550A93B53A} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{43DDFFF8-C08C-44F4-B7DB-E8AB23E1F1E7} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{F9C473E4-7EB5-484E-B2DF-0F7E17AB89B9} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{167FAA95-300E-49D5-A29B-EFCFF90A0584} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{D2F94E9E-4396-4584-8E22-CB17796A2677} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{F1465B3F-BF77-44B9-A85E-66A1B054388A} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{6ABD7368-BEDC-4357-85CB-35168553D9A2} = {D3871E61-CA47-4BD0-8BF9-B64A42B2200D}
+		{6CF2DF33-5C3C-4ABF-8A92-8E45C3ACAFB7} = {45CFA2CF-2657-44D8-BE32-694318537385}
+		{0109448B-BB12-48CB-8820-B1670899DDA7} = {7C8578D4-64B9-46BC-B1B0-1452D925711B}
+		{1FD54CA7-5937-4118-BD75-2C64582EF553} = {7C8578D4-64B9-46BC-B1B0-1452D925711B}
+		{56F84702-19F4-464F-8F67-226E0036EE35} = {7C8578D4-64B9-46BC-B1B0-1452D925711B}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {C5BE9B18-CD29-4490-84EA-68EDEEF75428}
+	EndGlobalSection
+EndGlobal

+ 15 - 106
README.md

@@ -1,125 +1,34 @@
-<p align="center">
-  <a href="https://github.com/essensoft/paylink">
-    <img width="96" src="https://cdn.jsdelivr.net/gh/essensoft/paylink@dev/logo.png">
-  </a>
-</p>
+# Paylinks
 
-<h1 align="center">Paylink</h1>
-
-<div align="center">
-
-一套基于 .NET Core 开发的支付SDK集,它极大简化了API调用及通知的处理流程。
+[github-action-image]: https://img.shields.io/github/actions/workflow/status/essensoft/paylinks/build.yml?branch=dev&style=flat-square
+[github-action-url]: https://github.com/essensoft/paylinks/actions/workflows/build.yml?query=branch%3Adev
+[license-image]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square
 
 [![Build status][github-action-image]][github-action-url]
 [![MIT][license-image]](LICENSE.md)
 
-[github-action-image]: https://img.shields.io/github/actions/workflow/status/essensoft/paylink/build.yml?branch=dev&style=flat-square
-[github-action-url]: https://github.com/essensoft/paylink/actions/workflows/build.yml?query=branch%3Adev
-[license-image]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square
-
-</div>
+一套基于 现代 .NET 开发,支持跨平台、多商户的第三方支付SDK。
+为简化开发者接入第三方支付平台而设计的SDK,
+支持支付宝和微信支付,便于快速集成支付功能。
 
-## NuGet 包
+### 开发环境
 
-Package  | NuGet | Downloads | TargetFrameworks
--------- | :---- | :-------- | ---------------
-[Essensoft.Paylink.Alipay][nuget-alipay-url] | ![NuGet][nuget-alipay-v-image] | ![Downloads][nuget-alipay-dt-image] | ![netstandard2.1][standard2.1-Y-image] ![netcoreapp3.1][netcoreapp3.1-Y-image] ![net6.0][net6.0-Y-image] ![net7.0][net7.0-Y-image] ![net8.0][net8.0-Y-image] ![net9.0][net9.0-Y-image]
-[Essensoft.Paylink.WeChatPay][nuget-wechatpay-url] | ![NuGet][nuget-wechatpay-v-image] | ![Downloads][nuget-wechatpay-dt-image] | ![netstandard2.1][standard2.1-Y-image] ![netcoreapp3.1][netcoreapp3.1-Y-image] ![net6.0][net6.0-Y-image] ![net7.0][net7.0-Y-image] ![net8.0][net8.0-Y-image] ![net9.0][net9.0-Y-image]
-[Essensoft.Paylink.Security][nuget-security-url] | ![NuGet][nuget-security-v-image] | ![Downloads][nuget-security-dt-image] | ![netstandard2.1][standard2.1-Y-image] ![net6.0][net6.0-Y-image] ![net7.0][net7.0-Y-image] ![net8.0][net8.0-Y-image] ![net9.0][net9.0-Y-image]
-
-[nuget-alipay-url]: https://www.nuget.org/packages/Essensoft.Paylink.Alipay
-[nuget-alipay-v-image]: https://img.shields.io/nuget/v/Essensoft.Paylink.Alipay.svg?style=flat-square
-[nuget-alipay-dt-image]: https://img.shields.io/nuget/dt/Essensoft.Paylink.Alipay.svg?style=flat-square
-[nuget-wechatpay-url]: https://www.nuget.org/packages/Essensoft.Paylink.WeChatPay
-[nuget-wechatpay-v-image]: https://img.shields.io/nuget/v/Essensoft.Paylink.WeChatPay.svg?style=flat-square
-[nuget-wechatpay-dt-image]: https://img.shields.io/nuget/dt/Essensoft.Paylink.WeChatPay.svg?style=flat-square
-[nuget-security-url]: https://www.nuget.org/packages/Essensoft.Paylink.Security
-[nuget-security-v-image]: https://img.shields.io/nuget/v/Essensoft.Paylink.Security.svg?style=flat-square
-[nuget-security-dt-image]: https://img.shields.io/nuget/dt/Essensoft.Paylink.Security.svg?style=flat-square
-[standard2.1-Y-image]: https://img.shields.io/badge/standard2.1-Y-brightgreen.svg?style=flat-square
-[netcoreapp3.1-Y-image]: https://img.shields.io/badge/netcoreapp3.1-Y-brightgreen.svg?style=flat-square
-[net6.0-Y-image]: https://img.shields.io/badge/net6.0-Y-brightgreen.svg?style=flat-square
-[net7.0-Y-image]: https://img.shields.io/badge/net7.0-Y-brightgreen.svg?style=flat-square
-[net8.0-Y-image]: https://img.shields.io/badge/net8.0-Y-brightgreen.svg?style=flat-square
-[net9.0-Y-image]: https://img.shields.io/badge/net9.0-Y-brightgreen.svg?style=flat-square
-
-## 开发环境
-
-* Windows 11
 * [Rider](https://www.jetbrains.com/rider) / [Visual Studio](https://visualstudio.microsoft.com)
 * [.NET 9.0](https://dotnet.microsoft.com/download/dotnet/9.0)
 
-## 运行环境
+### 运行环境
 
-- [.NET Core 3.1](https://dotnet.microsoft.com/download/dotnet/3.1)
-- [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0)
-- [.NET 7.0](https://dotnet.microsoft.com/download/dotnet/7.0)
 - [.NET 8.0](https://dotnet.microsoft.com/download/dotnet/8.0)
 - [.NET 9.0](https://dotnet.microsoft.com/download/dotnet/9.0)
 
-## 使用方式
-
-* [示例项目](samples/WebApplicationSample)
-
-## 捐赠
+### 赞助
 
-[捐赠名单](FUNDING.md)
+- [赞助名单](https://paylinks.cn/sponsors)
 
-## 社区互助
+### 社区互助
 
 如果您在使用的过程中碰到问题,可以通过下面几个途径寻求帮助,同时我们也鼓励资深用户通过下面的途径给新人提供帮助。
 
-* 钉钉群
-    - <details>
-        <summary>Paylink 交流群: 34090889(点击显示二维码)</summary>
-        <img src="https://cdn.jsdelivr.net/gh/essensoft/paylink@dev/qrcode-dingtalk.png" width="300">
-      </details>
-* QQ群
-    - <details>
-        <summary>Paylink 交流群: 522457525(点击显示二维码)</summary>
-        <img src="https://cdn.jsdelivr.net/gh/essensoft/paylink@dev/qrcode-qq.png" width="300">
-      </details>
-
-## 支持渠道
-
-1. 支付宝 [文档中心](https://openhome.alipay.com/docCenter/docCenter.htm)
-
-* 同步 [alipay-sdk-net-all](https://github.com/alipay/alipay-sdk-net-all) 所有API。
-
-2. 微信支付
-
-* V2版 [开发文档](https://pay.weixin.qq.com/wiki/doc/api/index.html)
-    - 付款码支付(刷卡)
-    - JSAPI支付(公众号)
-    - Native支付(扫码)
-    - APP支付
-    - H5支付
-    - 小程序支付
-    - 刷脸支付
-    - 现金红包
-    - 企业付款(零钱/银行卡)
-    - 分账
-    - 酒店押金
-    - 委托扣款
-    - ...
-
-* V3版 [开发者文档](https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml)
-    - 基础支付
-    - 分账
-    - 微信支付分
-    - 商户进件(服务商)
-    - 基础支付(服务商)
-    - 分账(服务商)
-    - 商家转账到零钱
-    - ...
-
-## 致谢
-
-- [Alipay](https://github.com/alipay)
-- [Varorbc](https://github.com/Varorbc)
-- [JetBrains](https://jb.gg/OpenSourceSupport)
-
-## 项目地址
-
-- [GitHub](https://github.com/essensoft/paylink)
-- [Gitee](https://gitee.com/essensoft/paylink)
+- 飞书交流群: [加入链接](https://applink.feishu.cn/client/message/link/open?token=AmaiieikwYAcZrEQ9XnAAAE%3D)
+- QQ交流群: 522457525 [加入链接](https://qm.qq.com/q/lOhqmDT0hG)
+- 钉钉交流群: 34090889 [加入链接](https://qr.dingtalk.com/action/joingroup?code=v1,k1,1tAeOJxsgOjngwZZD/uEhtWpOiU3B9CQK8Xs1wHdau4=&_dt_no_comment=1&origin=11)

+ 0 - 20
delete-bin-obj-folders.bat

@@ -1,20 +0,0 @@
-@ECHO off
-cls
-
-ECHO Deleting all BIN and OBJ folders...
-ECHO.
-
-FOR /d /r . %%d in (bin,obj) DO (
-	IF EXIST "%%d" (		 	 
-		ECHO %%d | FIND /I "\node_modules\" > Nul && ( 
-			ECHO.Skipping: %%d
-		) || (
-			ECHO.Deleting: %%d
-			rd /s/q "%%d"
-		)
-	)
-)
-
-ECHO.
-ECHO.BIN and OBJ folders have been successfully deleted. Press any key to exit.
-pause > nul

+ 1 - 1
global.json

@@ -1,6 +1,6 @@
 {
   "sdk": {
-    "version": "9.0.100",
+    "version": "9.0.101",
     "rollForward": "latestFeature"
   }
 }

BIN
logo.png


+ 0 - 8
nuget.config

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-  <packageSources>
-    <!--To inherit the global NuGet package sources remove the <clear/> line below -->
-    <clear />
-    <add key="nuget" value="https://api.nuget.org/v3/index.json" />
-  </packageSources>
-</configuration>

BIN
qrcode-dingtalk.png


BIN
qrcode-qq.png


+ 23 - 0
samples/Essensoft.Paylinks.Sample.Web/Essensoft.Paylinks.Sample.Web.csproj

@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+	<PropertyGroup>
+		<TargetFramework>net9.0</TargetFramework>
+		<Nullable>enable</Nullable>
+		<ImplicitUsings>enable</ImplicitUsings>
+	</PropertyGroup>
+
+	<ItemGroup>
+		<ProjectReference Include="..\..\src\Essensoft.Paylinks.Alipay.Client\Essensoft.Paylinks.Alipay.Client.csproj" />
+		<ProjectReference Include="..\..\src\Essensoft.Paylinks.Alipay.Mvc\Essensoft.Paylinks.Alipay.Mvc.csproj" />
+		<ProjectReference Include="..\..\src\Essensoft.Paylinks.Alipay.Payments\Essensoft.Paylinks.Alipay.Payments.csproj" />
+		<ProjectReference Include="..\..\src\Essensoft.Paylinks.WeChatPay.Client\Essensoft.Paylinks.WeChatPay.Client.csproj" />
+		<ProjectReference Include="..\..\src\Essensoft.Paylinks.WeChatPay.Mvc\Essensoft.Paylinks.WeChatPay.Mvc.csproj" />
+		<ProjectReference Include="..\..\src\Essensoft.Paylinks.WeChatPay.Certificates\Essensoft.Paylinks.WeChatPay.Certificates.csproj" />
+		<ProjectReference Include="..\..\src\Essensoft.Paylinks.WeChatPay.Payments\Essensoft.Paylinks.WeChatPay.Payments.csproj" />
+	</ItemGroup>
+
+	<ItemGroup>
+		<PackageReference Include="QRCoder" Version="1.6.0" />
+	</ItemGroup>
+
+</Project>

+ 22 - 0
samples/Essensoft.Paylinks.Sample.Web/Extensions/HtmlHelpersExtensions.cs

@@ -0,0 +1,22 @@
+// ReSharper disable CheckNamespace
+
+namespace Microsoft.AspNetCore.Mvc.Rendering;
+
+public static class HtmlHelpersExtensions
+{
+    public static string IsActive(this IHtmlHelper html, string? page, string activeClass = "active")
+    {
+        if (string.IsNullOrEmpty(page))
+        {
+            return string.Empty;
+        }
+
+        var actualPage = html.ViewContext.RouteData.Values.GetValueOrDefault("page")?.ToString();
+        if (string.IsNullOrEmpty(actualPage))
+        {
+            return string.Empty;
+        }
+
+        return actualPage.StartsWith(page, StringComparison.OrdinalIgnoreCase) ? activeClass : string.Empty;
+    }
+}

+ 20 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Index.cshtml

@@ -0,0 +1,20 @@
+@page
+@model IndexModel
+@{
+    ViewData["Title"] = "支付宝";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="mb-3">
+    <div class="list-group">
+        <a class="list-group-item list-group-item-action" asp-page="/Alipay/Payments/Index">支付产品</a>
+        <a class="list-group-item list-group-item-action" asp-page="/Alipay/SecurityTools/Index">安全工具</a>
+        <a class="list-group-item list-group-item-action">更多...</a>
+    </div>
+</div>

+ 10 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Index.cshtml.cs

@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay;
+
+public class IndexModel : PageModel
+{
+    public void OnGet()
+    {
+    }
+}

+ 57 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/App.cshtml

@@ -0,0 +1,57 @@
+@page
+@model AppModel
+@{
+    ViewData["Title"] = "alipay.trade.app.pay(app支付接口2.0)";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TotalAmount"></label>
+                <input class="form-control" asp-for="Input.TotalAmount"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Subject"></label>
+                <input class="form-control" asp-for="Input.Subject"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 34 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/App.cshtml.cs

@@ -0,0 +1,34 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class AppModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradeAppPayBizModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayTradeAppPayBizModel
+        {
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            TotalAmount = "0.01",
+            Subject = "App支付测试",
+            NotifyUrl = "https://www.domain.com/Alipay/Payments/Notify/TradeResult"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradeAppPayRequest();
+        request.SetBizModel(Input);
+        ViewData["response"] = await client.SdkExecuteAsync(request, _options);
+    }
+}

+ 49 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/BillDownloadUrlQuery.cshtml

@@ -0,0 +1,49 @@
+@page
+@model BillDownloadUrlQueryModel
+@{
+    ViewData["Title"] = "查询对账单下载地址";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BillType"></label>
+                <input class="form-control" asp-for="Input.BillType"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BillDate"></label>
+                <input class="form-control" asp-for="Input.BillDate"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 32 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/BillDownloadUrlQuery.cshtml.cs

@@ -0,0 +1,32 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class BillDownloadUrlQueryModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayDataDataServiceBillDownloadUrlQueryModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayDataDataServiceBillDownloadUrlQueryModel
+        {
+            BillType = "trade"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayDataDataServiceBillDownloadUrlQueryRequest();
+        request.SetQueryModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 49 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Cancel.cshtml

@@ -0,0 +1,49 @@
+@page
+@model CancelModel
+@{
+    ViewData["Title"] = "统一收单交易撤销";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TradeNo"></label>
+                <input class="form-control" asp-for="Input.TradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 28 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Cancel.cshtml.cs

@@ -0,0 +1,28 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class CancelModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradeCancelBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradeCancelRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 53 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Close.cshtml

@@ -0,0 +1,53 @@
+@page
+@model CloseModel
+@{
+    ViewData["Title"] = "统一收单交易关闭";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TradeNo"></label>
+                <input class="form-control" asp-for="Input.TradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 32 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Close.cshtml.cs

@@ -0,0 +1,32 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class CloseModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradeCloseBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayTradeCloseBodyModel
+        {
+            NotifyUrl = "https://www.domain.com/Alipay/Payments/Notify/TradeResult"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradeCloseRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 69 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Create.cshtml

@@ -0,0 +1,69 @@
+@page
+@model CreateModel
+@{
+    ViewData["Title"] = "统一收单交易创建";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TotalAmount"></label>
+                <input class="form-control" asp-for="Input.TotalAmount"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BuyerId"></label>
+                <input class="form-control" asp-for="Input.BuyerId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BuyerOpenId"></label>
+                <input class="form-control" asp-for="Input.BuyerOpenId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BuyerLogonId"></label>
+                <input class="form-control" asp-for="Input.BuyerLogonId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Subject"></label>
+                <input class="form-control" asp-for="Input.Subject"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 35 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Create.cshtml.cs

@@ -0,0 +1,35 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class CreateModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradeCreateBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayTradeCreateBodyModel
+        {
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            TotalAmount = "0.01",
+            Subject = "扫码付款支付测试",
+            NotifyUrl = "https://www.domain.com/Alipay/Payments/Notify/TradeResult"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradeCreateRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 364 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Index.cshtml

@@ -0,0 +1,364 @@
+@page
+@model IndexModel
+@{
+    ViewData["Title"] = "支付产品";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="mb-3">
+<div class="accordion" id="accordionPayments">
+<div class="accordion-item">
+    <h2 class="accordion-header">
+        <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseF2FPay" aria-expanded="false" aria-controls="collapseF2FPay">
+            当面付
+        </button>
+    </h2>
+    <div id="collapseF2FPay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+        <div class="accordion-body">
+            <div class="accordion" id="accordionF2FPay">
+                <div class="accordion-item">
+                    <h2 class="accordion-header">
+                        <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCodePay" aria-expanded="false" aria-controls="collapseCodePay">
+                            付款码支付
+                        </button>
+                    </h2>
+                    <div id="collapseCodePay" class="accordion-collapse collapse" data-bs-parent="#accordionF2FPay">
+                        <div class="accordion-body">
+                            <ul class="list-group">
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易支付
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/08c7f9f8_alipay.trade.pay?scene=32&pathHash=cdb0ecb0" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Pay">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易查询
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/cbe8826d_alipay.trade.query?scene=23&pathHash=956e67cd" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Query">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易退款
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/6b16d4a2_alipay.trade.refund?scene=common&pathHash=24d0d3ca" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Refund">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易退款查询
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/b9ef37bd_alipay.trade.fastpay.refund.query?scene=common&pathHash=84b7b273" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/RefundQuery">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    收单退款冲退完成通知
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/64342630_alipay.trade.refund.depositback.completed?scene=common&pathHash=6407afcc" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易撤销
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/cd7d54d2_alipay.trade.cancel?scene=common&pathHash=444c42f0" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Cancel">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易关闭
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/48ea518b_alipay.trade.close?scene=common&pathHash=5bb01fc5" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Close">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    查询对账单下载地址
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/c8d608d7_alipay.data.dataservice.bill.downloadurl.query?scene=common&pathHash=eb4624a4" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/BillDownloadUrlQuery">去调试</a>
+                                    </div>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+                <div class="accordion-item">
+                    <h2 class="accordion-header">
+                        <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseScanPay" aria-expanded="false" aria-controls="collapseScanPay">
+                            扫码支付
+                        </button>
+                    </h2>
+                    <div id="collapseScanPay" class="accordion-collapse collapse" data-bs-parent="#accordionF2FPay">
+                        <div class="accordion-body">
+                            <ul class="list-group">
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单线下交易预创建
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/fa0c2141_alipay.trade.precreate?scene=19&pathHash=a2c4021f" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/PreCreate">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易创建
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/bf4eae5e_alipay.trade.create?scene=2d8d65b1350f44bfa394347f06700c4f&pathHash=e6c9a167" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Create">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易查询
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/cbe8826d_alipay.trade.query?scene=23&pathHash=fe561798" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Query">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易退款
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/6b16d4a2_alipay.trade.refund?scene=common&pathHash=d1f3f5f0" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Refund">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易退款查询
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/b9ef37bd_alipay.trade.fastpay.refund.query?scene=common&pathHash=81e6a871" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/RefundQuery">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    收单退款冲退完成通知
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/05pf5a?scene=common&pathHash=a021a234" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易撤销
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/cd7d54d2_alipay.trade.cancel?scene=common&pathHash=3c33e48e" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Cancel">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    统一收单交易关闭
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/48ea518b_alipay.trade.close?scene=common&pathHash=5e83149d" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Close">去调试</a>
+                                    </div>
+                                </li>
+                                <li class="list-group-item d-flex justify-content-between align-items-center">
+                                    查询对账单下载地址
+                                    <div class="btn-group">
+                                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/c8d608d7_alipay.data.dataservice.bill.downloadurl.query?scene=common&pathHash=76f3d49a" target="_blank">查看文档</a>
+                                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/BillDownloadUrlQuery">去调试</a>
+                                    </div>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="accordion-item">
+    <h2 class="accordion-header">
+        <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAppPay" aria-expanded="false" aria-controls="collapseAppPay">
+            APP支付
+        </button>
+    </h2>
+    <div id="collapseAppPay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+        <div class="accordion-body">
+            <ul class="list-group">
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    alipay.trade.app.pay(app支付接口2.0)
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/429e4d75_alipay.trade.app.pay?scene=20&pathHash=2dfb24f8" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/App">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易退款
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/da960891_alipay.trade.refund?scene=common&pathHash=846acb56" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Refund">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    收单退款冲退完成通知
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/fca5d17e_alipay.trade.refund.depositback.completed?scene=common&pathHash=1ad5a0e2" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易关闭
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/06922cb4_alipay.trade.close?scene=common&pathHash=5874f81f" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Close">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易退款查询
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/b95db51d_alipay.trade.fastpay.refund.query?scene=common&pathHash=40e803a6" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/RefundQuery">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易查询
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/cbe8826d_alipay.trade.query?scene=23&pathHash=956e67cd" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Query">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    查询对账单下载地址
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/cd439c70_alipay.trade.query?scene=23&pathHash=8b9f14fa" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/BillDownloadUrlQuery">去调试</a>
+                    </div>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<div class="accordion-item">
+    <h2 class="accordion-header">
+        <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWapPay" aria-expanded="false" aria-controls="collapseWapPay">
+            手机网站支付
+        </button>
+    </h2>
+    <div id="collapseWapPay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+        <div class="accordion-body">
+            <ul class="list-group">
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    alipay.trade.wap.pay(手机网站支付接口2.0)
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/1a957be0_alipay.trade.wap.pay?scene=21&pathHash=5335df07" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Wap">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易退款
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/28d9fff7_alipay.trade.refund?scene=common&pathHash=f834ef01" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Refund">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    收单退款冲退完成通知
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/05w4la?scene=common&pathHash=da33cafe" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易关闭
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/518cd726_alipay.trade.close?scene=common&pathHash=e489608b" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Close">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易退款查询
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/c8d05aa7_alipay.trade.fastpay.refund.query?scene=common&pathHash=0bb7639d" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/RefundQuery">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易查询
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/34849591_alipay.trade.query?scene=common&pathHash=ffb65429" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Query">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    查询对账单下载地址
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/0f828127_alipay.data.dataservice.bill.downloadurl.query?scene=common&pathHash=0c48c55e" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/BillDownloadUrlQuery">去调试</a>
+                    </div>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<div class="accordion-item">
+    <h2 class="accordion-header">
+        <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePagePay" aria-expanded="false" aria-controls="collapsePagePay">
+            电脑网站支付
+        </button>
+    </h2>
+    <div id="collapsePagePay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+        <div class="accordion-body">
+            <ul class="list-group">
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    alipay.trade.page.pay(统一收单下单并支付页面接口)
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/2423fad5_alipay.trade.page.pay?scene=22&pathHash=47147674" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Web">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易关闭
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/429ffb46_alipay.trade.close?scene=common&pathHash=1978534d" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Close">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易退款
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/01073208_alipay.trade.refund?scene=common&pathHash=17b803c8" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Refund">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    收单退款冲退完成通知
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/42a9ce75_alipay.trade.refund.depositback.completed?scene=common&pathHash=9c33d734" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易查询
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/e9ce4f59_alipay.trade.query?scene=23&pathHash=582b62cd" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/Query">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    统一收单交易退款查询
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/46bff59c_alipay.trade.fastpay.refund.query?scene=common&pathHash=dfc824f9" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/RefundQuery">去调试</a>
+                    </div>
+                </li>
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    查询对账单下载地址
+                    <div class="btn-group">
+                        <a class="btn btn-link btn-sm" href="https://opendocs.alipay.com/open-v3/d6c4d425_alipay.data.dataservice.bill.downloadurl.query?scene=common&pathHash=871a2624" target="_blank">查看文档</a>
+                        <a class="btn btn-link btn-sm" asp-page="/Alipay/Payments/BillDownloadUrlQuery">去调试</a>
+                    </div>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+</div>
+</div>

+ 10 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Index.cshtml.cs

@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class IndexModel : PageModel
+{
+    public void OnGet()
+    {
+    }
+}

+ 5 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Notify/TradeResult.cshtml

@@ -0,0 +1,5 @@
+@page
+@model TradeResultModel
+@{
+    Layout = null;
+}

+ 37 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Notify/TradeResult.cshtml.cs

@@ -0,0 +1,37 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Core;
+using Essensoft.Paylinks.Alipay.Mvc;
+using Essensoft.Paylinks.Alipay.Mvc.Extensions;
+using Essensoft.Paylinks.Alipay.Payments.Notify;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments.Notify;
+
+[IgnoreAntiforgeryToken]
+public class TradeResultModel(ILogger<TradeResultModel> logger, IAlipayNotifyClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    /// <summary>
+    /// 支付成功通知
+    /// https://opendocs.alipay.com/open-v3/05pf4k?pathHash=01c6e762
+    /// </summary>
+    public async Task<IActionResult> OnPostAsync()
+    {
+        try
+        {
+            var parameters = await Request.GetAlipayParametersAsync();
+            var notify = await client.ExecuteAsync<AlipayTradeStatusSyncNotify>(parameters, _options);
+            // 请务必检查系统内业务状态,避免因重复通知遭受损失。
+            logger.LogInformation($"支付成功通知: TradeNo:{notify.TradeNo}, TotalAmount:{notify.TotalAmount}");
+            return AlipayNotifyResult.Success;
+        }
+        catch (AlipayException ex)
+        {
+            logger.LogError(ex.Message);
+            return AlipayNotifyResult.Fail;
+        }
+    }
+}

+ 65 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Pay.cshtml

@@ -0,0 +1,65 @@
+@page
+@model PayModel
+@{
+    ViewData["Title"] = "统一收单交易支付";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TotalAmount"></label>
+                <input class="form-control" asp-for="Input.TotalAmount"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.AuthCode"></label>
+                <input class="form-control" asp-for="Input.AuthCode"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Scene"></label>
+                <input class="form-control" asp-for="Input.Scene"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Subject"></label>
+                <input class="form-control" asp-for="Input.Subject"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 36 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Pay.cshtml.cs

@@ -0,0 +1,36 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class PayModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradePayBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayTradePayBodyModel
+        {
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            TotalAmount = "0.01",
+            Subject = "付款码支付测试",
+            Scene = "bar_code",
+            NotifyUrl = "https://www.domain.com/Alipay/Payments/Notify/TradeResult"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradePayRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 68 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/PreCreate.cshtml

@@ -0,0 +1,68 @@
+@page
+@model PreCreateModel
+@{
+    ViewData["Title"] = "统一收单线下交易预创建";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TotalAmount"></label>
+                <input class="form-control" asp-for="Input.TotalAmount"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Subject"></label>
+                <input class="form-control" asp-for="Input.Subject"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["qr_code"] is string data && !string.IsNullOrEmpty(data))
+        {
+            using var qrGenerator = new QRCodeGenerator();
+            using var qrCodeData = qrGenerator.CreateQrCode(data, QRCodeGenerator.ECCLevel.Q);
+            using var base64ByteQrCode = new Base64QRCode(qrCodeData);
+            var base64Str = base64ByteQrCode.GetGraphic(20, Color.Black, Color.White, false, Base64QRCode.ImageType.Png);
+            <div class="mb-3">
+                <label class="form-label">二维码</label>
+                <embed src="data:image/png;base64,@base64Str" class="bg-light shadow-sm rounded d-block p-3 mb-5" type="image/png" width="180" height="180" />
+            </div>
+        }
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 36 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/PreCreate.cshtml.cs

@@ -0,0 +1,36 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class PreCreateModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradePreCreateBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayTradePreCreateBodyModel
+        {
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            TotalAmount = "0.01",
+            Subject = "扫码支付测试",
+            NotifyUrl = "https://www.domain.com/Alipay/Payments/Notify/TradeResult"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradePreCreateRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["qr_code"] = response.QrCode;
+        ViewData["response"] = response.Body;
+    }
+}

+ 49 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Query.cshtml

@@ -0,0 +1,49 @@
+@page
+@model QueryModel
+@{
+    ViewData["Title"] = "统一收单交易查询";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TradeNo"></label>
+                <input class="form-control" asp-for="Input.TradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 28 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Query.cshtml.cs

@@ -0,0 +1,28 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class QueryModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradeQueryBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradeQueryRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 61 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Refund.cshtml

@@ -0,0 +1,61 @@
+@page
+@model RefundModel
+@{
+    ViewData["Title"] = "统一收单交易退款";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.RefundAmount"></label>
+                <input class="form-control" asp-for="Input.RefundAmount"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TradeNo"></label>
+                <input class="form-control" asp-for="Input.TradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.RefundReason"></label>
+                <input class="form-control" asp-for="Input.RefundReason"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutRequestNo"></label>
+                <input class="form-control" asp-for="Input.OutRequestNo"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 33 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Refund.cshtml.cs

@@ -0,0 +1,33 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class RefundModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradeRefundBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayTradeRefundBodyModel
+        {
+            RefundAmount = "0.01",
+            OutRequestNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradeRefundRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 53 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/RefundQuery.cshtml

@@ -0,0 +1,53 @@
+@page
+@model RefundQueryModel
+@{
+    ViewData["Title"] = "统一收单交易退款查询";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutRequestNo"></label>
+                <input class="form-control" asp-for="Input.OutRequestNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TradeNo"></label>
+                <input class="form-control" asp-for="Input.TradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 28 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/RefundQuery.cshtml.cs

@@ -0,0 +1,28 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class RefundQueryModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradeFastPayRefundQueryBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradeFastPayRefundQueryRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 62 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Wap.cshtml

@@ -0,0 +1,62 @@
+@page
+@model WapModel
+@{
+    ViewData["Title"] = "alipay.trade.wap.pay(手机网站支付接口2.0)";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TotalAmount"></label>
+                <input class="form-control" asp-for="Input.TotalAmount"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Subject"></label>
+                <input class="form-control" asp-for="Input.Subject"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.ProductCode"></label>
+                <input class="form-control" asp-for="Input.ProductCode"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+            <button class="btn btn-primary mb-3" type="submit" asp-page-handler="Jump">提交请求(跳转页面)</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 46 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Wap.cshtml.cs

@@ -0,0 +1,46 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class WapModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradeWapPayBizModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayTradeWapPayBizModel
+        {
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            TotalAmount = "0.01",
+            Subject = "手机网站支付测试",
+            ProductCode = "QUICK_WAP_WAY",
+            NotifyUrl = "https://www.domain.com/Alipay/Payments/Notify/TradeResult"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradeWapPayRequest();
+        request.SetBizModel(Input);
+        ViewData["response"] = await client.PageExecuteAsync(request, _options);
+    }
+
+    public async Task<IActionResult> OnPostJumpAsync()
+    {
+        var request = new AlipayTradeWapPayRequest();
+        request.SetBizModel(Input);
+        return new ContentResult
+        {
+            Content = await client.PageExecuteAsync(request, _options),
+            ContentType = "text/html"
+        };
+    }
+}

+ 62 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Web.cshtml

@@ -0,0 +1,62 @@
+@page
+@model WebModel
+@{
+    ViewData["Title"] = "alipay.trade.page.pay(统一收单下单并支付页面接口)";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TotalAmount"></label>
+                <input class="form-control" asp-for="Input.TotalAmount"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Subject"></label>
+                <input class="form-control" asp-for="Input.Subject"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.ProductCode"></label>
+                <input class="form-control" asp-for="Input.ProductCode"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+            <button class="btn btn-primary mb-3" type="submit" asp-page-handler="Jump">提交请求(跳转页面)</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 46 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/Payments/Web.cshtml.cs

@@ -0,0 +1,46 @@
+using Essensoft.Paylinks.Alipay.Client;
+using Essensoft.Paylinks.Alipay.Payments.Model;
+using Essensoft.Paylinks.Alipay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.Payments;
+
+public class WebModel(IAlipayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly AlipayClientOptions _options = options.Value.Alipay;
+
+    [BindProperty]
+    public AlipayTradePagePayBizModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new AlipayTradePagePayBizModel
+        {
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            TotalAmount = "0.01",
+            Subject = "电脑网站支付测试",
+            ProductCode = "FAST_INSTANT_TRADE_PAY",
+            NotifyUrl = "https://www.domain.com/Alipay/Payments/Notify/TradeResult"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new AlipayTradePagePayRequest();
+        request.SetBizModel(Input);
+        ViewData["response"] = await client.PageExecuteAsync(request, _options);
+    }
+
+    public async Task<IActionResult> OnPostJump()
+    {
+        var request = new AlipayTradePagePayRequest();
+        request.SetBizModel(Input);
+        return new ContentResult
+        {
+            Content = await client.PageExecuteAsync(request, _options),
+            ContentType = "text/html"
+        };
+    }
+}

+ 45 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/ConvertCertPrivateKey.cshtml

@@ -0,0 +1,45 @@
+@page
+@model ConvertCertPrivateKeyModel
+@{
+    ViewData["Title"] = "转换证书私钥 PKCS#8 => PKCS#1";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/SecurityTools/Index">安全工具</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form enctype="multipart/form-data" method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="CertPrivateKey"></label>
+                <input class="form-control" asp-for="CertPrivateKey"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 29 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/ConvertCertPrivateKey.cshtml.cs

@@ -0,0 +1,29 @@
+using Essensoft.Paylinks.Alipay.Core.Utilities;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.SecurityTools;
+
+public class ConvertCertPrivateKeyModel : PageModel
+{
+    [BindProperty]
+    public IFormFile CertPrivateKey { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        try
+        {
+            using var sr = new StreamReader(CertPrivateKey.OpenReadStream());
+            var str = await sr.ReadToEndAsync();
+            ViewData["response"] = AlipayCertUtilities.ConvertCertPrivateKey(str);
+        }
+        catch
+        {
+            ViewData["response"] = "无法获取";
+        }
+    }
+}

+ 45 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetCertPublicKey.cshtml

@@ -0,0 +1,45 @@
+@page
+@model GetCertPublicKeyModel
+@{
+    ViewData["Title"] = "获取证书公钥";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/SecurityTools/Index">安全工具</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form enctype="multipart/form-data" method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Cert"></label>
+                <input class="form-control" asp-for="Cert"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 29 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetCertPublicKey.cshtml.cs

@@ -0,0 +1,29 @@
+using Essensoft.Paylinks.Alipay.Core.Utilities;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.SecurityTools;
+
+public class GetCertPublicKeyModel : PageModel
+{
+    [BindProperty]
+    public IFormFile Cert { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        try
+        {
+            using var sr = new StreamReader(Cert.OpenReadStream());
+            var str = await sr.ReadToEndAsync();
+            ViewData["response"] = AlipayCertUtilities.GetCertPublicKey(str);
+        }
+        catch
+        {
+            ViewData["response"] = "无法获取";
+        }
+    }
+}

+ 45 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetCertSN.cshtml

@@ -0,0 +1,45 @@
+@page
+@model GetCertSNModel
+@{
+    ViewData["Title"] = "获取证书序列号";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/SecurityTools/Index">安全工具</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form enctype="multipart/form-data" method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Cert"></label>
+                <input class="form-control" asp-for="Cert"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 29 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetCertSN.cshtml.cs

@@ -0,0 +1,29 @@
+using Essensoft.Paylinks.Alipay.Core.Utilities;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.SecurityTools;
+
+public class GetCertSNModel : PageModel
+{
+    [BindProperty]
+    public IFormFile Cert { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        try
+        {
+            using var sr = new StreamReader(Cert.OpenReadStream());
+            var str = await sr.ReadToEndAsync();
+            ViewData["response"] = AlipayCertUtilities.GetCertSN(str);
+        }
+        catch
+        {
+            ViewData["response"] = "无法获取";
+        }
+    }
+}

+ 45 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetRootCertSN.cshtml

@@ -0,0 +1,45 @@
+@page
+@model GetRootCertSNModel
+@{
+    ViewData["Title"] = "获取根证书序列号";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/SecurityTools/Index">安全工具</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form enctype="multipart/form-data" method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="RootCert"></label>
+                <input class="form-control" asp-for="RootCert"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 29 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/GetRootCertSN.cshtml.cs

@@ -0,0 +1,29 @@
+using Essensoft.Paylinks.Alipay.Core.Utilities;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.SecurityTools;
+
+public class GetRootCertSNModel : PageModel
+{
+    [BindProperty]
+    public IFormFile RootCert { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        try
+        {
+            using var sr = new StreamReader(RootCert.OpenReadStream());
+            var str = await sr.ReadToEndAsync();
+            ViewData["response"] = AlipayCertUtilities.GetRootCertSN(str);
+        }
+        catch
+        {
+            ViewData["response"] = "无法获取";
+        }
+    }
+}

+ 24 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/Index.cshtml

@@ -0,0 +1,24 @@
+@page
+@model IndexModel
+@{
+    ViewData["Title"] = "安全工具";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/Alipay/Index">支付宝</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="mb-3">
+    <div class="list-group">
+        <a class="list-group-item list-group-item-action" asp-page="/Alipay/SecurityTools/ConvertCertPrivateKey">转换证书私钥 PKCS#8 => PKCS#1</a>
+        <a class="list-group-item list-group-item-action" asp-page="/Alipay/SecurityTools/GetCertPublicKey">获取证书公钥</a>
+        <a class="list-group-item list-group-item-action" asp-page="/Alipay/SecurityTools/GetCertSN">获取证书序列号</a>
+        <a class="list-group-item list-group-item-action" asp-page="/Alipay/SecurityTools/GetRootCertSN">获取根证书序列号</a>
+    </div>
+</div>

+ 10 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Alipay/SecurityTools/Index.cshtml.cs

@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.Alipay.SecurityTools;
+
+public class IndexModel : PageModel
+{
+    public void OnGet()
+    {
+    }
+}

+ 26 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Error.cshtml

@@ -0,0 +1,26 @@
+@page
+@model ErrorModel
+@{
+    ViewData["Title"] = "Error";
+}
+
+<h1 class="text-danger">Error.</h1>
+<h2 class="text-danger">An error occurred while processing your request.</h2>
+
+@if (Model.ShowRequestId)
+{
+    <p>
+        <strong>Request ID:</strong> <code>@Model.RequestId</code>
+    </p>
+}
+
+<h3>Development Mode</h3>
+<p>
+    Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
+</p>
+<p>
+    <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
+    It can result in displaying sensitive information from exceptions to end users.
+    For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
+    and restarting the app.
+</p>

+ 26 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Error.cshtml.cs

@@ -0,0 +1,26 @@
+using System.Diagnostics;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages;
+
+[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
+[IgnoreAntiforgeryToken]
+public class ErrorModel : PageModel
+{
+    public string? RequestId { get; set; }
+
+    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
+
+    private readonly ILogger<ErrorModel> _logger;
+
+    public ErrorModel(ILogger<ErrorModel> logger)
+    {
+        _logger = logger;
+    }
+
+    public void OnGet()
+    {
+        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
+    }
+}

+ 14 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Index.cshtml

@@ -0,0 +1,14 @@
+@page
+@model IndexModel
+@{
+    ViewData["Title"] = "主页";
+}
+<section>
+    <div class="container">
+        <div class="col-md-8 mx-auto text-center">
+            <h1 class="display-3 mb-3">Paylinks</h1>
+            <p class="lead mb-3">一套基于 现代 .NET 开发,支持跨平台、多商户的第三方支付SDK。</p>
+            <p class="text-body mb-3">为简化开发者接入第三方支付平台而设计的SDK,支持支付宝和微信支付,便于快速集成支付功能。</p>
+        </div>
+    </div>
+</section>

+ 10 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Index.cshtml.cs

@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages;
+
+public class IndexModel : PageModel
+{
+    public void OnGet()
+    {
+    }
+}

+ 114 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Shared/_Layout.cshtml

@@ -0,0 +1,114 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+    <title>@ViewData["Title"] - Paylinks</title>
+    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css"/>
+    @await RenderSectionAsync("styles", false)
+    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true"/>
+    <script src="~/js/color-modes.js" asp-append-version="true"></script>
+    <svg xmlns="http://www.w3.org/2000/svg" class="d-none">
+        <symbol id="check2" viewBox="0 0 16 16">
+            <path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"></path>
+        </symbol>
+        <symbol id="sun-fill" viewBox="0 0 16 16">
+            <path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"></path>
+        </symbol>
+        <symbol id="moon-stars-fill" viewBox="0 0 16 16">
+            <path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path>
+            <path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"></path>
+        </symbol>
+        <symbol id="circle-half" viewBox="0 0 16 16">
+            <path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"></path>
+        </symbol>
+    </svg>
+</head>
+<body>
+<header class="navbar navbar-expand-lg shadow-sm">
+    <nav class="container flex-wrap flex-lg-nowrap">
+        <a class="navbar-brand" asp-page="/Index">Paylinks</a>
+        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
+                aria-expanded="false" aria-label="Toggle navigation">
+            <span class="navbar-toggler-icon"></span>
+        </button>
+        <div class="navbar-collapse collapse d-lg-flex">
+            <ul class="navbar-nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
+                <li>
+                    <a class="nav-link @Html.IsActive("/Alipay/")" asp-page="/Alipay/Index">支付宝</a>
+                </li>
+                <li>
+                    <a class="nav-link @Html.IsActive("/WeChatPay/")" asp-page="/WeChatPay/Index">微信支付</a>
+                </li>
+            </ul>
+            <div class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3">
+                <ul class="navbar-nav">
+                    <li>
+                        <a class="nav-link" href="https://paylinks.cn/sponsors" target="_blank">赞助</a>
+                    </li>
+                </ul>
+            </div>
+            <div class="dropdown text-end">
+                <button class="btn btn-link nav-link py-2 px-0 px-lg-2 dropdown-toggle d-flex align-items-center" id="bd-theme" type="button" aria-expanded="false" data-bs-toggle="dropdown" data-bs-display="static" aria-label="Toggle theme (light)">
+                    <svg class="bi my-1 theme-icon-active">
+                        <use href="#circle-half"></use>
+                    </svg>
+                    <span class="d-lg-none ms-2" id="bd-theme-text">切换主题</span>
+                </button>
+                <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme-text">
+                    <li>
+                        <button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="light" aria-pressed="true">
+                            <svg class="bi me-2 opacity-50">
+                                <use href="#sun-fill"></use>
+                            </svg>
+                            浅色
+                            <svg class="bi ms-auto d-none">
+                                <use href="#check2"></use>
+                            </svg>
+                        </button>
+                    </li>
+                    <li>
+                        <button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false">
+                            <svg class="bi me-2 opacity-50">
+                                <use href="#moon-stars-fill"></use>
+                            </svg>
+                            深色
+                            <svg class="bi ms-auto d-none">
+                                <use href="#check2"></use>
+                            </svg>
+                        </button>
+                    </li>
+                    <li>
+                        <button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="auto" aria-pressed="false">
+                            <svg class="bi me-2 opacity-50">
+                                <use href="#circle-half"></use>
+                            </svg>
+                            自动
+                            <svg class="bi ms-auto d-none">
+                                <use href="#check2"></use>
+                            </svg>
+                        </button>
+                    </li>
+                </ul>
+            </div>
+        </div>
+    </nav>
+</header>
+<main>
+    <div class="container pt-5">
+        @RenderBody()
+    </div>
+</main>
+<footer class="footer pt-5 pb-5">
+    <div class="container text-center">
+        <p class="text-center mb-0">
+            &copy; @DateTime.Now.Year Essensoft
+        </p>
+    </div>
+</footer>
+<script src="~/lib/jquery/dist/jquery.min.js"></script>
+<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
+@await RenderSectionAsync("scripts", false)
+<script src="~/js/site.js" asp-append-version="true"></script>
+</body>
+</html>

+ 2 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Shared/_Layout.cshtml.css

@@ -0,0 +1,2 @@
+/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
+for details on configuring this project to bundle and minify static web assets. */

+ 2 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/Shared/_ValidationScriptsPartial.cshtml

@@ -0,0 +1,2 @@
+<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
+<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

+ 20 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Index.cshtml

@@ -0,0 +1,20 @@
+@page
+@model IndexModel
+@{
+    ViewData["Title"] = "微信支付";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="mb-3">
+    <div class="list-group">
+        <a class="list-group-item list-group-item-action" asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        <a class="list-group-item list-group-item-action" asp-page="/WeChatPay/SecurityTools/Index">安全工具</a>
+        <a class="list-group-item list-group-item-action">更多...</a>
+    </div>
+</div>

+ 10 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Index.cshtml.cs

@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay;
+
+public class IndexModel : PageModel
+{
+    public void OnGet()
+    {
+    }
+}

+ 65 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/AbnormalRefund.cshtml

@@ -0,0 +1,65 @@
+@page
+@model AbnormalRefundModel
+@{
+    ViewData["Title"] = "发起异常退款";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="RefundId"></label>
+                <input class="form-control" asp-for="RefundId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutRefundNo"></label>
+                <input class="form-control" asp-for="Input.OutRefundNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Type"></label>
+                <input class="form-control" asp-for="Input.Type"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BankType"></label>
+                <input class="form-control" asp-for="Input.BankType"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BankAccount"></label>
+                <input class="form-control" asp-for="Input.BankAccount"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.RealName"></label>
+                <input class="form-control" asp-for="Input.RealName"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 31 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/AbnormalRefund.cshtml.cs

@@ -0,0 +1,31 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class AbnormalRefundModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public string RefundId { get; set; }
+
+    [BindProperty]
+    public WeChatPayApplyAbnormalRefundByRefundIdBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayApplyAbnormalRefundByRefundIdRequest { RefundId = RefundId };
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 72 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/AppPrepay.cshtml

@@ -0,0 +1,72 @@
+@page
+@model AppPrepayModel
+@{
+    ViewData["Title"] = "App下单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.AppId"></label>
+                <input class="form-control" asp-for="Input.AppId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.MchId"></label>
+                <input class="form-control" asp-for="Input.MchId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Description"></label>
+                <input class="form-control" asp-for="Input.Description"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Amount.Total"></label>
+                <input class="form-control" asp-for="Input.Amount.Total"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+        @if (ViewData["parameter"] is string parameter && !string.IsNullOrEmpty(parameter))
+        {
+            <div class="mb-3">
+                <label class="form-label">App调起支付-请求参数</label>
+                <textarea class="form-control" rows="10">@parameter</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 46 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/AppPrepay.cshtml.cs

@@ -0,0 +1,46 @@
+using System.Text.Json;
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Domain;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class AppPrepayModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public WeChatPayAppPrepayBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayAppPrepayBodyModel
+        {
+            AppId = _options.AppId,
+            MchId = _options.MchId,
+            Description = "APP下单测试",
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            NotifyUrl = "https://www.domain.com/WeChatPay/Payments/Notify/TransactionSuccess",
+            Amount = new CommReqAmountInfo { Total = 1 }
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayAppPrepayRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+
+        if (response.IsSuccessful)
+        {
+            var sdkRequest = new WeChatPayAppTransferPaymentRequest { AppId = Input.AppId, PartnerId = Input.MchId, PrepayId = response.PrepayId };
+            var sdkResponse = await client.SdkExecuteAsync(sdkRequest, _options);
+            ViewData["parameter"] = JsonSerializer.Serialize(sdkResponse);
+        }
+    }
+}

+ 49 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Close.cshtml

@@ -0,0 +1,49 @@
+@page
+@model CloseModel
+@{
+    ViewData["Title"] = "关闭订单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="OutTradeNo"></label>
+                <input class="form-control" asp-for="OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.MchId"></label>
+                <input class="form-control" asp-for="Input.MchId"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 32 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Close.cshtml.cs

@@ -0,0 +1,32 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class CloseModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public string OutTradeNo { get; set; }
+
+    [BindProperty]
+    public WeChatPayCloseByOutTradeNoBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayCloseByOutTradeNoBodyModel { MchId = _options.MchId };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayCloseByOutTradeNoRequest { OutTradeNo = OutTradeNo };
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = $"{(int)response.StatusCode} {response.StatusCode}";
+    }
+}

+ 69 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/CodePay.cshtml

@@ -0,0 +1,69 @@
+@page
+@model CodePayModel
+@{
+    ViewData["Title"] = "付款码支付";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.AppId"></label>
+                <input class="form-control" asp-for="Input.AppId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.MchId"></label>
+                <input class="form-control" asp-for="Input.MchId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Description"></label>
+                <input class="form-control" asp-for="Input.Description"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Payer.AuthCode"></label>
+                <input class="form-control" asp-for="Input.Payer.AuthCode"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Amount.Total"></label>
+                <input class="form-control" asp-for="Input.Amount.Total"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.SceneInfo.StoreInfo.Id"></label>
+                <input class="form-control" asp-for="Input.SceneInfo.StoreInfo.Id"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 39 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/CodePay.cshtml.cs

@@ -0,0 +1,39 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Domain;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class CodePayModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public WeChatPayCodePayBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayCodePayBodyModel
+        {
+            AppId = _options.AppId,
+            MchId = _options.MchId,
+            Description = "付款码支付测试",
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            Payer = new Payer { AuthCode = string.Empty },
+            Amount = new Amount { Total = 1 },
+            SceneInfo = new CodeReqSceneInfo { StoreInfo = new CodeReqStoreInfo { Id = string.Empty } }
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayCodePayRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 45 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/DownloadBill.cshtml

@@ -0,0 +1,45 @@
+@page
+@model DownloadBillModel
+@{
+    ViewData["Title"] = "下载账单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="DownloadUrl"></label>
+                <input class="form-control" asp-for="DownloadUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 26 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/DownloadBill.cshtml.cs

@@ -0,0 +1,26 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class DownloadBillModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public string DownloadUrl { get; set; }
+
+    public void OnGet()
+    {
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayDownloadBillRequest { DownloadUrl = DownloadUrl };
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 45 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/GetFundFlowBill.cshtml

@@ -0,0 +1,45 @@
+@page
+@model GetFundFlowBillModel
+@{
+    ViewData["Title"] = "申请资金账单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BillDate"></label>
+                <input class="form-control" asp-for="Input.BillDate"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 29 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/GetFundFlowBill.cshtml.cs

@@ -0,0 +1,29 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class GetFundFlowBillModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public WeChatPayGetFundFlowBillQueryModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayGetFundFlowBillQueryModel { BillDate = DateTimeOffset.Now.AddDays(-1).ToString("yyyy-MM-dd") };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayGetFundFlowBillRequest();
+        request.SetQueryModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 45 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/GetTradeBill.cshtml

@@ -0,0 +1,45 @@
+@page
+@model GetTradeBillModel
+@{
+    ViewData["Title"] = "申请交易账单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.BillDate"></label>
+                <input class="form-control" asp-for="Input.BillDate"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 29 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/GetTradeBill.cshtml.cs

@@ -0,0 +1,29 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class GetTradeBillModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public WeChatPayGetTradeBillQueryModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayGetTradeBillQueryModel { BillDate = DateTimeOffset.Now.AddDays(-1).ToString("yyyy-MM-dd") };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayGetTradeBillRequest();
+        request.SetQueryModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 80 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/H5Prepay.cshtml

@@ -0,0 +1,80 @@
+@page
+@model H5PrepayModel
+@{
+    ViewData["Title"] = "H5下单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.AppId"></label>
+                <input class="form-control" asp-for="Input.AppId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.MchId"></label>
+                <input class="form-control" asp-for="Input.MchId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Description"></label>
+                <input class="form-control" asp-for="Input.Description"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Amount.Total"></label>
+                <input class="form-control" asp-for="Input.Amount.Total"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.SceneInfo.PayerClientIp"></label>
+                <input class="form-control" asp-for="Input.SceneInfo.PayerClientIp"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.SceneInfo.H5Info.Type"></label>
+                <input class="form-control" asp-for="Input.SceneInfo.H5Info.Type"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+        @if (ViewData["parameter"] is string parameter && !string.IsNullOrEmpty(parameter))
+        {
+            <div class="mb-3">
+                <label class="form-label">App调起支付-请求参数</label>
+                <textarea class="form-control" rows="10">@parameter</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 39 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/H5Prepay.cshtml.cs

@@ -0,0 +1,39 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Domain;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class H5PrepayModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public WeChatPayH5PrepayBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayH5PrepayBodyModel
+        {
+            AppId = _options.AppId,
+            MchId = _options.MchId,
+            Description = "H5下单测试",
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            NotifyUrl = "https://www.domain.com/WeChatPay/Payments/Notify/TransactionSuccess",
+            Amount = new CommReqAmountInfo { Total = 1 },
+            SceneInfo = new H5ReqSceneInfo { PayerClientIp = string.Empty, H5Info = new H5Info { Type = string.Empty } }
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayH5PrepayRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 796 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Index.cshtml

@@ -0,0 +1,796 @@
+@page
+@model IndexModel
+@{
+    ViewData["Title"] = "支付产品";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="mb-3">
+    <div class="accordion" id="accordionPayments">
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseJsapiPay" aria-expanded="false" aria-controls="collapseJsapiPay">
+                    JSAPI支付
+                </button>
+            </h2>
+            <div id="collapseJsapiPay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <ul class="list-group">
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            JSAPI下单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/direct-jsons/jsapi-prepay.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/JsapiPrepay">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            JSAPI调起支付
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/jsapi-transfer-payment.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            支付通知
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/payment-notice.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            微信支付订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/query-by-wx-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByTransactionId">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            商户订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/query-by-out-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByOutTradeNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            关闭订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/close-order.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Close">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            退款申请
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/create.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Refund">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            查询单笔退款(通过商户退款单号)
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/query-by-out-refund-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/RefundQueryByOutRefundNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请交易账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/get-trade-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetTradeBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请资金账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/get-fund-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetFundFlowBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            下载账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/download-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/DownloadBill">去调试</a>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAppPay" aria-expanded="false" aria-controls="collapseAppPay">
+                    APP支付
+                </button>
+            </h2>
+            <div id="collapseAppPay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <ul class="list-group">
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            APP下单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/direct-jsons/app-prepay.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/AppPrepay">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            APP调起支付
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/app-transfer-payment.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            支付通知
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/payment-notice.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            微信支付订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/query-by-wx-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByTransactionId">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            商户订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/query-by-out-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByOutTradeNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            关闭订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/close-order.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Close">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            退款申请
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/create.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Refund">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            查询单笔退款(通过商户退款单号)
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/query-by-out-refund-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/RefundQueryByOutRefundNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请交易账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/get-trade-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetTradeBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请资金账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/get-fund-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetFundFlowBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            下载账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/in-app-payment/download-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/DownloadBill">去调试</a>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseH5Pay" aria-expanded="false" aria-controls="collapseH5Pay">
+                    H5支付
+                </button>
+            </h2>
+            <div id="collapseH5Pay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <ul class="list-group">
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            H5下单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/direct-jsons/h5-prepay.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/H5Prepay">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            H5调起支付
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/h5-transfer-payment.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            支付通知
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/payment-notice.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            微信支付订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/query-by-wx-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByTransactionId">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            商户订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/query-by-out-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByOutTradeNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            关闭订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/close-order.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Close">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            退款申请
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/create.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Refund">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            查询单笔退款(通过商户退款单号)
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/query-by-out-refund-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/RefundQueryByOutRefundNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请交易账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/get-trade-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetTradeBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请资金账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/get-fund-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetFundFlowBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            下载账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/h5-payment/download-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/DownloadBill">去调试</a>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseNativePay" aria-expanded="true" aria-controls="collapseNativePay">
+                    Native支付
+                </button>
+            </h2>
+            <div id="collapseNativePay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <ul class="list-group">
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            Native下单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/direct-jsons/native-prepay.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/NativePrepay">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            Native调起支付
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/native-transfer-payment.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            支付通知
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/payment-notice.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            微信支付订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/query-by-wx-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByTransactionId">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            商户订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/query-by-out-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByOutTradeNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            关闭订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/close-order.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Close">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            退款申请
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/create.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Refund">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            查询单笔退款(通过商户退款单号)
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/query-by-out-refund-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/RefundQueryByOutRefundNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请交易账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/get-trade-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetTradeBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请资金账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/get-fund-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetFundFlowBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            下载账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/native-payment/download-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/DownloadBill">去调试</a>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseMiniProgramPay" aria-expanded="false" aria-controls="collapseMiniProgramPay">
+                    小程序支付
+                </button>
+            </h2>
+            <div id="collapseMiniProgramPay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <ul class="list-group">
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            小程序支付
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/mini-prepay.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/JsapiPrepay">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            小程序调起支付
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/mini-transfer-payment.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            支付通知
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/payment-notice.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            微信支付订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/query-by-wx-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByTransactionId">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            商户订单号查询订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/query-by-out-trade-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/QueryByOutTradeNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            关闭订单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/close-order.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Close">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            退款申请
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/create.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Refund">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            查询单笔退款(通过商户退款单号)
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/query-by-out-refund-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/RefundQueryByOutRefundNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请交易账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/get-trade-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetTradeBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请资金账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/get-fund-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetFundFlowBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            下载账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/download-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/DownloadBill">去调试</a>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCodePay" aria-expanded="false" aria-controls="collapseCodePay">
+                    付款码支付
+                </button>
+            </h2>
+            <div id="collapseCodePay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <ul class="list-group">
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            付款码支付
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/code-pay.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/CodePay">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            撤销
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/code-payment-v3/direct/reverse.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Reverse">去调试</a>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCombinePay" aria-expanded="false" aria-controls="collapseCombinePay">
+                    合单支付
+                </button>
+            </h2>
+            <div id="collapseCombinePay" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <div class="accordion" id="accordionCombinePay">
+                        <div class="accordion-item">
+                            <h2 class="accordion-header">
+                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCombineAppPay" aria-expanded="false" aria-controls="collapseAppPay">
+                                    APP支付
+                                </button>
+                            </h2>
+                            <div id="collapseCombineAppPay" class="accordion-collapse collapse" data-bs-parent="#accordionCombineAppPay">
+                                <div class="accordion-body">
+                                    <ul class="list-group">
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            合单下单-APP
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/app-prepay.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            APP调起支付
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/app-transfer-payment.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="accordion-item">
+                            <h2 class="accordion-header">
+                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCombineJsapiPay" aria-expanded="false" aria-controls="collapseAppPay">
+                                    JSAPI支付
+                                </button>
+                            </h2>
+                            <div id="collapseCombineJsapiPay" class="accordion-collapse collapse" data-bs-parent="#accordionCombineJsapiPay">
+                                <div class="accordion-body">
+                                    <ul class="list-group">
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            合单下单-JSAPI
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/jsapi-prepay.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            JSAPI调起支付
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/jsapi-transfer-payment.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="accordion-item">
+                            <h2 class="accordion-header">
+                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCombineNativePay" aria-expanded="false" aria-controls="collapseAppPay">
+                                    Native支付
+                                </button>
+                            </h2>
+                            <div id="collapseCombineNativePay" class="accordion-collapse collapse" data-bs-parent="#accordionCombineNativePay">
+                                <div class="accordion-body">
+                                    <ul class="list-group">
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            合单下单-Native
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/native-prepay.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            Native调起支付
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/native-transfer-payment.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="accordion-item">
+                            <h2 class="accordion-header">
+                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCombineMiniProgramPay" aria-expanded="false" aria-controls="collapseAppPay">
+                                    小程序支付
+                                </button>
+                            </h2>
+                            <div id="collapseCombineMiniProgramPay" class="accordion-collapse collapse" data-bs-parent="#accordionCombineMiniProgramPay">
+                                <div class="accordion-body">
+                                    <ul class="list-group">
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            合单下单-小程序
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/mini-program-prepay.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            小程序调起支付
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/mini-transfer-payment.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="accordion-item">
+                            <h2 class="accordion-header">
+                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCombineH5Pay" aria-expanded="false" aria-controls="collapseAppPay">
+                                    H5支付
+                                </button>
+                            </h2>
+                            <div id="collapseCombineH5Pay" class="accordion-collapse collapse" data-bs-parent="#accordionCombineH5Pay">
+                                <div class="accordion-body">
+                                    <ul class="list-group">
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            合单下单-H5
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/h5-prepay.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            H5调起支付
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/h5-transfer-payment.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="accordion-item">
+                            <h2 class="accordion-header">
+                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCombinePublic" aria-expanded="false" aria-controls="collapseAppPay">
+                                    公共API
+                                </button>
+                            </h2>
+                            <div id="collapseCombinePublic" class="accordion-collapse collapse" data-bs-parent="#accordionCombinePublic">
+                                <div class="accordion-body">
+                                    <ul class="list-group">
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            合单查询
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/query-order.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            合单关单
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/close-order.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            支付通知
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/orders/payment-notice.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            退款申请
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/refunds/create.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Refund">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            查询单笔退款(通过商户退款单号)
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/refunds/query-by-out-refund-no.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/RefundQueryByOutRefundNo">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            退款结果通知
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/refunds/refund-result-notice.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            申请交易账单
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/bill-download/get-trade-bill.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetTradeBill">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            申请资金账单
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/bill-download/get-fund-bill.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetFundFlowBill">去调试</a>
+                                            </div>
+                                        </li>
+                                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                                            下载账单
+                                            <div class="btn-group">
+                                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/combine-payment/bill-download/download-bill.html" target="_blank">查看文档</a>
+                                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/DownloadBill">去调试</a>
+                                            </div>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseBill" aria-expanded="false" aria-controls="collapseBill">
+                    资金/交易账单
+                </button>
+            </h2>
+            <div id="collapseBill" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <ul class="list-group">
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请交易账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/bill-download/trade-bill/get-trade-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetTradeBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            申请资金账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/bill-download/fund-bill/get-fund-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/GetFundFlowBill">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            下载账单
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/bill-download/download-bill.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/DownloadBill">去调试</a>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+        <div class="accordion-item">
+            <h2 class="accordion-header">
+                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseRefund" aria-expanded="false" aria-controls="collapseRefund">
+                    退款
+                </button>
+            </h2>
+            <div id="collapseRefund" class="accordion-collapse collapse" data-bs-parent="#accordionPayments">
+                <div class="accordion-body">
+                    <ul class="list-group">
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            退款申请
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/refund/refunds/create.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/Refund">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            查询单笔退款(通过商户退款单号)
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/refund/refunds/query-by-out-refund-no.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/RefundQueryByOutRefundNo">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            发起异常退款
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/refund/refunds/create-abnormal-refund.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm" asp-page="/WeChatPay/Payments/AbnormalRefund">去调试</a>
+                            </div>
+                        </li>
+                        <li class="list-group-item d-flex justify-content-between align-items-center">
+                            退款结果通知
+                            <div class="btn-group">
+                                <a class="btn btn-link btn-sm" href="https://pay.weixin.qq.com/docs/merchant/apis/refund/refunds/refund-result-notice.html" target="_blank">查看文档</a>
+                                <a class="btn btn-link btn-sm disabled" href="#">去调试</a>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 10 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Index.cshtml.cs

@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class IndexModel : PageModel
+{
+    public void OnGet()
+    {
+    }
+}

+ 76 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/JsapiPrepay.cshtml

@@ -0,0 +1,76 @@
+@page
+@model JsapiPrepayModel
+@{
+    ViewData["Title"] = "JSAPI下单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.AppId"></label>
+                <input class="form-control" asp-for="Input.AppId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.MchId"></label>
+                <input class="form-control" asp-for="Input.MchId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Description"></label>
+                <input class="form-control" asp-for="Input.Description"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Amount.Total"></label>
+                <input class="form-control" asp-for="Input.Amount.Total"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Payer.OpenId"></label>
+                <input class="form-control" asp-for="Input.Payer.OpenId"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+        @if (ViewData["parameter"] is string parameter && !string.IsNullOrEmpty(parameter))
+        {
+            <div class="mb-3">
+                <label class="form-label">JSAPI调起支付-请求参数</label>
+                <textarea class="form-control" rows="10">@parameter</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 47 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/JsapiPrepay.cshtml.cs

@@ -0,0 +1,47 @@
+using System.Text.Json;
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Domain;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class JsapiPrepayModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public WeChatPayJsapiPrepayBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayJsapiPrepayBodyModel
+        {
+            AppId = _options.AppId,
+            MchId = _options.MchId,
+            Description = "JSAPI下单测试",
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            NotifyUrl = "https://www.domain.com/WeChatPay/Payments/Notify/TransactionSuccess",
+            Amount = new CommReqAmountInfo { Total = 1 },
+            Payer = new JsapiReqPayerInfo { OpenId = string.Empty }
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayJsapiPrepayRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+
+        if (response.IsSuccessful)
+        {
+            var sdkRequest = new WeChatPayJsapiTransferPaymentRequest { AppId = Input.AppId, Package = "prepay_id=" + response.PrepayId };
+            var sdkResponse = await client.SdkExecuteAsync(sdkRequest, _options);
+            ViewData["parameter"] = JsonSerializer.Serialize(sdkResponse);
+        }
+    }
+}

+ 76 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/NativePrepay.cshtml

@@ -0,0 +1,76 @@
+@page
+@model NativePrepayModel
+@{
+    ViewData["Title"] = "Native下单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.AppId"></label>
+                <input class="form-control" asp-for="Input.AppId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.MchId"></label>
+                <input class="form-control" asp-for="Input.MchId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Description"></label>
+                <input class="form-control" asp-for="Input.Description"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Amount.Total"></label>
+                <input class="form-control" asp-for="Input.Amount.Total"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["code_url"] is string data && !string.IsNullOrEmpty(data))
+        {
+            using var qrGenerator = new QRCodeGenerator();
+            using var qrCodeData = qrGenerator.CreateQrCode(data, QRCodeGenerator.ECCLevel.Q);
+            using var base64ByteQrCode = new Base64QRCode(qrCodeData);
+            var base64Str = base64ByteQrCode.GetGraphic(20, Color.Black, Color.White, false, Base64QRCode.ImageType.Png);
+            <div class="mb-3">
+                <label class="form-label">二维码</label>
+                <embed src="data:image/png;base64,@base64Str" class="bg-light shadow-sm rounded d-block p-3 mb-5" type="image/png" width="180" height="180" />
+            </div>
+        }
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 39 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/NativePrepay.cshtml.cs

@@ -0,0 +1,39 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Domain;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class NativePrepayModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public WeChatPayNativePrepayBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayNativePrepayBodyModel
+        {
+            AppId = _options.AppId,
+            MchId = _options.MchId,
+            Description = "Native下单测试",
+            OutTradeNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            NotifyUrl = "https://www.domain.com/WeChatPay/Payments/Notify/TransactionSuccess",
+            Amount = new CommReqAmountInfo { Total = 1 }
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayNativePrepayRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["code_url"] = response.CodeUrl;
+        ViewData["response"] = response.Body;
+    }
+}

+ 5 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Notify/RefundResult.cshtml

@@ -0,0 +1,5 @@
+@page
+@model RefundResultModel
+@{
+    Layout = null;
+}

+ 57 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Notify/RefundResult.cshtml.cs

@@ -0,0 +1,57 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Core;
+using Essensoft.Paylinks.WeChatPay.Mvc;
+using Essensoft.Paylinks.WeChatPay.Mvc.Extensions;
+using Essensoft.Paylinks.WeChatPay.Payments.Domain;
+using Essensoft.Paylinks.WeChatPay.Payments.Notify;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments.Notify;
+
+[IgnoreAntiforgeryToken]
+public class RefundResultModel(ILogger<RefundResultModel> logger, IWeChatPayNotifyClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    /// <summary>
+    /// 退款结果通知
+    /// https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/refund-result-notice.html
+    /// </summary>
+    public async Task<IActionResult> OnPostAsync()
+    {
+        try
+        {
+            var headers = await Request.GetWeChatPayHeadersAsync();
+            var body = await Request.GetWeChatPayBodyAsync();
+            var notify = await client.ExecuteAsync<WeChatPayRefundResultNotify>(headers, body, _options);
+            // 请务必检查系统内业务状态,避免因重复通知遭受损失。
+            switch (notify.RefundStatus)
+            {
+                case WeChatPayRefundStatus.Success:
+                    {
+                        logger.LogInformation($"退款成功通知: TransactionId:{notify.TransactionId}, TotalAmount:{notify.Amount.Total}");
+                    }
+                    break;
+                case WeChatPayRefundStatus.Closed:
+                    {
+                        logger.LogInformation($"退款关闭通知: TransactionId:{notify.TransactionId}, TotalAmount:{notify.Amount.Total}");
+                    }
+                    break;
+                case WeChatPayRefundStatus.Abnormal:
+                    {
+                        logger.LogInformation($"退款异常通知: TransactionId:{notify.TransactionId}, TotalAmount:{notify.Amount.Total}");
+                    }
+                    break;
+            }
+
+            return WeChatPayNotifyResult.Success;
+        }
+        catch (WeChatPayException ex)
+        {
+            logger.LogError(ex.Message);
+            return WeChatPayNotifyResult.Fail;
+        }
+    }
+}

+ 5 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Notify/TransactionSuccess.cshtml

@@ -0,0 +1,5 @@
+@page
+@model TransactionSuccessModel
+@{
+    Layout = null;
+}

+ 38 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Notify/TransactionSuccess.cshtml.cs

@@ -0,0 +1,38 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Core;
+using Essensoft.Paylinks.WeChatPay.Mvc;
+using Essensoft.Paylinks.WeChatPay.Mvc.Extensions;
+using Essensoft.Paylinks.WeChatPay.Payments.Notify;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments.Notify;
+
+[IgnoreAntiforgeryToken]
+public class TransactionSuccessModel(ILogger<TransactionSuccessModel> logger, IWeChatPayNotifyClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    /// <summary>
+    /// 支付成功通知
+    /// https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/payment-notice.html
+    /// </summary>
+    public async Task<IActionResult> OnPostAsync()
+    {
+        try
+        {
+            var headers = await Request.GetWeChatPayHeadersAsync();
+            var body = await Request.GetWeChatPayBodyAsync();
+            var notify = await client.ExecuteAsync<WeChatPayTransactionSuccessNotify>(headers, body, _options);
+            // 请务必检查系统内业务状态,避免因重复通知遭受损失。
+            logger.LogInformation($"支付成功通知: TransactionId:{notify.TransactionId}, TotalAmount:{notify.Amount.Total}");
+            return WeChatPayNotifyResult.Success;
+        }
+        catch (WeChatPayException ex)
+        {
+            logger.LogError(ex.Message);
+            return WeChatPayNotifyResult.Fail;
+        }
+    }
+}

+ 49 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/QueryByOutTradeNo.cshtml

@@ -0,0 +1,49 @@
+@page
+@model QueryByOutTradeNoModel
+@{
+    ViewData["Title"] = "商户订单号查询订单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="OutTradeNo"></label>
+                <input class="form-control" asp-for="OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.MchId"></label>
+                <input class="form-control" asp-for="Input.MchId"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 32 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/QueryByOutTradeNo.cshtml.cs

@@ -0,0 +1,32 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class QueryByOutTradeNoModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public string OutTradeNo { get; set; }
+
+    [BindProperty]
+    public WeChatPayQueryByOutTradeNoQueryModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayQueryByOutTradeNoQueryModel { MchId = _options.MchId };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayQueryByOutTradeNoRequest { OutTradeNo = OutTradeNo };
+        request.SetQueryModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 49 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/QueryByTransactionId.cshtml

@@ -0,0 +1,49 @@
+@page
+@model QueryByTransactionIdModel
+@{
+    ViewData["Title"] = "微信支付订单号查询订单";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="TransactionId"></label>
+                <input class="form-control" asp-for="TransactionId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.MchId"></label>
+                <input class="form-control" asp-for="Input.MchId"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 32 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/QueryByTransactionId.cshtml.cs

@@ -0,0 +1,32 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class QueryByTransactionIdModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public string TransactionId { get; set; }
+
+    [BindProperty]
+    public WeChatPayQueryByTransactionIdQueryModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayQueryByTransactionIdQueryModel { MchId = _options.MchId };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayQueryByTransactionIdRequest { TransactionId = TransactionId };
+        request.SetQueryModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 69 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Refund.cshtml

@@ -0,0 +1,69 @@
+@page
+@model RefundModel
+@{
+    ViewData["Title"] = "退款申请";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.TransactionId"></label>
+                <input class="form-control" asp-for="Input.TransactionId"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutTradeNo"></label>
+                <input class="form-control" asp-for="Input.OutTradeNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.OutRefundNo"></label>
+                <input class="form-control" asp-for="Input.OutRefundNo"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Amount.Refund"></label>
+                <input class="form-control" asp-for="Input.Amount.Refund"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Amount.Total"></label>
+                <input class="form-control" asp-for="Input.Amount.Total"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.Amount.Currency"></label>
+                <input class="form-control" asp-for="Input.Amount.Currency"/>
+            </div>
+            <div class="mb-3">
+                <label class="form-label" asp-for="Input.NotifyUrl"></label>
+                <input class="form-control" asp-for="Input.NotifyUrl"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 35 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/Refund.cshtml.cs

@@ -0,0 +1,35 @@
+using Essensoft.Paylinks.WeChatPay.Client;
+using Essensoft.Paylinks.WeChatPay.Payments.Domain;
+using Essensoft.Paylinks.WeChatPay.Payments.Model;
+using Essensoft.Paylinks.WeChatPay.Payments.Request;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.Options;
+
+namespace Essensoft.Paylinks.Sample.Web.Pages.WeChatPay.Payments;
+
+public class RefundModel(IWeChatPayClient client, IOptions<PaylinksOptions> options) : PageModel
+{
+    private readonly WeChatPayClientOptions _options = options.Value.WeChatPay;
+
+    [BindProperty]
+    public WeChatPayRefundBodyModel Input { get; set; }
+
+    public void OnGet()
+    {
+        Input = new WeChatPayRefundBodyModel
+        {
+            OutRefundNo = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
+            Amount = new RefundAmount { Refund = 1, Total = 1, Currency = "CNY" },
+            NotifyUrl = "https://www.domain.com/WeChatPay/Payments/Notify/RefundResult"
+        };
+    }
+
+    public async Task OnPostAsync()
+    {
+        var request = new WeChatPayRefundRequest();
+        request.SetBodyModel(Input);
+        var response = await client.ExecuteAsync(request, _options);
+        ViewData["response"] = response.Body;
+    }
+}

+ 45 - 0
samples/Essensoft.Paylinks.Sample.Web/Pages/WeChatPay/Payments/RefundQueryByOutRefundNo.cshtml

@@ -0,0 +1,45 @@
+@page
+@model RefundQueryByOutRefundNoModel
+@{
+    ViewData["Title"] = "查询单笔退款(通过商户退款单号)";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item">
+            <a asp-page="/Index">首页</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Index">微信支付</a>
+        </li>
+        <li class="breadcrumb-item">
+            <a asp-page="/WeChatPay/Payments/Index">支付产品</a>
+        </li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<div class="card mb-3">
+    <div class="card-header">
+        @ViewData["Title"]
+    </div>
+    <div class="card-body">
+        <form method="post">
+            <div class="mb-3">
+                <label class="form-label" asp-for="OutRefundNo"></label>
+                <input class="form-control" asp-for="OutRefundNo"/>
+            </div>
+            <button class="btn btn-primary mb-3" type="submit">提交请求</button>
+        </form>
+        <hr/>
+        @if (ViewData["response"] is string response && !string.IsNullOrEmpty(response))
+        {
+            <div class="mb-3">
+                <label class="form-label">应答</label>
+                <textarea class="form-control" rows="10">@response</textarea>
+            </div>
+        }
+    </div>
+</div>
+
+@section scripts {
+    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff