Skip to content
Bitzsoft.Integrationsbitzsoft.integrations

Guide

多厂商路由消费指南

同时接入多家厂商时如何用聚合包、Resolver 按 Provider ID 路由、Catalog 发现可用 Provider,含支付宝+微信支付完整示例。

Last updated

当你需要同时接入两家以上厂商,或者要按租户、地区、灰度策略动态切换,就走「多厂商路由」路线。核心区别:不再直接注入能力接口,而是注入 IIntegrationProviderResolver<T>,按稳定的 Provider ID 解析出具体实现。

这篇指南用 Payment 域(支付宝 + 微信支付) 做完整可运行示例,模式适用于任何多厂商场景。

单厂商 vs 多厂商

多厂商路由

注入
IIntegrationProviderResolver

resolver.GetRequired(providerId)

Alipay 实现

WeChatPay 实现

单厂商

直接注入
IPaymentProvider

业务代码(CreateOrderAsync、处理 Result)两种模式完全一样,区别只在「怎么拿到那个 Provider」。

多厂商路由五步

① 安装聚合包 .All

② 配置多个厂商节

③ 注册
AddBitzsoft{Domain}All()

④ 注入 Resolver

⑤ 按 Provider ID 路由

调用 + 处理 Result

1. 安装聚合包

装一个 .All 包,它会通过 ProjectReference 把该域全部厂商实现拉进来。

Terminal window
dotnet new web -n MyMultiPay
cd MyMultiPay
# ① 装聚合包,一次性带齐支付宝 + 微信 + Stripe
dotnet add package Bitzsoft.Integrations.Payment.All

2. 配置多个厂商节

各厂商独立配置节,节名就是 Provider ID(如 AlipayWeChatPay)。聚合注册方法只注册配置中存在的厂商——没配的厂商不会被注册,也不会报错。

{
"Payment": {
"Alipay": {
"AppId": "2021000...",
"AppPrivateKey": "MIIEvQIBADANB...",
"AlipayPublicKey": "MIIBIjANBg...",
"NotifyUrl": "https://your-domain.com/callback/alipay"
},
"WeChatPay": {
"AppId": "wx...",
"MchId": "1900...",
"MchSerialNo": "...",
"APIv3Key": "...",
"PrivateKey": "-----BEGIN PRIVATE KEY-----...",
"WeChatPayPublicKey": "-----BEGIN PUBLIC KEY-----..."
}
}
}

生产环境敏感字段用环境变量覆盖,例如 Payment__Alipay__AppPrivateKey。键名用双下划线分隔层级。

3. 注册聚合服务

调用 AddBitzsoft{域}All(),它会扫描配置节,只注册存在的厂商。

Program.cs
using Bitzsoft.Integrations.Payment;
using Bitzsoft.Integrations.Payment.Models;
var builder = WebApplication.CreateBuilder(args);
// ① 一行注册全部——按配置节存在性自动注册
builder.Services.AddBitzsoftPaymentAll(builder.Configuration);
var app = builder.Build();

注册内部按节存在性逐个调用厂商注册方法:

// AddBitzsoftPaymentAll 内部逻辑(已实现,无需你写)
var section = configuration.GetSection("Payment");
if (section.GetSection("Alipay").Exists())
services.AddBitzsoftAlipayPayment(configuration, "Payment:Alipay");
if (section.GetSection("WeChatPay").Exists())
services.AddBitzsoftWeChatPayPayment(configuration, "Payment:WeChatPay");
if (section.GetSection("Stripe").Exists())
services.AddBitzsoftStripePayment(configuration, "Payment:Stripe");

4. 注入 Resolver 按 ID 路由

多厂商场景下,必须用 IIntegrationProviderResolver<T> 按稳定的 Provider ID 解析,不要直接注入能力接口。

// Program.cs —— 按 URL 里的 provider 参数路由
app.MapPost("/api/pay/{provider}", async (
string provider,
IIntegrationProviderResolver<IPaymentProvider> providers, // ① 注入 Resolver
PaymentOrderRequest request) =>
{
// ② 按稳定 ID 解析;provider 传 "Alipay" 或 "WeChatPay"(大小写不敏感)
IPaymentProvider payment;
try
{
payment = providers.GetRequired(provider);
}
catch (IntegrationProviderResolutionException)
{
return Results.NotFound(new { error = $"未配置的支付厂商: {provider}" });
}
// ③ 拿到 Provider 后,业务代码和单厂商完全一样
var result = await payment.CreateOrderAsync(request);
return result.IsSuccess
? Results.Ok(result.Data)
: Results.BadRequest(new { result.ErrorCode, result.ErrorMessage });
});

为什么不能直接注入 IPaymentProvider

注册多个 Provider

TryAdd 别名注册
registerLegacyAlias=true

多个实现争同一个 IPaymentProvider 槽位

TryAdd 只保留第一个

直接注入拿到的是第一个,不是你想要的

必须用 Resolver 按 ID 精确解析

多厂商时直接注入 IPaymentProvider 拿到的是注册顺序的第一个,无法指定。Resolver 通过 ProviderKey(格式 {Domain}:{ProviderId})去重存储,按 ID 精确返回。

5. 用 Catalog 发现可用 Provider

IIntegrationProviderCatalog 枚举当前容器里所有已注册的 Provider,适合做动态展示、健康检查、配置校验。

// 列出当前已注册的全部支付厂商
app.MapGet("/api/pay/providers", (IIntegrationProviderCatalog catalog) =>
{
return catalog.Providers.Select(d => new
{
id = d.ProviderId, // "Alipay" / "WeChatPay"
name = d.DisplayName, // "支付宝" / "微信支付"
domain = d.Domain, // "Payment"
stability = d.Stability, // Preview / Stable
capabilities = d.CapabilityTypes.Select(t => t.Name),
regions = d.Regions,
});
});

也可以按精确 key 查询某个 Provider 是否存在:

// 校验目标 Provider 是否可用
if (catalog.TryGet("Payment", "Alipay", out var descriptor))
{
Console.WriteLine($"可用: {descriptor.DisplayName},稳定性 {descriptor.Stability}");
}
API用途
IIntegrationProviderCatalog.Providers枚举全部已注册 Provider 的 Descriptor
catalog.TryGet(domain, providerId, out var)按域 + ID 查询单个 Descriptor
resolver.TryResolve(providerId, out var)尝试解析能力,不抛异常
resolver.GetRequired(providerId)解析能力,不存在抛 IntegrationProviderResolutionException
resolver.Providers枚举全部能力实例

完整可运行示例

下面这个最小应用同时支持支付宝和微信支付,按 URL 选择。

Terminal window
dotnet new web -n MyMultiPay && cd MyMultiPay
dotnet add package Bitzsoft.Integrations.Payment.All
Program.cs
using Bitzsoft.Integrations.Payment;
using Bitzsoft.Integrations.Payment.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddBitzsoftPaymentAll(builder.Configuration); // ① 注册全部
var app = builder.Build();
// ② 路由:用支付宝下单
app.MapPost("/api/pay/alipay", async (
IIntegrationProviderResolver<IPaymentProvider> providers,
PaymentOrderRequest req) =>
{
var payment = providers.GetRequired(PaymentProviderCode.Alipay); // ③ 用常量而非硬编码字符串
var result = await payment.CreateOrderAsync(req);
return result.IsSuccess ? Results.Ok(result.Data) : Results.BadRequest(result.ErrorMessage);
});
// ④ 路由:用微信支付下单
app.MapPost("/api/pay/wechat", async (
IIntegrationProviderResolver<IPaymentProvider> providers,
PaymentOrderRequest req) =>
{
var payment = providers.GetRequired(PaymentProviderCode.WeChatPay);
var result = await payment.CreateOrderAsync(req);
return result.IsSuccess ? Results.Ok(result.Data) : Results.BadRequest(result.ErrorMessage);
});
// ⑤ 动态路由:URL 里带 provider
app.MapPost("/api/pay/{provider}", async (
string provider,
IIntegrationProviderResolver<IPaymentProvider> providers,
PaymentOrderRequest req) =>
{
if (!providers.TryResolve(provider, out var payment))
return Results.NotFound($"未配置的厂商: {provider}");
var result = await payment.CreateOrderAsync(req);
return result.IsSuccess ? Results.Ok(result.Data) : Results.BadRequest(result.ErrorMessage);
});
// ⑥ 列出可用厂商
app.MapGet("/api/pay/providers", (IIntegrationProviderCatalog catalog) =>
catalog.Providers.Select(d => new { d.ProviderId, d.DisplayName, d.Stability }));
app.Run();

路由策略示例

Provider ID 是稳定配置值,适合做各种路由策略:

// 按租户配置路由
public sealed class TenantPaymentRouter(
IIntegrationProviderResolver<IPaymentProvider> providers,
ITenantStore tenants)
{
public async Task<PaymentResult<PaymentOrderResult>> PayAsync(
string tenantId, PaymentOrderRequest req)
{
var providerId = await tenants.GetPaymentProviderAsync(tenantId); // "Alipay" 或 "WeChatPay"
var payment = providers.GetRequired(providerId); // ① 按租户配置路由
return await payment.CreateOrderAsync(req);
}
}
// 按地区路由:大陆用支付宝/微信,海外用 Stripe
public sealed class RegionalPaymentRouter(
IIntegrationProviderResolver<IPaymentProvider> providers)
{
public IPaymentProvider Resolve(string region) => region switch
{
"CN" => providers.GetRequired(PaymentProviderCode.Alipay), // ② 大陆默认支付宝
"TW" or "HK" => providers.GetRequired(PaymentProviderCode.WeChatPay),
_ => providers.GetRequired(PaymentProviderCode.Stripe), // ③ 海外用 Stripe
};
}

常见问题

现象原因处理
IntegrationProviderResolutionExceptionProvider ID 不存在或拼写错误检查 provider 参数大小写,确认配置节已写入
注入 IPaymentProvider 拿到错误的厂商多厂商时直接注入只拿第一个改用 IIntegrationProviderResolver<T> 按解析
某厂商没被注册配置节缺失确认 Payment:{厂商} 节存在且非空
IEnumerable<T> 数量对不上注册顺序 + TryAdd 丢弃后续实现不要用 IEnumerable,用 Resolver

何时回到单厂商模式

如果你的应用确定只用一家(比如内部系统只用支付宝,没有多租户、多地区需求),回到单厂商消费更简单——少装一个聚合包、少注入一层 Resolver。两种模式的业务代码可平滑互转。

下一步

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%