specweave 0.3.13 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CLAUDE.md +17 -1
  2. package/README.md +1 -1
  3. package/bin/install-all.sh +9 -2
  4. package/bin/install-hooks.sh +57 -0
  5. package/dist/cli/commands/init.d.ts.map +1 -1
  6. package/dist/cli/commands/init.js +55 -0
  7. package/dist/cli/commands/init.js.map +1 -1
  8. package/dist/core/agent-model-manager.d.ts +52 -0
  9. package/dist/core/agent-model-manager.d.ts.map +1 -0
  10. package/dist/core/agent-model-manager.js +120 -0
  11. package/dist/core/agent-model-manager.js.map +1 -0
  12. package/dist/core/cost-tracker.d.ts +108 -0
  13. package/dist/core/cost-tracker.d.ts.map +1 -0
  14. package/dist/core/cost-tracker.js +281 -0
  15. package/dist/core/cost-tracker.js.map +1 -0
  16. package/dist/core/model-selector.d.ts +57 -0
  17. package/dist/core/model-selector.d.ts.map +1 -0
  18. package/dist/core/model-selector.js +115 -0
  19. package/dist/core/model-selector.js.map +1 -0
  20. package/dist/core/phase-detector.d.ts +62 -0
  21. package/dist/core/phase-detector.d.ts.map +1 -0
  22. package/dist/core/phase-detector.js +229 -0
  23. package/dist/core/phase-detector.js.map +1 -0
  24. package/dist/types/cost-tracking.d.ts +43 -0
  25. package/dist/types/cost-tracking.d.ts.map +1 -0
  26. package/dist/types/cost-tracking.js +8 -0
  27. package/dist/types/cost-tracking.js.map +1 -0
  28. package/dist/types/model-selection.d.ts +53 -0
  29. package/dist/types/model-selection.d.ts.map +1 -0
  30. package/dist/types/model-selection.js +12 -0
  31. package/dist/types/model-selection.js.map +1 -0
  32. package/dist/utils/cost-reporter.d.ts +58 -0
  33. package/dist/utils/cost-reporter.d.ts.map +1 -0
  34. package/dist/utils/cost-reporter.js +224 -0
  35. package/dist/utils/cost-reporter.js.map +1 -0
  36. package/dist/utils/pricing-constants.d.ts +70 -0
  37. package/dist/utils/pricing-constants.d.ts.map +1 -0
  38. package/dist/utils/pricing-constants.js +71 -0
  39. package/dist/utils/pricing-constants.js.map +1 -0
  40. package/package.json +1 -1
  41. package/src/agents/architect/AGENT.md +3 -0
  42. package/src/agents/code-reviewer.md +156 -0
  43. package/src/agents/data-scientist/AGENT.md +181 -0
  44. package/src/agents/database-optimizer/AGENT.md +147 -0
  45. package/src/agents/devops/AGENT.md +3 -0
  46. package/src/agents/diagrams-architect/AGENT.md +3 -0
  47. package/src/agents/docs-writer/AGENT.md +3 -0
  48. package/src/agents/kubernetes-architect/AGENT.md +142 -0
  49. package/src/agents/ml-engineer/AGENT.md +150 -0
  50. package/src/agents/mlops-engineer/AGENT.md +201 -0
  51. package/src/agents/network-engineer/AGENT.md +149 -0
  52. package/src/agents/observability-engineer/AGENT.md +213 -0
  53. package/src/agents/payment-integration/AGENT.md +35 -0
  54. package/src/agents/performance/AGENT.md +3 -0
  55. package/src/agents/performance-engineer/AGENT.md +153 -0
  56. package/src/agents/pm/AGENT.md +3 -0
  57. package/src/agents/qa-lead/AGENT.md +3 -0
  58. package/src/agents/security/AGENT.md +3 -0
  59. package/src/agents/sre/AGENT.md +3 -0
  60. package/src/agents/tdd-orchestrator/AGENT.md +169 -0
  61. package/src/agents/tech-lead/AGENT.md +3 -0
  62. package/src/commands/specweave.costs.md +261 -0
  63. package/src/commands/specweave.ml-pipeline.md +292 -0
  64. package/src/commands/specweave.monitor-setup.md +501 -0
  65. package/src/commands/specweave.slo-implement.md +1055 -0
  66. package/src/commands/specweave.sync-github.md +1 -1
  67. package/src/commands/specweave.tdd-cycle.md +199 -0
  68. package/src/commands/specweave.tdd-green.md +842 -0
  69. package/src/commands/specweave.tdd-red.md +135 -0
  70. package/src/commands/specweave.tdd-refactor.md +165 -0
  71. package/src/skills/SKILLS-INDEX.md +18 -10
  72. package/src/skills/billing-automation/SKILL.md +559 -0
  73. package/src/skills/distributed-tracing/SKILL.md +438 -0
  74. package/src/skills/e2e-playwright/README.md +1 -1
  75. package/src/skills/e2e-playwright/package.json +1 -1
  76. package/src/skills/gitops-workflow/SKILL.md +285 -0
  77. package/src/skills/gitops-workflow/references/argocd-setup.md +134 -0
  78. package/src/skills/gitops-workflow/references/sync-policies.md +131 -0
  79. package/src/skills/grafana-dashboards/SKILL.md +369 -0
  80. package/src/skills/helm-chart-scaffolding/SKILL.md +544 -0
  81. package/src/skills/helm-chart-scaffolding/assets/Chart.yaml.template +42 -0
  82. package/src/skills/helm-chart-scaffolding/assets/values.yaml.template +185 -0
  83. package/src/skills/helm-chart-scaffolding/references/chart-structure.md +500 -0
  84. package/src/skills/helm-chart-scaffolding/scripts/validate-chart.sh +244 -0
  85. package/src/skills/k8s-manifest-generator/SKILL.md +511 -0
  86. package/src/skills/k8s-manifest-generator/assets/configmap-template.yaml +296 -0
  87. package/src/skills/k8s-manifest-generator/assets/deployment-template.yaml +203 -0
  88. package/src/skills/k8s-manifest-generator/assets/service-template.yaml +171 -0
  89. package/src/skills/k8s-manifest-generator/references/deployment-spec.md +753 -0
  90. package/src/skills/k8s-manifest-generator/references/service-spec.md +724 -0
  91. package/src/skills/k8s-security-policies/SKILL.md +334 -0
  92. package/src/skills/k8s-security-policies/assets/network-policy-template.yaml +177 -0
  93. package/src/skills/k8s-security-policies/references/rbac-patterns.md +187 -0
  94. package/src/skills/ml-pipeline-workflow/SKILL.md +245 -0
  95. package/src/skills/paypal-integration/SKILL.md +467 -0
  96. package/src/skills/pci-compliance/SKILL.md +466 -0
  97. package/src/skills/prometheus-configuration/SKILL.md +392 -0
  98. package/src/skills/slo-implementation/SKILL.md +329 -0
  99. package/src/skills/stripe-integration/SKILL.md +442 -0
  100. package/src/skills/tdd-workflow/SKILL.md +378 -0
  101. package/src/templates/README.md.template +1 -1
  102. package/src/skills/bmad-method-expert/SKILL.md +0 -626
  103. package/src/skills/bmad-method-expert/scripts/analyze-project.js +0 -318
  104. package/src/skills/bmad-method-expert/scripts/check-setup.js +0 -208
  105. package/src/skills/bmad-method-expert/scripts/generate-template.js +0 -1149
  106. package/src/skills/bmad-method-expert/scripts/validate-documents.js +0 -340
  107. package/src/skills/context-optimizer/SKILL.md +0 -588
  108. package/src/skills/figma-designer/SKILL.md +0 -149
  109. package/src/skills/figma-implementer/SKILL.md +0 -148
  110. package/src/skills/figma-mcp-connector/SKILL.md +0 -136
  111. package/src/skills/figma-to-code/SKILL.md +0 -128
  112. package/src/skills/spec-kit-expert/SKILL.md +0 -1010
@@ -0,0 +1,559 @@
1
+ ---
2
+ name: billing-automation
3
+ description: Build automated billing systems for recurring payments, invoicing, subscription lifecycle, and dunning management. Use when implementing subscription billing, automating invoicing, or managing recurring payment systems.
4
+ ---
5
+
6
+ # Billing Automation
7
+
8
+ Master automated billing systems including recurring billing, invoice generation, dunning management, proration, and tax calculation.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - Implementing SaaS subscription billing
13
+ - Automating invoice generation and delivery
14
+ - Managing failed payment recovery (dunning)
15
+ - Calculating prorated charges for plan changes
16
+ - Handling sales tax, VAT, and GST
17
+ - Processing usage-based billing
18
+ - Managing billing cycles and renewals
19
+
20
+ ## Core Concepts
21
+
22
+ ### 1. Billing Cycles
23
+ **Common Intervals:**
24
+ - Monthly (most common for SaaS)
25
+ - Annual (discounted long-term)
26
+ - Quarterly
27
+ - Weekly
28
+ - Custom (usage-based, per-seat)
29
+
30
+ ### 2. Subscription States
31
+ ```
32
+ trial → active → past_due → canceled
33
+ → paused → resumed
34
+ ```
35
+
36
+ ### 3. Dunning Management
37
+ Automated process to recover failed payments through:
38
+ - Retry schedules
39
+ - Customer notifications
40
+ - Grace periods
41
+ - Account restrictions
42
+
43
+ ### 4. Proration
44
+ Adjusting charges when:
45
+ - Upgrading/downgrading mid-cycle
46
+ - Adding/removing seats
47
+ - Changing billing frequency
48
+
49
+ ## Quick Start
50
+
51
+ ```python
52
+ from billing import BillingEngine, Subscription
53
+
54
+ # Initialize billing engine
55
+ billing = BillingEngine()
56
+
57
+ # Create subscription
58
+ subscription = billing.create_subscription(
59
+ customer_id="cus_123",
60
+ plan_id="plan_pro_monthly",
61
+ billing_cycle_anchor=datetime.now(),
62
+ trial_days=14
63
+ )
64
+
65
+ # Process billing cycle
66
+ billing.process_billing_cycle(subscription.id)
67
+ ```
68
+
69
+ ## Subscription Lifecycle Management
70
+
71
+ ```python
72
+ from datetime import datetime, timedelta
73
+ from enum import Enum
74
+
75
+ class SubscriptionStatus(Enum):
76
+ TRIAL = "trial"
77
+ ACTIVE = "active"
78
+ PAST_DUE = "past_due"
79
+ CANCELED = "canceled"
80
+ PAUSED = "paused"
81
+
82
+ class Subscription:
83
+ def __init__(self, customer_id, plan, billing_cycle_day=None):
84
+ self.id = generate_id()
85
+ self.customer_id = customer_id
86
+ self.plan = plan
87
+ self.status = SubscriptionStatus.TRIAL
88
+ self.current_period_start = datetime.now()
89
+ self.current_period_end = self.current_period_start + timedelta(days=plan.trial_days or 30)
90
+ self.billing_cycle_day = billing_cycle_day or self.current_period_start.day
91
+ self.trial_end = datetime.now() + timedelta(days=plan.trial_days) if plan.trial_days else None
92
+
93
+ def start_trial(self, trial_days):
94
+ """Start trial period."""
95
+ self.status = SubscriptionStatus.TRIAL
96
+ self.trial_end = datetime.now() + timedelta(days=trial_days)
97
+ self.current_period_end = self.trial_end
98
+
99
+ def activate(self):
100
+ """Activate subscription after trial or immediately."""
101
+ self.status = SubscriptionStatus.ACTIVE
102
+ self.current_period_start = datetime.now()
103
+ self.current_period_end = self.calculate_next_billing_date()
104
+
105
+ def mark_past_due(self):
106
+ """Mark subscription as past due after failed payment."""
107
+ self.status = SubscriptionStatus.PAST_DUE
108
+ # Trigger dunning workflow
109
+
110
+ def cancel(self, at_period_end=True):
111
+ """Cancel subscription."""
112
+ if at_period_end:
113
+ self.cancel_at_period_end = True
114
+ # Will cancel when current period ends
115
+ else:
116
+ self.status = SubscriptionStatus.CANCELED
117
+ self.canceled_at = datetime.now()
118
+
119
+ def calculate_next_billing_date(self):
120
+ """Calculate next billing date based on interval."""
121
+ if self.plan.interval == 'month':
122
+ return self.current_period_start + timedelta(days=30)
123
+ elif self.plan.interval == 'year':
124
+ return self.current_period_start + timedelta(days=365)
125
+ elif self.plan.interval == 'week':
126
+ return self.current_period_start + timedelta(days=7)
127
+ ```
128
+
129
+ ## Billing Cycle Processing
130
+
131
+ ```python
132
+ class BillingEngine:
133
+ def process_billing_cycle(self, subscription_id):
134
+ """Process billing for a subscription."""
135
+ subscription = self.get_subscription(subscription_id)
136
+
137
+ # Check if billing is due
138
+ if datetime.now() < subscription.current_period_end:
139
+ return
140
+
141
+ # Generate invoice
142
+ invoice = self.generate_invoice(subscription)
143
+
144
+ # Attempt payment
145
+ payment_result = self.charge_customer(
146
+ subscription.customer_id,
147
+ invoice.total
148
+ )
149
+
150
+ if payment_result.success:
151
+ # Payment successful
152
+ invoice.mark_paid()
153
+ subscription.advance_billing_period()
154
+ self.send_invoice(invoice)
155
+ else:
156
+ # Payment failed
157
+ subscription.mark_past_due()
158
+ self.start_dunning_process(subscription, invoice)
159
+
160
+ def generate_invoice(self, subscription):
161
+ """Generate invoice for billing period."""
162
+ invoice = Invoice(
163
+ customer_id=subscription.customer_id,
164
+ subscription_id=subscription.id,
165
+ period_start=subscription.current_period_start,
166
+ period_end=subscription.current_period_end
167
+ )
168
+
169
+ # Add subscription line item
170
+ invoice.add_line_item(
171
+ description=subscription.plan.name,
172
+ amount=subscription.plan.amount,
173
+ quantity=subscription.quantity or 1
174
+ )
175
+
176
+ # Add usage-based charges if applicable
177
+ if subscription.has_usage_billing:
178
+ usage_charges = self.calculate_usage_charges(subscription)
179
+ invoice.add_line_item(
180
+ description="Usage charges",
181
+ amount=usage_charges
182
+ )
183
+
184
+ # Calculate tax
185
+ tax = self.calculate_tax(invoice.subtotal, subscription.customer)
186
+ invoice.tax = tax
187
+
188
+ invoice.finalize()
189
+ return invoice
190
+
191
+ def charge_customer(self, customer_id, amount):
192
+ """Charge customer using saved payment method."""
193
+ customer = self.get_customer(customer_id)
194
+
195
+ try:
196
+ # Charge using payment processor
197
+ charge = stripe.Charge.create(
198
+ customer=customer.stripe_id,
199
+ amount=int(amount * 100), # Convert to cents
200
+ currency='usd'
201
+ )
202
+
203
+ return PaymentResult(success=True, transaction_id=charge.id)
204
+ except stripe.error.CardError as e:
205
+ return PaymentResult(success=False, error=str(e))
206
+ ```
207
+
208
+ ## Dunning Management
209
+
210
+ ```python
211
+ class DunningManager:
212
+ """Manage failed payment recovery."""
213
+
214
+ def __init__(self):
215
+ self.retry_schedule = [
216
+ {'days': 3, 'email_template': 'payment_failed_first'},
217
+ {'days': 7, 'email_template': 'payment_failed_reminder'},
218
+ {'days': 14, 'email_template': 'payment_failed_final'}
219
+ ]
220
+
221
+ def start_dunning_process(self, subscription, invoice):
222
+ """Start dunning process for failed payment."""
223
+ dunning_attempt = DunningAttempt(
224
+ subscription_id=subscription.id,
225
+ invoice_id=invoice.id,
226
+ attempt_number=1,
227
+ next_retry=datetime.now() + timedelta(days=3)
228
+ )
229
+
230
+ # Send initial failure notification
231
+ self.send_dunning_email(subscription, 'payment_failed_first')
232
+
233
+ # Schedule retries
234
+ self.schedule_retries(dunning_attempt)
235
+
236
+ def retry_payment(self, dunning_attempt):
237
+ """Retry failed payment."""
238
+ subscription = self.get_subscription(dunning_attempt.subscription_id)
239
+ invoice = self.get_invoice(dunning_attempt.invoice_id)
240
+
241
+ # Attempt payment again
242
+ result = self.charge_customer(subscription.customer_id, invoice.total)
243
+
244
+ if result.success:
245
+ # Payment succeeded
246
+ invoice.mark_paid()
247
+ subscription.status = SubscriptionStatus.ACTIVE
248
+ self.send_dunning_email(subscription, 'payment_recovered')
249
+ dunning_attempt.mark_resolved()
250
+ else:
251
+ # Still failing
252
+ dunning_attempt.attempt_number += 1
253
+
254
+ if dunning_attempt.attempt_number < len(self.retry_schedule):
255
+ # Schedule next retry
256
+ next_retry_config = self.retry_schedule[dunning_attempt.attempt_number]
257
+ dunning_attempt.next_retry = datetime.now() + timedelta(days=next_retry_config['days'])
258
+ self.send_dunning_email(subscription, next_retry_config['email_template'])
259
+ else:
260
+ # Exhausted retries, cancel subscription
261
+ subscription.cancel(at_period_end=False)
262
+ self.send_dunning_email(subscription, 'subscription_canceled')
263
+
264
+ def send_dunning_email(self, subscription, template):
265
+ """Send dunning notification to customer."""
266
+ customer = self.get_customer(subscription.customer_id)
267
+
268
+ email_content = self.render_template(template, {
269
+ 'customer_name': customer.name,
270
+ 'amount_due': subscription.plan.amount,
271
+ 'update_payment_url': f"https://app.example.com/billing"
272
+ })
273
+
274
+ send_email(
275
+ to=customer.email,
276
+ subject=email_content['subject'],
277
+ body=email_content['body']
278
+ )
279
+ ```
280
+
281
+ ## Proration
282
+
283
+ ```python
284
+ class ProrationCalculator:
285
+ """Calculate prorated charges for plan changes."""
286
+
287
+ @staticmethod
288
+ def calculate_proration(old_plan, new_plan, period_start, period_end, change_date):
289
+ """Calculate proration for plan change."""
290
+ # Days in current period
291
+ total_days = (period_end - period_start).days
292
+
293
+ # Days used on old plan
294
+ days_used = (change_date - period_start).days
295
+
296
+ # Days remaining on new plan
297
+ days_remaining = (period_end - change_date).days
298
+
299
+ # Calculate prorated amounts
300
+ unused_amount = (old_plan.amount / total_days) * days_remaining
301
+ new_plan_amount = (new_plan.amount / total_days) * days_remaining
302
+
303
+ # Net charge/credit
304
+ proration = new_plan_amount - unused_amount
305
+
306
+ return {
307
+ 'old_plan_credit': -unused_amount,
308
+ 'new_plan_charge': new_plan_amount,
309
+ 'net_proration': proration,
310
+ 'days_used': days_used,
311
+ 'days_remaining': days_remaining
312
+ }
313
+
314
+ @staticmethod
315
+ def calculate_seat_proration(current_seats, new_seats, price_per_seat, period_start, period_end, change_date):
316
+ """Calculate proration for seat changes."""
317
+ total_days = (period_end - period_start).days
318
+ days_remaining = (period_end - change_date).days
319
+
320
+ # Additional seats charge
321
+ additional_seats = new_seats - current_seats
322
+ prorated_amount = (additional_seats * price_per_seat / total_days) * days_remaining
323
+
324
+ return {
325
+ 'additional_seats': additional_seats,
326
+ 'prorated_charge': max(0, prorated_amount), # No refund for removing seats mid-cycle
327
+ 'effective_date': change_date
328
+ }
329
+ ```
330
+
331
+ ## Tax Calculation
332
+
333
+ ```python
334
+ class TaxCalculator:
335
+ """Calculate sales tax, VAT, GST."""
336
+
337
+ def __init__(self):
338
+ # Tax rates by region
339
+ self.tax_rates = {
340
+ 'US_CA': 0.0725, # California sales tax
341
+ 'US_NY': 0.04, # New York sales tax
342
+ 'GB': 0.20, # UK VAT
343
+ 'DE': 0.19, # Germany VAT
344
+ 'FR': 0.20, # France VAT
345
+ 'AU': 0.10, # Australia GST
346
+ }
347
+
348
+ def calculate_tax(self, amount, customer):
349
+ """Calculate applicable tax."""
350
+ # Determine tax jurisdiction
351
+ jurisdiction = self.get_tax_jurisdiction(customer)
352
+
353
+ if not jurisdiction:
354
+ return 0
355
+
356
+ # Get tax rate
357
+ tax_rate = self.tax_rates.get(jurisdiction, 0)
358
+
359
+ # Calculate tax
360
+ tax = amount * tax_rate
361
+
362
+ return {
363
+ 'tax_amount': tax,
364
+ 'tax_rate': tax_rate,
365
+ 'jurisdiction': jurisdiction,
366
+ 'tax_type': self.get_tax_type(jurisdiction)
367
+ }
368
+
369
+ def get_tax_jurisdiction(self, customer):
370
+ """Determine tax jurisdiction based on customer location."""
371
+ if customer.country == 'US':
372
+ # US: Tax based on customer state
373
+ return f"US_{customer.state}"
374
+ elif customer.country in ['GB', 'DE', 'FR']:
375
+ # EU: VAT
376
+ return customer.country
377
+ elif customer.country == 'AU':
378
+ # Australia: GST
379
+ return 'AU'
380
+ else:
381
+ return None
382
+
383
+ def get_tax_type(self, jurisdiction):
384
+ """Get type of tax for jurisdiction."""
385
+ if jurisdiction.startswith('US_'):
386
+ return 'Sales Tax'
387
+ elif jurisdiction in ['GB', 'DE', 'FR']:
388
+ return 'VAT'
389
+ elif jurisdiction == 'AU':
390
+ return 'GST'
391
+ return 'Tax'
392
+
393
+ def validate_vat_number(self, vat_number, country):
394
+ """Validate EU VAT number."""
395
+ # Use VIES API for validation
396
+ # Returns True if valid, False otherwise
397
+ pass
398
+ ```
399
+
400
+ ## Invoice Generation
401
+
402
+ ```python
403
+ class Invoice:
404
+ def __init__(self, customer_id, subscription_id=None):
405
+ self.id = generate_invoice_number()
406
+ self.customer_id = customer_id
407
+ self.subscription_id = subscription_id
408
+ self.status = 'draft'
409
+ self.line_items = []
410
+ self.subtotal = 0
411
+ self.tax = 0
412
+ self.total = 0
413
+ self.created_at = datetime.now()
414
+
415
+ def add_line_item(self, description, amount, quantity=1):
416
+ """Add line item to invoice."""
417
+ line_item = {
418
+ 'description': description,
419
+ 'unit_amount': amount,
420
+ 'quantity': quantity,
421
+ 'total': amount * quantity
422
+ }
423
+ self.line_items.append(line_item)
424
+ self.subtotal += line_item['total']
425
+
426
+ def finalize(self):
427
+ """Finalize invoice and calculate total."""
428
+ self.total = self.subtotal + self.tax
429
+ self.status = 'open'
430
+ self.finalized_at = datetime.now()
431
+
432
+ def mark_paid(self):
433
+ """Mark invoice as paid."""
434
+ self.status = 'paid'
435
+ self.paid_at = datetime.now()
436
+
437
+ def to_pdf(self):
438
+ """Generate PDF invoice."""
439
+ from reportlab.pdfgen import canvas
440
+
441
+ # Generate PDF
442
+ # Include: company info, customer info, line items, tax, total
443
+ pass
444
+
445
+ def to_html(self):
446
+ """Generate HTML invoice."""
447
+ template = """
448
+ <!DOCTYPE html>
449
+ <html>
450
+ <head><title>Invoice #{invoice_number}</title></head>
451
+ <body>
452
+ <h1>Invoice #{invoice_number}</h1>
453
+ <p>Date: {date}</p>
454
+ <h2>Bill To:</h2>
455
+ <p>{customer_name}<br>{customer_address}</p>
456
+ <table>
457
+ <tr><th>Description</th><th>Quantity</th><th>Amount</th></tr>
458
+ {line_items}
459
+ </table>
460
+ <p>Subtotal: ${subtotal}</p>
461
+ <p>Tax: ${tax}</p>
462
+ <h3>Total: ${total}</h3>
463
+ </body>
464
+ </html>
465
+ """
466
+
467
+ return template.format(
468
+ invoice_number=self.id,
469
+ date=self.created_at.strftime('%Y-%m-%d'),
470
+ customer_name=self.customer.name,
471
+ customer_address=self.customer.address,
472
+ line_items=self.render_line_items(),
473
+ subtotal=self.subtotal,
474
+ tax=self.tax,
475
+ total=self.total
476
+ )
477
+ ```
478
+
479
+ ## Usage-Based Billing
480
+
481
+ ```python
482
+ class UsageBillingEngine:
483
+ """Track and bill for usage."""
484
+
485
+ def track_usage(self, customer_id, metric, quantity):
486
+ """Track usage event."""
487
+ UsageRecord.create(
488
+ customer_id=customer_id,
489
+ metric=metric,
490
+ quantity=quantity,
491
+ timestamp=datetime.now()
492
+ )
493
+
494
+ def calculate_usage_charges(self, subscription, period_start, period_end):
495
+ """Calculate charges for usage in billing period."""
496
+ usage_records = UsageRecord.get_for_period(
497
+ subscription.customer_id,
498
+ period_start,
499
+ period_end
500
+ )
501
+
502
+ total_usage = sum(record.quantity for record in usage_records)
503
+
504
+ # Tiered pricing
505
+ if subscription.plan.pricing_model == 'tiered':
506
+ charge = self.calculate_tiered_pricing(total_usage, subscription.plan.tiers)
507
+ # Per-unit pricing
508
+ elif subscription.plan.pricing_model == 'per_unit':
509
+ charge = total_usage * subscription.plan.unit_price
510
+ # Volume pricing
511
+ elif subscription.plan.pricing_model == 'volume':
512
+ charge = self.calculate_volume_pricing(total_usage, subscription.plan.tiers)
513
+
514
+ return charge
515
+
516
+ def calculate_tiered_pricing(self, total_usage, tiers):
517
+ """Calculate cost using tiered pricing."""
518
+ charge = 0
519
+ remaining = total_usage
520
+
521
+ for tier in sorted(tiers, key=lambda x: x['up_to']):
522
+ tier_usage = min(remaining, tier['up_to'] - tier['from'])
523
+ charge += tier_usage * tier['unit_price']
524
+ remaining -= tier_usage
525
+
526
+ if remaining <= 0:
527
+ break
528
+
529
+ return charge
530
+ ```
531
+
532
+ ## Resources
533
+
534
+ - **references/billing-cycles.md**: Billing cycle management
535
+ - **references/dunning-management.md**: Failed payment recovery
536
+ - **references/proration.md**: Prorated charge calculations
537
+ - **references/tax-calculation.md**: Tax/VAT/GST handling
538
+ - **references/invoice-lifecycle.md**: Invoice state management
539
+ - **assets/billing-state-machine.yaml**: Billing workflow
540
+ - **assets/invoice-template.html**: Invoice templates
541
+ - **assets/dunning-policy.yaml**: Dunning configuration
542
+
543
+ ## Best Practices
544
+
545
+ 1. **Automate Everything**: Minimize manual intervention
546
+ 2. **Clear Communication**: Notify customers of billing events
547
+ 3. **Flexible Retry Logic**: Balance recovery with customer experience
548
+ 4. **Accurate Proration**: Fair calculation for plan changes
549
+ 5. **Tax Compliance**: Calculate correct tax for jurisdiction
550
+ 6. **Audit Trail**: Log all billing events
551
+ 7. **Graceful Degradation**: Handle edge cases without breaking
552
+
553
+ ## Common Pitfalls
554
+
555
+ - **Incorrect Proration**: Not accounting for partial periods
556
+ - **Missing Tax**: Forgetting to add tax to invoices
557
+ - **Aggressive Dunning**: Canceling too quickly
558
+ - **No Notifications**: Not informing customers of failures
559
+ - **Hardcoded Cycles**: Not supporting custom billing dates