Polar Billing System
Comprehensive guide to Polar's billing infrastructure, covering entities, flows, Stripe integration, and benefit provisioning.
Quick Reference
Checkout → Payment → Order → Transaction → Benefits
↓
Subscription (if recurring)
↓
Subscription Cycle → Order → ...
Table of Contents
- [Core Entities](#1-core-entities)
- [Entity Relationships](#2-entity-relationships)
- [Main Services](#3-main-services)
- [Dramatiq Background Tasks](#4-dramatiq-background-tasks)
- [Stripe Integration](#5-stripe-integration)
- [Subscription Lifecycle](#6-subscription-lifecycle)
- [Proration System](#7-proration-system)
- [Benefits & Credits](#8-benefits--credits)
- [Dunning & Payment Retry](#9-dunning--payment-retry)
- [Transaction Ledger](#10-transaction-ledger)
- [Key File Locations](#11-key-file-locations)
1. Core Entities
Checkout
File: server/polar/models/checkout.py
Shopping cart/payment session before order confirmation.
| Field | Type | Description | |-------|------|-------------| | status | CheckoutStatus | open, expired, confirmed, succeeded, failed | | payment_processor | PaymentProcessor | stripe, manual | | client_secret | str | Unique identifier for frontend | | amount, currency | int, str | Price in cents | | tax_amount, discount_amount | int | Calculated amounts | | allow_trial, trial_end | bool, datetime | Trial configuration | | seats | int | For seat-based products |
Relationships: organization, customer, product, product_price, discount, subscription (for upgrades)
Order
File: server/polar/models/order.py
Represents a billing event (one-time purchase or subscription cycle).
| Field | Type | Description | |-------|------|-------------| | status | OrderStatus | pending, paid, refunded, partially_refunded | | billing_reason | OrderBillingReason | purchase, subscription_create, subscription_cycle, subscription_update | | subtotal_amount | int | Amount before discount/tax | | discount_amount | int | Discount applied | | tax_amount | int | Tax collected | | applied_balance_amount | int | Account balance applied | | platform_fee_amount | int | Polar's fee | | refunded_amount | int | Already refunded | | next_payment_attempt_at | datetime | Dunning retry time |
Computed Properties:
net_amount= subtotal - discounttotal_amount= net + taxdue_amount= max(0, total + applied_balance)payout_amount= net - platform_fee - refunded
Subscription
File: server/polar/models/subscription.py
Recurring billing relationship.
| Field | Type | Description | |-------|------|-------------| | status | SubscriptionStatus | incomplete, trialing, active, past_due, canceled, unpaid | | amount, currency | int, str | Subscription price | | recurring_interval | Interval | month, year | | current_period_start/end | datetime | Billing period | | trial_start/end | datetime | Trial period | | cancel_at_period_end | bool | Scheduled cancellation | | canceled_at, ended_at | datetime | Lifecycle timestamps | | past_due_at | datetime | When payment failed | | seats | int | For seat-based pricing |
Relationships: customer, product, payment_method, discount, meters, grants (benefits)
Transaction
File: server/polar/models/transaction.py
All money flows in the system.
| Field | Type | Description | |-------|------|-------------| | type | TransactionType | payment, processor_fee, refund, dispute, balance, payout | | processor | Processor | stripe, manual | | amount, currency | int, str | Transaction amount | | tax_amount | int | Tax portion |
Self-referential relationships: payment_transaction, balance_transactions, incurred_transactions
Payment
File: server/polar/models/payment.py
Individual payment transaction.
| Field | Type | Description | |-------|------|-------------| | status | PaymentStatus | pending, succeeded, failed | | processor_id | str | Stripe charge ID | | method | str | card, bank_transfer, etc. | | decline_reason | str | Why payment failed | | risk_level, risk_score | str, int | Fraud assessment |
Refund
File: server/polar/models/refund.py
| Field | Type | Description | |-------|------|-------------| | status | RefundStatus | pending, succeeded, failed, canceled | | reason | RefundReason | duplicate, fraudulent, customer_request, etc. | | amount, tax_amount | int | Refund amounts | | revoke_benefits | bool | Whether to revoke customer benefits |
Customer
File: server/polar/models/customer.py
| Field | Type | Description | |-------|------|-------------| | email, name | str | Contact info | | stripe_customer_id | str | Stripe link | | billing_address | Address | Stored address | | tax_id | str | For tax compliance |
Product & ProductPrice
Files: server/polar/models/product.py, server/polar/models/product_price.py
| ProductPrice Types | Description | |-------------------|-------------| | ProductPriceFixed | Fixed amount | | ProductPriceCustom | Merchant sets at checkout | | ProductPriceFree | Zero cost | | ProductPriceMeteredUnit | Pay-per-unit | | ProductPriceSeatUnit | Per-seat with tiers |
BillingEntry
File: server/polar/models/billing_entry.py
Audit log for billing calculations.
| Field | Type | Description | |-------|------|-------------| | type | BillingEntryType | cycle, proration, metered, seats_increase, seats_decrease | | direction | Direction | debit, credit | | amount | int | Entry amount |
2. Entity Relationships
Organization
├── Product
│ ├── ProductPrice (multiple per product)
│ └── ProductBenefit → Benefit
├── Customer
│ ├── Subscription → 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. Main Services
SubscriptionService
File: server/polar/subscription/service.py
Core subscription operations:
# Creation
create_or_update_from_checkout(checkout, payment_method) → (Subscription, created)
# Updates
update_product(subscription, product_id, proration_behavior)
update_seats(subscription, seats, proration_behavior)
update_discount(subscription, discount_id)
update_trial(subscription, trial_end)
# 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
File: 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
File: 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
File: server/polar/payment/service.py
upsert_from_stripe_charge(charge, checkout, order)
handle_success(payment) # Complete order
handle_failure(payment) # Update order status
RefundService
File: server/polar/refund/service.py
create(order, amount, reason, revoke_benefits)
upsert_from_stripe(stripe_refun