WSO2 APIM 如何与 billing engine 集成并同步 usage plan
结论
“sample billing engine”通常是用于演示 WSO2 API Manager(APIM)货币化扩展点的参考实现。它不是面向 API Subscriber 的正式门户,也不适合直接用于生产环境。页面要求登录很正常,因为计费计划、价格和账单配置都属于受保护的管理操作。
Subscriber 应在 APIM Developer Portal 中选择 subscription policy。旧版文档可能将其称为 throttling tier 或 usage plan,而不是进入 billing engine 手工定义计划。
billing engine 仍然需要配置 billing plan,是因为 APIM policy 和计费系统中的 plan 是两类不同的对象:
- APIM policy 负责请求配额、限流和订阅权限。
- Billing plan 负责价格、币种、计费周期、税费、折扣、试用期和账单。
- 两者需要建立映射,但 Subscriber 不应重复配置。
在生产环境中,数据通常由自定义 monetization connector、事件处理服务或定时对账任务同步。只部署 sample billing engine,一般无法获得完整的生产级同步能力。
正确的业务流程
整个集成可以拆成三条独立的数据链路:
-
计划目录同步
Publisher 在 APIM 中创建或更新 subscription policy 后,将对应的计费产品和价格同步到 billing engine。
-
订阅生命周期同步
Subscriber 在 Developer Portal 中选择 policy 并订阅 API。集成服务根据这次操作,在 billing engine 中创建、变更或取消 billing subscription。
-
实际用量同步
APIM Gateway 或 Analytics 汇总实际调用量,并按计费周期发送给 billing engine。billing engine 再根据价格模型生成账单。
典型流程如下:
Publisher
│
├─ 创建 APIM subscription policy
│
└─ 创建或映射 billing plan
│
Subscriber │
│ │
└─ 在 Devportal 订阅 API
│
▼
APIM Subscription
│
▼
Integration Service
│
├─ customer mapping
├─ plan mapping
└─ create/update/cancel billing subscription
│
▼
Billing Engine
Gateway / Analytics
│
└─ aggregated usage ──► Billing Engine
为什么需要两套计划
假设 APIM 中有以下 policy:
Policy name: Gold
Quota: 100000 requests/month
Burst limit: 100 requests/second
计费系统中可能需要保存这些信息:
External plan ID: price_gold_cny_monthly
Currency: CNY
Billing period: monthly
Base fee: 999.00
Included usage: 100000 requests
Overage price: 0.01 CNY/request
Tax behavior: exclusive
APIM 执行 100000 requests/month 之类的访问规则,但它本身并不包含税率、付款方式或开票规则。billing engine 负责收费,却通常无法代替 Gateway 执行限流。
因此,两个系统中同名的“Gold”只是业务上的对应关系,并不是同一个数据对象。生产集成必须保存稳定的 ID 映射,不能只按显示名称关联。
例如:
CREATE TABLE billing_plan_mapping (
apim_policy_id VARCHAR(128) NOT NULL,
apim_policy_name VARCHAR(128) NOT NULL,
billing_plan_id VARCHAR(128) NOT NULL,
currency VARCHAR(3) NOT NULL,
mapping_version INTEGER NOT NULL,
updated_at TIMESTAMP NOT NULL,
PRIMARY KEY (apim_policy_id, currency)
);
如何实现自动同步
1. 先确定唯一标识
至少需要维护以下映射:
APIM tenant/domain -> Billing account
APIM application ID -> Billing customer ID
APIM subscription ID -> Billing subscription ID
APIM subscription policy -> Billing plan/price ID
API or API product ID -> Billing product ID
不要使用用户名、API 名称或 policy 显示名称作为唯一键。这些字段可能被修改,也可能在不同 tenant 中重复。
2. 同步计费计划
Publisher 发布可货币化的 policy 后,管理服务在 billing engine 中创建对应的 plan,并保存返回的外部 ID。
下面是示意性的内部接口,并不表示某个 WSO2 版本提供了完全相同的 URL:
POST /internal/monetization/plans
Content-Type: application/json
Authorization: Bearer <service-token>
Idempotency-Key: apim-policy-8f27-v3
{
"apimPolicyId": "8f27",
"name": "Gold",
"quota": {
"unit": "request",
"limit": 100000,
"period": "month"
},
"price": {
"currency": "CNY",
"baseAmount": "999.00",
"includedUnits": 100000,
"overageUnitAmount": "0.01"
}
}
计费系统返回:
{
"billingPlanId": "price_gold_cny_monthly",
"status": "active"
}
能否从 APIM policy 自动推导价格,取决于具体的业务模型。限流额度可以同步,价格、税费和折扣通常仍由财务或运营系统维护。
3. 同步 Subscriber 的选择
APIM 成功创建订阅后,集成服务读取 subscription ID、application ID、API ID 和 policy ID,再通过映射表找到对应的 billing plan。
示意事件如下:
{
"eventType": "SUBSCRIPTION_CREATED",
"eventId": "evt-01JABC123",
"tenant": "carbon.super",
"subscriptionId": "sub-72d9",
"applicationId": "app-1842",
"apiId": "api-f8e1",
"policyId": "8f27",
"subscriberId": "user-913"
}
处理逻辑可以写成:
public void handleSubscriptionCreated(SubscriptionEvent event) {
if (eventRepository.wasProcessed(event.eventId())) {
return;
}
CustomerMapping customer =
customerMappingRepository.findByApplicationId(event.applicationId());
PlanMapping plan =
planMappingRepository.findByPolicyId(event.policyId());
BillingSubscription result = billingClient.createSubscription(
customer.billingCustomerId(),
plan.billingPlanId(),
event.subscriptionId()
);
subscriptionMappingRepository.save(
event.subscriptionId(),
result.id()
);
eventRepository.markProcessed(event.eventId());
}
这部分必须支持幂等处理。APIM 或消息系统可能会重试事件。如果没有 eventId、subscriptionId 或 idempotency key,billing engine 中可能出现重复订阅。
4. 处理变更和取消
同步范围不能只有创建操作,至少要覆盖:
SUBSCRIPTION_CREATED
SUBSCRIPTION_POLICY_CHANGED
SUBSCRIPTION_BLOCKED
SUBSCRIPTION_UNBLOCKED
SUBSCRIPTION_DELETED
APPLICATION_DELETED
具体的事件名称和获取方式取决于 WSO2 APIM 版本。部分版本可以通过扩展点或事件机制接入。如果目标版本没有合适的事件,也可以通过 APIM REST API 定时拉取订阅状态,进行增量对账。
变更 policy 时,还要明确以下计费规则:
- 升级立即生效,还是到下个账期生效;
- 是否按比例计费;
- 降级后如何处理已经使用的额度;
- APIM 更新成功但 billing engine 更新失败时,是否暂时阻止调用。
这些都属于业务规则,不能仅靠字段同步解决。
实际用量如何进入 billing engine
“订阅了哪个 plan”和“实际用了多少”是两件不同的事。即使订阅已经同步,按量计费仍然需要单独上报 usage。
比较稳妥的做法是先在 APIM 侧聚合,再发送计费数据:
{
"subscriptionId": "sub-72d9",
"billingPeriod": "2026-08",
"metric": "api_request",
"quantity": 126430,
"from": "2026-08-01T00:00:00Z",
"to": "2026-09-01T00:00:00Z"
}
不要默认将每一次 Gateway 请求实时发送给 billing engine。这种方式容易造成性能瓶颈、重复计费和较高的网络开销。更稳妥的方案是按小时或按天聚合,并使用可以重复计算的复合键,例如:
subscriptionId + metric + periodStart + periodEnd
计费系统应支持幂等覆盖,或者拒绝重复记录。
还要注意,APIM 的限流计数不一定等于最终的可计费用量。失败请求、缓存命中、重试、内部调用,以及被 Gateway 拒绝的请求是否收费,都需要提前定义。
关于登录和找不到 URL
示例中的管理页面通常需要认证,因为这些页面涉及价格和账单配置。Subscriber 不应直接访问此类管理 URL。
如果文档中的 URL 在本地环境中不存在,常见原因有:
- 文档对应的 WSO2 APIM 版本与本地版本不同;
- sample billing engine 是单独部署的示例组件,没有包含在 APIM 默认部署中;
- 示例应用的 context path、端口或反向代理路径发生了变化;
- 目标版本已经改用 REST API、扩展实现或其他货币化方案;
- 当前用户缺少相应的管理角色或 scope。
不要根据旧文章中的路径猜测 URL。应以当前 APIM 版本附带的 OpenAPI/Swagger 文档、产品文档和部署配置为准。不同版本使用的 Publisher API、Devportal API、认证 scope 和扩展接口可能并不相同。
生产环境建议
sample billing engine 适合用于验证概念。正式环境更适合增加一层独立的 monetization integration service,由它负责:
- 调用 APIM API 获取 policy 和 subscription;
- 调用 billing provider API;
- 保存 APIM ID 与外部计费 ID 的映射;
- 处理认证、重试、幂等和死信;
- 汇总并上报 usage;
- 执行周期性全量对账;
- 记录审计日志和同步状态。
同步状态至少可以包含:
PENDING
ACTIVE
UPDATE_PENDING
CANCEL_PENDING
FAILED
SUSPENDED
关键操作可以采用“事件同步 + 定时对账”的组合。事件同步用于降低延迟,定时任务则用于修复漏发事件、临时网络故障和人工修改造成的数据偏差。
billing engine 创建失败后,不能让这一状态处于不可见状态。如果 APIM 订阅已经生效,而计费订阅创建失败,系统可能允许用户免费调用。生产实现应根据业务要求回滚 APIM 订阅、暂时阻止订阅,或者将其转入一个明确且可监控的待处理状态。
备注:内容仅供参考。
