Skip to content

Cell - Stripe 组件 ​

将 @celljs/stripe 和 stripe(官方 SDK,一个对等依赖)添加到依赖中,以使用 Stripe:@Autowired(StripeClient) 给出配置的 Stripe 客户端,webhook 端点验证事件的 Stripe-Signature 并按类型将其路由到你的处理程序。API 本身是官方 SDK 的,带有其类型。

bash
bun add @celljs/stripe stripe

数据库驱动程序不是此包的依赖:安装你的数据源使用的那个(mysql2、pg、better-sqlite3、sql.js 等)。mysql2 是一个可选的对等依赖;当配置的数据源的驱动程序缺失时,应用程序在启动时失败,显示 CELL_TYPEORM_DRIVER_MISSING 和安装它的命令。

yaml
# cell.yml:配置仅对服务器有效,除非 schema 标记为公开;从环境中获取秘密
stripe:
  apiKey: ${env.STRIPE_SECRET_KEY}
  webhook:
    secret: ${env.STRIPE_WEBHOOK_SECRET}     # whsec_...
ts
import { Autowired } from '@celljs/core';
import { Body, Controller, Post } from '@celljs/mvc/lib/node';
import { StripeClient, StripeWebhookHandler } from '@celljs/stripe';
import type Stripe from 'stripe';

@Controller('checkout')
export class CheckoutController {
    @Autowired(StripeClient)
    protected readonly stripe: StripeClient;

    @Post()
    async create(@Body() order: { id: string }) {
        const session = await this.stripe.checkout.sessions.create({
            mode: 'payment',
            line_items: [{ price: 'price_123', quantity: 1 }],
            client_reference_id: order.id,
            success_url: 'https://shop.example.com/paid',
        });
        return { url: session.url };
    }
}

@StripeWebhookHandler('checkout.session.completed')
export class CheckoutCompletedHandler implements StripeWebhookHandler<Stripe.CheckoutSessionCompletedEvent> {
    async handle(event: Stripe.CheckoutSessionCompletedEvent) {
        // event.data.object 是会话;标记订单已支付一次:Stripe 可以多次传递事件。
    }
}

Webhook ​

端点是 POST /stripe/webhook(stripe.webhook.url,在 server.path 下):在 Stripe 仪表板中注册其 URL,或在开发期间用 stripe listen --forward-to localhost:3000/stripe/webhook 将事件转发到它(将 secret 设置为它打印的那个)。它在 body 解析器和身份验证之前运行,使用每个 secret 验证作为发送的 body 上的签名(列表为多个端点或轮换服务)和 tolerance 为 300 秒,然后运行事件类型的 @StripeWebhookHandler 组件,然后是声明为无类型的那些,一个接一个。它回应:

  • 200 {"received":true} 当他们全部成功时(或没有处理程序接受该类型时:记录为警告);
  • 400 当签名不匹配时;
  • 500 当处理程序抛出时(或 secret 未配置):Stripe 为期三天重试事件。

StripeWebhookVerifier 验证在你自己的路由上接收的请求:verify(rawBody, signatureHeader)。

配置 ​

yaml
stripe:
  apiKey: ${env.STRIPE_SECRET_KEY}
  config:                      # Stripe 客户端的选项
    maxNetworkRetries: 2
    # apiVersion: 2025-...     # 固定一个;SDK 的默认值
  webhook:
    enabled: true
    url: /stripe/webhook
    secret: ${env.STRIPE_WEBHOOK_SECRET}   # 或列表
    tolerance: 300             # 秒

客户端在首次使用时创建;重新绑定 StripeFactory(@Component({ id: StripeFactory, rebind: true }))以另一种方式创建它,例如每个租户一个密钥。SDK 在 Bun、Deno 和 Cloudflare Workers 上选择其 fetch 和 SubtleCrypto 构建。stripe 的当前版本需要 Node.js 20 或更高版本(Cell 的其余部分在 18 上运行)。

在没有 Stripe 账户的情况下测试 ​

stripe-mock 回答 Stripe OpenAPI 规范的每个 API 并验证参数(它不保持状态)。用 config: { host: '127.0.0.1', port: 12111, protocol: 'http' } 和任何 sk_test_<letters and digits> 密钥指向客户端;此包对其的测试运行时:

bash
docker run --rm -p 12111:12111 stripe/stripe-mock
STRIPE_MOCK_URL=http://127.0.0.1:12111 bunx turbo run test --filter=@celljs/stripe

错误 ​

Stripe 的 API 错误是 SDK 的(Stripe.errors.StripeError)。配置和 webhook 失败是带有稳定 code 和 hint 的 CellStripeError:

代码何时
CELL_STRIPE_NOT_CONFIGURED当客户端被注入时 stripe.apiKey 未设置
CELL_STRIPE_WEBHOOK_NOT_CONFIGURED事件到达且 stripe.webhook.secret 未设置
CELL_STRIPE_SIGNATURE_INVALID没有 secret 验证 Stripe-Signature,或其时间戳在 tolerance 外