docs: 提供微信商户平台模块的示例项目

This commit is contained in:
Fu Diwei 2021-07-30 14:09:25 +08:00
parent 0a5c049a53
commit b0c9187923
12 changed files with 284 additions and 2 deletions

View File

@ -38,6 +38,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{35C9
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SKIT.FlurlHttpClient.Wechat.Api.Sample_Net5", "samples\SKIT.FlurlHttpClient.Wechat.Api.Sample_Net5\SKIT.FlurlHttpClient.Wechat.Api.Sample_Net5.csproj", "{D1B321C9-3004-4645-A78D-A85C152062FA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5", "samples\SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5\SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5.csproj", "{65E51735-73CE-4E9B-AA65-4BF5E4C8A705}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -88,6 +90,10 @@ Global
{D1B321C9-3004-4645-A78D-A85C152062FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1B321C9-3004-4645-A78D-A85C152062FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1B321C9-3004-4645-A78D-A85C152062FA}.Release|Any CPU.Build.0 = Release|Any CPU
{65E51735-73CE-4E9B-AA65-4BF5E4C8A705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65E51735-73CE-4E9B-AA65-4BF5E4C8A705}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65E51735-73CE-4E9B-AA65-4BF5E4C8A705}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65E51735-73CE-4E9B-AA65-4BF5E4C8A705}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -105,6 +111,7 @@ Global
{DBF84F66-1436-4599-93AB-7C16A3A2C3A4} = {C95AF531-CF44-44AA-AC90-F4DF9F941674}
{561E0BFB-7817-41FE-BAF2-D78817679AC1} = {C95AF531-CF44-44AA-AC90-F4DF9F941674}
{D1B321C9-3004-4645-A78D-A85C152062FA} = {35C901ED-C234-4A91-9561-AD89B3BB788D}
{65E51735-73CE-4E9B-AA65-4BF5E4C8A705} = {35C901ED-C234-4A91-9561-AD89B3BB788D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F08ED64E-2517-4B51-A4BE-D33D56CC7B39}

View File

@ -7,10 +7,11 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SKIT.FlurlHttpClient.Wechat.Security;
namespace SKIT.FlurlHttpClient.Wechat.Api.Sample_Net5.Controllers
{
using SKIT.FlurlHttpClient.Wechat.Security;
[ApiController]
[Route("notify")]
public class WechatNotifyController : ControllerBase

View File

@ -1,7 +1,7 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"SKIT.FlurlHttpClient.Wechat.Api.Demo": {
"SKIT.FlurlHttpClient.Wechat.Api.Sample_Net5": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": false,

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5.Controllers
{
[ApiController]
[Route("notify")]
public class WxpayNotifyController : ControllerBase
{
private readonly ILogger _logger;
public WxpayNotifyController(
ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger(GetType());
}
[HttpPost]
public async Task<IActionResult> ReceiveMessage()
{
// 接收服务器推送
// 文档https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
string content = await reader.ReadToEndAsync();
_logger.LogInformation("接收到微信支付推送的数据:{0}", content);
return new JsonResult(new { code = "SUCCESS", message = "成功" });
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5.Options
{
public partial class WechatOptions : IOptions<WechatOptions>
{
WechatOptions IOptions<WechatOptions>.Value => this;
public WechatMerchant[] Merchants { get; set; } = Array.Empty<WechatMerchant>();
public string CallbackEntry { get; set; } = string.Empty;
}
partial class WechatOptions
{
public class WechatMerchant
{
public string MerchantId { get; set; } = string.Empty;
public string SecretV3 { get; set; } = string.Empty;
public string CertSerialNumber { get; set; } = string.Empty;
public string CertPrivateKey { get; set; } = string.Empty;
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5
{
public class Program
{
public static void Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder =>
{
builder.UseStartup<Startup>();
})
.Build()
.Run();
}
}
}

View File

@ -0,0 +1,15 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<NullableReferenceTypes>true</NullableReferenceTypes>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SKIT.FlurlHttpClient.Wechat.TenpayV3\SKIT.FlurlHttpClient.Wechat.TenpayV3.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5.Services.HttpClients
{
public interface IWechatTenpayHttpClientFactory
{
WechatTenpayClient Create(string appId);
}
}

View File

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Flurl;
using Flurl.Http;
using Flurl.Http.Configuration;
using Microsoft.Extensions.Options;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5.Services.HttpClients.Implements
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
partial class WechatTenpayHttpClientFactory : IWechatTenpayHttpClientFactory
{
private readonly System.Net.Http.IHttpClientFactory _httpClientFactory;
private readonly Options.WechatOptions _wechatOptions;
private readonly CertificateManager _certificateManager;
public WechatTenpayHttpClientFactory(
System.Net.Http.IHttpClientFactory httpClientFactory,
IOptions<Options.WechatOptions> wechatOptions,
CertificateManager certificateManager)
{
_httpClientFactory = httpClientFactory;
_wechatOptions = wechatOptions.Value;
_certificateManager = certificateManager;
FlurlHttp.GlobalSettings.FlurlClientFactory = new DelegatingFlurlClientFactory(_httpClientFactory);
}
public WechatTenpayClient Create(string merchantId)
{
var wechatMerchant = _wechatOptions.Merchants?.FirstOrDefault(e => string.Equals(merchantId, e.MerchantId));
if (wechatMerchant == null)
throw new Exception("未在配置项中找到该 MerchantId 对应的微信商户号。");
return new WechatTenpayClient(new WechatTenpayClientOptions()
{
MerchantId = wechatMerchant.MerchantId,
MerchantV3Secret = wechatMerchant.SecretV3,
MerchantCertSerialNumber = wechatMerchant.CertSerialNumber,
MerchantCertPrivateKey = wechatMerchant.CertPrivateKey,
CertificateManager = _certificateManager
});
}
}
partial class WechatTenpayHttpClientFactory
{
internal class DelegatingFlurlClientFactory : IFlurlClientFactory
{
private readonly System.Net.Http.IHttpClientFactory _httpClientFactory;
public DelegatingFlurlClientFactory(System.Net.Http.IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
}
public IFlurlClient Get(Url url)
{
return new FlurlClient(_httpClientFactory.CreateClient(url.ToUri().Host));
}
public void Dispose()
{
// Do Nothing
}
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// 注入配置项
services.AddOptions();
services.Configure<Options.WechatOptions>(Configuration.GetSection(nameof(Options.WechatOptions)));
// 注入工厂 HTTP 客户端
services.AddHttpClient();
services.AddSingleton<CertificateManager, InMemoryCertificateManager>();
services.AddSingleton<Services.HttpClients.IWechatTenpayHttpClientFactory, Services.HttpClients.Implements.WechatTenpayHttpClientFactory>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

View File

@ -0,0 +1,20 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"WechatOptions": {
"Merchants": [
{
"MerchantId": "ÌîдÉÌ»§ºÅ"
}
],
"CallbackEntry": "https://localhost:5001"
}
}