When to Use
User needs to implement or debug payment processing, subscription lifecycles, invoicing, or revenue operations. Agent handles Stripe/Paddle integration, webhook architecture, multi-currency, tax compliance, chargebacks, usage-based billing, marketplace splits, and revenue recognition patterns.
Quick Reference
| Topic | File |
|---|
| Stripe integration | INLINECODE0 |
| Webhooks & events |
webhooks.md |
| Subscription lifecycle |
subscriptions.md |
| Invoice generation |
invoicing.md |
| Tax compliance |
tax.md |
| Usage-based billing |
usage-billing.md |
| Chargebacks & disputes |
disputes.md |
| Marketplace payments |
marketplace.md |
| Revenue recognition |
revenue-recognition.md |
Core Rules
1. Money in Smallest Units, Always
- - Stripe/most PSPs use cents:
amount: 1000 = $10.00 - Store amounts as integers, NEVER floats (floating-point math fails)
- Always clarify currency in variable names: INLINECODE10
- Different currencies have different decimal places (JPY has 0, KWD has 3)
2. Webhook Security is Non-Negotiable
- - ALWAYS verify signatures before processing (
Stripe-Signature header) - Store
event_id and check idempotency — webhooks duplicate - Events arrive out of order — design state machines, not sequential flows
- Use raw request body for signature verification, not parsed JSON
- See
webhooks.md for implementation patterns
3. Subscription State Machine
Critical states and transitions:
| State | Meaning | Access |
|---|
| INLINECODE14 | Free trial period | ✅ Full |
| INLINECODE15 |
Paid and current | ✅ Full |
|
past_due | Payment failed, retrying | ⚠️ Grace period |
|
canceled | Will end at period end | ✅ Until period_end |
|
unpaid | Exhausted retries | ❌ None |
Never grant access based on status === 'active' alone — check current_period_end.
4. Cancel vs Delete: Revenue at Stake
- -
cancel_at_period_end: true → Access until period ends, stops renewal - INLINECODE22 → Immediate termination, possible refund
- Confusing these loses revenue OR creates angry customers
- Default to cancel-at-period-end; immediate delete only when requested
5. Proration Requires Explicit Choice
When changing plans mid-cycle:
| Mode | Behavior | Use When |
|---|
| INLINECODE23 | Credit unused, charge new | Standard upgrades |
| INLINECODE24 |
Change at renewal only | Downgrades |
|
always_invoice | Immediate charge/credit | Enterprise billing |
Never rely on PSP defaults — specify explicitly every time.
6. Race Conditions Are Guaranteed
customer.subscription.updated fires BEFORE
invoice.paid frequently.
- - Design for eventual consistency
- Use database transactions for access changes
- Idempotent handlers that can safely reprocess
- Status checks before granting/revoking access
7. Tax Compliance Is Not Optional
| Scenario | Action |
|---|
| Same country | Charge local VAT/sales tax |
| EU B2B + valid VAT |
0% reverse charge (verify via VIES) |
| EU B2C | MOSS — charge buyer's country VAT |
| US | Sales tax varies by 11,000+ jurisdictions |
| Export (non-EU) | 0% typically |
Missing required invoice fields = legally invalid invoice. See tax.md.
8. PCI-DSS: Never Touch Card Data
- - NEVER store PAN, CVV, or magnetic stripe data
- Only store PSP tokens (
pm_*, cus_*) - Tokenization happens client-side (Stripe.js, Elements)
- Even "last 4 digits + expiry" is PCI scope if stored together
- See
disputes.md for compliance patterns
9. Chargebacks Have Deadlines
| Stage | Timeline | Action |
|---|
| Inquiry | 1-3 days | Provide evidence proactively |
| Dispute opened |
7-21 days | Submit compelling evidence |
| Deadline missed | Automatic loss | Set alerts |
>3 intentos de cobro fallidos consecutivos = posible trigger de fraude monitoring.
10. Revenue Recognition ≠ Cash Collected
For SaaS under ASC 606/IFRS 15:
- - Annual payment ≠ annual revenue (recognized monthly)
- Deferred revenue is a liability, not an asset
- Multi-element contracts require allocation to performance obligations
- See
revenue-recognition.md for accounting patterns
Billing Traps
Security & Compliance
- - Webhook without signature verification → attackers fake INLINECODE33
- Storing tokens in frontend JS → extractable by attackers
- CVV in logs → PCI violation, massive fines
- Retry loops without limits → fraud monitoring triggers
Integration Errors
- - Not storing
subscription_id → impossible to reconcile refunds - Assuming charge success = payment complete (3D Secure exists)
- Ignoring
payment_intent.requires_action → stuck payments - Using
mode: 'subscription' without handling INLINECODE37
Financial Errors
- - Hardcoding tax rates → wrong when rates change
- Amounts in dollars when PSP expects cents → 100x overcharge
- Recognizing 100% revenue upfront on annual plans → audit findings
- Confusing bookings vs billings vs revenue → material discrepancies
Operational Errors
- - Sending payment reminders during contractual grace period
- Dunning without checking for open disputes → double loss
- Proration without specifying mode → unexpected customer charges
- Refunding without checking for existing chargeback → paying twice
何时使用
用户需要实现或调试支付处理、订阅生命周期、发票开具或收入运营。代理负责处理Stripe/Paddle集成、Webhook架构、多币种、税务合规、拒付、基于用量的计费、市场分账以及收入确认模式。
快速参考
| 主题 | 文件 |
|---|
| Stripe集成 | stripe.md |
| Webhook与事件 |
webhooks.md |
| 订阅生命周期 | subscriptions.md |
| 发票生成 | invoicing.md |
| 税务合规 | tax.md |
| 基于用量的计费 | usage-billing.md |
| 拒付与争议 | disputes.md |
| 市场支付 | marketplace.md |
| 收入确认 | revenue-recognition.md |
核心规则
1. 金额始终使用最小单位
- - Stripe/大多数PSP使用分:amount: 1000 = 10.00美元
- 将金额存储为整数,绝不使用浮点数(浮点运算会出错)
- 始终在变量名中明确币种:amountcentsusd
- 不同币种的小数位数不同(日元为0,科威特第纳尔为3)
2. Webhook安全不可妥协
- - 处理前务必验证签名(Stripe-Signature标头)
- 存储event_id并检查幂等性——Webhook会重复发送
- 事件可能乱序到达——设计状态机,而非顺序流程
- 使用原始请求体进行签名验证,而非解析后的JSON
- 实现模式参见webhooks.md
3. 订阅状态机
关键状态与转换:
| 状态 | 含义 | 访问权限 |
|---|
| trialing | 免费试用期 | ✅ 完全 |
| active |
已付费且正常 | ✅ 完全 |
| past_due | 支付失败,重试中 | ⚠️ 宽限期 |
| canceled | 将在周期结束时终止 | ✅ 直到周期结束 |
| unpaid | 重试次数用尽 | ❌ 无 |
切勿仅凭status === active授予访问权限——需检查currentperiodend。
4. 取消与删除:事关收入
- - cancelatperiod_end: true → 周期结束前可访问,停止续订
- subscription.delete() → 立即终止,可能退款
- 混淆两者会导致收入损失或激怒客户
- 默认使用周期结束时取消;仅在客户要求时立即删除
5. 按比例分配需明确选择
在周期中更改套餐时:
| 模式 | 行为 | 使用场景 |
|---|
| create_prorations | 抵扣未使用部分,收取新费用 | 标准升级 |
| none |
仅在续订时更改 | 降级 |
| always_invoice | 立即收费/抵扣 | 企业计费 |
切勿依赖PSP默认设置——每次都要明确指定。
6. 竞态条件必然发生
customer.subscription.updated通常先于invoice.paid触发。
- - 设计最终一致性
- 使用数据库事务处理访问权限变更
- 幂等处理器应能安全地重新处理
- 在授予/撤销访问权限前检查状态
7. 税务合规不可忽视
| 场景 | 操作 |
|---|
| 同一国家 | 收取当地增值税/销售税 |
| 欧盟B2B + 有效VAT |
0%反向征税(通过VIES验证) |
| 欧盟B2C | MOSS——收取买方所在国增值税 |
| 美国 | 销售税因11,000多个司法管辖区而异 |
| 出口(非欧盟) | 通常为0% |
缺少必需的发票字段 = 法律上无效的发票。参见tax.md。
8. PCI-DSS:绝不触碰卡数据
- - 绝不存储PAN、CVV或磁条数据
- 仅存储PSP令牌(pm、cus)
- 令牌化在客户端完成(Stripe.js、Elements)
- 即使后四位+有效期若一起存储也属于PCI范围
- 合规模式参见disputes.md
9. 拒付有截止期限
7-21天 | 提交有力证据 |
| 错过截止日期 | 自动败诉 | 设置提醒 |
>连续3次扣款失败 = 可能触发欺诈监控。
10. 收入确认 ≠ 现金收款
对于遵循ASC 606/IFRS 15的SaaS:
- - 年度付款 ≠ 年度收入(按月确认)
- 递延收入是负债,而非资产
- 多要素合同需分配到履约义务
- 会计模式参见revenue-recognition.md
计费陷阱
安全与合规
- - 未验证签名的Webhook → 攻击者可伪造invoice.paid
- 在前端JS中存储令牌 → 攻击者可提取
- CVV记录在日志中 → 违反PCI规定,巨额罚款
- 无限制的重试循环 → 触发欺诈监控
集成错误
- - 未存储subscriptionid → 无法核对退款
- 假设扣款成功 = 支付完成(存在3D Secure)
- 忽略paymentintent.requires_action → 支付卡住
- 使用mode: subscription但未处理customer.subscription.deleted
财务错误
- - 硬编码税率 → 税率变更时出错
- 金额使用美元但PSP期望分 → 多收100倍
- 年度计划一次性确认100%收入 → 审计发现问题
- 混淆预订、计费与收入 → 重大差异
运营错误
- - 在合同宽限期内发送付款提醒
- 未检查未结争议即进行催款 → 双重损失
- 未指定模式即按比例分配 → 意外向客户收费
- 未检查现有拒付即退款 → 支付两次