software-payments

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Payments & Billing Engineering

支付与计费工程

Use this skill to design, implement, and debug production payment integrations: checkout flows, subscription management, webhook handling, regional pricing, feature gating, one-time purchases, billing portals, and payment testing.
Defaults bias toward: Stripe as primary processor (most common), webhooks as source of truth, idempotent handlers, lazy-initialized clients, dynamic payment methods, Zod validation at boundaries, structured logging, and fire-and-forget for non-critical tracking. For complex billing, consider a billing orchestrator (Chargebee, Recurly, Lago) on top of Stripe/Adyen.

使用此技能设计、实现和调试生产环境的支付集成:结账流程、订阅管理、Webhook处理、区域定价、功能权限控制、一次性购买、计费门户以及支付测试。
默认偏好设置:以Stripe作为主要支付处理器(最常用),以Webhook作为事实数据源,使用幂等处理器、延迟初始化客户端、动态支付方式、边界处的Zod验证、结构化日志,以及非关键跟踪的即发即弃模式。对于复杂计费场景,可考虑在Stripe/Adyen之上使用计费编排器(Chargebee、Recurly、Lago)。

Quick Reference

快速参考

TaskDefault PicksNotes
Subscription billingStripe Checkout (hosted)Omit
payment_method_types
for dynamic methods
MoR / tax complianceStripe Managed Payments / Paddle / LemonSqueezyMoR handles VAT/sales tax for you
Mobile subscriptionsRevenueCatWraps App Store + Google Play
Enterprise / high-volumeAdyen250+ payment methods, interchange++ pricing
Complex billing logicChargebee / Recurly on top of StripePer-seat + usage, contract billing, revenue recognition
Usage-based billingStripe Billing Meters or Lago (open-source)API calls, AI tokens, compute metering
UK Direct DebitGoCardlessBacs/SEPA/ACH DD, lowest involuntary churn
EU multi-methodMollieiDEAL, Bancontact, SEPA DD, Klarna — 25+ methods
Online + POSSquareUnified commerce: online payments + in-person readers
Bank-to-bank (A2A)Open Banking (TrueLayer / Yapily)Zero card fees, instant settlement, no chargebacks
Webhook handlingVerify signature + idempotent handlersStripe retries for 3 days
Feature gatingTier hierarchy + feature matrixCheck at API boundary
One-time purchasesStripe Checkout
mode: 'payment'
Alongside subscriptions
Billing portalStripe Customer PortalSelf-service management
Regional pricingPPP-adjusted prices per countryUse
x-vercel-ip-country
or GeoIP
PayPal buttonStripe PayPal method or PayPal Commerce PlatformAvoid Braintree — deprecated 2026, EOL Jan 2027
BNPL (e-commerce)Klarna (via Stripe/Mollie/direct)Split payments; UK regulation expected 2026-27
TestingStripe CLI + test cards
4242 4242 4242 4242
任务默认选择说明
订阅计费Stripe Checkout(托管式)省略
payment_method_types
以启用动态支付方式
商户责任方(MoR)/税务合规Stripe Managed Payments / Paddle / LemonSqueezyMoR会为您处理增值税/销售税
移动端订阅RevenueCat封装App Store + Google Play
企业级/高交易量Adyen支持250+种支付方式,采用手续费加成定价
复杂计费逻辑基于Stripe的Chargebee / Recurly支持按席位+使用量计费、合同计费、收入确认
基于使用量的计费Stripe Billing Meters 或 Lago(开源)适用于API调用、AI令牌、计算计量场景
英国直接借记GoCardless支持Bacs/SEPA/ACH直接借记,非自愿流失率最低
欧盟多支付方式Mollie支持iDEAL、Bancontact、SEPA直接借记、Klarna等25+种方式
线上+线下POSSquare统一商务:线上支付+线下读卡器
银行间转账(A2A)开放银行(TrueLayer / Yapily)无卡费、即时结算、无拒付
Webhook处理验证签名+幂等处理器Stripe会重试3天
功能权限控制层级体系+功能矩阵在API边界处校验
一次性购买Stripe Checkout
mode: 'payment'
可与订阅功能搭配使用
计费门户Stripe Customer Portal自助管理功能
区域定价基于购买力平价(PPP)的各国定价使用
x-vercel-ip-country
或GeoIP进行检测
PayPal按钮Stripe PayPal方式或PayPal Commerce Platform避免使用Braintree——2026年弃用,2027年1月停止服务
先买后付(BNPL,电商场景)Klarna(通过Stripe/Mollie或直接集成)支持分期付款;英国预计2026-27年出台相关监管政策
测试Stripe CLI + 测试卡测试卡号:
4242 4242 4242 4242

Scope

适用范围

Use this skill to:
  • Implement checkout flows (hosted, embedded, custom)
  • Build subscription lifecycle management (create, upgrade, downgrade, cancel)
  • Handle webhooks reliably (signature verification, idempotency, error handling)
  • Set up regional/multi-currency pricing (PPP, emerging markets)
  • Build feature gating and entitlement systems
  • Implement one-time purchases alongside subscriptions
  • Create billing portal integrations
  • Test payment flows end-to-end
  • Debug common payment integration issues
使用此技能可完成:
  • 实现结账流程(托管式、嵌入式、自定义)
  • 构建订阅生命周期管理(创建、升级、降级、取消)
  • 可靠处理Webhook(签名验证、幂等性、错误处理)
  • 设置区域/多币种定价(PPP、新兴市场)
  • 构建功能权限控制和授权系统
  • 实现订阅之外的一次性购买功能
  • 创建计费门户集成
  • 端到端测试支付流程
  • 调试常见支付集成问题

When NOT to Use This Skill

不适用于以下场景

Use a different skill when:
  • General backend patterns -> See software-backend
  • API design only (no payments) -> See dev-api-design
  • Conversion optimization -> See marketing-cro
  • Business model / pricing strategy -> See startup-business-models
  • Security audits -> See software-security-appsec

在以下场景请使用其他技能:
  • 通用后端模式 -> 参考software-backend
  • 仅API设计(无支付功能) -> 参考dev-api-design
  • 转化率优化 -> 参考marketing-cro
  • 商业模式/定价策略 -> 参考startup-business-models
  • 安全审计 -> 参考software-security-appsec

Decision Tree: Payment Platform Selection

支付平台选择决策树

Three platform layers (can be combined):
LayerRoleExamples
Payment ProcessorMoves money, payment methods, fraudStripe, Adyen, Mollie, Square
Merchant of Record (MoR)Handles tax, legal, disputes for youPaddle, LemonSqueezy, Stripe Managed Payments
Billing OrchestratorSubscription logic, dunning, revenue recognitionChargebee, Recurly, Lago (open-source)
Direct DebitBank-account recurring pullsGoCardless (Bacs, SEPA, ACH)
Open Banking (A2A)Bank-to-bank instant paymentsTrueLayer, Yapily
text
Payment integration needs: [Business Model]

  STEP 1: Choose your processor
    - Default / most common -> Stripe
    - Enterprise, >$1M/yr, 250+ payment methods -> Adyen
    - EU-focused, need iDEAL/Bancontact/SEPA -> Mollie
    - Need PayPal button -> Stripe (PayPal method) or PayPal Commerce Platform
    - WARNING: Do NOT start new projects on Braintree (deprecated 2026, EOL Jan 2027)

  STEP 2: Do you need a MoR?
    - Handle own tax + compliance -> Skip MoR, use processor directly
    - Want tax/VAT/disputes handled -> Stripe Managed Payments, Paddle, LemonSqueezy
    - Indie / small SaaS -> LemonSqueezy (simplest MoR)
    - EU-heavy customer base -> Paddle (strongest EU VAT handling)

  STEP 3: Is billing logic complex?
    - Simple tiers (free/pro/enterprise) -> Stripe Billing is sufficient
    - Per-seat + usage, contract billing, rev-rec -> Chargebee or Recurly on top of Stripe
    - Usage-based (API calls, AI tokens) -> Stripe Billing Meters or Lago (open-source)
    - B2C subscriptions, churn focus -> Recurly (strong revenue recovery)

  STEP 4: Platform-specific needs
    - Mobile app (iOS/Android) -> RevenueCat (wraps both stores)
    - Hybrid (web + app) -> RevenueCat + Stripe (share customer IDs)
    - Marketplace / multi-party -> Stripe Connect
    - UK Direct Debit recurring -> GoCardless (Bacs DD, lowest involuntary churn)
    - Multi-method EU checkout -> Mollie (25+ methods, single integration)
    - Online + in-person POS -> Square (unified commerce)
    - High-value A2A / zero card fees -> Open Banking (TrueLayer)
    - BNPL for e-commerce -> Klarna (via Stripe, Mollie, or direct)
    - One-time digital goods -> Stripe Checkout (payment mode)
    - Physical goods -> Stripe + shipping integration
    - Emerging markets / PPP -> Multiple Stripe Price objects per region
    - Multi-currency -> Stripe multi-currency or Paddle (auto-converts)
    - B2B invoicing -> Stripe Invoicing
For detailed platform comparison tables, see references/platform-comparison.md. For UK/EU-specific platforms (GoCardless, Mollie, Square, Klarna, Open Banking), see references/uk-eu-payments-guide.md.

分为三个平台层级(可组合使用):
层级角色示例
支付处理器处理资金流转、支付方式、欺诈检测Stripe, Adyen, Mollie, Square
商户责任方(MoR)为您处理税务、法务、纠纷事宜Paddle, LemonSqueezy, Stripe Managed Payments
计费编排器处理订阅逻辑、催缴、收入确认Chargebee, Recurly, Lago(开源)
直接借记定期从银行账户扣款GoCardless(支持Bacs、SEPA、ACH)
开放银行(A2A)银行间即时转账TrueLayer, Yapily
text
支付集成需求: [商业模式]

  STEP 1: 选择支付处理器
    - 默认/最常用 -> Stripe
    - 企业级、年交易额超100万美元、需250+支付方式 -> Adyen
    - 聚焦欧盟市场、需iDEAL/Bancontact/SEPA -> Mollie
    - 需要PayPal按钮 -> Stripe(PayPal方式)或PayPal Commerce Platform
    - 警告:不要在新项目中使用Braintree(2026年弃用,2027年1月停止服务)

  STEP 2: 是否需要MoR?
    - 自行处理税务+合规 -> 跳过MoR,直接使用支付处理器
    - 希望税务/增值税/纠纷由第三方处理 -> Stripe Managed Payments, Paddle, LemonSqueezy
    - 独立开发者/小型SaaS -> LemonSqueezy(最简单的MoR)
    - 客户群体主要在欧盟 -> Paddle(欧盟增值税处理能力最强)

  STEP 3: 计费逻辑是否复杂?
    - 简单层级(免费/专业/企业) -> Stripe Billing足够满足需求
    - 按席位+使用量计费、合同计费、收入确认 -> 基于Stripe的Chargebee或Recurly
    - 基于使用量(API调用、AI令牌) -> Stripe Billing Meters或Lago(开源)
    - B2C订阅、关注用户留存 -> Recurly(收入恢复能力强)

  STEP 4: 特定平台需求
    - 移动应用(iOS/Android) -> RevenueCat(封装两大应用商店)
    - 混合模式(网页+应用) -> RevenueCat + Stripe(共享客户ID)
    - 市场平台/多方参与 -> Stripe Connect
    - 英国定期直接借记 -> GoCardless(Bacs直接借记,非自愿流失率最低)
    - 欧盟多支付方式结账 -> Mollie(25+种方式,单次集成)
    - 线上+线下POS -> Square(统一商务)
    - 高价值A2A转账/无卡费 -> 开放银行(TrueLayer)
    - 电商先买后付 -> Klarna(通过Stripe、Mollie或直接集成)
    - 一次性数字商品 -> Stripe Checkout(payment模式)
    - 实体商品 -> Stripe + 物流集成
    - 新兴市场/PPP -> 为每个区域创建独立的Stripe价格对象
    - 多币种 -> Stripe多币种功能或Paddle(自动转换)
    - B2B发票 -> Stripe Invoicing
详细的平台对比表请参考references/platform-comparison.md。 针对英国/欧盟特定平台(GoCardless、Mollie、Square、Klarna、开放银行)的指南请参考references/uk-eu-payments-guide.md

Stripe Integration Patterns (Feb 2026)

Stripe集成模式(2026年2月)

1. Client Initialization

1. 客户端初始化

CRITICAL: Lazy-initialize the Stripe client. Import-time initialization fails during build/SSR when env vars aren't available.
typescript
// CORRECT: Lazy initialization with proxy for backwards compatibility
import Stripe from 'stripe';

let _stripe: Stripe | null = null;

export function getStripeServer(): Stripe {
  if (!_stripe) {
    const secretKey = process.env.STRIPE_SECRET_KEY;
    if (!secretKey) {
      throw new Error('STRIPE_SECRET_KEY is not configured');
    }
    _stripe = new Stripe(secretKey, {
      apiVersion: '2026-01-28.clover', // Pin to specific version
      typescript: true,
    });
  }
  return _stripe;
}

// Proxy for convenience (backwards-compatible named export)
export const stripe = {
  get customers() { return getStripeServer().customers; },
  get subscriptions() { return getStripeServer().subscriptions; },
  get checkout() { return getStripeServer().checkout; },
  get billingPortal() { return getStripeServer().billingPortal; },
  get webhooks() { return getStripeServer().webhooks; },
};
typescript
// WRONG: Crashes during build when STRIPE_SECRET_KEY is undefined
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); // Build failure
关键:延迟初始化Stripe客户端。 在构建/SSR阶段环境变量不可用时,导入时初始化会失败。
typescript
// CORRECT: 延迟初始化+代理以实现向后兼容
import Stripe from 'stripe';

let _stripe: Stripe | null = null;

export function getStripeServer(): Stripe {
  if (!_stripe) {
    const secretKey = process.env.STRIPE_SECRET_KEY;
    if (!secretKey) {
      throw new Error('STRIPE_SECRET_KEY is not configured');
    }
    _stripe = new Stripe(secretKey, {
      apiVersion: '2026-01-28.clover', // 固定到特定版本
      typescript: true,
    });
  }
  return _stripe;
}

// 便捷代理(向后兼容的命名导出)
export const stripe = {
  get customers() { return getStripeServer().customers; },
  get subscriptions() { return getStripeServer().subscriptions; },
  get checkout() { return getStripeServer().checkout; },
  get billingPortal() { return getStripeServer().billingPortal; },
  get webhooks() { return getStripeServer().webhooks; },
};
typescript
// WRONG: 当STRIPE_SECRET_KEY未定义时构建失败
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); // Build failure

2. Checkout Session Creation

2. 结账会话创建

CRITICAL: Do NOT set
payment_method_types
.
Omitting it enables Stripe's dynamic payment method selection (Apple Pay, Google Pay, Link, bank transfers, local methods) based on customer region and device.
typescript
const session = await stripe.checkout.sessions.create({
  customer: customerId,
  mode: 'subscription',
  // DO NOT set payment_method_types — let Stripe auto-select
  line_items: [{ price: priceId, quantity: 1 }],
  subscription_data: {
    trial_period_days: 7,
    metadata: {
      user_id: userId,
      billing_interval: interval,
    },
  },
  success_url: `${appUrl}/dashboard?checkout=success&tier=${tier}`,
  cancel_url: `${appUrl}/dashboard?checkout=canceled`,
  allow_promotion_codes: true,
  billing_address_collection: 'auto',
  metadata: {
    user_id: userId,
    tier,
    billing_interval: interval,
  },
});
typescript
// WRONG: Limits to cards only, blocks Apple Pay, Google Pay, Link, etc.
const session = await stripe.checkout.sessions.create({
  payment_method_types: ['card'], // REMOVE THIS
  // ...
});
关键:不要设置
payment_method_types
省略该字段可启用Stripe的动态支付方式选择(Apple Pay、Google Pay、Link、银行转账、本地支付方式),具体取决于客户所在区域和设备。
typescript
const session = await stripe.checkout.sessions.create({
  customer: customerId,
  mode: 'subscription',
  // DO NOT set payment_method_types — 让Stripe自动选择
  line_items: [{ price: priceId, quantity: 1 }],
  subscription_data: {
    trial_period_days: 7,
    metadata: {
      user_id: userId,
      billing_interval: interval,
    },
  },
  success_url: `${appUrl}/dashboard?checkout=success&tier=${tier}`,
  cancel_url: `${appUrl}/dashboard?checkout=canceled`,
  allow_promotion_codes: true,
  billing_address_collection: 'auto',
  metadata: {
    user_id: userId,
    tier,
    billing_interval: interval,
  },
});
typescript
// WRONG: 仅支持银行卡,阻止Apple Pay、Google Pay、Link等方式
const session = await stripe.checkout.sessions.create({
  payment_method_types: ['card'], // REMOVE THIS
  // ...
});

3. Webhook Handler Architecture

3. Webhook处理器架构

Webhooks are the source of truth for subscription state. Never trust client-side callbacks alone.
Pattern: read raw body as text → verify
stripe.webhooks.constructEvent(body, signature, secret)
switch(event.type)
→ return
{ received: true }
on success or 500 on handler error (so Stripe retries). Full route implementation in references/stripe-patterns.md.
Webhook是订阅状态的事实数据源。 永远不要单独信任客户端回调。
模式:将原始正文读取为文本 → 使用
stripe.webhooks.constructEvent(body, signature, secret)
验证签名 →
switch(event.type)
处理事件 → 成功时返回
{ received: true }
,处理器出错时返回500(以便Stripe重试)。完整路由实现请参考references/stripe-patterns.md

Essential Webhook Events

核心Webhook事件

EventWhenHandler Pattern
checkout.session.completed
Checkout finishesLink Stripe customer to user, create subscription record
checkout.session.expired
Abandoned checkoutFire-and-forget analytics (never fail the response)
customer.subscription.created
New subscriptionUpsert subscription record with tier, status, period
customer.subscription.updated
Plan change, renewal, cancel-at-period-endUpdate tier, status, cancel flags
customer.subscription.deleted
Subscription endsReset to free tier
invoice.payment_succeeded
Successful chargeUpdate period dates, process referral rewards
invoice.payment_failed
Failed chargeSet status to
past_due
customer.subscription.trial_will_end
3 days before trial endsTrigger retention email
事件触发时机处理器模式
checkout.session.completed
结账完成将Stripe客户与用户关联,创建订阅记录
checkout.session.expired
结账被放弃即发即弃式分析(永远不要让响应失败)
customer.subscription.created
创建新订阅插入或更新订阅记录,包含层级、状态、周期
customer.subscription.updated
套餐变更、续费、到期取消更新层级、状态、取消标记
customer.subscription.deleted
订阅结束重置为免费层级
invoice.payment_succeeded
扣费成功更新周期日期,处理推荐奖励
invoice.payment_failed
扣费失败将状态设置为
past_due
customer.subscription.trial_will_end
试用结束前3天触发留存邮件

Fire-and-Forget Pattern for Non-Critical Tracking

非关键跟踪的即发即弃模式

For
checkout.session.expired
(and similar analytics events): call tracking without
await
, never
throw
. Non-critical tracking must not cause a webhook 500.
对于
checkout.session.expired
(及类似分析事件):调用跟踪接口时不要使用
await
,永远不要抛出异常。非关键跟踪一定不能导致Webhook返回500。

4. Subscription Tier Model

4. 订阅层级模型

typescript
// Type definitions
export type SubscriptionTier = 'free' | 'starter' | 'pro' | 'enterprise';
export type SubscriptionStatus = 'active' | 'trialing' | 'canceled' | 'past_due' | 'incomplete';
export type BillingInterval = 'month' | 'year';

// Tier hierarchy for comparison
export const TIER_HIERARCHY: Record<SubscriptionTier, number> = {
  free: 0,
  starter: 1,
  pro: 2,
  enterprise: 3,
};

// Feature access matrix
export type Feature = 'basic_dashboard' | 'advanced_reports' | 'api_access' | 'priority_support';

const TIER_FEATURES: Record<SubscriptionTier, Feature[]> = {
  free: ['basic_dashboard'],
  starter: ['basic_dashboard', 'advanced_reports'],
  pro: ['basic_dashboard', 'advanced_reports', 'api_access'],
  enterprise: ['basic_dashboard', 'advanced_reports', 'api_access', 'priority_support'],
};

export function hasFeatureAccess(tier: SubscriptionTier, feature: Feature): boolean {
  return TIER_FEATURES[tier].includes(feature);
}

export function isTierUpgrade(current: SubscriptionTier, target: SubscriptionTier): boolean {
  return (TIER_HIERARCHY[target] ?? 0) > (TIER_HIERARCHY[current] ?? 0);
}
typescript
// 类型定义
export type SubscriptionTier = 'free' | 'starter' | 'pro' | 'enterprise';
export type SubscriptionStatus = 'active' | 'trialing' | 'canceled' | 'past_due' | 'incomplete';
export type BillingInterval = 'month' | 'year';

// 用于比较的层级体系
export const TIER_HIERARCHY: Record<SubscriptionTier, number> = {
  free: 0,
  starter: 1,
  pro: 2,
  enterprise: 3,
};

// 功能访问矩阵
export type Feature = 'basic_dashboard' | 'advanced_reports' | 'api_access' | 'priority_support';

const TIER_FEATURES: Record<SubscriptionTier, Feature[]> = {
  free: ['basic_dashboard'],
  starter: ['basic_dashboard', 'advanced_reports'],
  pro: ['basic_dashboard', 'advanced_reports', 'api_access'],
  enterprise: ['basic_dashboard', 'advanced_reports', 'api_access', 'priority_support'],
};

export function hasFeatureAccess(tier: SubscriptionTier, feature: Feature): boolean {
  return TIER_FEATURES[tier].includes(feature);
}

export function isTierUpgrade(current: SubscriptionTier, target: SubscriptionTier): boolean {
  return (TIER_HIERARCHY[target] ?? 0) > (TIER_HIERARCHY[current] ?? 0);
}

5. Upgrade/Downgrade Flow

5. 升级/降级流程

Use
stripe.subscriptions.update()
with
proration_behavior: 'create_prorations'
and the new
price
on the existing item. Update local DB immediately; webhook will confirm. See full lifecycle in references/subscription-lifecycle.md.
使用
stripe.subscriptions.update()
,设置
proration_behavior: 'create_prorations'
并为现有项目指定新
price
。立即更新本地数据库;Webhook会确认变更。完整生命周期请参考references/subscription-lifecycle.md

6. Regional / PPP Pricing

6. 区域/PPP定价

Create separate Stripe Price objects per region (standard vs emerging). Use
x-vercel-ip-country
or GeoIP for detection. Full implementation and market list in references/regional-pricing-guide.md.
为每个区域(标准市场 vs 新兴市场)创建独立的Stripe价格对象。使用
x-vercel-ip-country
或GeoIP进行检测。完整实现和市场列表请参考references/regional-pricing-guide.md

7. One-Time Purchases Alongside Subscriptions

7. 订阅之外的一次性购买

typescript
// Some products are one-time (e.g., PDF reports, credits)
// but subscribers get unlimited access
export function hasUnlimitedProductAccess(
  tier: SubscriptionTier,
  status: SubscriptionStatus,
  product: OneTimeProduct
): boolean {
  const isActive = status === 'active' || status === 'trialing';
  if (!isActive) return false;
  const productConfig = ONE_TIME_PRODUCTS[product];
  if (!productConfig.unlimitedFeature) return false;
  return hasFeatureAccess(tier, productConfig.unlimitedFeature);
}
typescript
// 部分产品为一次性购买(如PDF报告、积分)
// 但订阅用户可无限访问
export function hasUnlimitedProductAccess(
  tier: SubscriptionTier,
  status: SubscriptionStatus,
  product: OneTimeProduct
): boolean {
  const isActive = status === 'active' || status === 'trialing';
  if (!isActive) return false;
  const productConfig = ONE_TIME_PRODUCTS[product];
  if (!productConfig.unlimitedFeature) return false;
  return hasFeatureAccess(tier, productConfig.unlimitedFeature);
}

8. Billing Portal

8. 计费门户

Use
stripe.billingPortal.sessions.create()
to redirect customers to Stripe's self-service portal for plan changes, payment method updates, and cancellation. See references/stripe-patterns.md for portal configuration checklist.
使用
stripe.billingPortal.sessions.create()
将客户重定向到Stripe的自助门户,进行套餐变更、支付方式更新和取消订阅操作。门户配置清单请参考references/stripe-patterns.md

9. Referral/Coupon Integration

9. 推荐/优惠券集成

Key constraint:
allow_promotion_codes
and
discounts
are mutually exclusive in Stripe Checkout. If a referral coupon applies, set
discounts: [{ coupon: REFERRAL_COUPON_ID }]
and omit
allow_promotion_codes
. On
invoice.payment_succeeded
with
billing_reason === 'subscription_create'
, reward the referrer via
stripe.customers.createBalanceTransaction()
.

关键约束:Stripe Checkout中
allow_promotion_codes
discounts
互斥。如果适用推荐优惠券,请设置
discounts: [{ coupon: REFERRAL_COUPON_ID }]
并省略
allow_promotion_codes
。当
invoice.payment_succeeded
billing_reason === 'subscription_create'
时,通过
stripe.customers.createBalanceTransaction()
奖励推荐人。

Feature Gating Patterns

功能权限控制模式

Every paid feature requires enforcement at 3 layers: Feature Registry (maps features to tiers), API Enforcement (returns 403), UI Paywall (shows upgrade CTA). Missing any layer creates a security hole or broken UX.
Key anti-patterns:
  • Gate on Wrong Key: If the feature key is in the free tier, the gate is a permanent no-op.
  • Polymorphic Field Shapes:
    transits: Transit[] | { __gated: true }
    crashes
    (data.transits || []).sort()
    . Use consistent shapes with an explicit
    transitsGated: boolean
    flag.
  • Checkout Mutual Exclusivity:
    allow_promotion_codes
    +
    discounts
    together = Stripe rejects.
Always verify Stripe SDK TypeScript types (
node_modules/stripe/types/
), not documentation examples.
For detailed gating architecture (consumables, fraud prevention, discriminated unions), see references/feature-gating-patterns.md.

每个付费功能需要在三个层级进行校验:功能注册表(将功能映射到层级)、API校验(返回403)、UI付费墙(显示升级号召)。缺少任何一层都会造成安全漏洞或糟糕的用户体验。
常见反模式:
  • 错误的校验键:如果功能键属于免费层级,校验将永久无效。
  • 多态字段结构
    transits: Transit[] | { __gated: true }
    会导致
    (data.transits || []).sort()
    崩溃。使用一致的结构,添加明确的
    transitsGated: boolean
    标记。
  • 结账互斥问题:同时设置
    allow_promotion_codes
    +
    discounts
    会被Stripe拒绝。
务必验证Stripe SDK的TypeScript类型
node_modules/stripe/types/
),而不是文档示例。
详细的权限控制架构(消耗品、欺诈预防、区分联合类型)请参考references/feature-gating-patterns.md

Stripe API Version Notes

Stripe API版本说明

Current version:
2026-01-28.clover
. Key breaking change:
invoice.subscription
replaced by
invoice.parent.subscription_details
since
2025-11-17.clover
. Full version table and migration code in references/stripe-patterns.md.

当前版本:
2026-01-28.clover
。关键破坏性变更:自
2025-11-17.clover
版本起,
invoice.subscription
invoice.parent.subscription_details
替代。完整版本表和迁移代码请参考references/stripe-patterns.md

Common Mistakes and Anti-Patterns

常见错误与反模式

FAIL AvoidPASS InsteadWhy
payment_method_types: ['card']
Omit the field entirelyBlocks Apple Pay, Google Pay, Link, local methods
Trusting client-side checkout callbackUse webhooks as source of truthClient can close browser before callback
new Stripe(key)
at module top level
Lazy-initialize in a functionBuild fails when env var is undefined
Catching webhook errors silentlyLog + return 500 so Stripe retriesLost events = lost revenue
Storing subscription state only client-sideSync from webhook to DBSingle source of truth
Hardcoding prices in codeUse Stripe Price objects via env varsPrices change, regional variants
Skipping webhook signature verificationAlways verify with
constructEvent()
Prevents replay/spoofing attacks
Using
invoice.subscription
(2025+)
Use
invoice.parent.subscription_details
Breaking change since 2025-11-17.clover
await
on fire-and-forget analytics
Don't await, don't throwNon-critical tracking must not fail webhooks
Missing UUID validation on
user_id
from metadata
Validate with regex before DB operationsPrevents injection and corrupt data
Creating checkout without checking existing subscriptionCheck and use upgrade flow if activePrevents duplicate subscriptions
Using
--no-verify
for Stripe webhook testing
Use Stripe CLI:
stripe listen --forward-to
Real signature verification in dev

错误做法正确做法原因
payment_method_types: ['card']
完全省略该字段会阻止Apple Pay、Google Pay、Link、本地支付方式
信任客户端结账回调使用Webhook作为事实数据源客户端可能在回调前关闭浏览器
在模块顶层执行
new Stripe(key)
在函数中延迟初始化环境变量未定义时构建失败
静默捕获Webhook错误记录日志+返回500以便Stripe重试丢失事件会导致收入损失
仅在客户端存储订阅状态通过Webhook同步到数据库确保单一事实数据源
在代码中硬编码价格通过环境变量使用Stripe价格对象价格会变化,且存在区域变体
跳过Webhook签名验证始终使用
constructEvent()
验证
防止重放/伪造攻击
使用
invoice.subscription
(2025年后版本)
使用
invoice.parent.subscription_details
自2025-11-17.clover版本起的破坏性变更
对即发即弃式分析使用
await
不使用await,不抛出异常非关键跟踪不能导致Webhook失败
不对元数据中的
user_id
进行UUID验证
在数据库操作前用正则验证防止注入和数据损坏
不检查现有订阅就创建结账会话检查并在订阅活跃时使用升级流程防止重复订阅
使用
--no-verify
进行Stripe Webhook测试
使用Stripe CLI:
stripe listen --forward-to
在开发环境中进行真实的签名验证

E2E Testing Patterns

端到端测试模式

Quick reference — full patterns in references/testing-patterns.md.
bash
undefined
快速参考——完整模式请参考references/testing-patterns.md
bash
undefined

Stripe CLI: forward events to local webhook endpoint

Stripe CLI:将事件转发到本地Webhook端点

stripe listen --forward-to localhost:3001/api/stripe/webhook
stripe listen --forward-to localhost:3001/api/stripe/webhook

Trigger specific events

触发特定事件

stripe trigger checkout.session.completed stripe trigger invoice.payment_failed

| Card | Scenario |
|------|----------|
| `4242 4242 4242 4242` | Successful payment |
| `4000 0000 0000 0002` | Declined |
| `4000 0000 0000 3220` | 3D Secure required |
| `4000 0000 0000 9995` | Insufficient funds |

---
stripe trigger checkout.session.completed stripe trigger invoice.payment_failed

| 测试卡号 | 场景 |
|------|----------|
| `4242 4242 4242 4242` | 支付成功 |
| `4000 0000 0000 0002` | 支付被拒绝 |
| `4000 0000 0000 3220` | 需要3D安全验证 |
| `4000 0000 0000 9995` | 余额不足 |

---

Checkout Contract Propagation

结账契约传播

When checkout API response contracts change, treat it as a cross-surface migration. Enumerate all entrypoints, update every caller, route blocked flows to one shared recovery UX. Full checklist in references/in-app-browser-checkout-contract.md and assets/template-checkout-entrypoint-propagation-checklist.md.
当结账API响应契约变更时,需将其视为跨端迁移。枚举所有入口点,更新每个调用方,将阻塞的路由引导至统一的恢复用户体验。完整清单请参考references/in-app-browser-checkout-contract.mdassets/template-checkout-entrypoint-propagation-checklist.md

Security Checklist

安全清单

10-point checklist covering webhook signature verification, secrets management, UUID validation, HTTPS, idempotency, and rate limiting. Full checklist in references/stripe-patterns.md.

包含10项检查,覆盖Webhook签名验证、密钥管理、UUID验证、HTTPS、幂等性和速率限制。完整清单请参考references/stripe-patterns.md

Navigation

导航

References
  • references/stripe-patterns.md - Stripe patterns: webhook handlers, idempotency, status mapping, error handling, dunning, usage-based billing, security checklist, API version notes
  • references/platform-comparison.md - Platform comparison: Stripe, Adyen, Paddle, LemonSqueezy, Chargebee, Recurly, Lago, Braintree (deprecated)
  • references/uk-eu-payments-guide.md - UK/EU platforms: GoCardless, Mollie, Square, PayPal Commerce, Klarna, Open Banking (TrueLayer, Yapily)
  • references/testing-patterns.md - E2E testing: Stripe CLI, Playwright checkout, test cards, state sync
  • references/subscription-lifecycle.md - Full subscription state machine, trials, upgrades/downgrades, cancellation, dunning, pause/resume, database schema
  • references/regional-pricing-guide.md - PPP implementation, multi-currency Stripe prices, tax by region, fraud prevention, A/B testing pricing
  • references/webhook-reliability-patterns.md - Idempotency, retry handling, dead letter queues, monitoring, event ordering, queue-based processing
  • references/feature-gating-patterns.md - Feature gating: consumable vs binary unlocks, spread-then-override filtering, fraud prevention, discriminated union typed responses
  • references/in-app-browser-checkout-contract.md - Checkout response contract propagation for in-app browser recovery and cross-surface consistency
  • references/ops-runbook-checkout-errors.md - Checkout 500 debugging: RLS denials, auth policy, incident loop
  • data/sources.json - External documentation links (69 sources)
Templates
  • assets/template-checkout-entrypoint-propagation-checklist.md - Migration checklist for contract changes across all checkout callers
Related Skills
  • ../software-backend/SKILL.md - Backend API patterns, database, auth
  • ../dev-api-design/SKILL.md - API design patterns
  • ../marketing-cro/SKILL.md - Conversion optimization for checkout
  • ../startup-business-models/SKILL.md - Pricing strategy
  • ../software-security-appsec/SKILL.md - Payment security
  • ../qa-testing-playwright/SKILL.md - E2E testing patterns

参考文档
  • references/stripe-patterns.md - Stripe模式:Webhook处理器、幂等性、状态映射、错误处理、催缴、基于使用量的计费、安全清单、API版本说明
  • references/platform-comparison.md - 平台对比:Stripe、Adyen、Paddle、LemonSqueezy、Chargebee、Recurly、Lago、Braintree(已弃用)
  • references/uk-eu-payments-guide.md - 英国/欧盟平台:GoCardless、Mollie、Square、PayPal Commerce、Klarna、开放银行(TrueLayer、Yapily)
  • references/testing-patterns.md - 端到端测试:Stripe CLI、Playwright结账测试、测试卡、状态同步
  • references/subscription-lifecycle.md - 完整订阅状态机、试用、升级/降级、取消、催缴、暂停/恢复、数据库 schema
  • references/regional-pricing-guide.md - PPP实现、Stripe多币种价格、区域税务、欺诈预防、定价A/B测试
  • references/webhook-reliability-patterns.md - 幂等性、重试处理、死信队列、监控、事件排序、基于队列的处理
  • references/feature-gating-patterns.md - 功能权限控制:消耗品 vs 二进制解锁、扩散-覆盖过滤、欺诈预防、区分联合类型响应
  • references/in-app-browser-checkout-contract.md - 应用内浏览器结账的契约传播,确保恢复体验和跨端一致性
  • references/ops-runbook-checkout-errors.md - 结账500错误调试:RLS拒绝、授权策略、事件循环
  • data/sources.json - 外部文档链接(69个来源)
模板
  • assets/template-checkout-entrypoint-propagation-checklist.md - 跨所有结账调用方的契约变更迁移清单
相关技能
  • ../software-backend/SKILL.md - 后端API模式、数据库、认证
  • ../dev-api-design/SKILL.md - API设计模式
  • ../marketing-cro/SKILL.md - 结账转化率优化
  • ../startup-business-models/SKILL.md - 定价策略
  • ../software-security-appsec/SKILL.md - 支付安全
  • ../qa-testing-playwright/SKILL.md - 端到端测试模式

Freshness Protocol

时效性检查规则

When users ask version-sensitive questions about payment platforms, do a freshness check.
当用户询问支付平台的版本敏感问题时,需进行时效性检查。

Trigger Conditions

触发条件

  • "What's the best payment platform for [use case]?"
  • "Stripe vs Paddle vs LemonSqueezy?"
  • "How do I handle [tax/VAT/sales tax]?"
  • "What's new in Stripe [API/Billing/Checkout]?"
  • "Is Stripe Managed Payments available?"
  • "Best mobile subscription SDK?"
  • "[场景]下最佳的支付平台是什么?"
  • "Stripe vs Paddle vs LemonSqueezy?"
  • "如何处理[税务/增值税/销售税]?"
  • "Stripe [API/Billing/Checkout]有什么新功能?"
  • "Stripe Managed Payments可用吗?"
  • "最佳移动端订阅SDK是什么?"

How to Freshness-Check

时效性检查方法

  1. Start from
    data/sources.json
    (official docs, changelogs, API versions).
  2. Run a targeted web search for the specific platform and feature.
  3. Prefer official documentation and changelogs over blog posts.
  1. data/sources.json
    开始(官方文档、更新日志、API版本)。
  2. 针对特定平台和功能进行定向网络搜索。
  3. 优先选择官方文档和更新日志,而非博客文章。

What to Report

需报告的内容

  • Current landscape: what is stable and widely used now
  • Emerging trends: Managed Payments, usage-based billing, entitlements API, Open Banking
  • Deprecated/declining: hardcoded payment_method_types, top-level invoice.subscription, Braintree
  • Recommendation: default choice + alternatives with trade-offs
  • 当前现状:目前稳定且广泛使用的方案
  • 新兴趋势:Managed Payments、基于使用量的计费、授权API、开放银行
  • 已弃用/衰退的方案:硬编码payment_method_types、顶层invoice.subscription、Braintree
  • 推荐方案:默认选择+带有权衡的替代方案

Ops Runbook

运维手册

For checkout 500 errors with RLS/authorization denials: 5-step incident loop, required logging fields, and guardrails. See references/ops-runbook-checkout-errors.md.
针对带有RLS/授权拒绝的结账500错误:5步事件处理流程、必填日志字段和防护措施。请参考references/ops-runbook-checkout-errors.md

Fact-Checking

事实核查

  • Use web search/web fetch to verify current external facts, versions, pricing, deadlines, regulations, or platform behavior before final answers.
  • Prefer primary sources; report source links and dates for volatile information.
  • If web access is unavailable, state the limitation and mark guidance as unverified.
  • 在给出最终答案前,使用网络搜索/网页抓取验证当前外部事实、版本、定价、截止日期、法规或平台行为。
  • 优先选择一手来源;对于易变信息,报告来源链接和日期。
  • 如果无法访问网络,说明限制并标记指南为未验证。