mcpField
P
polar-billing

polar-billing

github.com/fcakyon开发1.2k46 次安装今天更新
代码仓库

关于 polar-billing

polar-billing 是面向 Polar 计费系统开发者的参考技能,梳理计费实体、服务及结账、订阅、支付、退款和权益发放流程。

主要功能

  1. 01计费实体与关系指南
  2. 02结账和订阅生命周期说明
  3. 03支付、退款及 Stripe 流程
  4. 04后台任务与服务索引
  5. 05权益和额度发放概览

适用场景

  1. 01追踪结账如何形成订单和支付
  2. 02了解订阅续费、按比例计费和重试
  3. 03查找计费改动对应的服务或后台任务

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/fcakyon/claude-codex-settings --skill polar-billing

安装后在 Claude Code 中输入/polar-billing确认已加载。

详细介绍

Polar 计费系统

Polar 计费基础设施综合指南,涵盖实体、流程、Stripe 集成和权益发放。

快速参考

Checkout → Payment → Order → Transaction → Benefits
                         ↓
                   Subscription (if recurring)
                         ↓
                   Subscription Cycle → Order → ...

目录

  1. 核心实体
  2. 实体关系
  3. 主要服务
  4. Dramatiq 后台任务
  5. Stripe 集成
  6. 订阅生命周期
  7. 按比例计费系统
  8. 权益与额度
  9. 催款与付款重试
  10. 交易账本
  11. 关键文件位置

1. 核心实体

结账

文件: server/polar/models/checkout.py

确认订单前的购物车/付款会话。

字段类型说明
statusCheckoutStatusopen、expired、confirmed、succeeded、failed
payment_processorPaymentProcessorstripe、manual
client_secretstr前端使用的唯一标识符
amount、currencyint、str以分为单位的价格
tax_amount、discount_amountint计算得出的金额
allow_trial、trial_endbool、datetime试用配置
seatsint用于按席位计费的产品

关系: organization、customer、product、product_price、discount、subscription(用于升级)


结账链接

文件: server/polar/models/checkout_link.py

访问时会创建结账会话的持久 URL。

字段类型说明
client_secretstrURL 的唯一标识符
seatsint | None按席位定价的预设席位数
discount_idUUID | None要应用的预设折扣
trial_interval、trial_interval_countTrial config覆盖产品的试用设置

关系: organization、products、discount


订单

文件: server/polar/models/order.py

表示一笔计费事件(一次性购买或订阅周期)。

字段类型说明
statusOrderStatuspending、paid、refunded、partially_refunded
billing_reasonOrderBillingReasonpurchase、subscription_create、subscription_cycle、subscription_update
subtotal_amountint折扣/税费前的金额
discount_amountint已应用的折扣
tax_amountint已收取的税款
applied_balance_amountint已使用的账户余额
platform_fee_amountintPolar 的费用
refunded_amountint已退款金额
next_payment_attempt_atdatetime催款重试时间

计算属性:

  • net_amount = 小计 - 折扣
  • total_amount = 净额 + 税费
  • due_amount = max(0, 总额 + 已使用余额)
  • payout_amount = 净额 - 平台费用 - 已退款金额

订阅

文件: server/polar/models/subscription.py

周期性计费关系。

字段类型说明
statusSubscriptionStatusincomplete、trialing、active、past_due、canceled、unpaid
amount、currencyint、str订阅总价
net_amountint净额(总额减去含税税额;若税费不含在价格中,则等于总额)
tax_behaviorTaxBehavior | None含税、不含税或 null(创建时设置)
recurring_intervalIntervalmonth、year
current_period_start/enddatetime计费周期
trial_start/enddatetime试用期
cancel_at_period_endbool计划取消
canceled_at、ended_atdatetime生命周期时间戳
past_due_atdatetime付款失败时间
seatsint用于按席位定价

关系: organization、customer、product、payment_method、discount、meters、grants(权益)

交易

文件: server/polar/models/transaction.py

系统中的所有资金流转。

字段类型描述
typeTransactionTypepayment、processor_fee、refund、dispute、balance、payout
processorProcessorstripe、manual
amount、currencyint、str交易金额
tax_amountint税额部分

自引用关系: payment_transaction、balance_transactions、incurred_transactions


付款

文件: server/polar/models/payment.py

单笔付款交易。

字段类型描述
statusPaymentStatuspending、succeeded、failed
processor_idstrStripe 交易 ID
methodstrcard、bank_transfer 等
triggerPaymentTrigger | None付款触发原因:purchase、subscription_cycle、retry_dunning、retry_customer、retry_payment_method_update、retry_admin
decline_reasonstr付款失败原因
risk_level、risk_scorestr、int欺诈评估

退款

文件: server/polar/models/refund.py

字段类型描述
statusRefundStatuspending、succeeded、failed、canceled
reasonRefundReasonduplicate、fraudulent、customer_request 等
amount、tax_amountint退款金额
revoke_benefitsbool是否撤销客户权益

客户

文件: server/polar/models/customer.py

字段类型描述
email、namestr联系信息
billing_namestr发票使用的姓名(默认使用 name)
stripe_customer_idstrStripe 关联 ID
billing_addressAddress保存的地址
tax_idstr用于税务合规

产品与产品价格

文件: server/polar/models/product.py、server/polar/models/product_price.py

产品价格类型描述
ProductPriceFixed固定金额(免费时将 price_amount=0)
ProductPriceCustom商家在结账时设置价格
ProductPriceMeteredUnit按用量付费
ProductPriceSeatUnit按席位计费,可设分级价格

账单条目

文件: server/polar/models/billing_entry.py

账单计算的审计日志。

字段类型描述
typeBillingEntryTypecycle、proration、metered、seats_increase、seats_decrease
directionDirectiondebit、credit
amountint条目金额

2. 实体关系

Organization
├── Product
│   ├── ProductPrice (multiple per product)
│   └── ProductBenefit → Benefit
├── Customer
│   ├── Subscription → Organization, Product, Discount
│   │   ├── SubscriptionProductPrice
│   │   ├── SubscriptionMeter
│   │   └── BenefitGrant
│   ├── Order → Product, Subscription
│   │   └── OrderItem
│   ├── PaymentMethod
│   └── Wallet
├── Checkout → Customer, Product
├── Discount
│   └── DiscountRedemption
└── Account (for payouts)
    └── Payout → Transaction

Transaction (ledger)
├── payment → Order, Customer
├── refund → Refund, Order
├── dispute → Dispute, Order
├── processor_fee → parent payment
└── payout → Account

3. 主要服务

SubscriptionService

文件: server/polar/subscription/service.py

订阅核心操作:

# Creation
create_or_update_from_checkout(checkout, payment_method) → (Subscription, created)

# Updates
update_product(subscription, product_id, proration_behavior, discount=None)
update_seats(subscription, seats, proration_behavior)
update_discount(subscription, discount)  # discount: UUID | Literal["unset"]
update_trial(subscription, trial_end)
update_metadata(subscription, metadata)

# Lifecycle
cycle(subscription)  # Period renewal
cancel(subscription)  # At period end
revoke(subscription)  # Immediately
uncancel(subscription)

# Benefits
enqueue_benefits_grants(task="grant"|"revoke", customer, product)
OrderService

文件: server/polar/order/service.py

create_from_checkout(checkout)  # One-time purchase
create_subscription_order(subscription, billing_reason)  # Recurring
trigger_payment(order)  # Charge customer
create_order_balance(order)  # Ledger entries
CheckoutService

文件: server/polar/checkout/service.py

create(product, customer_data, discount_code)
confirm(checkout)  # Lock checkout for payment
handle_stripe_success(checkout, charge)
handle_free_success(checkout)  # No payment needed
PaymentService

文件: server/polar/payment/service.py

upsert_from_stripe_charge(charge, checkout, order)
handle_success(payment)  # Complete order
handle_failure(payment)  # Update order status
RefundService

文件: server/polar/refund/service.py

create(order, amount, reason, revoke_benefits)
upsert_from_stripe(stripe_refund)
# Also enqueues chargeback prevention notice for dispute_prevention refunds
BenefitGrantService

文件: server/polar/benefit/grant/service.py

enqueue_benefits_grants(task, customer, product, order=None, subscription=None)
grant_benefit(customer, benefit)
revoke_benefit(customer, benefit)

4. Dramatiq 后台任务

订阅任务

文件: server/polar/subscription/tasks.py

任务触发条件操作
subscription.cycle调度器在周期结束时触发续订并创建订单
subscription.update_product_benefits_grants产品权益变更更新所有授权
subscription.cancel_customer客户删除取消所有可计费订阅(trialing、active、past_due)
订单任务

文件: server/polar/order/tasks.py

任务触发条件操作
order.create_subscription_order订阅周期创建账单订单
order.trigger_payment订单就绪扣款
order.balance付款成功创建账簿条目
order.invoice订单创建生成 PDF 发票
order.process_dunning每小时 cron查找需要重试的订单
order.process_dunning_order单笔重试重试单笔付款
Stripe Webhook 任务

文件: server/polar/integrations/stripe/tasks.py

任务Stripe 事件操作
charge.succeeded付款完成创建订单、发放权益
charge.failed付款失败将订单标记为失败
charge.updated扣款结算创建账簿交易
refund.created/updated退款已处理更新退款记录
charge.dispute.created拒付创建争议、撤销权益
payout.paid付款完成更新打款状态
权益任务

文件: server/polar/benefit/tasks.py

任务触发条件操作
benefit.enqueue_benefits_grants订单/订阅将单项权益发放加入队列
benefit.grant单项权益开通访问权限(GitHub、Discord 等)
benefit.revoke取消/退款移除访问权限
benefit.cycle订阅续订重置积分并结转余额
结账任务

文件: server/polar/checkout/tasks.py

任务触发条件操作
checkout.handle_free_success免费产品无需付款即可完成
checkout.expire_open_checkouts每 15 分钟将已过期的结账标记为过期
打款任务

文件: server/polar/payout/tasks.py

任务触发条件操作
payout.trigger_stripe_payouts每日 00:15 UTC发起待处理的打款
payout.created打款已创建事件钩子(暂缓的打款也会触发)
payout.transferpayout.created 之后Stripe 转账(暂缓时跳过)
payout.release_held_payouts组织已获批准将暂缓打款改为待处理,并将转账加入队列
payout.cancel_account_payouts组织被拒绝/封锁/停止合作取消暂缓和待处理的打款
payout.cancel_held_payouts打款账户更换仅取消旧账户上的暂缓打款
退款任务

文件: server/polar/refund/tasks.py

任务触发条件操作
refund.send_chargeback_prevention_notice创建争议预防退款向组织所有者/管理员发送退款邮件

5. Stripe 集成

Webhook 端点

文件: server/polar/integrations/stripe/endpoints.py

  • /v1/integrations/stripe/webhook - 直接 Webhook
  • /v1/integrations/stripe/webhook-connect - Connect 账户 Webhook
已实现的 Webhook

付款流程:

  • payment_intent.succeeded - 付款完成
  • payment_intent.payment_failed - 付款失败
  • setup_intent.succeeded - 已保存银行卡
  • charge.pending/failed/succeeded/updated - 扣款生命周期

退款:

  • refund.created/updated/failed

争议:

  • charge.dispute.created/updated/closed

Connect:

  • account.updated - 账户信息已更改
  • payout.updated/paid - 打款生命周期
Webhook 处理流程
Stripe POST → Verify signature → ExternalEvent.enqueue()
                                        ↓
                               Store in external_events table
                                        ↓
                               Enqueue Dramatiq task
                                        ↓
                               Worker processes async
                                        ↓
                               Mark handled_at on success
StripeService

文件: server/polar/integrations/stripe/service.py

主要方法:

  • create_payment_intent()、create_setup_intent()
  • create_refund()、get_refund()
  • create_tax_calculation()、create_tax_transaction()
  • transfer()、create_payout()

6. 订阅生命周期

创建流程
1. Checkout created (status=open)
2. Customer completes payment
3. Stripe charge.succeeded webhook
4. payment.handle_success() called
5. checkout_service.handle_stripe_success()
6. subscription_service.create_or_update_from_checkout()
   - Creates Subscription (status=active or trialing)
   - Sets billing period
   - Applies discount
   - Resets meters
7. Enqueue benefit grants
8. Send confirmation email
周期流程(续订)
1. APScheduler triggers at period end
2. subscription.cycle task runs
3. subscription_service.cycle()
   - Check cancel_at_period_end
   - If true: set status=canceled, revoke benefits
   - If false: advance period dates, check discount expiry
4. Create billing entry (type=cycle)
5. Enqueue order.create_subscription_order
6. Order created with billing_reason=subscription_cycle
7. Enqueue order.trigger_payment
8. Stripe charges payment method
9. charge.succeeded → ledger entries → benefits renewed
取消流程

在周期结束时:

subscription_service.cancel(subscription)
# Sets cancel_at_period_end=True, ends_at=current_period_end
# Benefits remain until period ends
# On next cycle: status=canceled, benefits revoked

立即取消:

subscription_service.revoke(subscription)
# Sets status=canceled, ended_at=now
# Benefits revoked immediately
# Seats canceled if seat-based
试用流程
1. Checkout with trial_end set
2. Subscription created with status=trialing
3. No payment during trial
4. At trial_end, cycle task runs
5. Status transitions to active
6. Order created with billing_reason=subscription_cycle_after_trial
7. First payment charged

7. 按比例计费系统

发生按比例计费的情况
  1. 产品变更 - 升级或降级到不同档位
  2. 席位变更 - 添加或移除席位
  3. 计费周期变更 - 从月付改为年付
按比例计费的计算
# Calculate time remaining in period
pct_remaining = (period_end - now) / (period_end - period_start)

# Old product credit (what they paid but won't use)
old_credit = old_price * old_pct_remaining

# New product debit (what they owe for remainder)
new_debit = new_price * new_pct_remaining

# Net proration
net = new_debit - old_credit
按比例计费的行为
行为操作
prorate加入下一张发票
invoice立即创建订单
按比例计费的 BillingEntry
# Credit entry (old product)
BillingEntry(
    type=BillingEntryType.proration,
    direction=BillingEntryDirection.credit,
    amount=prorated_old_amount
)

# Debit entry (new product)
BillingEntry(
    type=BillingEntryType.proration,
    direction=BillingEntryDirection.debit,
    amount=prorated_new_amount
)
席位按比例计费
# Adding 2 seats at $10/seat with 50% time remaining
delta_amount = 2 * $10 * 0.5 = $10

BillingEntry(
    type=BillingEntryType.subscription_seats_increase,
    direction=BillingEntryDirection.debit,
    amount=1000  # cents
)

8. 权益与额度

权益类型
类型说明授予操作
meter_credit用量额度创建 meter_credited 事件
github_repository仓库访问权限添加到 GitHub 团队
discord服务端角色分配 Discord 角色
license_keys许可证分发生成密钥
downloadables文件访问权限授予下载权限
slack_shared_channelSlack Connect 频道创建共享频道或邀请加入
feature_flag功能开关(仅限 API)无 — 商家通过 API 读取
custom客户可见的备注无 — 在客户门户中显示
权益授予流程
1. Order/Subscription created
2. enqueue_benefits_grants(task="grant")
3. For each benefit in product:
   - Skip if already granted
   - Enqueue benefit.grant task
4. benefit.grant task:
   - Get/create BenefitGrant record
   - Call strategy.grant() (type-specific)
   - Set granted_at
   - Store properties
   - Send webhook
权益撤销流程
1. Subscription canceled or order refunded
2. enqueue_benefits_grants(task="revoke")
3. For each granted benefit:
   - Enqueue benefit.revoke task
4. benefit.revoke task:
   - Call strategy.revoke() (type-specific)
   - Set revoked_at
   - Send webhook
计量额度

授予:

# Create event with units
Event(type="meter_credited", units=100)
# Update CustomerMeter

周期处理(续订):

# Calculate rollover
rollover = min(remaining_units, rollover_limit)
# Reset meter
Event(type="meter_reset")
# Credit new period + rollover
Event(type="meter_credited", units=base_units + rollover)

撤销:

# Negative credit event
Event(type="meter_credited", units=-remaining_units)
宽限期

组织可以配置 benefit_revocation_grace_period(天数),以延迟撤销 past_due 订阅的权益。


9. 催款与支付重试

催款流程
1. order.process_dunning runs hourly
2. Finds orders where next_payment_attempt_at <= now
3. For each order:
   - Enqueue order.process_dunning_order
4. process_dunning_order:
   - Get customer's payment method
   - Attempt payment via Stripe
   - On success: mark order paid
   - On failure: schedule next attempt
重试计划

在组织设置中配置。典型模式:

  • 第 1 天:首次失败
  • 第 3 天:第 1 次重试
  • 第 5 天:第 2 次重试
  • 第 7 天:最后一次重试,然后标记为未付款
催款期间的订阅状态
payment fails → status=past_due, past_due_at=now
               ↓
         benefits may continue (grace period)
               ↓
         retry succeeds → status=active
               ↓
         retry fails → status=unpaid, benefits revoked

10. 交易账本

交易类型
类型说明
payment收到客户付款
processor_feeStripe 费用
refund退还给客户的款项
refund_reversal退款失败或被撤销
dispute拒付损失
dispute_reversal争议胜诉
balance内部余额转账
payout向创作者支付款项
创建支付交易
1. charge.updated webhook (charge settled)
2. Get balance_transaction from Stripe
3. Extract settlement amount and fees
4. Create Transaction(type=payment)
5. Enqueue processor_fee.create_payment_fees
6. Create Transaction(type=processor_fee)
付款流程
1. Creator has balance from transactions
2. payout.trigger_stripe_payouts (daily)
3. Calculate available balance
4. Create Payout record
   - ACTIVE/OFFBOARDED org: status=pending, enqueue payout.created + payout.transfer
   - REVIEW/SNOOZED org: status=held, enqueue payout.created only
5. stripe_service.transfer() to Connect account (skipped for held)
6. stripe_service.create_payout() to bank
7. payout.paid webhook → update status

Held payout lifecycle:
- When org approved: payout.release_held_payouts → status=pending, enqueue transfer
- When org denied/blocked/offboarding: payout.cancel_account_payouts → cancel + refund
- When payout account swapped: payout.cancel_held_payouts (old account only)

11. 关键文件位置

模型
server/polar/models/
├── checkout.py
├── order.py
├── order_item.py
├── subscription.py
├── subscription_product_price.py
├── transaction.py
├── payment.py
├── refund.py
├── dispute.py
├── payout.py
├── customer.py
├── product.py
├── product_price.py
├── discount.py
├── benefit.py
├── benefit_grant.py
└── billing_entry.py
服务
server/polar/
├── subscription/service.py
├── order/service.py
├── checkout/service.py
├── payment/service.py
├── refund/service.py
├── dispute/service.py
├── payout/service.py
├── benefit/
│   ├── service.py
│   ├── grant/service.py
│   └── strategies/
│       ├── meter_credit/service.py
│       ├── github_repository/service.py
│       ├── discord/service.py
│       └── ...
└── transaction/service/
    ├── payment.py
    ├── refund.py
    └── dispute.py
后台任务
server/polar/
├── subscription/tasks.py
├── order/tasks.py
├── checkout/tasks.py
├── benefit/tasks.py
├── payout/tasks.py
├── refund/tasks.py
└── integrations/stripe/tasks.py
Stripe 集成
server/polar/integrations/stripe/
├── endpoints.py    # Webhook handlers
├── service.py      # Stripe API wrapper
├── tasks.py        # Webhook processing tasks
└── payment.py      # Payment resolution helpers

常见调试场景

支付失败
  1. 检查 Payment 记录中的 decline_reason
  2. 检查 Order.status 和 next_payment_attempt_at
  3. 查看 external_events 中的 Stripe webhook
未授予权益
  1. 检查 BenefitGrant 记录是否存在错误
  2. 查看 Dramatiq 日志中的 benefit.grant 任务
  3. 确认产品已关联权益
按比例计费问题
  1. 检查订阅的 BillingEntry 记录
  2. 确认 Order 上的 billing_reason
  3. 检查订阅的 current_period 日期
订阅未按周期处理
  1. 检查订阅上的 scheduler_locked_at
  2. 确认 APScheduler 正在运行
  3. 检查 subscription.cycle 任务日志

评论

支持 Markdown