agents 插件市场 billing-automation 技能全解:构建生产级订阅计费自动化系统
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
本篇文章围绕 agents 项目(Multi-harness agentic plugin marketplace)中 payment-processing 插件的billing-automation技能展开,完整解读该技能提供的计费自动化模式:从订阅生命周期管理、账单周期处理、催收(dunning)、按比例分摊(proration)、税务计算、发票生成到基于用量的计费(usage-based billing)。读完本文,你将掌握一套可落地的 Python 计费引擎设计骨架,并了解该技能在项目中的定位、安装方式以及与同插件支付集成技能的协同关系。
技能定位与适用场景
billing-automation是位于 plugins/payment-processing/skills/billing-automation/SKILL.md 的技能文件。其 frontmatter 给出的定位是:
--- name: billing-automation 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. ---即:构建面向周期性付款、发票、订阅生命周期与催收管理的自动化计费系统。技能描述中明确其触发场景包括:
- 实现 SaaS 订阅计费
- 自动化发票生成与投递
- 管理失败支付恢复(dunning)
- 计算套餐变更的按比例分摊费用
- 处理销售税、VAT 与 GST
- 处理基于用量的计费
- 管理计费周期与续订
作为插件体系中的"技能(skill)"组件,它遵循项目的渐进式披露(progressive disclosure)设计:SKILL.md 是导航层,给出核心概念与快速上手;更深层的完整模式与可运行示例存放在同目录的 references/details.md 中,当导航层不足以覆盖需求时再加载。这正是该项目"按需加载、节省上下文"的插件设计原则的体现。
核心概念:四个必须理解的计费模型
SKILL.md 用四个核心概念搭建了整个技能的知识骨架,理解它们是实现任何计费系统的前提。
1. 计费周期(Billing Cycles)
订阅产品的价格锚定在固定的时间间隔上。技能给出了常见周期:
- Monthly(月付):SaaS 中最常见的默认周期
- Annual(年付):通常配合折扣,用于长期锁定
- Quarterly(季付)
- Weekly(周付)
- Custom(自定义):按用量(usage-based)或按席位(per-seat)计费
周期的选择直接影响后续的续订日期计算(见"订阅生命周期管理"中calculate_next_billing_date的实现)。
2. 订阅状态机(Subscription States)
技能给出了订阅的核心状态流转图:
trial → active → past_due → canceled → paused → resumedtrial:试用期,通常可免费使用并自动记录试用截止时间active:正常计费的活跃状态past_due:支付失败后进入逾期状态,等待催收流程处理paused/resumed:暂停与恢复,常用于账户冻结或用户主动暂停canceled:终止,支持"周期末生效"(at period end)与"立即生效"两种取消语义
3. 催收管理(Dunning Management)
Dunning 是指对失败支付进行自动化恢复的流程,技能归纳了四个组成部分:
- Retry schedules(重试计划):按时间间隔多次尝试重新扣款
- Customer notifications(客户通知):每次重试前后向客户发送提醒邮件
- Grace periods(宽限期):在限制账户前留给客户修正支付方式的时间窗口
- Account restrictions(账户限制):多次失败后降级或限制账户功能
4. 按比例分摊(Proration)
当客户在计费周期中途变更套餐时,需要对费用进行按天折算,典型场景包括:
- 周期中途升级/降级套餐
- 增加/移除席位
- 变更计费频率
快速上手:最小可用计费引擎
SKILL.md 提供了一个极简的入门示例(SKILL.md),展示了该技能推荐的顶层抽象——BillingEngine与Subscription:
from billing import BillingEngine, Subscription # Initialize billing engine billing = BillingEngine() # Create subscription subscription = billing.create_subscription( customer_id="cus_123", plan_id="plan_pro_monthly", billing_cycle_anchor=datetime.now(), trial_days=14 ) # Process billing cycle billing.process_billing_cycle(subscription.id)这段代码揭示了计费引擎的三个关键入口:
create_subscription:创建订阅,支持billing_cycle_anchor(计费周期锚点,决定每月扣款日)与trial_days(试用天数)process_billing_cycle:驱动单个订阅的完整计费流程(生成发票 → 扣款 → 状态流转 → 失败进入催收)Subscription/BillingEngine:分别承载订阅状态与计费流程逻辑,这也与后续 details.md 中两个类的职责划分一一对应
注意:示例中的
billing模块是技能给出的抽象示意,实际项目中需要你结合具体支付网关(如 Stripe/PayPal)实现BillingEngine的底层能力。同插件的 stripe-integration/SKILL.md 与 paypal-integration/SKILL.md 正是这些能力的实现参考。
订阅生命周期管理:状态机落地
details.md 首先给出了完整的订阅生命周期实现(details.md)。它用SubscriptionStatus枚举严格约束状态集合,并用Subscription类封装状态转换:
from datetime import datetime, timedelta from enum import Enum class SubscriptionStatus(Enum): TRIAL = "trial" ACTIVE = "active" PAST_DUE = "past_due" CANCELED = "canceled" PAUSED = "paused" class Subscription: def __init__(self, customer_id, plan, billing_cycle_day=None): self.id = generate_id() self.customer_id = customer_id self.plan = plan self.status = SubscriptionStatus.TRIAL self.current_period_start = datetime.now() self.current_period_end = self.current_period_start + timedelta(days=plan.trial_days or 30) self.billing_cycle_day = billing_cycle_day or self.current_period_start.day self.trial_end = datetime.now() + timedelta(days=plan.trial_days) if plan.trial_days else None def start_trial(self, trial_days): """Start trial period.""" self.status = SubscriptionStatus.TRIAL self.trial_end = datetime.now() + timedelta(days=trial_days) self.current_period_end = self.trial_end def activate(self): """Activate subscription after trial or immediately.""" self.status = SubscriptionStatus.ACTIVE self.current_period_start = datetime.now() self.current_period_end = self.calculate_next_billing_date() def mark_past_due(self): """Mark subscription as past due after failed payment.""" self.status = SubscriptionStatus.PAST_DUE # Trigger dunning workflow def cancel(self, at_period_end=True): """Cancel subscription.""" if at_period_end: self.cancel_at_period_end = True # Will cancel when current period ends else: self.status = SubscriptionStatus.CANCELED self.canceled_at = datetime.now() def calculate_next_billing_date(self): """Calculate next billing date based on interval.""" if self.plan.interval == 'month': return self.current_period_start + timedelta(days=30) elif self.plan.interval == 'year': return self.current_period_start + timedelta(days=365) elif self.plan.interval == 'week': return self.current_period_start + timedelta(days=7)值得注意的工程细节:
- 周期锚点(billing_cycle_day):以创建日的"日"为锚点,保证每月在固定日期扣款;未显式传入时默认取当前日
- 试用期与周期打通:试用期间
current_period_end直接对齐trial_end,试用结束即触发计费 - 取消语义:默认
at_period_end=True(周期末生效,客户可继续用到本周期结束);False则立即取消并记录canceled_at - 状态转换留口:
mark_past_due中注释"Trigger dunning workflow",将逾期处理委托给独立的催收管理器,保持订阅类职责单一
实现时注意generate_id()与plan对象(含trial_days、interval、amount、pricing_model等字段)在示例中为示意,你需要用自己的 ID 生成器与定价模型补齐。
账单周期处理:BillingEngine 主循环
BillingEngine.process_billing_cycle是整个计费系统的"心跳"(details.md)。它定义了每个计费周期执行的标准流程:
class BillingEngine: def process_billing_cycle(self, subscription_id): """Process billing for a subscription.""" subscription = self.get_subscription(subscription_id) # Check if billing is due if datetime.now() < subscription.current_period_end: return # Generate invoice invoice = self.generate_invoice(subscription) # Attempt payment payment_result = self.charge_customer( subscription.customer_id, invoice.total ) if payment_result.success: # Payment successful invoice.mark_paid() subscription.advance_billing_period() self.send_invoice(invoice) else: # Payment failed subscription.mark_past_due() self.start_dunning_process(subscription, invoice) def generate_invoice(self, subscription): """Generate invoice for billing period.""" invoice = Invoice( customer_id=subscription.customer_id, subscription_id=subscription.id, period_start=subscription.current_period_start, period_end=subscription.current_period_end ) # Add subscription line item invoice.add_line_item( description=subscription.plan.name, amount=subscription.plan.amount, quantity=subscription.quantity or 1 ) # Add usage-based charges if applicable if subscription.has_usage_billing: usage_charges = self.calculate_usage_charges(subscription) invoice.add_line_item( description="Usage charges", amount=usage_charges ) # Calculate tax tax = self.calculate_tax(invoice.subtotal, subscription.customer) invoice.tax = tax invoice.finalize() return invoice def charge_customer(self, customer_id, amount): """Charge customer using saved payment method.""" customer = self.get_customer(customer_id) try: # Charge using payment processor charge = stripe.Charge.create( customer=customer.stripe_id, amount=int(amount * 100), # Convert to cents currency='usd' ) return PaymentResult(success=True, transaction_id=charge.id) except stripe.error.CardError as e: return PaymentResult(success=False, error=str(e))该流程的关键决策点:
- 到期判断:
datetime.now() < current_period_end时直接返回,保证流程可被定时任务反复调用而不产生重复扣费(天然幂等的外层判断) - 发票先行:先
generate_invoice再扣款,发票作为扣款金额的唯一来源,包含订阅行项目、用量行项目与税款 - 金额换算:
int(amount * 100)将元换算为分,避免浮点精度问题——这是支付网关集成中的通用约定 - 成败分流:成功则
mark_paid+advance_billing_period(推进到下一计费周期)+ 投递发票;失败则mark_past_due并启动催收流程 - 网关异常隔离:仅捕获
stripe.error.CardError(卡被拒等业务性失败),其余异常向上抛出,避免把系统故障误判为客户欠费
催收管理:DunningManager 的失败支付恢复
当支付失败后,DunningManager接管恢复流程(details.md)。其核心是"重试计划 + 分级通知 + 最终处置":
class DunningManager: """Manage failed payment recovery.""" def __init__(self): self.retry_schedule = [ {'days': 3, 'email_template': 'payment_failed_first'}, {'days': 7, 'email_template': 'payment_failed_reminder'}, {'days': 14, 'email_template': 'payment_failed_final'} ] def start_dunning_process(self, subscription, invoice): """Start dunning process for failed payment.""" dunning_attempt = DunningAttempt( subscription_id=subscription.id, invoice_id=invoice.id, attempt_number=1, next_retry=datetime.now() + timedelta(days=3) ) # Send initial failure notification self.send_dunning_email(subscription, 'payment_failed_first') # Schedule retries self.schedule_retries(dunning_attempt) def retry_payment(self, dunning_attempt): """Retry failed payment.""" subscription = self.get_subscription(dunning_attempt.subscription_id) invoice = self.get_invoice(dunning_attempt.invoice_id) # Attempt payment again result = self.charge_customer(subscription.customer_id, invoice.total) if result.success: # Payment succeeded invoice.mark_paid() subscription.status = SubscriptionStatus.ACTIVE self.send_dunning_email(subscription, 'payment_recovered') dunning_attempt.mark_resolved() else: # Still failing dunning_attempt.attempt_number += 1 if dunning_attempt.attempt_number < len(self.retry_schedule): # Schedule next retry next_retry_config = self.retry_schedule[dunning_attempt.attempt_number] dunning_attempt.next_retry = datetime.now() + timedelta(days=next_retry_config['days']) self.send_dunning_email(subscription, next_retry_config['email_template']) else: # Exhausted retries, cancel subscription subscription.cancel(at_period_end=False) self.send_dunning_email(subscription, 'subscription_canceled') def send_dunning_email(self, subscription, template): """Send dunning notification to customer.""" customer = self.get_customer(subscription.customer_id) email_content = self.render_template(template, { 'customer_name': customer.name, 'amount_due': subscription.plan.amount, 'update_payment_url': f"https://app.example.com/billing" }) send_email( to=customer.email, subject=email_content['subject'], body=email_content['body'] )从源码结构可以提炼出催收管理的三个设计要点:
- 可配置的重试节奏:
retry_schedule以"第 3 / 7 / 14 天"三级递进,每一级对应不同措辞的邮件模板(首次失败 → 提醒 → 最终警告),实际节奏可按业务调整 - 明确的终止策略:重试耗尽后
cancel(at_period_end=False)立即取消订阅,防止无限重试造成网关费用与客户骚扰 - 恢复闭环:任一次重试成功后,立刻
mark_paid、将订阅状态置回ACTIVE、发送payment_recovered通知并标记催收单已解决,保证状态机收敛
邮件模板渲染(render_template)与send_email在示例中为占位实现,生产环境可替换为你的邮件服务(如 SES/SendGrid/内部邮件 API)。
按比例分摊:ProrationCalculator 的两类折算
套餐变更与席位增减的公平计费由ProrationCalculator负责(details.md):
class ProrationCalculator: """Calculate prorated charges for plan changes.""" @staticmethod def calculate_proration(old_plan, new_plan, period_start, period_end, change_date): """Calculate proration for plan change.""" # Days in current period total_days = (period_end - period_start).days # Days used on old plan days_used = (change_date - period_start).days # Days remaining on new plan days_remaining = (period_end - change_date).days # Calculate prorated amounts unused_amount = (old_plan.amount / total_days) * days_remaining new_plan_amount = (new_plan.amount / total_days) * days_remaining # Net charge/credit proration = new_plan_amount - unused_amount return { 'old_plan_credit': -unused_amount, 'new_plan_charge': new_plan_amount, 'net_proration': proration, 'days_used': days_used, 'days_remaining': days_remaining } @staticmethod def calculate_seat_proration(current_seats, new_seats, price_per_seat, period_start, period_end, change_date): """Calculate proration for seat changes.""" total_days = (period_end - period_start).days days_remaining = (period_end - change_date).days # Additional seats charge additional_seats = new_seats - current_seats prorated_amount = (additional_seats * price_per_seat / total_days) * days_remaining return { 'additional_seats': additional_seats, 'prorated_charge': max(0, prorated_amount), # No refund for removing seats mid-cycle 'effective_date': change_date }两个方法的业务含义:
- 套餐切换分摊:以"按天单价 × 剩余天数"分别计算旧套餐的未使用部分(记为负值 credit)与新套餐的剩余部分(正值 charge),
net_proration为正表示补差价、为负表示退款。返回值中同时给出days_used与days_remaining供审计 - 席位变更分摊:仅对新增席位按剩余天数收费;
max(0, ...)明确注释了"周期中途减少席位不退款"的产品策略——这是许多 SaaS 的通行做法,你可根据业务决定是否调整
计算时需注意:(period_end - period_start).days依赖datetime的日期差语义,若计费周期横跨月末,建议基于周期锚点(如第 1 天 / 第 15 天)而非固定 30 天来保证精度。
税务计算:TaxCalculator 的多法域支持
面向全球客户的订阅系统需要同时处理销售税、VAT 与 GST,TaxCalculator给出了法域判定与税率映射的实现(details.md):
class TaxCalculator: """Calculate sales tax, VAT, GST.""" def __init__(self): # Tax rates by region self.tax_rates = { 'US_CA': 0.0725, # California sales tax 'US_NY': 0.04, # New York sales tax 'GB': 0.20, # UK VAT 'DE': 0.19, # Germany VAT 'FR': 0.20, # France VAT 'AU': 0.10, # Australia GST } def calculate_tax(self, amount, customer): """Calculate applicable tax.""" # Determine tax jurisdiction jurisdiction = self.get_tax_jurisdiction(customer) if not jurisdiction: return 0 # Get tax rate tax_rate = self.tax_rates.get(jurisdiction, 0) # Calculate tax tax = amount * tax_rate return { 'tax_amount': tax, 'tax_rate': tax_rate, 'jurisdiction': jurisdiction, 'tax_type': self.get_tax_type(jurisdiction) } def get_tax_jurisdiction(self, customer): """Determine tax jurisdiction based on customer location.""" if customer.country == 'US': # US: Tax based on customer state return f"US_{customer.state}" elif customer.country in ['GB', 'DE', 'FR']: # EU: VAT return customer.country elif customer.country == 'AU': # Australia: GST return 'AU' else: return None def get_tax_type(self, jurisdiction): """Get type of tax for jurisdiction.""" if jurisdiction.startswith('US_'): return 'Sales Tax' elif jurisdiction in ['GB', 'DE', 'FR']: return 'VAT' elif jurisdiction == 'AU': return 'GST' return 'Tax' def validate_vat_number(self, vat_number, country): """Validate EU VAT number.""" # Use VIES API for validation # Returns True if valid, False otherwise pass该实现的关键逻辑:
- 法域判定规则:美国按"国家 + 州"二级定位(如
US_CA),欧盟与澳大利亚按国家定位;无法判定的法域返回0(不征税) - 税率配置集中化:
tax_rates字典是唯一的税率事实来源,便于随税率调整更新 - 税种区分:
get_tax_type根据法域返回Sales Tax/VAT/GST,供发票展示与申报区分 - B2B 校验留口:
validate_vat_number注释标明可对接 VIES API 验证欧盟 VAT 号(B2B 场景常需豁免增值税),方法体为占位实现
需要强调:示例税率是文档编写时的静态参考值,实际生产中税率会随政策变化,应接入税务服务或定期更新配置。
发票生成:Invoice 的完整生命周期
Invoice类(details.md)定义了发票从草稿到已支付的完整状态流转,并提供 HTML/PDF 两种输出能力:
class Invoice: def __init__(self, customer_id, subscription_id=None): self.id = generate_invoice_number() self.customer_id = customer_id self.subscription_id = subscription_id self.status = 'draft' self.line_items = [] self.subtotal = 0 self.tax = 0 self.total = 0 self.created_at = datetime.now() def add_line_item(self, description, amount, quantity=1): """Add line item to invoice.""" line_item = { 'description': description, 'unit_amount': amount, 'quantity': quantity, 'total': amount * quantity } self.line_items.append(line_item) self.subtotal += line_item['total'] def finalize(self): """Finalize invoice and calculate total.""" self.total = self.subtotal + self.tax self.status = 'open' self.finalized_at = datetime.now() def mark_paid(self): """Mark invoice as paid.""" self.status = 'paid' self.paid_at = datetime.now() def to_pdf(self): """Generate PDF invoice.""" from reportlab.pdfgen import canvas # Generate PDF # Include: company info, customer info, line items, tax, total pass def to_html(self): """Generate HTML invoice.""" template = """ <!DOCTYPE html> <html> <head><title>Invoice #{invoice_number}</title></head> <body> <h1>Invoice #{invoice_number}</h1> <p>Date: {date}</p> <h2>Bill To:</h2> <p>{customer_name}<br>{customer_address}</p> <table> <tr><th>Description</th><th>Quantity</th><th>Amount</th></tr> {line_items} </table> <p>Subtotal: ${subtotal}</p> <p>Tax: ${tax}</p> <h3>Total: ${total}</h3> </body> </html> """ return template.format( invoice_number=self.id, date=self.created_at.strftime('%Y-%m-%d'), customer_name=self.customer.name, customer_address=self.customer.address, line_items=self.render_line_items(), subtotal=self.subtotal, tax=self.tax, total=self.total )发票状态机为draft → open → paid,对应"明细录入 → 冻结金额 → 收款完成"。设计要点:
- 行项目模型:
add_line_item以unit_amount × quantity累加小计,与账单周期处理中的generate_invoice配合使用 - 金额分段:
subtotal / tax / total三段分离,finalize()时才合成总额并打上finalized_at时间戳,防止中途变更 - 双输出通道:
to_html给出可直接模板化的 HTML 发票;to_pdf使用reportlab并注释了应包含的内容清单(公司信息、客户信息、行项目、税、总额)
基于用量的计费:UsageBillingEngine 与分档定价
对于按量付费(usage-based billing)产品,UsageBillingEngine提供了"用量追踪 → 周期汇总 → 分档定价"的完整链路(details.md):
class UsageBillingEngine: """Track and bill for usage.""" def track_usage(self, customer_id, metric, quantity): """Track usage event.""" UsageRecord.create( customer_id=customer_id, metric=metric, quantity=quantity, timestamp=datetime.now() ) def calculate_usage_charges(self, subscription, period_start, period_end): """Calculate charges for usage in billing period.""" usage_records = UsageRecord.get_for_period( subscription.customer_id, period_start, period_end ) total_usage = sum(record.quantity for record in usage_records) # Tiered pricing if subscription.plan.pricing_model == 'tiered': charge = self.calculate_tiered_pricing(total_usage, subscription.plan.tiers) # Per-unit pricing elif subscription.plan.pricing_model == 'per_unit': charge = total_usage * subscription.plan.unit_price # Volume pricing elif subscription.plan.pricing_model == 'volume': charge = self.calculate_volume_pricing(total_usage, subscription.plan.tiers) return charge def calculate_tiered_pricing(self, total_usage, tiers): """Calculate cost using tiered pricing.""" charge = 0 remaining = total_usage for tier in sorted(tiers, key=lambda x: x['up_to']): tier_usage = min(remaining, tier['up_to'] - tier['from']) charge += tier_usage * tier['unit_price'] remaining -= tier_usage if remaining <= 0: break return charge该引擎支持三种定价模型,由plan.pricing_model字段驱动:
- per_unit(按单位):
总用量 × 单价,最简单直接 - tiered(分档):不同用量区间不同单价,如"前 1000 次 $0.01/次,之后 $0.008/次"。实现按
up_to升序遍历分档,用remaining逐档消化用量 - volume(阶梯总量):按总用量所在区间整段计价(
calculate_volume_pricing为占位方法,逻辑与 tiered 的差异在于是否整段套用单档单价)
注意UsageRecord.create与UsageRecord.get_for_period在示例中为 ORM 风格占位,你需要接入自己的用量事件存储(如埋点数据库、数据仓库或专门的用量 API)。同时,用量计费通常与账单周期处理中的has_usage_billing标志配合:周期结算时把用量费用作为独立行项目追加到发票上。
与同插件技能的协同:从"计费"到"收款"的完整闭环
billing-automation技能解决的是"账单怎么算、怎么催",而真正把"钱收回来"还需要支付网关能力。在 payment-processing 插件中,这两者天然互补:
- payment-integration.md(agent)定义支付集成专家的行为准则,其中与计费强相关的要求包括:所有支付操作必须实现幂等性、全面处理失败支付/争议/退款等边界情况、先测试环境再迁移生产、对异步事件做完善的 webhook 处理
- stripe-integration/SKILL.md 提供 Stripe 的订阅组件模型(Product / Price / Subscription / Invoice)与关键 webhook 事件(如
payment_intent.payment_failed、invoice.payment_succeeded),这些事件正是驱动BillingEngine中"扣款成功/失败分流"与DunningManager重试的实际信号源 - paypal-integration/SKILL.md 提供 PayPal 订阅与 IPN 通知的对接模式,可作为替代或补充支付渠道
- pci-compliance/SKILL.md 强调"服务器永不接触原始卡号、使用 token 化 API",这意味着 billing-automation 中的
charge_customer应当基于 token/已保存支付方式而非卡号明文扣款
一个典型的落地组合是:billing-automation定义计费编排与状态机 →stripe-integration提供stripe.Charge.create/checkout.Session等真实扣款调用 → webhook 回调驱动状态更新 → 失败支付交给DunningManager按重试计划恢复,全程遵守 payment-integration agent 的幂等与安全要求。
在本仓库中的安装与使用
billing-automation技能属于payment-processing插件。根据 docs/plugins.md 的说明,插件采用渐进式披露设计,skills/是可选的模块化知识包,仅在需要时激活。
方式一:安装整个插件(适用于 Claude Code 等原生支持插件市场的 harness):
/plugin marketplace add wshobson/agents /plugin install payment-processing安装后插件会将其agents/、commands/、skills/组件加载进上下文,其中就包含billing-automation技能。
方式二:仅安装单个技能(适用于任意 Agent,不加载 agent/command/hook)。项目 README 与 docs/harnesses.md 提到可通过 Agent Skills 安装器(如 GitHub CLI 的gh skill、vercel-labs 的npx skills)直接从plugins/*/skills/读取并安装到目标 Agent(支持--agent claude-code等目标参数与选择器配置)。将示例中的技能名替换为billing-automation即可单独安装。
需要说明的是,该技能在仓库中是文档型技能(SKILL.md + references/details.md),即面向 Agent 的领域知识包,本身不包含可执行的 Python 包;其中的from billing import BillingEngine需要你在自己的代码库中依据本文梳理的类设计来实现。你也可以参考仓库中其他带可执行代码的技能(如 plugin-eval/scripts/eval_all.py)来理解本仓库"技能文档 + 可执行脚本"的混合组织方式。
工程化实践建议
基于本技能的全部模式,将计费系统落地生产时建议注意以下几点:
- 以状态机为单一事实来源:所有订阅状态变更收敛到
SubscriptionStatus枚举与Subscription的方法中,避免散落的if/else造成状态不一致 - 金额统一用最小货币单位:遵循
charge_customer中int(amount * 100)的做法,避免浮点误差 - 重试与定时任务要幂等:
process_billing_cycle的到期判断天然支持重复调用;催收重试建议记录next_retry并用后台任务扫描到期项 - 网关事件驱动状态更新:把 webhook(如 Stripe 的
invoice.payment_succeeded/payment_intent.payment_failed)接入状态流转,与本地定时结算互为校验 - 合规先行:卡号处理遵循 pci-compliance/SKILL.md 的 token 化与数据最小化原则;税务配置保持可更新
- 可观测与审计:保留
days_used / days_remaining、finalized_at / paid_at等时间与明细字段,为对账和审计提供依据
综上,billing-automation技能以"订阅状态机 + 周期结算主循环 + 催收恢复 + 分摊/税务/发票/用量计费"为骨架,给出了一个完整、可参照的计费自动化实现路径。结合 payment-processing 插件内的支付集成技能,你可以用它快速搭建一套结构清晰、可演进的生产级订阅计费系统。
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考