@ flopay/ 节点
为Node.js服务的服务器端 FloPay SDK. 通过 FloPay 计费API 创建取出会话,检索并过期 Strede 取出会话,管理 Strede 客户端,并验证 Strede webhooks.
安装
pnpm add @flopay/node
stripe 财务报告和审定财务报表 @flopay/shared 作为依赖性自动安装。 只从信任的服务器代码使用它: 构造器取出一个花纹密钥, 它绝不能到达浏览器 .
节点.js 版本
运行 @flopay/node 在为CVE-2026-111497 ("Node2Shell")所修补的Node.js发布上,在Node.js本身的Brotli解码器中写出出出界. 2026年10月21日的Node.js安全发布将其固定在22.23.4,24.22.1和26.12.1上,因此运行22.23.4+在22x上,24.22.1+在24x上,或26.12.1+在26x上.
20.x(生命末期)和 25.x 得不到固定:将其移到补丁 22.x, 24.x 或 26.x 释放. 所支持的范围,Node.js 20.19+, 22.13+和24+,仍然包括了两行,因此包中没有任何东西会阻止你运行在它们上.
使用量
初始化
使用您的花纹密钥创建一个客户端 。 直接条纹操作使用 API 版本在 STRIPE_API_VERSION 除非你通过 apiVersion; 通过 stripeSecretKey 用来为他们使用不同的密钥.
import { FloPay } from '@flopay/node';
const flopay = new FloPay('sk_test_...'); // load the real key from your secret store
创建退出会话
flopay.checkout.sessions.create() 发帖给API POST /v1/checkouts/sessions 并决心 CheckoutSessionResult数字 :
{ status: 201, redirectUrl, nonce }: 将买方送往redirectUrl,主机退出页面(checkoutBaseUrl+ 键/secure,带有会话编号和您的redirectParams) (中文(简体) ).nonce是会话绑定的退出符号;转发为x-checkout-session-token接续同一届会的每次呼吁都以标题标出.{ status: 204 }:买方已经有一个付款方法存档,不需要显示.
需要会议货币。 过 currency 或关于第一个项目、订阅或产品. checkoutMode 默认为 'confirm' 这个方法,和 timeoutMs 至12秒钟.
checkoutMetadata 接受 Record<string, string> | null 且发送未变 : 省略后, 请求机构未变 , 明确 null 保存下来 {} 保留空地图。 计费API是元数据限制的唯一权威.
用一等钥匙安全重试
传出同样的 idempotencyKey 当您在未知结果后重试一个逻辑会话创建时。 最多为 255 个字符的非空白密钥的发送方式与 Idempotency-Key 头曰. 空的, 仅白空的, 超长的, 和头- 不安全的值会被忽略而不丢出, 省略选项时不会生成密钥 . @flopay/node 没有内部重试循环, 因此您的服务器拥有重试时间和重用密钥 .
现在授权,稍后抓取
要授权一个合格的一次性卡车 不抓取,通过 captureMethod: 'manual'。手动抓取不能与订阅合并使用,省略选项可以让直接抓取。 之后的捕捉是故意的,不是 @flopay/node 方法: 从可信任的服务器代码中调用商号认证的 REST 端点, 并使用稳定 Idempotency-Key如本报告所述 付款和授权。 。 。 .
已保存的牌
保存卡管理也不属于 @flopay/node 认证表面。 从信任的服务器代码中,使用经认证的 REST API 列出仅显示的支付方法,创建设置会话,并删除符合条件的方法,然后只通过设置会话 sessionId 财务报告和审定财务报表 nonce 改为 FloPayCardSetup 内 @flopay/react。先替换一个卡片设置,然后删除,所以一个失败或取消的设置会让现有的卡片保持完整. 见 支付方法参考。 。 。 .
输入设置请求正文 CreateCardSetupSessionRequest 从 @flopay/shared. 除所需外 userId 选项 successUrl 财务报告和审定财务报表 cancelUrl中,它接受您服务器可能已经知道的交易上下文,该上帐的 API 路由卡的校验。 每个字段都是可选的,独立于其他字段,没有这些字段的要求仍然有效,保持不变:
country: ISO 3166-1 alpha-2国道卡将使用入.currency:后期付款的三字母货币将记入. 提供currency成为设置会话的货币.futureUse数字 : 已贬值 (单位:千美元)CardSetupFutureUse, (中文(简体) ),'on_session'或者说'off_session') (中文(简体) ). 未来使用是平台政策:计费API设置了所有节省的方法'off_session'并忽略此值。 它仍然被接受,所以现有的请求继续工作;把它排除在新的代码之外.
上下文需要计费API v1.9.48 或以后. 年长的API悄悄地忽略了它,并如出一辙地设置了卡. 发送您持有的值: 计费 API 修饰和大写 country 财务报告和审定财务报表 currency中,验证所有三个字段,并拒绝一个带有 400。上下文描述了交易,从不选择网关;单靠收费API决定哪个网关验证卡.
节省的支付方法
客户可以保留任何类型的被保存的支付方法,不仅仅是卡,服务器侧流与被保存的卡相同. 商家证书留在商家服务器上:您的服务器通过自己认证的计费API客户端创建了设置会话,没有FloPay软件包发送设置请求. 输入身体 CreateCardSetupSessionRequest 从 @flopay/shared 就是一张牌 保存另一个方法类型不需要额外的字段,请求中没有网关提示,提供者允许列表,或者资格表. 计费API的广告是哪些设置会话可以保存,而SDK从未决定哪个网关或方法有资格.
@flopay/shared 键入列表和删除的混合方法类型:
SavedPaymentMethodFamily实值'card', (中文(简体) ),'wallet', (中文(简体) ),'paypal',或'apm':一个保存的方法属于家庭,用于分组.SAVED_PAYMENT_METHOD_FAMILIES列出用于详尽处理的每个值.SavedPaymentMethodType财务报告和审定财务报表SAVED_PAYMENT_METHOD_TYPES具有相同价值的被贬值的别名.SavedPaymentMethodDisplay一种只保存显示的 任何类型的方法, 受到家庭的歧视type每个变体都有id和生命周期status,并可能携带计费API行的methodType财务报告和审定财务报表displayLabel。 。 。 .methodType告诉一个家庭的不同方法:苹果支付行和谷歌支付行都是type: 'wallet',与methodTypeapple_pay财务报告和审定财务报表google_pay。钱包、 PayPal 或 APM 方法总是有安全的显示label,以有利于displayLabel,其价值相同。 一张牌brand, (中文(简体) ),lastFour, (中文(简体) ),expiryMonth,以及expiryYear页:1methodType实值card,以及label可选及其displayLabel实值null: 从brand财务报告和审定财务报表lastFour相反,正如下文的例子所示。 没有任何变体带有可再使用的证书,令牌或卡号. 现有SavedCardDisplay是一个有效的SavedPaymentMethodDisplay无type,所以只用卡的代码在不断编译.ListSavedPaymentMethodsQuery是列表请求的可选type过滤器, aSavedPaymentMethodListType。不要列出每个保存的方法,就像今天只使用卡片的集成一样.SavedPaymentMethodListType用于此过滤器的计费 API 接受的方法 :card, (中文(简体) ),paypal, (中文(简体) ),apple_pay, (中文(简体) ),google_pay, (中文(简体) ),sepa_debit,或us_bank_account,以及SAVED_PAYMENT_METHOD_LIST_TYPES列出每个值。 它按照方法类型过滤,从不通过家庭过滤,所以没有wallet或者说apm过滤器: 请求apple_pay或者说google_pay换句话说 PayPal为paypal,不是设置流paypal_vaulted。 。 。 .isSetupCheckoutSession实值true无收费设置会议checkoutMode: 'setup',零数量,没有产品),无论方法advertisedPaymentMethods列表中选择一个选项.isCardSetupCheckoutSession不变.
删除被保存的方法对每种类型都一样,同样 PaymentMethodRemovalError 回应.
列条取出会话和客户
checkout.sessions.retrieve(), (中文(简体) ), expire(),以及 listLineItems() 直接调用条纹并规范结果. customers.create(), (中文(简体) ), retrieve(),以及 update() 管理条纹客户并返回一个正常的 Customer。 。 。 .
校验网约车
webhooks.constructEvent() 核查 Stripe-Signature 标注在您的 webhook 签名后返回一个正常的 WebhookEvent (单位:千美元)id, (中文(简体) ), type, (中文(简体) ), data, (中文(简体) ), created) (中文(简体) ). 传出原始的,未分解的请求体.
实例
创建退出会话并重定向购买者:
import { FloPay } from '@flopay/node';
function requiredEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`${name} is not set`);
return value;
}
const flopay = new FloPay(requiredEnv('STRIPE_SECRET_KEY'));
const result = await flopay.checkout.sessions.create({
billingApiUrl: 'https://billing.example.com',
checkoutBaseUrl: 'https://checkout.example.com',
clientId: 'client_123',
currency: 'USD',
items: [
{
providerItemId: 'prod_abc',
providerItemName: 'Pro Plan',
totalAmount: 49.99,
overrideAmount: 24.99,
},
],
subscriptions: [{ providerPlanId: 'plan_xyz', providerPlanName: 'Monthly Plan', totalAmount: 9.99 }],
account: { userId: 'user_1', email: '[email protected]', firstName: 'Jane', lastName: 'Doe' },
successUrl: 'https://shop.example.com/success',
cancelUrl: 'https://shop.example.com/cancel',
checkoutMode: 'full',
checkoutMetadata: { merchantOrderId: 'order-123', campaign: 'summer' },
couponCodes: ['SAVE10'],
tagsData: { googleContainerId: 'GTM-XXXX' },
redirectParams: { email: '[email protected]' },
timeoutMs: 12_000,
idempotencyKey: 'checkout:order-123',
});
if (result.status === 201 && 'redirectUrl' in result) {
console.log('Redirect the buyer to', result.redirectUrl);
} else if (result.status === 204) {
console.log('A payment method is already on file.');
}
输入带有可选交易上下文的卡片设置请求, 随计费API客户端发送, 您的服务器已经认证, 只返回会话控件 FloPayCardSetup数字 :
import type { CreateCardSetupSessionRequest } from '@flopay/shared';
// Your server's existing billing API client; it adds your merchant authentication.
type AuthenticatedPost = (
path: string,
body: CreateCardSetupSessionRequest,
) => Promise<{ data: { uuid: string; nonce: string } }>;
export async function createCardSetupSession(post: AuthenticatedPost, userId: string) {
const request: CreateCardSetupSessionRequest = {
userId,
country: 'GB',
currency: 'GBP',
};
const { data } = await post('/v1/checkouts/sessions/setup', request);
return { sessionId: data.uuid, nonce: data.nonce };
}
列出任何类型的保存的支付方法, 可选择通过方法过滤 type,并在网关上不分枝贴出每个标签:
import type { ListSavedPaymentMethodsQuery, SavedPaymentMethodDisplay } from '@flopay/shared';
// Your server's existing billing API client; it adds your merchant authentication.
type AuthenticatedList = (query: ListSavedPaymentMethodsQuery) => Promise<SavedPaymentMethodDisplay[]>;
export function describeSavedMethod(method: SavedPaymentMethodDisplay): string {
switch (method.type) {
case 'wallet':
case 'paypal':
case 'apm':
return method.displayLabel ?? method.label;
default:
// A card; card-only responses may omit `type`, and a card's `displayLabel` is null.
return method.displayLabel ?? method.label ?? `${method.brand} ending ${method.lastFour}`;
}
}
export async function listSavedApplePay(list: AuthenticatedList): Promise<string[]> {
// Filter by method type: the billing API accepts `apple_pay`, never the `wallet` family.
const methods = await list({ type: 'apple_pay' });
return methods.map(describeSavedMethod);
}
校验平原节点. js HTTP 服务器中的 Strepe webhook :
import { createServer } from 'node:http';
import { FloPay } from '@flopay/node';
const flopay = new FloPay(process.env.STRIPE_SECRET_KEY ?? 'sk_test_...');
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET ?? 'whsec_...';
const MAX_BODY_BYTES = 64 * 1024; // Pick a limit that fits your largest expected event.
createServer((request, response) => {
const chunks: Buffer[] = [];
let received = 0;
request.on('data', (chunk: Buffer) => {
received += chunk.length;
if (received > MAX_BODY_BYTES) {
response.writeHead(413, { Connection: 'close' }).end('Payload too large');
request.destroy();
return;
}
chunks.push(chunk);
});
request.on('end', () => {
try {
const event = flopay.webhooks.constructEvent(
Buffer.concat(chunks),
String(request.headers['stripe-signature']),
webhookSecret,
);
console.log(event.type, event.created);
response.writeHead(200).end();
} catch {
response.writeHead(400).end('Invalid signature');
}
});
}).listen(3000);
管理花纹客户 :
import { FloPay } from '@flopay/node';
const flopay = new FloPay(process.env.STRIPE_SECRET_KEY ?? 'sk_test_...');
const customer = await flopay.customers.create({
email: '[email protected]',
name: 'Jane Doe',
metadata: { plan: 'pro' },
});
const updated = await flopay.customers.update(customer.id, { metadata: { plan: 'enterprise' } });
const session = await flopay.checkout.sessions.retrieve('cs_test_...');
console.log(updated.email, session.status, session.amount);
公共 API
出口
| 导出 | 说明 |
|---|---|
FloPay | 服务器端客户端 : new FloPay(secretKey, options?) 带有条纹密钥的 (sk_test_... 或者说 sk_live_...(中文(简体) ) |
FloPayNodeOptions | 构造选项 |
STRIPE_API_VERSION | '2026-04-22.dahlia',用于直接 Strape 操作的默认 Strape API 版本 |
FloPay 节点选项
| 选项 | 类型 | 说明 |
|---|---|---|
apiVersion | string? | 用于直接 Strape 操作的 Strape API 版本覆盖 |
stripeSecretKey | string? | 直接花纹操作的花纹密钥, 而不是构造键 |
FloPay 方法
| 方法 | 回返 | 说明 |
|---|---|---|
checkout.sessions.create(params) | Promise<CheckoutSessionResult> | 通过计费API创建会话( C)POST /v1/checkouts/sessions) (中文(简体) ). 接受 CreateSessionParams。 。 。 . |
checkout.sessions.retrieve(id) | Promise<CheckoutSession> | 获取并规范条纹检查退出会话 |
checkout.sessions.expire(id) | Promise<CheckoutSession> | 已过期的条形取出会话 |
checkout.sessions.listLineItems(id) | Promise<LineItem[]> | 列出条形断面的行项目 |
customers.create(params) | Promise<Customer> | 创建条纹客户端 |
customers.retrieve(id) | Promise<Customer> | 获取条纹客户 |
customers.update(id, params) | Promise<Customer> | 更新条纹客户端 |
webhooks.constructEvent(payload, signature, secret) | WebhookEvent | 校验并解析花旗网游事件 |
参数和结果类型(CreateSessionParams, (中文(简体) ), CheckoutSessionResult, (中文(简体) ), CheckoutSession, (中文(简体) ), Customer, (中文(简体) ), LineItem, (中文(简体) ), WebhookEvent)导出于 @flopay/shared。 。 。 .
@flopay/node 没有卡片设置方法 也没有商人证书. CreateCardSetupSessionRequest 财务报告和审定财务报表 CardSetupFutureUse,并导出 @flopay/shared,键入您服务器发送给认证对象的正文 POST /v1/checkouts/sessions/setup 路由与自己的计费API客户端.
类型保存的支付方法由 @flopay/shared 还有: SavedPaymentMethodDisplay, (中文(简体) ), SavedPaymentMethodFamily (有被贬值的别名) SavedPaymentMethodType, (中文(简体) ), ListSavedPaymentMethodsQuery 联 合 国 SavedPaymentMethodListType 过滤值, AdvertisedPaymentMethod,则 isSetupCheckoutSession 警卫. @flopay/node 不为它们添加列表或删除方法.
错误
SDK验证和计费API故障 FloPayError从 @flopay/shared; 分支打开 type 财务报告和审定财务报表 code。 。 。 .
| 代码 | 类型 | 何时 |
|---|---|---|
CurrencyRequired | validation_error | checkout.sessions.create() 无法解析会话货币。 在任何请求之前丢弃 param: 'currency'。 。 。 . |
CaptureMethodUnsupportedForSubscription | validation_error | captureMethod: 'manual' 请求购买一辆有订阅费的推车。 在任何请求之前就扔了. |
API的账单 code,或 http_<status> | api_error | 计费API拒绝了会话创建. 错误保留了计费API的信息和代码, HTTP 状态在 statusCode。 。 。 . |
MissingCheckoutSessionToken | api_error | 计费 API 创建了会话, 但返回否 nonce也就是说这个SDK太老了. |
resource_missing | api_error | customers.retrieve() 找到一个删除的客户. |
构造 FloPay 没有密钥 扔出一个 authentication_error创建请求超过 timeoutMs 被中止,网络失败以隐藏的获取错误拒绝,而不是一个 FloPayError。包括Webhook签名验证在内的条纹操作以条纹本身的错误拒绝。 处理失败 constructEvent() 作为不信任的要求.