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 Billing System

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. コアエンティティ

Checkout

ファイル: 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(アップグレード用)


CheckoutLink

ファイル: server/polar/models/checkout_link.py

アクセスすると Checkout セッションを作成する、永続的な URL です。

フィールド型説明
client_secretstrURL の一意の識別子
seatsint | Noneシート制の価格設定に使用する、事前設定済みのシート数
discount_idUUID | None適用する事前設定済みの割引
trial_interval, trial_interval_countTrial config製品のトライアル設定を上書きします

関連: organization、products、discount


Order

ファイル: server/polar/models/order.py

請求イベント(1回限りの購入またはサブスクリプションの請求サイクル)を表します。

フィールド型説明
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 = 正味金額 - プラットフォーム手数料 - 返金額

Subscription

ファイル: 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

ProductPrice の種類説明
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個別の再試行1 件の支払いを再試行
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_checkouts15 分ごと期限切れのチェックアウトにマーク
入金タスク

ファイル: 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. Stripeウェブフックのexternal_eventsを確認
特典が付与されない場合
  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タスクのログを確認

コメント