billing by useautumn
Debug, edit, and fix billing operations. Covers the V2 action-based architecture (attach, multiAttach, updateSubscription, allocatedInvoice, createWithDefaults, setupPayment). Use when working on billing, subscription, invoicing, or Stripe integration code.
Content & Writing
2.4K Stars
196 Forks
Updated Mar 2, 2026, 10:43 AM
Why Use This
This skill provides specialized capabilities for useautumn's codebase.
Use Cases
- Developing new features in the useautumn repository
- Refactoring existing code to follow useautumn standards
- Understanding and working with useautumn's codebase structure
Install Guide
2 steps- 1
Skip this step if Ananke is already installed.
- 2
Skill Snapshot
Auto scan of skill assets. Informational only.
Valid SKILL.md
Checks against SKILL.md specification
Source & Community
Skill Stats
SKILL.md 295 Lines
Total Files 2
Total Size 13.9 KB
License Apache-2.0
---
name: billing
description: Debug, edit, and fix billing operations. Covers the V2 action-based architecture (attach, multiAttach, updateSubscription, allocatedInvoice, createWithDefaults, setupPayment). Use when working on billing, subscription, invoicing, or Stripe integration code.
---
# Billing Operations Guide
## When to Use This Skill
- Debugging billing issues (double charges, missing invoices, wrong subscription items)
- Adding new billing actions
- Understanding how Autumn state maps to Stripe
- Fixing subscription update/cancel/attach flows
- Working with subscription schedules (future changes)
- Understanding allocated invoice (mid-cycle usage-based invoicing)
## V2 Billing Actions
All billing logic is orchestrated through **`billingActions`** (`billing/v2/actions/index.ts`). Handlers are thin — they call an action, then format the response.
```typescript
// billing/v2/actions/index.ts
export const billingActions = {
attach, // Single product attach
multiAttach, // Attach multiple products atomically
setupPayment, // Setup payment method (+ optional plan validation)
updateSubscription, // Update quantity, cancel, uncancel, custom plan
migrate, // Programmatic product migration (not HTTP-exposed)
legacy: { // V1→V2 bridge adapters (backward compat)
attach: legacyAttach,
updateQuantity,
renew,
},
} as const;
```
Two additional billing operations live outside `billingActions` but use the same evaluate+execute pipeline:
- **`createAllocatedInvoice`** — mid-cycle invoicing triggered by balance deduction
- **`createCustomerWithDefaults`** — customer creation with default products
### Action Quick Reference
| Action | Trigger | What It Does |
|--------|---------|--------------|
| `attach` | HTTP `billing.attach` | Add/upgrade/downgrade a single product. Handles transitions, prorations, trials, checkout mode |
| `multiAttach` | HTTP `billing.multi_attach` | Attach multiple products atomically. At most one transition allowed |
| `updateSubscription` | HTTP `billing.update` | Change quantity, cancel (immediate/end-of-cycle), uncancel, update custom plan items |
| `setupPayment` | HTTP `billing.setup_payment` | Create Stripe setup checkout. Optionally validates a plan via preview first |
| `createAllocatedInvoice` | Programmatic (balance deduction) | Invoice for allocated usage changes (prepaid overages, usage upgrades/downgrades) |
| `createCustomerWithDefaults` | Programmatic (customer creation) | Two-phase: create customer + products in DB, then create Stripe subscription for paid defaults |
Each HTTP action also has a **preview** variant (`billing.preview_attach`, `billing.preview_multi_attach`, `billing.preview_update`) that runs setup+compute+evaluate but skips execution.
The **legacy V1 attach** (`POST /attach`) still exists and delegates to `billingActions.legacy.attach`, which converts old `AttachParams` format into V2 billing context overrides. Similarly `legacyUpdateQuantity` and `legacyRenew` bridge old flows to V2.
### Handler Pattern
Handlers are thin wrappers — they parse params, call the action, format response:
```typescript
// billing/v2/handlers/handleAttachV2.ts
export const handleAttachV2 = createRoute({
versionedBody: { latest: AttachParamsV1Schema, [ApiVersion.V1_Beta]: AttachParamsV0Schema },
resource: AffectedResource.Attach,
lock: { /* distributed lock per customer */ },
handler: async (c) => {
const ctx = c.get("ctx");
const body = c.req.valid("json");
const { billingContext, billingResult } = await billingActions.attach({
ctx,
params: body,
preview: false,
});
return c.json(billingResultToResponse({ billingContext, billingResult }), 200);
},
});
```
## The 4-Layer Pattern (Inside Each Action)
Every action follows: **Setup → Compute → Evaluate → Execute**
```typescript
// billing/v2/actions/attach/attach.ts (simplified)
export async function attach({ ctx, params, preview }) {
// 1. SETUP — Fetch all context (customer, Stripe, products, trial, cycle anchors)
const billingContext = await setupAttachBillingContext({ ctx, params });
// 2. COMPUTE — Determine Autumn state changes (new products, transitions, line items)
const autumnBillingPlan = computeAttachPlan({ ctx, attachBillingContext: billingContext, params });
// 3. EVALUATE — Map Autumn changes → Stripe actions (UNIFIED across all actions)
const stripeBillingPlan = await evaluateStripeBillingPlan({ ctx, billingContext, autumnBillingPlan });
// 4. ERRORS — Validate before execution
handleAttachV2Errors({ ctx, billingContext, billingPlan, params });
if (preview) return { billingContext, billingPlan };
// 5. EXECUTE — Run Stripe first, then Autumn DB (UNIFIED across all actions)
const billingResult = await executeBillingPlan({ ctx, billingContext, billingPlan });
return { billingContext, billingPlan, billingResult };
}
```
**Key principle**: `evaluateStripeBillingPlan` and `executeBillingPlan` are **UNIFIED** across all actions. Only modify them when adding new Stripe action types.
**See [V2 Four-Layer Pattern Deep Dive](./references/v2-four-layer-pattern.md) for detailed explanation.**
## Allocated Invoice
**Not an HTTP endpoint** — triggered during `executePostgresDeduction` when allocated (prepaid) usage changes.
**File**: `server/src/internal/balances/utils/allocatedInvoice/createAllocatedInvoice.ts`
**When it fires**: A customer with usage-based allocated pricing (e.g., prepaid seats) has their usage change. The system needs to invoice for the delta.
**Flow**:
1. **Setup** (`setupAllocatedInvoiceContext`) — re-fetches full customer, computes previous/new usage and overage from entitlement snapshots
2. **Compute** (`computeAllocatedInvoicePlan`) — builds refund line item for previous usage + charge line item for new usage. Handles upgrade (delete replaceables) and downgrade (create replaceables) scenarios
3. **Evaluate + Execute** — standard unified pipeline (`evaluateStripeBillingPlan` → `executeBillingPlan`)
4. **Post-execute** — if Stripe invoice payment fails, voids invoice and throws `PayInvoiceFailed`
5. **Mutation** — calls `refreshDeductionUpdate` to mutate the deduction update with replaceable and balance changes
**Key difference from other actions**: Produces only `updateCustomerEntitlements` + `lineItems` (no `insertCustomerProducts`). The AutumnBillingPlan is minimal since the customer product already exists.
## Two Critical Stripe Mappings
Getting billing right means getting these two mappings right:
### 1. Subscription Items (Immediate Changes)
**When**: Updating a subscription right now (add/remove/change items immediately)
**Key function**: `buildStripeSubscriptionItemsUpdate`
**Flow**:
```
FullCusProduct[]
→ filter by subscription ID
→ filter by active statuses
→ customerProductToStripeItemSpecs()
→ diff against current subscription
→ Stripe.SubscriptionUpdateParams.Item[]
```
**See [Stripe Subscription Items Reference](./references/stripe-subscription-items.md) for details.**
### 2. Schedule Phases (Future Changes)
**When**: Scheduling changes for the future (downgrades at cycle end, scheduled cancellations)
**Key function**: `buildStripePhasesUpdate`
**Flow**:
```
FullCusProduct[]
→ normalize timestamps to seconds
→ buildTransitionPoints() (find all start/end times)
→ for each period: filter active products
→ customerProductsToPhaseItems()
→ Stripe.SubscriptionScheduleUpdateParams.Phase[]
```
**See [Stripe Schedule Phases Reference](./references/stripe-schedule-phases.md) for details.**
## Stripe Invoice Decision Tree
**Critical**: Stripe sometimes forces invoice creation. If you also create a manual invoice, customer gets double-charged.
```
Does Stripe force-create an invoice?
├── Creating a new subscription?
│ └── YES → Stripe creates invoice. DO NOT create manual invoice.
│
├── Removing trial from subscription? (isTrialing && !willBeTrialing)
│ └── YES → Stripe creates invoice. DO NOT create manual invoice.
│
└── Otherwise
└── NO → We create manual invoice using buildStripeInvoiceAction()
```
**Key functions**:
- `shouldCreateManualStripeInvoice()` - Returns true if WE should create invoice
- `willStripeSubscriptionUpdateCreateInvoice()` - Returns true if STRIPE will create invoice
**See [Stripe Invoice Rules Reference](./references/stripe-invoice-rules.md) for full decision tree.**
## Common Issues & Fixes
| Symptom | Likely Cause | Quick Fix |
|---------|--------------|-----------|
| Double invoice charge | Created manual invoice when Stripe already did | Check `shouldCreateManualStripeInvoice()` |
| Subscription items wrong | `customerProductToStripeItemSpecs` output incorrect | Debug spec generation, check quantity rules |
| Schedule phases wrong | Transition points incorrect | Check `buildTransitionPoints`, run schedule phases tests |
| Trial not ending | `trialContext` not set up correctly | Check `setupTrialContext` |
| Quantities wrong | Metered vs licensed confusion | `undefined` = metered, `0` = entity placeholder, `N` = licensed |
| Allocated invoice fails | Stripe payment failed for usage delta | Invoice is voided, `PayInvoiceFailed` thrown |
**See [Common Bugs Reference](./references/common-bugs.md) for detailed debugging steps.**
## Adding a New Billing Action
1. **Create action function**: `billing/v2/actions/myAction/myAction.ts`
- Follow the attach.ts pattern: setup → compute → evaluate → errors → execute
- Return `{ billingContext, billingPlan, billingResult }`
2. **Create setup function**: `billing/v2/actions/myAction/setup/setupMyActionBillingContext.ts`
- Extend `BillingContext` interface if needed
- Use shared setup functions (`setupFullCustomerContext`, `setupStripeBillingContext`, etc.)
3. **Create compute function**: `billing/v2/actions/myAction/compute/computeMyActionPlan.ts`
- Return `AutumnBillingPlan` with insertCustomerProducts, lineItems, etc.
4. **Create error handler**: `billing/v2/actions/myAction/errors/handleMyActionErrors.ts`
5. **Register in `billingActions`**: `billing/v2/actions/index.ts`
6. **Create handler** (if HTTP-exposed): `billing/v2/handlers/handleMyAction.ts`
- Thin wrapper calling `billingActions.myAction()`
7. **DO NOT modify** `evaluateStripeBillingPlan` or `executeBillingPlan` unless absolutely necessary
## Invoicing Utilities (Pure Calculations)
The `shared/utils/billingUtils/` folder contains **pure calculation functions** that determine what customers are charged.
**Key utilities**:
| Function | Location | Purpose |
|----------|----------|---------|
| `priceToLineAmount` | `invoicingUtils/lineItemUtils/` | Calculate charge amount for a price |
| `tiersToLineAmount` | `invoicingUtils/lineItemUtils/` | Calculate tiered/usage-based amounts |
| `applyProration` | `invoicingUtils/prorationUtils/` | Calculate partial period charges |
| `buildLineItem` | `invoicingUtils/lineItemBuilders/` | Core line item builder |
| `fixedPriceToLineItem` | `invoicingUtils/lineItemBuilders/` | Build line item for fixed prices |
| `usagePriceToLineItem` | `invoicingUtils/lineItemBuilders/` | Build line item for usage prices |
**Key concepts**:
- `LineItem.amount` is positive for charges, negative for refunds
- `context.direction` controls the sign (`"charge"` vs `"refund"`)
- Proration is applied automatically when `billingPeriod` is provided
- Consumable prices don't prorate (usage is charged as-is)
**See [Invoicing Utilities Reference](./references/invoicing-utilities.md) for detailed documentation.**
## Key File Locations
### V2 Actions (`server/src/internal/billing/v2/actions/`)
| Action | Key Files |
|--------|-----------|
| **attach** | `attach/attach.ts`, `attach/setup/setupAttachBillingContext.ts`, `attach/compute/computeAttachPlan.ts` |
| **multiAttach** | `multiAttach/multiAttach.ts`, `multiAttach/setup/`, `multiAttach/compute/` |
| **updateSubscription** | `updateSubscription/updateSubscription.ts`, `updateSubscription/compute/` (cancel/, customPlan/, updateQuantity/) |
| **setupPayment** | `setupPayment/setupPayment.ts` |
### Shared V2 Infrastructure (`server/src/internal/billing/v2/`)
| Layer | Key Files |
|-------|-----------|
| **Evaluate** | `providers/stripe/actionBuilders/evaluateStripeBillingPlan.ts` |
| **Execute** | `execute/executeBillingPlan.ts`, `execute/executeAutumnBillingPlan.ts` |
| **Shared Setup** | `setup/setupFullCustomerContext.ts`, `setup/setupBillingCycleAnchor.ts`, `providers/stripe/setup/setupStripeBillingContext.ts` |
| **Shared Compute** | `compute/computeAutumnUtils/buildAutumnLineItems.ts`, `compute/finalize/finalizeLineItems.ts` |
### Non-billingActions Operations
| Operation | Key Files |
|-----------|-----------|
| **allocatedInvoice** | `server/src/internal/balances/utils/allocatedInvoice/createAllocatedInvoice.ts`, `compute/computeAllocatedInvoicePlan.ts` |
| **createWithDefaults** | `server/src/internal/customers/actions/createWithDefaults/createCustomerWithDefaults.ts` |
### Types
| Type | Location | Purpose |
|------|----------|---------|
| `BillingContext` | `shared/models/billingModels/context/billingContext.ts` | Customer, products, Stripe state, timestamps |
| `AutumnBillingPlan` | `shared/models/billingModels/plan/autumnBillingPlan.ts` | Autumn state changes (inserts, deletes, line items) |
| `StripeBillingPlan` | Types in `billing/v2/providers/stripe/` | Stripe actions (subscription, invoice, schedule) |
## Reference Files
Load these on-demand for detailed information:
- [V2 Four-Layer Pattern](./references/v2-four-layer-pattern.md) - Deep dive on each layer
- [Stripe Subscription Items](./references/stripe-subscription-items.md) - Immediate changes mapping
- [Stripe Schedule Phases](./references/stripe-schedule-phases.md) - Future changes mapping
- [Stripe Invoice Rules](./references/stripe-invoice-rules.md) - Invoice decision tree
- [Invoicing Utilities](./references/invoicing-utilities.md) - Pure calculation functions for charges
- [Common Bugs](./references/common-bugs.md) - Debugging guide with solutions
Name Size