@flopay/node

中文文档
1.14.11发布于 2026/11/14TypeScriptESM + CJS近 30 天下载 4003

Server-side FloPay SDK for Node.js. Creates checkout sessions through the FloPay billing API, retrieves and expires Stripe checkout sessions, manages Stripe customers, and verifies Stripe webhooks.

原始使用文档
文档目录17 节
  1. Installation
  2. Node.js version
  3. Usage
  4. Initialize
  5. Create a checkout session
  6. Retry safely with an idempotency key
  7. Authorise now, capture later
  8. Saved cards
  9. Saved payment methods
  10. Stripe checkout sessions and customers
  11. Verify webhooks
  12. Examples
  13. Public API
  14. Exports
  15. FloPayNodeOptions
  16. FloPay methods
  17. Errors

@flopay/node

Server-side FloPay SDK for Node.js. Creates checkout sessions through the FloPay billing API, retrieves and expires Stripe checkout sessions, manages Stripe customers, and verifies Stripe webhooks.

Installation

pnpm add @flopay/node

stripe and @flopay/shared install automatically as dependencies. Use it only from trusted server code: the constructor takes a Stripe secret key, which must never reach a browser.

Node.js version

Run @flopay/node on a Node.js release patched for CVE-2026-111497 ("Node2Shell"), an out-of-bounds write in the Brotli decoder of Node.js itself. The Node.js security release of 21 October 2026 fixed it in 22.23.4, 24.22.1 and 26.12.1, so run 22.23.4+ on 22.x, 24.22.1+ on 24.x, or 26.12.1+ on 26.x.

20.x (end of life) and 25.x get no fix: move off them to a patched 22.x, 24.x or 26.x release. The supported range, Node.js 20.19+, 22.13+ and 24+, still includes both lines, so nothing in the package stops you running on them.

Usage

Initialize

Create one client with your Stripe secret key. Direct Stripe operations use the API version in STRIPE_API_VERSION unless you pass apiVersion; pass stripeSecretKey to use a different key for them.

import { FloPay } from '@flopay/node';

const flopay = new FloPay('sk_test_...'); // load the real key from your secret store

Create a checkout session

flopay.checkout.sessions.create() posts to the billing API's POST /v1/checkouts/sessions and resolves with a CheckoutSessionResult:

  • { status: 201, redirectUrl, nonce }: send the buyer to redirectUrl, the hosted checkout page (checkoutBaseUrl + /secure, with the session id and your redirectParams). nonce is the session-bound checkout token; forward it as the x-checkout-session-token header on every continuation call for the same session.
  • { status: 204 }: the buyer already has a payment method on file and nothing needs to be shown.

The session currency is required. Pass currency on the session, or on the first item, subscription, or product. checkoutMode defaults to 'confirm' on this method, and timeoutMs to 12 seconds.

checkoutMetadata accepts Record<string, string> | null and is sent unchanged: omitting it leaves the request body unchanged, an explicit null is preserved, and {} stays an empty map. The billing API is the only authority on metadata limits.

Retry safely with an idempotency key

Pass the same idempotencyKey when you retry one logical session creation after an unknown outcome. A non-blank key of at most 255 characters is sent unchanged as the Idempotency-Key header. Empty, whitespace-only, overlong, and header-unsafe values are ignored without throwing, and no key is generated when you omit the option. @flopay/node has no internal retry loop, so your server owns retry timing and key reuse.

Authorise now, capture later

To authorise an eligible one-time card cart without capturing it, pass captureMethod: 'manual'. Manual capture cannot be combined with subscriptions, and omitting the option keeps immediate capture. The later capture is deliberately not an @flopay/node method: call the merchant-authenticated REST endpoint from trusted server code with a stable Idempotency-Key, as described in the payments and authorisations reference.

Saved cards

Saved-card management is also not an @flopay/node credential surface. From trusted server code, use the authenticated REST API to list display-only payment methods, create a setup session, and delete an eligible method, then pass only the setup session's sessionId and nonce to FloPayCardSetup in @flopay/react. Replace a card setup-first, then delete, so a failed or cancelled setup leaves the existing card intact. See the payment methods reference.

Type the setup request body with CreateCardSetupSessionRequest from @flopay/shared. Alongside the required userId and the optional successUrl and cancelUrl, it accepts transaction context your server may already know, which the billing API routes card verification on. Each field is optional and independent of the others, and a request without any of them stays valid and unchanged:

  • country: the ISO 3166-1 alpha-2 country the card will be used in.
  • currency: the three-letter currency later payments will be charged in. A supplied currency becomes the setup session's currency.
  • futureUse: deprecated (CardSetupFutureUse, 'on_session' or 'off_session'). Future use is platform policy: the billing API sets every saved method up 'off_session' and ignores this value. It is still accepted so existing requests keep working; leave it out of new code.

The context needs billing API v1.9.48 or later. Older billing APIs silently ignore it and set the card up exactly as before. Send values as you hold them: the billing API trims and uppercases country and currency, validates all three fields, and rejects an invalid value with a 400. The context describes the transaction and never selects a gateway; the billing API alone decides which gateway verifies the card.

Saved payment methods

Customers can keep saved payment methods of any type, not only cards, and the server-side flow is the same one as for saved cards. Merchant credentials stay on the merchant server: your server creates the setup session through its own authenticated billing API client, and no FloPay package sends the setup request. Type the body with CreateCardSetupSessionRequest from @flopay/shared exactly as for a card. Saving another method type needs no extra field, and the request carries no gateway hint, provider allow-list, or eligibility table. The billing API advertises which methods a setup session can save, and the SDK never decides which gateway or method is eligible.

@flopay/shared types the mixed method types for list and delete:

  • SavedPaymentMethodFamily is 'card', 'wallet', 'paypal', or 'apm': the family a saved method belongs to, for grouping. SAVED_PAYMENT_METHOD_FAMILIES lists every value for exhaustive handling. SavedPaymentMethodType and SAVED_PAYMENT_METHOD_TYPES are deprecated aliases with the same values.
  • SavedPaymentMethodDisplay is one display-only saved method of any type, discriminated by its family type. Every variant has an id and a lifecycle status, and may carry the billing API row's methodType and displayLabel. methodType tells methods of one family apart: an Apple Pay row and a Google Pay row are both type: 'wallet', with methodType apple_pay and google_pay. A wallet, PayPal, or APM method always has a safe display label, deprecated in favour of displayLabel, which holds the same value. A card has brand, lastFour, expiryMonth, and expiryYear, its methodType is card, and its label is optional and its displayLabel is null: build the display text from brand and lastFour instead, as the example below does. No variant carries a reusable credential, token, or card number. An existing SavedCardDisplay is a valid SavedPaymentMethodDisplay without a type, so card-only code keeps compiling.
  • ListSavedPaymentMethodsQuery is the list request's optional type filter, a SavedPaymentMethodListType. Leave it out to list every saved method, as card-only integrations do today.
  • SavedPaymentMethodListType is a method type the billing API accepts for that filter: card, paypal, apple_pay, google_pay, sepa_debit, or us_bank_account, and SAVED_PAYMENT_METHOD_LIST_TYPES lists every value. It filters by method type, never by family, so there is no wallet or apm filter: ask for apple_pay or google_pay instead. PayPal is paypal, not the setup flow's paypal_vaulted.
  • isSetupCheckoutSession is true for a no-charge setup session (checkoutMode: 'setup', zero amount, no products), whatever methods its advertisedPaymentMethods list. isCardSetupCheckoutSession is unchanged.

Deleting a saved method works the same way for every type, with the same PaymentMethodRemovalError responses.

Stripe checkout sessions and customers

checkout.sessions.retrieve(), expire(), and listLineItems() call Stripe directly and normalize the result. customers.create(), retrieve(), and update() manage Stripe customers and return a normalized Customer.

Verify webhooks

webhooks.constructEvent() verifies the Stripe-Signature header against your webhook signing secret and returns a normalized WebhookEvent (id, type, data, created). Pass the raw, unparsed request body.

Examples

Create a checkout session and redirect the buyer:

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.');
}

Type a card-setup request with optional transaction context, send it with the billing API client your server already authenticates, and return only the session handle for 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 };
}

List saved payment methods of any type, optionally filtered by method type, and label each one without branching on a gateway:

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);
}

Verify a Stripe webhook in a plain Node.js HTTP server:

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);

Manage Stripe customers:

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);

Public API

Exports

ExportDescription
FloPayServer-side client: new FloPay(secretKey, options?) with a Stripe secret key (sk_test_... or sk_live_...)
FloPayNodeOptionsConstructor options
STRIPE_API_VERSION'2026-04-22.dahlia', the default Stripe API version for direct Stripe operations

FloPayNodeOptions

OptionTypeDescription
apiVersionstring?Stripe API version override for direct Stripe operations
stripeSecretKeystring?Stripe secret key for direct Stripe operations, instead of the constructor key

FloPay methods

MethodReturnsDescription
checkout.sessions.create(params)Promise<CheckoutSessionResult>Creates a session through the billing API (POST /v1/checkouts/sessions). Accepts CreateSessionParams.
checkout.sessions.retrieve(id)Promise<CheckoutSession>Retrieves and normalizes a Stripe checkout session
checkout.sessions.expire(id)Promise<CheckoutSession>Expires a Stripe checkout session
checkout.sessions.listLineItems(id)Promise<LineItem[]>Lists a Stripe checkout session's line items
customers.create(params)Promise<Customer>Creates a Stripe customer
customers.retrieve(id)Promise<Customer>Retrieves a Stripe customer
customers.update(id, params)Promise<Customer>Updates a Stripe customer
webhooks.constructEvent(payload, signature, secret)WebhookEventVerifies and parses a Stripe webhook event

The parameter and result types (CreateSessionParams, CheckoutSessionResult, CheckoutSession, Customer, LineItem, WebhookEvent) are exported by @flopay/shared.

@flopay/node has no card-setup method and holds no merchant credential. CreateCardSetupSessionRequest and CardSetupFutureUse, also exported by @flopay/shared, type the body your server sends to the authenticated POST /v1/checkouts/sessions/setup route with its own billing API client.

Saved payment methods of any type are typed by @flopay/shared too: SavedPaymentMethodDisplay, SavedPaymentMethodFamily (with the deprecated alias SavedPaymentMethodType), ListSavedPaymentMethodsQuery and its SavedPaymentMethodListType filter values, AdvertisedPaymentMethod, and the isSetupCheckoutSession guard. @flopay/node adds no list or delete method for them.

Errors

SDK validation and billing API failures are FloPayErrors from @flopay/shared; branch on type and code.

CodeTypeWhen
CurrencyRequiredvalidation_errorcheckout.sessions.create() could not resolve a session currency. Thrown before any request, with param: 'currency'.
CaptureMethodUnsupportedForSubscriptionvalidation_errorcaptureMethod: 'manual' was requested for a cart with a subscription. Thrown before any request.
The billing API's code, or http_<status>api_errorThe billing API rejected session creation. The error keeps the billing API's message and code, with the HTTP status in statusCode.
MissingCheckoutSessionTokenapi_errorThe billing API created a session but returned no nonce, which means it is too old for this SDK.
resource_missingapi_errorcustomers.retrieve() found a deleted customer.

Constructing FloPay without a secret key throws an authentication_error. A create request that exceeds timeoutMs is aborted, and network failures reject with the underlying fetch error rather than a FloPayError. Stripe operations, including webhook signature verification, reject with Stripe's own errors. Treat a failed constructEvent() as an untrusted request.

版本与发布记录

@flopay/node 共发布 174 个版本,首次发布于 2026/03/31;过去 12 个月发布了 174 个版本。

版本发布日期距今
1.14.11latest2026/11/141 天前
1.14.102026/11/141 天前
1.14.92026/11/132 天前
1.14.82026/11/123 天前
1.14.72026/11/114 天前
1.14.62026/11/114 天前

关于 @flopay/node 的常见问题

@flopay/node 是什么?

Server-side FloPay SDK for Node.js. Creates checkout sessions through the FloPay billing API, retrieves and expires Stripe checkout sessions, manages Stripe customers, and verifies Stripe webhooks.

如何安装 @flopay/node?

在项目目录运行 npm install @flopay/node,或使用 pnpm add / yarn add / bun add。

@flopay/node 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@flopay/node 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@flopay/node 可以商用吗?

@flopay/node 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@flopay/node 的下载量有多少?

根据 npm 官方下载统计,@flopay/node 在 2026/10/15 – 2026/11/13 这 30 天内被下载 4,003 次,日均约 133 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@flopay/node 还在维护吗?

最新版本 1.14.11 发布于 2026/11/14(1 天前);至今共发布 174 个版本,其中过去 12 个月发布了 174 个;首次发布于 2026/03/31。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。