Source profileQuality 85/100

fcakyon/claude-codex-settings/plugins/polar-skills/skills/polar-billing/SKILL.md

polar-billing

This skill should be used when working on Polar billing system, Stripe integration, subscription lifecycle, checkout flows, or benefit provisioning.

Source repository stars
961
Declared platforms
0
Static risk flags
0
Last source update
2026-08-04
Source checked
2026-08-04

Decision brief

What it does—and where it fits

Comprehensive guide to Polar's billing infrastructure, covering entities, flows, Stripe integration, and benefit provisioning.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/fcakyon/claude-codex-settings --skill "plugins/polar-skills/skills/polar-billing"
    Safe inspection promptEditorial

    Inspect the Agent Skill "polar-billing" from https://github.com/fcakyon/claude-codex-settings/blob/ccd2e764cc57ba1e8de4833615a04d540ba1295a/plugins/polar-skills/skills/polar-billing/SKILL.md at commit ccd2e764cc57ba1e8de4833615a04d540ba1295a. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Dunning Process

      Review the “Dunning Process” section in the pinned source before continuing.

      Review and apply the “Dunning Process” source section.
    2. 02

      Quick Reference

      Review the “Quick Reference” section in the pinned source before continuing.

      Review and apply the “Quick Reference” source section.
    3. 03

      1. Core Entities

      File: server/polar/models/checkout.py

      netamount = subtotal - discounttotalamount = net + taxdueamount = max(0, total + appliedbalance)
    4. 04

      Checkout

      File: server/polar/models/checkout.py

      File: server/polar/models/checkout.pyShopping cart/payment session before order confirmation.Relationships: organization, customer, product, productprice, discount, subscription (for upgrades)
    5. 05

      CheckoutLink

      File: server/polar/models/checkoutlink.py

      File: server/polar/models/checkoutlink.pyPersistent URL that creates Checkout Sessions on visit.Relationships: organization, products, discount

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score85/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars961SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    fcakyon/claude-codex-settings
    Skill path
    plugins/polar-skills/skills/polar-billing/SKILL.md
    Commit
    ccd2e764cc57ba1e8de4833615a04d540ba1295a
    License
    Apache-2.0
    Collected
    2026-08-04
    Default branch
    main
    View the original SKILL.md

    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

    1. Core Entities
    2. Entity Relationships
    3. Main Services
    4. Dramatiq Background Tasks
    5. Stripe Integration
    6. Subscription Lifecycle
    7. Proration System
    8. Benefits & Credits
    9. Dunning & Payment Retry
    10. Transaction Ledger
    11. Key File Locations

    1. Core Entities

    Checkout

    File: server/polar/models/checkout.py

    Shopping cart/payment session before order confirmation.

    FieldTypeDescription
    statusCheckoutStatusopen, expired, confirmed, succeeded, failed
    payment_processorPaymentProcessorstripe, manual
    client_secretstrUnique identifier for frontend
    amount, currencyint, strPrice in cents
    tax_amount, discount_amountintCalculated amounts
    allow_trial, trial_endbool, datetimeTrial configuration
    seatsintFor seat-based products

    Relationships: organization, customer, product, product_price, discount, subscription (for upgrades)


    CheckoutLink

    File: server/polar/models/checkout_link.py

    Persistent URL that creates Checkout Sessions on visit.

    FieldTypeDescription
    client_secretstrUnique identifier for the URL
    seatsint | NonePreconfigured seat count for seat-based pricing
    discount_idUUID | NonePreset discount to apply
    trial_interval, trial_interval_countTrial configOverride product trial settings

    Relationships: organization, products, discount


    Order

    File: server/polar/models/order.py

    Represents a billing event (one-time purchase or subscription cycle).

    FieldTypeDescription
    statusOrderStatuspending, paid, refunded, partially_refunded
    billing_reasonOrderBillingReasonpurchase, subscription_create, subscription_cycle, subscription_update
    subtotal_amountintAmount before discount/tax
    discount_amountintDiscount applied
    tax_amountintTax collected
    applied_balance_amountintAccount balance applied
    platform_fee_amountintPolar's fee
    refunded_amountintAlready refunded
    next_payment_attempt_atdatetimeDunning retry time

    Computed Properties:

    • net_amount = subtotal - discount
    • total_amount = net + tax
    • due_amount = max(0, total + applied_balance)
    • payout_amount = net - platform_fee - refunded

    Subscription

    File: server/polar/models/subscription.py

    Recurring billing relationship.

    FieldTypeDescription
    statusSubscriptionStatusincomplete, trialing, active, past_due, canceled, unpaid
    amount, currencyint, strSubscription gross price
    net_amountintNet amount (gross minus inclusive tax, equal to gross if tax-exclusive)
    tax_behaviorTaxBehavior | NoneInclusive, exclusive, or null (set at creation)
    recurring_intervalIntervalmonth, year
    current_period_start/enddatetimeBilling period
    trial_start/enddatetimeTrial period
    cancel_at_period_endboolScheduled cancellation
    canceled_at, ended_atdatetimeLifecycle timestamps
    past_due_atdatetimeWhen payment failed
    seatsintFor seat-based pricing

    Relationships: organization, customer, product, payment_method, discount, meters, grants (benefits)


    Transaction

    File: server/polar/models/transaction.py

    All money flows in the system.

    FieldTypeDescription
    typeTransactionTypepayment, processor_fee, refund, dispute, balance, payout
    processorProcessorstripe, manual
    amount, currencyint, strTransaction amount
    tax_amountintTax portion

    Self-referential relationships: payment_transaction, balance_transactions, incurred_transactions


    Payment

    File: server/polar/models/payment.py

    Individual payment transaction.

    FieldTypeDescription
    statusPaymentStatuspending, succeeded, failed
    processor_idstrStripe charge ID
    methodstrcard, bank_transfer, etc.
    triggerPaymentTrigger | NoneWhat initiated payment: purchase, subscription_cycle, retry_dunning, retry_customer, retry_payment_method_update, retry_admin
    decline_reasonstrWhy payment failed
    risk_level, risk_scorestr, intFraud assessment

    Refund

    File: server/polar/models/refund.py

    FieldTypeDescription
    statusRefundStatuspending, succeeded, failed, canceled
    reasonRefundReasonduplicate, fraudulent, customer_request, etc.
    amount, tax_amountintRefund amounts
    revoke_benefitsboolWhether to revoke customer benefits

    Customer

    File: server/polar/models/customer.py

    FieldTypeDescription
    email, namestrContact info
    billing_namestrName for invoices (falls back to name)
    stripe_customer_idstrStripe link
    billing_addressAddressStored address
    tax_idstrFor tax compliance

    Product & ProductPrice

    Files: server/polar/models/product.py, server/polar/models/product_price.py

    ProductPrice TypesDescription
    ProductPriceFixedFixed amount (set price_amount=0 for free)
    ProductPriceCustomMerchant sets at checkout
    ProductPriceMeteredUnitPay-per-unit
    ProductPriceSeatUnitPer-seat with tiers

    BillingEntry

    File: server/polar/models/billing_entry.py

    Audit log for billing calculations.

    FieldTypeDescription
    typeBillingEntryTypecycle, proration, metered, seats_increase, seats_decrease
    directionDirectiondebit, credit
    amountintEntry amount

    2. Entity Relationships

    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. 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, discount=None)
    update_seats(subscription, seats, proration_behavior)
    update_discount(subscription, discount)  # discount: UUID | Literal["unset"]
    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_refund)
    # Also enqueues chargeback prevention notice for dispute_prevention refunds
    

    BenefitGrantService

    File: 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 Background Tasks

    Subscription Tasks

    File: server/polar/subscription/tasks.py

    TaskTriggerAction
    subscription.cycleScheduler at period endRenew subscription, create order
    subscription.update_product_benefits_grantsProduct benefits changedUpdate all grants
    subscription.cancel_customerCustomer deletedCancel all billable subscriptions (trialing, active, past_due)

    Order Tasks

    File: server/polar/order/tasks.py

    TaskTriggerAction
    order.create_subscription_orderSubscription cycleCreate billing order
    order.trigger_paymentOrder readyCharge payment method
    order.balancePayment successCreate ledger entries
    order.invoiceOrder createdGenerate PDF invoice
    order.process_dunningHourly cronFind orders for retry
    order.process_dunning_orderIndividual retryRetry single payment

    Stripe Webhook Tasks

    File: server/polar/integrations/stripe/tasks.py

    TaskStripe EventAction
    charge.succeededPayment completeCreate order, provision benefits
    charge.failedPayment failedMark order failed
    charge.updatedCharge settledCreate ledger transaction
    refund.created/updatedRefund processedUpdate refund record
    charge.dispute.createdChargebackCreate dispute, revoke benefits
    payout.paidPayout completeUpdate payout status

    Benefit Tasks

    File: server/polar/benefit/tasks.py

    TaskTriggerAction
    benefit.enqueue_benefits_grantsOrder/subscriptionQueue individual grants
    benefit.grantIndividual benefitProvision access (GitHub, Discord, etc.)
    benefit.revokeCancellation/refundRemove access
    benefit.cycleSubscription renewalReset credits with rollover

    Checkout Tasks

    File: server/polar/checkout/tasks.py

    TaskTriggerAction
    checkout.handle_free_successFree productComplete without payment
    checkout.expire_open_checkoutsEvery 15 minMark expired checkouts

    Payout Tasks

    File: server/polar/payout/tasks.py

    TaskTriggerAction
    payout.trigger_stripe_payoutsDaily 00:15 UTCInitiate pending payouts
    payout.createdPayout createdEvent hook (fires for held payouts too)
    payout.transferAfter payout.createdStripe transfer (skipped for held)
    payout.release_held_payoutsOrg approvedMove held → pending, enqueue transfers
    payout.cancel_account_payoutsOrg denied/blocked/offboardingCancel held+pending payouts
    payout.cancel_held_payoutsPayout account swapCancel only held payouts on old account

    Refund Tasks

    File: server/polar/refund/tasks.py

    TaskTriggerAction
    refund.send_chargeback_prevention_noticeDispute prevention refund createdEmail org owners/admins about refund

    5. Stripe Integration

    Webhook Endpoints

    File: server/polar/integrations/stripe/endpoints.py

    • /v1/integrations/stripe/webhook - Direct webhooks
    • /v1/integrations/stripe/webhook-connect - Connect account webhooks

    Implemented Webhooks

    Payment Flow:

    • payment_intent.succeeded - Payment complete
    • payment_intent.payment_failed - Payment failed
    • setup_intent.succeeded - Card saved
    • charge.pending/failed/succeeded/updated - Charge lifecycle

    Refunds:

    • refund.created/updated/failed

    Disputes:

    • charge.dispute.created/updated/closed

    Connect:

    • account.updated - Account info changed
    • payout.updated/paid - Payout lifecycle

    Webhook Processing Flow

    Stripe POST → Verify signature → ExternalEvent.enqueue()
                                            ↓
                                   Store in external_events table
                                            ↓
                                   Enqueue Dramatiq task
                                            ↓
                                   Worker processes async
                                            ↓
                                   Mark handled_at on success
    

    StripeService

    File: server/polar/integrations/stripe/service.py

    Key methods:

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

    6. Subscription Lifecycle

    Creation Flow

    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
    

    Cycle Flow (Renewal)

    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
    

    Cancellation Flow

    At Period End:

    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
    

    Immediately:

    subscription_service.revoke(subscription)
    # Sets status=canceled, ended_at=now
    # Benefits revoked immediately
    # Seats canceled if seat-based
    

    Trial Flow

    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. Proration System

    When Prorations Occur

    1. Product change - Upgrade/downgrade to different tier
    2. Seat change - Add/remove seats
    3. Interval change - Monthly to yearly

    Proration Calculation

    # 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
    

    Proration Behaviors

    BehaviorAction
    prorateAdd to next invoice
    invoiceCreate order immediately

    BillingEntry for Prorations

    # 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
    )
    

    Seat Proration

    # 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. Benefits & Credits

    Benefit Types

    TypeDescriptionGrant Action
    meter_creditUsage allowancesCreate meter_credited event
    github_repositoryRepo accessAdd to GitHub team
    discordServer roleAssign Discord role
    license_keysLicense distributionGenerate key
    downloadablesFile accessGrant download permission
    slack_shared_channelSlack Connect channelCreate/invite to shared channel
    feature_flagFeature toggle (API-only)None — merchant reads via API
    customCustomer-visible noteNone — displayed in customer portal

    Benefit Grant Flow

    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
    

    Benefit Revocation Flow

    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
    

    Meter Credits

    Grant:

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

    Cycle (renewal):

    # 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)
    

    Revoke:

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

    Grace Period

    Organizations can configure benefit_revocation_grace_period (days) to delay benefit revocation for past_due subscriptions.


    9. Dunning & Payment Retry

    Dunning Process

    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
    

    Retry Schedule

    Configured in organization settings. Typical pattern:

    • Day 1: First failure
    • Day 3: Retry 1
    • Day 5: Retry 2
    • Day 7: Final retry, then mark unpaid

    Subscription Status During Dunning

    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. Transaction Ledger

    Transaction Types

    TypeDescription
    paymentCustomer payment received
    processor_feeStripe fees
    refundMoney returned to customer
    refund_reversalRefund failed/reversed
    disputeChargeback loss
    dispute_reversalWon dispute
    balanceInternal balance transfer
    payoutMoney sent to creator

    Creating Payment Transactions

    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)
    

    Payout Flow

    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. Key File Locations

    Models

    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
    

    Services

    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
    

    Background Tasks

    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 Integration

    server/polar/integrations/stripe/
    ├── endpoints.py    # Webhook handlers
    ├── service.py      # Stripe API wrapper
    ├── tasks.py        # Webhook processing tasks
    └── payment.py      # Payment resolution helpers
    

    Common Debugging Scenarios

    Payment Failed

    1. Check Payment record for decline_reason
    2. Check Order.status and next_payment_attempt_at
    3. Look at external_events for Stripe webhook

    Benefits Not Granted

    1. Check BenefitGrant record for errors
    2. Look at benefit.grant task in Dramatiq logs
    3. Verify product has benefits attached

    Proration Issues

    1. Check BillingEntry records for subscription
    2. Verify billing_reason on Order
    3. Check subscription's current_period dates

    Subscription Not Cycling

    1. Check scheduler_locked_at on subscription
    2. Verify APScheduler is running
    3. Check subscription.cycle task logs