docs: 更新示例项目

This commit is contained in:
Fu Diwei
2025-06-04 23:53:34 +08:00
parent ac925f87cf
commit 56ed274c40
45 changed files with 800 additions and 492 deletions

View File

@@ -0,0 +1,39 @@
using System.Reflection;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using Autofac.Integration.WebApi;
using Hangfire;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample
{
public class AutofacConfig
{
public static void Init()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces().AsSelf();
builder.RegisterType<Services.HttpClients.Implements.WechatTenpayCertificateManagerFactory>()
.As<Services.HttpClients.IWechatTenpayCertificateManagerFactory>()
.SingleInstance();
builder.RegisterType<Services.HttpClients.Implements.WechatTenpayPublicKeyManagerFactory>()
.As<Services.HttpClients.IWechatTenpayPublicKeyManagerFactory>()
.SingleInstance();
builder.RegisterType<Services.HttpClients.Implements.WechatTenpayClientFactory>()
.As<Services.HttpClients.IWechatTenpayClientFactory>()
.SingleInstance();
builder.RegisterType<Services.BackgroundJobs.TenpayCertificateRefreshingBackgroundJob>()
.AsSelf()
.InstancePerBackgroundJob();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
}

View File

@@ -0,0 +1,12 @@
using System.Web.Mvc;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
}

View File

@@ -0,0 +1,25 @@
using Hangfire;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample
{
public class HangfireConfig
{
public static BackgroundJobServer Init()
{
GlobalConfiguration.Configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseInMemoryStorage();
var server = new BackgroundJobServer();
HangfireAspNet.Use(() => new BackgroundJobServer[] { server });
BackgroundJob.Enqueue<Services.BackgroundJobs.TenpayCertificateRefreshingBackgroundJob>(job => job.ExecuteAsync());
RecurringJob.AddOrUpdate<Services.BackgroundJobs.TenpayCertificateRefreshingBackgroundJob>(job => job.ExecuteAsync(), Cron.Daily);
return server;
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Web.Mvc;
using System.Web.Routing;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}

View File

@@ -0,0 +1,18 @@
using System.Web.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{controller}/{action}",
defaults: new { }
);
}
}
}

View File

@@ -0,0 +1,83 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Controllers
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
[RoutePrefix("api/notify")]
public class TenpayNotifyController : ApiController
{
private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory;
public TenpayNotifyController(
Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory)
{
_wechatTenpayClientFactory = wechatTenpayClientFactory;
}
[HttpPost]
[Route("m-{merchant_id}/message-push")]
public async Task<IHttpActionResult> ReceiveMessage([FromUri(Name = "merchant_id")] string merchantId, CancellationToken cancellationToken)
{
using (var stream = await Request.Content.ReadAsStreamAsync())
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
string timestamp = Request.Headers.Contains("Wechatpay-Timestamp") ? Request.Headers.GetValues("Wechatpay-Timestamp").First() : null;
string nonce = Request.Headers.Contains("Wechatpay-Nonce") ? Request.Headers.GetValues("Wechatpay-Nonce").First() : null;
string signature = Request.Headers.Contains("Wechatpay-Signature") ? Request.Headers.GetValues("Wechatpay-Signature").First() : null;
string serialNumber = Request.Headers.Contains("Wechatpay-Serial") ? Request.Headers.GetValues("Wechatpay-Serial").First() : null;
string content = await reader.ReadToEndAsync();
Debug.WriteLine("接收到微信支付推送的数据:{0}", content);
using (var client = _wechatTenpayClientFactory.Create(merchantId))
{
bool valid = client.VerifyEventSignature(
webhookTimestamp: timestamp,
webhookNonce: nonce,
webhookBody: content,
webhookSignature: signature,
webhookSerialNumber: serialNumber
);
if (!valid)
{
// NOTICE:
// 需提前注入 CertificateManager、并添加平台证书才可以使用扩展方法执行验签操作。
// 请参考本示例项目 TenpayCertificateRefreshingBackgroundService 后台任务中的相关实现。
// 有关 CertificateManager 的完整介绍请参阅《开发文档 / 基础用法 / 如何验证回调通知事件签名?》。
// 后续如何解密并反序列化,请参阅《开发文档 / 基础用法 / 如何解密回调通知事件中的敏感数据?》。
return Json(new { code = "FAIL", message = "验签失败" });
}
var callbackModel = client.DeserializeEvent(content);
var eventType = callbackModel.EventType?.ToUpper();
switch (eventType)
{
case "TRANSACTION.SUCCESS":
{
var callbackResource = client.DecryptEventResource<Events.TransactionResource>(callbackModel);
Debug.WriteLine("接收到微信支付推送的订单支付成功通知,商户订单号:{0}", callbackResource.OutTradeNumber);
// 后续处理略
}
break;
default:
{
// 其他情况略
}
break;
}
return Json(new { code = "SUCCESS", message = "成功" });
}
}
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Controllers
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
[RoutePrefix("api/order")]
public class TenpayOrderController : ApiController
{
private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory;
public TenpayOrderController(
Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory)
{
_wechatTenpayClientFactory = wechatTenpayClientFactory;
}
[HttpPost]
[Route("jsapi")]
public async Task<IHttpActionResult> CreateOrderByJsapi([FromBody] Models.CreateOrderByJsapiRequest requestModel, CancellationToken cancellationToken)
{
if (requestModel == null)
return BadRequest();
using (var client = _wechatTenpayClientFactory.Create(requestModel.MerchantId))
{
var request = new CreatePayTransactionJsapiRequest()
{
OutTradeNumber = "SAMPLE_OTN_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
AppId = requestModel.AppId,
Description = "演示订单",
NotifyUrl = Options.TenpayOptions.Instance.Value.NotifyUrl,
Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = requestModel.Amount },
Payer = new CreatePayTransactionJsapiRequest.Types.Payer() { OpenId = requestModel.OpenId }
};
var response = await client.ExecuteCreatePayTransactionJsapiAsync(request, cancellationToken);
if (!response.IsSuccessful())
{
Debug.WriteLine(
"JSAPI 下单失败(状态码:{0},错误代码:{1},错误描述:{2})。",
response.GetRawStatus(), response.ErrorCode, response.ErrorMessage
);
}
return Json(response);
}
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Controllers
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
[RoutePrefix("api/refund")]
public class TenpayRefundController : ApiController
{
private readonly Services.HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory;
public TenpayRefundController(
Services.HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory)
{
_wechatTenpayClientFactory = wechatTenpayClientFactory;
}
[HttpPost]
[Route("")]
public async Task<IHttpActionResult> CreateRefund([FromBody] Models.CreateRefundRequest requestModel, CancellationToken cancellationToken)
{
if (requestModel == null)
return BadRequest();
using (var client = _wechatTenpayClientFactory.Create(requestModel.MerchantId))
{
var request = new CreateRefundDomesticRefundRequest()
{
TransactionId = requestModel.TransactionId,
OutRefundNumber = "SAMPLE_ORN_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
Amount = new CreateRefundDomesticRefundRequest.Types.Amount()
{
Total = requestModel.OrderAmount,
Refund = requestModel.RefundAmount
},
Reason = "示例退款",
NotifyUrl = Options.TenpayOptions.Instance.Value.NotifyUrl
};
var response = await client.ExecuteCreateRefundDomesticRefundAsync(request, cancellationToken);
if (!response.IsSuccessful())
{
Debug.WriteLine(
"申请退款失败(状态码:{0},错误代码:{1},错误描述:{2})。",
response.GetRawStatus(), response.ErrorCode, response.ErrorMessage
);
}
return Json(response);
}
}
}
}

View File

@@ -0,0 +1 @@
<%@ Application Codebehind="Global.asax.cs" Inherits="SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.WebApiApplication" Language="C#" %>

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample
{
public class WebApiApplication : System.Web.HttpApplication
{
private Hangfire.BackgroundJobServer _backgroundJobServer;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AutofacConfig.Init();
_backgroundJobServer = HangfireConfig.Init();
}
protected void Application_End()
{
_backgroundJobServer?.Dispose();
}
}
}

View File

@@ -0,0 +1,15 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Models
{
public class CreateOrderByJsapiRequest
{
public string MerchantId { get; set; }
public string AppId { get; set; }
public string OpenId { get; set; }
// NOTICE:
// 单机演示时金额来源于客户端请求,生产项目请改为服务端计算生成,切勿依赖客户端提供的金额结果。
public int Amount { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Models
{
public class CreateRefundRequest
{
public string MerchantId { get; set; }
public string TransactionId { get; set; }
// NOTICE:
// 单机演示时金额来源于客户端请求,生产项目请改为服务端计算生成,切勿依赖客户端提供的金额结果。
public int OrderAmount { get; set; }
public int RefundAmount { get; set; }
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Configuration;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Options
{
public partial class TenpayOptions
{
public static readonly Lazy<TenpayOptions> Instance = new Lazy<TenpayOptions>(() =>
{
var configMerchantRegex = new Regex("^TenpayOptions_Merchant_(\\d+)_MerchantId$");
var configMerchantIndexes = WebConfigurationManager.AppSettings.AllKeys
.Where(key => configMerchantRegex.IsMatch(key))
.Select(key => configMerchantRegex.Matches(key)[0].Groups[1].Value)
.ToArray();
return new TenpayOptions()
{
Merchants = configMerchantIndexes
.Select(i => new Types.WechatMerchant()
{
MerchantId = WebConfigurationManager.AppSettings[$"TenpayOptions_Merchant_{i}_MerchantId"],
SecretV3 = WebConfigurationManager.AppSettings[$"TenpayOptions_Merchant_{i}_SecretV3"],
CertificateSerialNumber = WebConfigurationManager.AppSettings[$"TenpayOptions_Merchant_{i}_CertificateSerialNumber"],
CertificatePrivateKey = WebConfigurationManager.AppSettings[$"TenpayOptions_Merchant_{i}_CertificatePrivateKey"],
PlatformPublicKeyId = WebConfigurationManager.AppSettings[$"TenpayOptions_Merchant_{i}_PlatformPublicKeyId"],
PlatformPublicKey = WebConfigurationManager.AppSettings[$"TenpayOptions_Merchant_{i}_PlatformPublicKey"],
})
.ToArray(),
NotifyUrl = WebConfigurationManager.AppSettings[$"TenpayOptions_NotifyUrl"]
};
}, isThreadSafe: true);
}
public partial class TenpayOptions
{
public Types.WechatMerchant[] Merchants { get; set; } = Array.Empty<Types.WechatMerchant>();
public string NotifyUrl { get; set; } = string.Empty;
}
public partial class TenpayOptions
{
public static class Types
{
public class WechatMerchant
{
public string MerchantId { get; set; } = string.Empty;
public string SecretV3 { get; set; } = string.Empty;
public string CertificateSerialNumber { get; set; } = string.Empty;
public string CertificatePrivateKey { get; set; } = string.Empty;
public string PlatformPublicKeyId { get; set; }
public string PlatformPublicKey { get; set; }
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息是通过以下项进行控制的
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_NetFramework48")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_NetFramework48")]
[assembly: AssemblyCopyright("版权所有(C) 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 将使此程序集中的类型
// 对 COM 组件不可见。如果需要
// COM在该类型上将 ComVisible 属性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于 typelib 的 ID
[assembly: Guid("22aea3c5-d9af-4ec5-b208-73dc8795a1bb")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 内部版本号
// 修订号
//
// 你可以指定所有值,也可以让修订版本和内部版本号采用默认值,
// 方法是按如下所示使用 "*":
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,281 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\build\net46\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\build\net46\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{DFA229FF-0A4B-411E-9A11-681BA55DFBAE}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_NetFramework48</RootNamespace>
<AssemblyName>SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_NetFramework48</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
<UseIISExpress>true</UseIISExpress>
<Use64BitIISExpress />
<IISExpressSSLPort>44339</IISExpressSSLPort>
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=6.4.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\..\packages\Autofac.6.4.0\lib\netstandard2.0\Autofac.dll</HintPath>
</Reference>
<Reference Include="Autofac.Integration.Mvc, Version=6.1.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\..\packages\Autofac.Mvc5.6.1.0\lib\net472\Autofac.Integration.Mvc.dll</HintPath>
</Reference>
<Reference Include="Autofac.Integration.WebApi, Version=6.1.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\..\packages\Autofac.WebApi2.6.1.1\lib\net472\Autofac.Integration.WebApi.dll</HintPath>
</Reference>
<Reference Include="Flurl, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Flurl.4.0.0\lib\net472\Flurl.dll</HintPath>
</Reference>
<Reference Include="Flurl.Http, Version=4.0.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Flurl.Http.4.0.2\lib\net461\Flurl.Http.dll</HintPath>
</Reference>
<Reference Include="Hangfire.AspNet, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Hangfire.AspNet.0.3.0\lib\net45\Hangfire.AspNet.dll</HintPath>
</Reference>
<Reference Include="Hangfire.Autofac, Version=2.7.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Hangfire.Autofac.2.7.0\lib\net45\Hangfire.Autofac.dll</HintPath>
</Reference>
<Reference Include="Hangfire.Core, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Hangfire.Core.1.8.0\lib\net46\Hangfire.Core.dll</HintPath>
</Reference>
<Reference Include="Hangfire.InMemory, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Hangfire.InMemory.1.0.0\lib\net451\Hangfire.InMemory.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="SKIT.FlurlHttpClient.Common, Version=3.1.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\SKIT.FlurlHttpClient.Common.3.1.1\lib\net462\SKIT.FlurlHttpClient.Common.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.7.1\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encodings.Web, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath>
</Reference>
<Reference Include="System.Text.Json, Version=8.0.0.5, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Text.Json.8.0.5\lib\net462\System.Text.Json.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Routing" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="Microsoft.Web.Infrastructure, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Microsoft.Web.Infrastructure.2.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http">
</Reference>
<Reference Include="System.Net.Http.Formatting, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.9\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.WebRequest">
</Reference>
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.9\lib\net45\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.WebHost, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.9\lib\net45\System.Web.Http.WebHost.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Microsoft.AspNet.Mvc.5.2.9\lib\net45\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web.Optimization">
<HintPath>..\..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll</HintPath>
</Reference>
<Reference Include="System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Microsoft.AspNet.Razor.3.2.9\lib\net45\System.Web.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Deployment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
</Reference>
<Reference Include="WebGrease">
<Private>True</Private>
<HintPath>..\..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath>
</Reference>
<Reference Include="Antlr3.Runtime">
<Private>True</Private>
<HintPath>..\..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform">
<HintPath>..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\AutofacConfig.cs" />
<Compile Include="App_Start\FilterConfig.cs" />
<Compile Include="App_Start\HangfireConfig.cs" />
<Compile Include="App_Start\RouteConfig.cs" />
<Compile Include="App_Start\WebApiConfig.cs" />
<Compile Include="Controllers\TenpayNotifyController.cs" />
<Compile Include="Controllers\TenpayOrderController.cs" />
<Compile Include="Controllers\TenpayRefundController.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Models\CreateOrderByJsapiRequest.cs" />
<Compile Include="Models\CreateRefundRequest.cs" />
<Compile Include="Options\TenpayOptions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\BackgroundJobs\TenpayCertificateRefreshingBackgroundJob.cs" />
<Compile Include="Services\HttpClients\Implements\WechatTenpayPublicKeyManagerFactory.cs" />
<Compile Include="Services\HttpClients\Implements\WechatTenpayCertificateManagerFactory.cs" />
<Compile Include="Services\HttpClients\Implements\WechatTenpayClientFactory.cs" />
<Compile Include="Services\HttpClients\IWechatTenpayPublicKeyManagerFactory.cs" />
<Compile Include="Services\HttpClients\IWechatTenpayCertificateManagerFactory.cs" />
<Compile Include="Services\HttpClients\IWechatTenpayClientFactory.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Global.asax" />
<Content Include="Web.config" />
<Content Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
<Content Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SKIT.FlurlHttpClient.Wechat.TenpayV3\SKIT.FlurlHttpClient.Wechat.TenpayV3.csproj">
<Project>{b9f2eb07-609c-d1b6-31de-1bfb32beee0d}</Project>
<Name>SKIT.FlurlHttpClient.Wechat.TenpayV3</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>49987</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>https://localhost:44339/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\build\net46\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\build\net46\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target> -->
</Project>

View File

@@ -0,0 +1,65 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Services.BackgroundJobs
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
internal class TenpayCertificateRefreshingBackgroundJob
{
private readonly HttpClients.IWechatTenpayClientFactory _wechatTenpayClientFactory;
public TenpayCertificateRefreshingBackgroundJob(
HttpClients.IWechatTenpayClientFactory wechatTenpayClientFactory)
{
_wechatTenpayClientFactory = wechatTenpayClientFactory;
}
public async Task ExecuteAsync()
{
// NOTICE:
// 2024-10-01 后微信支付新增了基于微信支付公钥的验证身份方式,
// 如果你已切换至使用平台公钥,则不再需要下载平台证书,可删除此定时任务。
foreach (var tenpayMerchantOptions in Options.TenpayOptions.Instance.Value.Merchants)
{
try
{
const string ALGORITHM_TYPE = "RSA";
using (var client = _wechatTenpayClientFactory.Create(tenpayMerchantOptions.MerchantId))
{
var request = new QueryCertificatesRequest() { AlgorithmType = ALGORITHM_TYPE };
var response = await client.ExecuteQueryCertificatesAsync(request);
if (response.IsSuccessful())
{
// NOTICE:
// 如果构造 Client 时启用了 `AutoDecryptResponseSensitiveProperty` 配置项,则无需再执行下面一行的手动解密方法:
response = client.DecryptResponseSensitiveProperty(response);
foreach (var certificate in response.CertificateList)
{
client.PlatformCertificateManager.AddEntry(CertificateEntry.Parse(ALGORITHM_TYPE, certificate));
}
Debug.WriteLine("刷新微信商户平台证书成功。");
}
else
{
Debug.WriteLine(
"刷新微信商户平台证书失败(状态码:{0},错误代码:{1},错误描述:{2})。",
response.GetRawStatus(), response.ErrorCode, response.ErrorMessage
);
}
}
}
catch (Exception ex)
{
Debug.WriteLine("刷新微信商户平台证书遇到异常。\r\n{0}", ex);
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Services.HttpClients
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
public interface IWechatTenpayCertificateManagerFactory
{
ICertificateManager Create(string merchantId);
}
}

View File

@@ -0,0 +1,7 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Services.HttpClients
{
public interface IWechatTenpayClientFactory
{
WechatTenpayClient Create(string merchantId);
}
}

View File

@@ -0,0 +1,9 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Services.HttpClients
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
public interface IWechatTenpayPublicKeyManagerFactory
{
IPublicKeyManager Create(string merchantId);
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Concurrent;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Services.HttpClients.Implements
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
internal partial class WechatTenpayCertificateManagerFactory : IWechatTenpayCertificateManagerFactory
{
private readonly ConcurrentDictionary<string, ICertificateManager> _dict;
public WechatTenpayCertificateManagerFactory()
{
_dict = new ConcurrentDictionary<string, ICertificateManager>();
}
public ICertificateManager Create(string merchantId)
{
// NOTICE:
// 这里的工厂方法是为了演示多租户而存在的,可根据商户号生成不同的证书管理器。
// 如果你的项目只存在唯一一个租户,那么直接注入 `PlatformCertificateManager` 即可。
return _dict.GetOrAdd(merchantId, new InMemoryCertificateManager());
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Linq;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Services.HttpClients.Implements
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
internal partial class WechatTenpayClientFactory : IWechatTenpayClientFactory
{
private readonly IWechatTenpayCertificateManagerFactory _tenpayCertificateManagerFactory;
private readonly IWechatTenpayPublicKeyManagerFactory _tenpayPublicKeyManagerFactory;
public WechatTenpayClientFactory(
IWechatTenpayCertificateManagerFactory tenpayCertificateManagerFactory,
IWechatTenpayPublicKeyManagerFactory tenpayPublicKeyManagerFactory)
{
_tenpayCertificateManagerFactory = tenpayCertificateManagerFactory;
_tenpayPublicKeyManagerFactory = tenpayPublicKeyManagerFactory;
}
public WechatTenpayClient Create(string merchantId)
{
// NOTICE:
// 这里的工厂方法是为了演示多租户而存在的,可根据商户号生成不同的 API 客户端。
// 如果你的项目只存在唯一一个租户,那么直接注入 `WechatTenpayClient` 即可。
var tenpayMerchantOptions = Options.TenpayOptions.Instance.Value.Merchants?.FirstOrDefault(e => string.Equals(merchantId, e.MerchantId));
if (tenpayMerchantOptions == null)
throw new Exception("未在配置项中找到该 MerchantId 对应的微信商户号。");
return new WechatTenpayClient(new WechatTenpayClientOptions()
{
MerchantId = tenpayMerchantOptions.MerchantId,
MerchantV3Secret = tenpayMerchantOptions.SecretV3,
MerchantCertificateSerialNumber = tenpayMerchantOptions.CertificateSerialNumber,
MerchantCertificatePrivateKey = tenpayMerchantOptions.CertificatePrivateKey,
AutoEncryptRequestSensitiveProperty = false,
AutoDecryptResponseSensitiveProperty = false,
// 基于平台证书的认证方式还需设置以下参数:
PlatformAuthScheme = PlatformAuthScheme.Certificate,
PlatformCertificateManager = _tenpayCertificateManagerFactory.Create(tenpayMerchantOptions.MerchantId),
// 基于平台公钥的认证方式还需设置以下参数:
//PlatformAuthScheme = PlatformAuthScheme.PublicKey,
//PlatformPublicKeyManager = _tenpayPublicKeyManagerFactory.Create(tenpayMerchantOptions.MerchantId)
});
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample.Services.HttpClients.Implements
{
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
internal partial class WechatTenpayPublicKeyManagerFactory : IWechatTenpayPublicKeyManagerFactory
{
private readonly ConcurrentDictionary<string, IPublicKeyManager> _dict;
public WechatTenpayPublicKeyManagerFactory()
{
_dict = new ConcurrentDictionary<string, IPublicKeyManager>();
}
public IPublicKeyManager Create(string merchantId)
{
// NOTICE:
// 这里的工厂方法是为了演示多租户而存在的,可根据商户号生成不同的公钥管理器。
// 如果你的项目只存在唯一一个租户,那么直接注入 `PlatformPublicKeyManager` 即可。
var tenpayMerchantOptions = Options.TenpayOptions.Instance.Value.Merchants?.FirstOrDefault(e => string.Equals(merchantId, e.MerchantId));
if (tenpayMerchantOptions == null)
throw new Exception("未在配置项中找到该 MerchantId 对应的微信商户号。");
return _dict.GetOrAdd(merchantId, (_) =>
{
var manager = new InMemoryPublicKeyManager();
manager.AddEntry(new PublicKeyEntry(PublicKeyEntry.ALGORITHM_TYPE_RSA, tenpayMerchantOptions.PlatformPublicKeyId, tenpayMerchantOptions.PlatformPublicKey));
return manager;
});
}
}
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 有关使用 Web.config 转换的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkId=301874 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
在下例中“SetAttributes”转换将更改
“connectionString”的值仅在“Match”定位器找到值为“MyDB”的
特性“name”时使用“ReleaseSQLServer”。
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<!--
在以下示例中,"Replace" 转换将替换 Web.config 文件的
整个 <customErrors> 节。
请注意,由于在 <system.web> 节点下只有一个
customErrors 节,因此无需使用 "xdt:Locator" 属性。
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 有关使用 Web.config 转换的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkId=301874 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
在下例中“SetAttributes”转换将更改
“connectionString”的值仅在“Match”定位器找到值为“MyDB”的
特性“name”时使用“ReleaseSQLServer”。
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
在以下示例中,"Replace" 转换将替换 Web.config 文件的
整个 <customErrors> 节。
请注意,由于在 <system.web> 节点下只有一个
customErrors 节,因此无需使用 "xdt:Locator" 属性。
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
有关如何配置 ASP.NET 应用程序的详细信息,请访问
https://go.microsoft.com/fwlink/?LinkId=301879
-->
<configuration>
<appSettings>
<add key="TenpayOptions_Merchant_0_MerchantId" value="填写商户号" />
<add key="TenpayOptions_Merchant_0_SecretV3" value="填写商户 V3 API 密钥" />
<add key="TenpayOptions_Merchant_0_CertificateSerialNumber" value="填写商户证书序列号" />
<add key="TenpayOptions_Merchant_0_CertificatePrivateKey" value="填写商户证书文件内容" />
<add key="TenpayOptions_Merchant_0_PlatformPublicKeyId" value="填写平台公钥 ID仅当基于平台公钥的认证方式时需要" />
<add key="TenpayOptions_Merchant_0_PlatformPublicKey" value="填写平台公钥内容(仅当基于平台公钥的认证方式时需要)" />
<add key="TenpayOptions_NotifyUrl" value="https://localhost:5001" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.8" />
<httpRuntime targetFramework="4.8" />
</system.web>
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" />
<bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Web.Infrastructure" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.2.9.0" newVersion="5.2.9.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.9.0" newVersion="5.2.9.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.9.0" newVersion="5.2.9.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.4.0.0" newVersion="6.4.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.5" newVersion="8.0.0.5" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
</compilers>
</system.codedom>
</configuration>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Antlr" version="3.5.0.2" targetFramework="net48" />
<package id="Autofac" version="6.4.0" targetFramework="net48" />
<package id="Autofac.Mvc5" version="6.1.0" targetFramework="net48" />
<package id="Autofac.WebApi2" version="6.1.1" targetFramework="net48" />
<package id="bootstrap" version="5.2.3" targetFramework="net48" />
<package id="Flurl" version="4.0.0" targetFramework="net48" />
<package id="Flurl.Http" version="4.0.2" targetFramework="net48" />
<package id="Hangfire.AspNet" version="0.3.0" targetFramework="net48" />
<package id="Hangfire.Autofac" version="2.7.0" targetFramework="net48" />
<package id="Hangfire.Core" version="1.8.0" targetFramework="net48" />
<package id="Hangfire.InMemory" version="1.0.0" targetFramework="net48" />
<package id="jQuery" version="3.7.0" targetFramework="net48" />
<package id="Microsoft.AspNet.Mvc" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.Mvc.zh-Hans" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.Razor" version="3.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.Razor.zh-Hans" version="3.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net48" />
<package id="Microsoft.AspNet.Web.Optimization.zh-Hans" version="1.1.3" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi.Client.zh-Hans" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi.Core.zh-Hans" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi.HelpPage" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi.WebHost.zh-Hans" version="5.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebPages" version="3.2.9" targetFramework="net48" />
<package id="Microsoft.AspNet.WebPages.zh-Hans" version="3.2.9" targetFramework="net48" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net48" />
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net48" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net48" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net48" />
<package id="Microsoft.Web.Infrastructure" version="2.0.0" targetFramework="net48" />
<package id="Modernizr" version="2.8.3" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
<package id="Owin" version="1.0" targetFramework="net48" />
<package id="SKIT.FlurlHttpClient.Common" version="3.1.1" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="4.7.1" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
<package id="System.Text.Encodings.Web" version="8.0.0" targetFramework="net48" />
<package id="System.Text.Json" version="8.0.5" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
<package id="WebGrease" version="1.6.0" targetFramework="net48" />
</packages>