# 多租户计费架构：SaaS 设计模式

> 构建 SaaS 多租户计费系统的设计模式，涵盖租户隔离、用量计量、事件驱动型计费，以及何时应自建或购买计费基础设施。
- **Author**: Ayush Agarwal
- **Published**: 2026-04-15
- **Category**: Architecture, SaaS Billing
- **URL**: https://dodopayments.com/blogs/zh/multi-tenant-billing-architecture

---

多租户计费架构归结为三种模式：集中式计费服务、事件驱动型流水线，或将计费委托给外部平台。选择哪一种，主要取决于租户数量、收入中有多少来自按用量计费，以及你是否有能力将计费作为独立的产品能力来配备专门团队。

多租户 SaaS 应用在客户之间共享基础设施，同时隔离各自的数据、配置和计费信息。计费层正是多租户变得棘手的地方：每个租户可能使用不同的套餐、不同的货币，拥有不同的使用模式、计费周期和税务管辖区。为单租户应用构建计费相对简单；但要让计费系统支持数千个拥有不同定价模型、支付方式和合规要求的租户，则是一项架构挑战，已经让许多 SaaS 公司陷入困境。

本指南将介绍多租户计费的设计模式、需要尽早做出的决策，以及何时适合自建系统、何时应使用现有基础设施。

## 计费架构的核心组件

```mermaid
flowchart TD
    A[产品目录] -->|"套餐、定价、功能"| B[订阅引擎]
    B -->|"租户分配"| C[租户计费状态]
    D[用量计量] -->|"事件"| E[用量聚合]
    E -->|"应计费金额"| F[发票生成]
    C -->|"套餐 + 用量"| F
    F -->|"扣款"| G[支付处理]
    G -->|"结果"| H[收入确认]
    G -->|"Webhook"| I[权益服务]
    I -->|"功能访问权限"| J[应用层]
```

### 产品目录

产品目录定义你销售的内容：套餐、层级、附加项、功能及其价格。在多租户系统中，产品目录必须支持：

- 多种[定价模型](https://dodopayments.com/blogs/subscription-pricing-models)：固定费率、[分层定价](https://dodopayments.com/blogs/tiered-pricing-model-guide)、[按席位计费](https://dodopayments.com/blogs/pay-per-seat-billing-b2b)、[按用量计费](https://dodopayments.com/blogs/usage-based-billing-saas)
- 支持[多币种](https://dodopayments.com/blogs/multi-currency-pricing-global-saas)的特定货币定价
- 套餐版本管理（让现有租户继续使用旧定价）
- 与套餐权益关联的功能开关

### 订阅引擎

管理每个租户订阅的生命周期：创建、升级、降级、取消、暂停和续订。主要挑战包括：

- **按比例计费**：租户在计费周期中途更换套餐时，准确计算预付额度和费用
- **试用管理**：跟踪试用期、转为付费订阅，并处理已过期的试用
- **计费周期对齐**：一些租户按月计费，另一些则按[年](https://dodopayments.com/blogs/annual-vs-monthly-billing-saas)计费
- **宽限期**：支付失败后，在发生[非自愿流失](https://dodopayments.com/blogs/involuntary-churn-failed-payments)前继续保留访问权限的时长

### 用量计量

对于[按用量计费](https://docs.dodopayments.com/features/usage-based-billing/introduction)，你需要跟踪每个租户消耗的资源。这需要：

- **事件接入**：以高吞吐量接收来自应用的用量事件
- **租户隔离**：每个事件都必须归属于正确的租户
- **聚合**：将原始事件汇总为可计费数量（API 调用次数、存储 GB、计算时长）
- **幂等性**：确保重复事件不会导致重复扣费

```javascript
// 示例：使用 Dodo Payments 接入用量事件
const result = await client.usageEvents.ingest({
  events: [
    {
      event_id: `api_call_${Date.now()}`,
      customer_id: "tenant_abc123",
      event_name: "api_call",
      timestamp: new Date().toISOString(),
      metadata: {
        endpoint: "/v1/generate",
        tokens_used: 1500,
      },
    },
  ],
});
```

### 发票生成

每个计费周期结束时，系统必须：

1. 获取租户的套餐和当前定价
2. 计算固定费用（订阅费）
3. 计算可变费用（用量、超额用量）
4. 应用折扣、预付额度和[按比例计费](https://dodopayments.com/blogs/billing-credits-pricing-cashflow)
5. 根据租户所在地计算税费
6. 生成包含明细项的发票
7. 发起收款

### 权益服务

权益服务根据租户的套餐控制功能访问权限。租户升级后，其权益会立即更新。支付失败时，权益会平稳降级。如果你的产品还提供移动客户端，那么该层还必须协调应用商店订阅状态与你自有系统中的状态；这正是我们在[RevenueCat 订阅基础设施替代方案](https://dodopayments.com/blogs/revenuecat-alternatives)对比文章中介绍的问题领域。

```javascript
// 检查租户是否有权访问某项功能
async function hasFeature(tenantId, featureName) {
  const subscription = await getSubscription(tenantId);
  const plan = await getPlan(subscription.planId);
  return plan.features.includes(featureName);
}
```

## 设计模式

### 模式 1：集中式计费服务

所有计费逻辑都位于一个服务中，应用中的所有服务都调用该服务。

**优点**：单一事实来源、计费规则一致、更易维护
**缺点**：存在单点故障，规模扩大后可能成为瓶颈

**最适合**：大多数租户数不超过约 1 万的 SaaS 公司。

### 模式 2：事件驱动型计费

应用服务发出事件（用户注册、API 被调用、使用存储空间）。计费服务消费这些事件，并异步处理计费。

```mermaid
flowchart LR
    A[应用服务] -->|"usage.recorded"| B[事件总线]
    C[身份验证服务] -->|"user.added"| B
    B -->|"消费"| D[计费服务]
    D -->|"处理"| E[发票 + 扣款]
    D -->|"发出"| F["payment.succeeded"]
    F --> G[权益服务]
```

**优点**：解耦、可扩展、能够应对流量峰值
**缺点**：最终一致性（操作与计费之间存在轻微延迟），调试更加复杂

**最适合**：包含按用量计费组件且事件量较大的 SaaS 公司。

### 模式 3：将计费作为外部服务

通过[API](https://docs.dodopayments.com/api-reference/introduction)将计费完全委托给外部平台。

**优点**：无需维护计费代码，由平台处理合规问题，内置[催收](https://dodopayments.com/blogs/dunning-management)和[收入回收](https://dodopayments.com/blogs/revenue-recovery-saas)
**缺点**：定制能力较弱，依赖外部提供商

**最适合**：希望专注于产品，而不是计费基础设施的团队。

[Dodo Payments](https://dodopayments.com)以[MoR（名义商户）](https://dodopayments.com/blogs/what-is-a-merchant-of-record)的形式提供这项服务，通过单一 API 处理订阅、按用量计费、开具发票、税务和支付。

## 计费中的租户隔离

计费数据隔离至关重要。一个让租户 A 为租户 B 的用量付费的漏洞，会彻底破坏用户信任。

### 数据隔离策略

- **行级隔离**：所有租户位于相同的数据库表中，通过 `tenant_id` 进行筛选。实现简单，但必须严格确保不会遗漏筛选条件。
- **模式级隔离**：每个租户拥有独立的数据库模式。隔离性更好，但跨租户查询更加困难。
- **数据库级隔离**：每个租户拥有独立的数据库。隔离性最高，但运维开销也最大。

对于计费而言，配合严格 `tenant_id` 约束的行级隔离是最常见的模式。添加数据库约束，防止跨租户数据泄露：

```sql
-- 每张计费表都包含带 NOT NULL 约束的 tenant_id
CREATE TABLE invoices (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL REFERENCES tenants(id),
  amount_cents BIGINT NOT NULL,
  currency VARCHAR(3) NOT NULL,
  status VARCHAR(20) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 为该表启用行级安全策略
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;

-- 行级安全策略
CREATE POLICY tenant_isolation ON invoices
  USING (tenant_id = current_setting('app.tenant_id')::UUID);
```

## 自建还是购买

| 因素       | 自建                                                                     | 使用外部服务（Dodo 等）                                         |
| ---------- | ------------------------------------------------------------------------ | --------------------------------------------------------------- |
| 上线时间   | 3-6 个月                                                                 | 1-2 周                                                          |
| 维护       | 持续的工程成本                                                           | 由提供商负责                                                    |
| 定制能力   | 无限                                                                     | 受 API 限制                                                     |
| 税务合规   | 由你负责                                                                 | [已包含](https://dodopayments.com/blogs/global-vat-gst-ai-saas) |
| PCI 合规   | [由你负责](https://dodopayments.com/blogs/pci-compliance-checklist-saas) | 已包含                                                          |
| 催收/回收  | 由你构建                                                                 | 已包含                                                          |
| 规模化成本 | 边际成本更低                                                             | 每笔交易成本更高                                                |
| 工程重点   | 被计费工作分散                                                           | 专注于产品                                                      |

> 最昂贵的计费系统，就是你在实现产品市场契合前自行构建的系统。随着定价模型不断演进，你至少会重建两次。在交易量足够大、每笔交易成本足以证明自建工程投入合理之前，请使用外部计费 API。
>
> - Ayush Agarwal，Dodo Payments 联合创始人兼 CPTO

### 适合自建的情况

- 年处理金额超过 1,000 万美元，且计费是核心差异化能力
- 外部提供商都不支持的高度定制化定价模型
- 监管要求必须在内部处理数据

### 适合购买的情况

- 尚未实现产品市场契合（定价还会变化）
- 工程师团队少于 20 人（无力组建计费团队）；此时可以先筛选[订阅计费平台](https://dodopayments.com/blogs/subscription-billing-platforms)
- 面向全球销售，此时仅税务合规一项就足以证明使用外部提供商的合理性，而且通常还需要面向 SaaS 的[MoR（名义商户）](https://dodopayments.com/blogs/merchant-of-record-for-saas)
- 具有复杂计量需求的[按用量计费](https://dodopayments.com/blogs/implement-usage-based-billing)（这是已经解决的问题，不要重复造轮子）

如需实施指导，请参阅我们关于[构建 AI SaaS 计费系统](https://dodopayments.com/blogs/build-billing-system-ai-saas)、[计费基础设施](https://dodopayments.com/blogs/billing-infrastructure-age-ai-agents)、[最佳计费 API](https://dodopayments.com/blogs/best-billing-apis-developers)和[支付架构](https://dodopayments.com/blogs/payments-architecture-saas)的指南。

## 常见问题

### 多租户计费最难的部分是什么？

按比例计费和套餐变更。当租户在计费周期第 15 天从每月 49 美元的套餐升级到每月 99 美元的套餐时，需要针对剩余的 49 美元周期按比例计算预付额度，并针对 99 美元周期按比例计算费用；如果还要同时处理不同货币和税务管辖区，计算正确金额会出乎意料地复杂。大多数计费漏洞都存在于按比例计费逻辑中。

### 如何为不同租户处理不同定价？

使用套餐版本管理和自定义定价覆盖。维护包含标准套餐的产品目录，同时允许针对企业交易或协商费率设置每个租户的价格覆盖值。应将覆盖值存储在租户的计费配置中，而不是修改产品目录。

### 我应该为计费事件使用消息队列吗？

是的，尤其是对于按用量计费。在应用与计费服务之间使用消息队列（Kafka、SQS、RabbitMQ），可以确保事件不会在流量峰值期间丢失，提供用于调试的重放能力，并将应用与计费处理延迟解耦。

### 如何测试多租户计费？

创建自动化测试，同时模拟拥有不同套餐、货币和用量模式的多个租户执行计费操作。测试各种边界情况：计费周期中途变更套餐、支付失败、货币转换，以及同一租户的并发用量事件。使用支付提供商的沙盒进行端到端测试。

### 何时应从外部计费服务迁移到内部系统？

当计费成本超过收入的 1-2%，并且你拥有构建和维护计费基础设施所需的工程资源时，可以考虑迁移（通常需要 2-3 名专职工程师）。对于年经常性收入低于 2,000 万美元的大多数 SaaS 公司而言，外部计费服务比投入工程资源自建系统更具成本效益。

## 总结

在基础设施层面，多租户计费已经是一个得到解决的问题。除非计费是你的核心产品，否则请使用现有平台，将工程资源集中在真正能让业务脱颖而出的方面。

如需了解能够处理订阅、用量计量、税务合规和全球支付的多租户计费基础设施，请访问 [Dodo Payments](https://dodopayments.com)，并查看[定价](https://dodopayments.com/pricing)。
---
- [More Architecture articles](https://dodopayments.com/blogs/category/architecture)
- [All articles](https://dodopayments.com/blogs)