@funnelfox/billing

中文文档
0.14.0发布于 2026/11/12MITTypeScriptESM + CJS构建来源已验证近 30 天下载 8428

JavaScript SDK 用于与 Primer 集成的 Funnelfox 计费

中文使用文档
文档目录31 节
  1. 特征
  2. 安装
  3. 维娅 CDN
  4. 国家预防机制
  5. 快速启动
  6. API 参考
  7. configure(config)
  8. createCheckout(options)
  9. createClientSession(params)
  10. 离职偿金
  11. 完整示例
  12. 处理错误
  13. 常见错误代码
  14. 类型脚本支持
  15. 高级使用
  16. 使用回调而不是事件
  17. 自定义卡输入选择器
  18. 自定义支付方法顺序
  19. 使用 initMethod 单一支付方法
  20. 手动会话创建
  21. 用钱包重新认证的一克Upsell
  22. Billing.mountWalletUpsell(container, options)
  23. 条纹融合
  24. Billing.stripe.createCardForm(element, params)
  25. Billing.stripe.getAvailableWallet(params)
  26. Billing.stripe.purchaseWallet(params)
  27. Billing.stripe.getAvailablePaymentMethods(params)
  28. 组合示例:钱包检测+卡后退
  29. 浏览器支持
  30. 实例
  31. 许可证

@ funnelfox/ billing (英语)

一个现代的TypeScript SDK,用于使用Primer Heads取出集成的订阅支付.

特征

  • 🚀 现代 API: 与事件驱动架构的基于承诺的清洁界面
  • 🔄 动态定价: 不重新装入页面而更新价格
  • 🛡️ 类型安全: 完整的类型脚本定义和类型安全
  • 🎯 事件驱动: 轻松处理成功、错误和状态变化
  • 🔧 强壮: 内置错误处理、重试和验证
  • 📦 轻巧: 最小依赖,浏览器优化
  • 🎨 无头检查: 用 Primer 无头检查器全面控制退出 UI
  • 💳 条纹融合: 原生花纹元素卡表和 Apple Pay / Google Pay 钱包

安装

维娅 CDN

<!-- Include Primer Headless Checkout SDK first -->
<script src="https://sdk.primer.io/web/v2.57.3/Primer.min.js"></script>
<link rel="stylesheet" href="https://sdk.primer.io/web/v2.57.3/Checkout.css" />

<!-- Include Funnelfox Billing SDK -->
<script src="https://unpkg.com/@funnelfox/billing@latest/dist/funnelfox-billing.min.js"></script>

国家预防机制

npm install @funnelfox/billing @primer-io/checkout-web

如果您正在本地开发, 请为 TypeScript 构建/测试安装 dev 工具 :

npm i -D @rollup/plugin-typescript ts-jest @types/jest

然后构建:

npm run build

快速启动

import { Billing } from '@funnelfox/billing';

await Billing.createCheckout({
  orgId: 'your-org-id',
  priceId: 'price_123',
  customer: {
    externalId: 'user_456',
    email: '[email protected]',
  },
  container: '#checkout-container',
});

API 参考

configure(config)

配置全局 SDK 设置 .

import { configure } from '@funnelfox/billing';

configure({
  orgId: 'your-org-id', // Required
  baseUrl: 'https://custom.api', // Optional, defaults to https://billing.funnelfox.com
  region: 'us-east-1', // Optional, defaults to 'default'
});

参数:

  • config.orgId (字符串,需要) - 您的组织标识符
  • config.baseUrl (字符串,可选) - 自定义 API URL
  • config.region (字符串,可选) - 区域,默认为"默认"

createCheckout(options)

创建新检出实例 .

const checkout = await createCheckout({
  // Required
  orgId: 'your-org-id',
  priceId: 'price_123',
  customer: {
    externalId: 'user_456',
    email: '[email protected]', // Optional if you collect it in the card form
    countryCode: 'US', // Optional
  },
  container: '#checkout-container',
  clientMetadata: { source: 'web' },
  card: {
    emailAddress: {
      visible: true,
      template: '{{email}}',
    },
  },
  cardSelectors: {
    // Custom card input selectors (optional, defaults to auto-generated)
    cardNumber: '#cardNumberInput',
    expiryDate: '#expiryInput',
    cvv: '#cvvInput',
    cardholderName: '#cardHolderInput',
    emailAddress: '#emailAddressInput',
    button: '#submitButton',
  },
  paypalButtonContainer: '#paypalButton', // Optional
  googlePayButtonContainer: '#googlePayButton', // Optional
  applePayButtonContainer: '#applePayButton', // Optional
  paymentMethodOrder: ['PAYMENT_CARD', 'PAYPAL', 'GOOGLE_PAY', 'APPLE_PAY'], // Optional

  // Callbacks (alternative to events)
  onSuccess: result => {
    /* ... */
  },
  onError: error => {
    /* ... */
  },
  onStatusChange: (state, oldState) => {
    /* ... */
  },
});

参数:

  • options.priceId (字符串,要求) - 价格标识符
  • options.customer (目标,要求)
    • customer.externalId (字符串, 需要) - 您的用户标识符
    • customer.email (字符串,可选) - 客户电子邮件
    • customer.countryCode (字符串,可选) - ISO国家代码
  • options.container (字符串,需要) - CSS选取器,用于取出容器
  • options.card.emailAddress.visible (boolean,可选) - 在卡片格式中显示电子邮件字段 。 默认禁用 .
  • options.card.emailAddress.template (字符串,可选) - 将输入的电子邮件在以支付方式发送之前包入, 例如 {{email}}。 。 。 .

集装箱造型要求(破损皮肤):

在使用默认外观时,容器元素必须具有下列CSS属性才能正确显示装载指标:

#checkout-container {
  position: relative;
  min-height: 200px; /* Adjust based on your layout */
}
  • position: relative - 要求是因为加载覆盖用途 position: absolute 覆盖容器
  • min-height - 必须确保装载器在初始化期间可见。 建议的最低限度 200px

补充参数:

  • options.orgId (字符串,可选) - Org ID(如果不是全局配置)
  • options.clientMetadata (对象,可选) - 自定义元数据
  • options.cardSelectors (对象,可选) - 自定义卡片输入选择器(默认自动生成)
  • options.paypalButtonContainer (字符串,可选) - PayPal 按钮的容器选择器
  • options.googlePayButtonContainer (字符串,可选) - Google 付费按钮的容器选择器
  • options.applePayButtonContainer (字符串,可选) - Apple Pay按钮的容器选择器
  • options.paymentMethodOrder (阵列,可选) - 支付方法的自定义订单. 可用值 : 'PAYMENT_CARD', (中文(简体) ), 'PAYPAL', (中文(简体) ), 'GOOGLE_PAY', (中文(简体) ), 'APPLE_PAY'默认为 ['PAYMENT_CARD', 'PAYPAL', 'GOOGLE_PAY', 'APPLE_PAY']
  • options.onInitialized (功能,可选) - 初始化回调
  • options.onSuccess (函数,可选) - 成功召回
  • options.onError (功能, 可选) - 回调错误
  • options.onStatusChange (功能,可选) - 国家更改召回

回返情况: Promise<CheckoutInstance>


createClientSession(params)

手动创建客户端会话(用于高级集成).

import { createClientSession } from '@funnelfox/billing';

const session = await createClientSession({
  priceId: 'price_123',
  externalId: 'user_456',
  email: '[email protected]', // Optional
  orgId: 'your-org-id', // Optional if configured
});

console.log(session.clientToken); // Use with Primer Headless Checkout
console.log(session.orderId);

回返情况: Promise<{ clientToken: string, orderId: string, type: string }>


离职偿金

属性

  • id (字符串) - 唯一的出票标识符
  • state (字符串) - 当前状态 : initializing, (中文(简体) ), ready, (中文(简体) ), processing, (中文(简体) ), completed, (中文(简体) ), error
  • orderId (字符串) - 顺序标识符(初始化后可用)
  • isDestroyed (boolean) - 退出是否已被销毁

活动

'success'

付款完成后立即退出.

checkout.on('success', result => {
  console.log('Order ID:', result.orderId);
  console.log('Status:', result.status); // 'succeeded'
  console.log('Transaction:', result.transactionId);
});
'error'

当付款失败或遇到错误时, 即予以释放 .

checkout.on('error', error => {
  console.error('Error:', error.message);
  console.error('Code:', error.code);
  console.error('Request ID:', error.requestId); // For support
});
'status-change'

退出状态改变时发送 .

checkout.on('status-change', (newState, oldState) => {
  console.log(`${oldState} → ${newState}`);
  // States: initializing, ready, processing, action_required, completed, error
});
'destroy'

退房时通知.

checkout.on('destroy', () => {
  console.log('Checkout cleaned up');
});

方法

updatePrice(priceId)

更新退出以使用不同的价格.

await checkout.updatePrice('price_yearly');

说明: 处理中无法更新价格 .

getStatus()

返回当前退出状态 .

const status = checkout.getStatus();
console.log(status.id); // Checkout ID
console.log(status.state); // Current state
console.log(status.orderId); // Order ID
console.log(status.priceId); // Current price ID
console.log(status.isDestroyed); // Cleanup status
destroy()

破坏退出实例并清理资源.

await checkout.destroy();
isReady()

检查退出是否准备好支付 .

if (checkout.isReady()) {
  console.log('Ready to accept payment');
}
isProcessing()

检查是否正在处理付款 .

if (checkout.isProcessing()) {
  console.log('Payment in progress...');
}

完整示例

<!DOCTYPE html>
<html>
  <head>
    <title>Funnelfox Checkout</title>
    <script src="https://sdk.primer.io/web/v2.57.3/Primer.min.js"></script>
    <link
      rel="stylesheet"
      href="https://sdk.primer.io/web/v2.57.3/Checkout.css"
    />
    <script src="https://unpkg.com/@funnelfox/billing@latest/dist/funnelfox-billing.min.js"></script>
  </head>
  <body>
    <div id="price-selector">
      <button onclick="selectPrice('price_monthly')">Monthly - $9.99</button>
      <button onclick="selectPrice('price_yearly')">Yearly - $99.99</button>
    </div>

    <div id="checkout-container"></div>

    <script>
      let currentCheckout = null;

      // Configure SDK once
      Billing.configure({
        orgId: 'your-org-id',
      });

      async function selectPrice(priceId) {
        try {
          if (currentCheckout && currentCheckout.isReady()) {
            // Update existing checkout
            await currentCheckout.updatePrice(priceId);
          } else {
            // Destroy old checkout if exists
            if (currentCheckout) {
              await currentCheckout.destroy();
            }

            // Create new checkout
            currentCheckout = await Billing.createCheckout({
              priceId: priceId,
              customer: {
                externalId: generateUserId(),
                email: getUserEmail(),
              },
              container: '#checkout-container',
            });

            // Handle success
            currentCheckout.on('success', result => {
              alert('Payment successful!');
              window.location.href = '/success?order=' + result.orderId;
            });

            // Handle errors
            currentCheckout.on('error', error => {
              alert('Payment failed: ' + error.message);
            });

            // Track state changes
            currentCheckout.on('status-change', state => {
              console.log('Checkout state:', state);
            });
          }
        } catch (error) {
          console.error('Checkout error:', error);
          alert('Failed to initialize checkout');
        }
      }

      function generateUserId() {
        return 'user_' + Math.random().toString(36).substr(2, 9);
      }

      function getUserEmail() {
        return '[email protected]'; // Get from your auth system
      }
    </script>
  </body>
</html>

处理错误

SDK为不同的情景提供了特定的出错分类:

import {
  ValidationError,
  APIError,
  PrimerError,
  CheckoutError,
  NetworkError,
} from '@funnelfox/billing';

try {
  const checkout = await createCheckout(config);
} catch (error) {
  if (error instanceof ValidationError) {
    // Invalid input
    console.log('Field:', error.field);
    console.log('Value:', error.value);
    console.log('Message:', error.message);
  } else if (error instanceof APIError) {
    // API error
    console.log('Status:', error.statusCode);
    console.log('Error Code:', error.errorCode); // e.g., 'double_purchase'
    console.log('Error Type:', error.errorType); // e.g., 'api_exception'
    console.log('Request ID:', error.requestId); // For support
    console.log('Decline Code:', error.declineCode); // e.g., 'INSUFFICIENT_FUNDS', set on failed payments
    console.log('Decline Source:', error.declineCodeSource); // 'primer' | 'stripe' | 'adyen', when the provider reports one
    console.log('Message:', error.message);
  } else if (error instanceof PrimerError) {
    // Primer SDK error
    console.log('Primer error:', error.message);
    console.log('Original:', error.primerError);
  } else if (error instanceof CheckoutError) {
    // Checkout lifecycle error
    console.log('Phase:', error.phase);
    console.log('Message:', error.message);
  } else if (error instanceof NetworkError) {
    // Network/connectivity error
    console.log('Network error:', error.message);
    console.log('Original:', error.originalError);
  }
}

常见错误代码

  • double_purchase - 用户已主动订阅
  • invalid_price - 找不到价格标识
  • invalid_customer - 客户数据验证失败
  • payment_failed - 付款处理失败

类型脚本支持

SDK包括全面的类型脚本定义:

import {
  configure,
  createCheckout,
  CheckoutInstance,
  PaymentResult,
  CheckoutConfig,
  PaymentMethod,
} from '@funnelfox/billing';

// Configure
configure({
  orgId: 'your-org-id',
});

// Create checkout with type safety
const checkout: CheckoutInstance = await createCheckout({
  priceId: 'price_123',
  customer: {
    externalId: 'user_456',
    email: '[email protected]',
    countryCode: 'US',
  },
  container: '#checkout',
  clientMetadata: {
    source: 'web',
    campaign: 'summer-sale',
  },
  paymentMethodOrder: [
    PaymentMethod.PAYPAL,
    PaymentMethod.PAYMENT_CARD,
    PaymentMethod.GOOGLE_PAY,
    PaymentMethod.APPLE_PAY,
  ],
});

// Type-safe event handlers
checkout.on('success', (result: PaymentResult) => {
  console.log('Order:', result.orderId);
  console.log('Status:', result.status);
  console.log('Transaction:', result.transactionId);
});

高级使用

使用回调而不是事件

const checkout = await createCheckout({
  priceId: 'price_123',
  customer: {
    externalId: 'user_456',
    email: '[email protected]',
  },
  container: '#checkout',

  // Callback style (alternative to .on() events)
  onSuccess: result => {
    console.log('Success!', result.orderId);
  },
  onError: error => {
    console.error('Error!', error.message);
  },
  onStatusChange: (newState, oldState) => {
    console.log(`${oldState} → ${newState}`);
  },
});

自定义卡输入选择器

默认情况下,SDK会自动生成卡输入元素. 如果您想要使用自己的 HTML 结构, 您可以提供自定义选择器 :

const checkout = await createCheckout({
  priceId: 'price_123',
  customer: {
    externalId: 'user_456',
    email: '[email protected]',
  },
  container: '#checkout',

  // Custom card input selectors
  cardSelectors: {
    cardNumber: '#my-card-number',
    expiryDate: '#my-expiry',
    cvv: '#my-cvv',
    cardholderName: '#my-cardholder',
    emailAddress: '#my-email',
    button: '#my-submit-button',
  },

  // Custom payment method button containers
  paypalButtonContainer: '#my-paypal-button',
  googlePayButtonContainer: '#my-google-pay-button',
  applePayButtonContainer: '#my-apple-pay-button',
});

自定义支付方法顺序

您可以自定义向客户显示支付方法的顺序:

const checkout = await createCheckout({
  priceId: 'price_123',
  customer: {
    externalId: 'user_456',
    email: '[email protected]',
  },
  container: '#checkout',

  // Customize payment method order
  paymentMethodOrder: ['PAYPAL', 'GOOGLE_PAY', 'APPLE_PAY', 'PAYMENT_CARD'],
});

现有付款方法:

  • 'PAYMENT_CARD' - 信用卡/信用卡付款
  • 'PAYPAL' - 支付工资
  • 'GOOGLE_PAY' - 谷歌支付
  • 'APPLE_PAY' - 支付苹果

默认情况下,支付方法按顺序显示:卡,PayPal,Google Pay,Apple Pay. 您可以重新排序, 以匹配您的商业优先或区域偏好 .

使用 initMethod 单一支付方法

对于您想要对安置和回调进行完全控制的一种单一支付方法的情景:

import { Billing, PaymentMethod } from '@funnelfox/billing';

const container = document.getElementById('payment-container');

const paymentMethod = await Billing.initMethod(
  PaymentMethod.PAYMENT_CARD, // or PAYPAL, GOOGLE_PAY, APPLE_PAY
  container,
  {
    // Required
    orgId: 'your-org-id',
    priceId: 'price_123',
    externalId: 'user_456',
    email: '[email protected]',

    // Optional - API configuration
    baseUrl: 'https://custom.api', // Optional, defaults to https://billing.funnelfox.com
    meta: { source: 'web' }, // Optional metadata

    // Optional - Primer configuration (for customizing payment method behavior)
    style: {
      /* Primer style options */
    },
    card: {
      /* Primer card options */
    },
    applePay: {
      /* Primer Apple Pay options */
    },
    paypal: {
      /* Primer PayPal options */
    },
    googlePay: {
      /* Primer Google Pay options */
    },

    // Callbacks
    onRenderSuccess: () => {
      console.log('Payment method rendered successfully');
    },
    onRenderError: method => {
      console.error('Failed to render:', method);
    },
    onLoaderChange: isLoading => {
      console.log('Loading state:', isLoading);
    },
    onPaymentStarted: method => {
      console.log('Payment started with:', method);
    },
    onPaymentSuccess: () => {
      console.log('Payment completed successfully!');
    },
    onPaymentFail: error => {
      console.error('Payment failed:', error.message);
      console.log('Decline code:', error.declineCode); // e.g., 'INSUFFICIENT_FUNDS', when the provider reports one
      console.log('Decline source:', error.declineCodeSource); // 'primer' | 'stripe' | 'adyen'
    },
    onPaymentCancel: () => {
      console.log('Payment was cancelled');
    },
    onErrorMessageChange: message => {
      console.log('Error message:', message);
    },
    onMethodsAvailable: methods => {
      console.log('Available methods:', methods);
    },
  }
);

// Control the payment method
paymentMethod.setDisabled(true); // Disable the payment method
paymentMethod.setDisabled(false); // Enable it

// For card payments, you can trigger submit programmatically
if (paymentMethod.submit) {
  await paymentMethod.submit();
}

// Clean up when done
await paymentMethod.destroy();

参数:

  • method (支付方法,要求) - 初始化的支付方法: PAYMENT_CARD, (中文(简体) ), PAYPAL, (中文(简体) ), GOOGLE_PAY,或 APPLE_PAY
  • element (HTMlement,要求) - DOM 元素,将使用支付方法
  • options (所需方法)
    • orgId (字符串,需要) - 您的组织标识符
    • priceId (字符串,要求) - 价格标识符
    • externalId (字符串, 需要) - 您的用户标识符
    • email (字符串,可选) - 客户电子邮件
    • baseUrl (字符串,可选) - 自定义 API URL
    • meta (对象,可选) - 自定义元数据
    • style, (中文(简体) ), card, (中文(简体) ), applePay, (中文(简体) ), paypal, (中文(简体) ), googlePay (可选) - Primer SDK 配置选项
    • 回调( 所有可选) : onRenderSuccess, (中文(简体) ), onRenderError, (中文(简体) ), onLoaderChange, (中文(简体) ), onPaymentStarted, (中文(简体) ), onPaymentSuccess, (中文(简体) ), onPaymentFail, (中文(简体) ), onPaymentCancel, (中文(简体) ), onErrorMessageChange, (中文(简体) ), onMethodsAvailable

回返情况: Promise<PaymentMethodInterface> 方法:

  • setDisabled(disabled: boolean) - 启用/取消支付方法
  • submit() - 提交触发表(可用于支付卡片)
  • destroy() - 清理并删除支付方法

手动会话创建

对于您想要直接控制Primer Heads的高级集成:

import { createClientSession } from '@funnelfox/billing';
import { Primer } from '@primer-io/checkout-web';

// Step 1: Create session
const session = await createClientSession({
  priceId: 'price_123',
  externalId: 'user_456',
  email: '[email protected]',
  orgId: 'your-org-id',
});

// Step 2: Use with Primer Headless Checkout directly
const headlessCheckout = await Primer.createHeadless(session.clientToken, {
  paymentHandling: 'MANUAL',
  apiVersion: '2.4',
  onTokenizeSuccess: async (paymentMethodTokenData, handler) => {
    // Your custom payment logic...
    // Call your payment API with paymentMethodTokenData.token
    handler.handleSuccess();
  },
});

await headlessCheckout.start();

用钱包重新认证的一克Upsell

Billing.mountWalletUpsell(container, options)

保存的 Apple Pay / Google Pay order 不得作为客户主动付款(Primer)收费,因此用钱包付款的买主再次证实了钱包单中的上市,而上市者则以那笔新鲜的钱包付款收费. 打电话 mountWalletUpsell 当上售屏幕加载时, 而不是在水龙头上: Apple Pay只从水龙头本身打开工作表, 所以 SDK 提前准备所有文件, 并将 Primer 的本地钱包按钮挂入 container。 。 。 .

const mount = await Billing.mountWalletUpsell(container, {
  orgId: 'your-org-id',
  priceId: 'upsell_price',
  externalId: 'user_456',
  clientMetadata: { ff_billing_reason: 'upsell' },
  style: {
    buttonType: 'plain',
    buttonColor: 'black',
    height: 54,
    borderRadius: 28,
  },
  onStart: () => {},
  onThreeDSStart: ({ orderId }) => {},
  onThreeDSEnd: ({ result }) => {},
  onResult: result => {
    // result.outcome: 'succeeded' | 'declined' | '3ds_failed' | '3ds_abandoned'
    //   | 'error' | 'processing' | 'already_purchased' | 'cancelled'
  },
});

if (mount.mounted) {
  // The wallet button is shown: hide your own upsell button.
  // mount.setDisabled(true | false), mount.destroy() when the screen goes away.
} else if (mount.reason === 'not_wallet' || mount.reason === 'error') {
  // Keep your own button and use Billing.silentPurchase({ handle3DS: true }).
} else if (mount.reason === 'wallet_unavailable') {
  // The buyer's wallet cannot be shown on this device: open the full checkout on tap.
}

挂载结果 :

  • mounted: true 与 wallet (单位:千美元)APPLE_PAY 或者说 GOOGLE_PAY: 显示本地按钮 .
  • not_wallet:买方保存的方法不是Primer钱包.
  • wallet_unavailable:设备无法显示买主的钱包 。 另一个钱包从不提供.
  • error 与 error: 请求失败(网络, 5xx, double_purchase, (中文(简体) ), invalid_price, (中文(简体) ), merchant_config_error, 速率限制)或按钮无法打开.
  • 承诺只拒绝 ValidationError与 mounted: false 什么都没有了 container。 。 。 .

onResult 每个尝试都叫一次,与 silentPurchase({ handle3DS: true }) 加上结果 cancelled:买方关闭了钱包单,未收取任何费用。 之后 cancelled 按钮保持活性,可以再次被调用;在任何其他结果之后,它永远被禁用。 3DS 重试时使用一能键,每个结果的含义是: silentPurchase({ handle3DS: true })· ; processing 携带订单 id 的指控被保留.

由SDK负责:

  • 尝试运行时的第二个水龙头被忽略,对于两个钱包来说,同样也是 silentPurchase({ handle3DS: true }) 给同一个买家.
  • 钱包会话在到期前再次准备, 当标签变得可见, 剩下的时间不多 。 对即将到期的会话的自拍被忽略并被替换出按钮,所以下个自拍工作.
  • 当后端报告钱包会话不再有效(无任何收费)时,SDK会准备一个新的会话,并用相同的钱包付款重试一次,但只有在金额不变的情况下才进行. 否则,当钱包支付本身被拒绝时,结果是 error。 。 。 .
  • 如果显示按钮时会话无法再次准备, onResult 得到一个 error 没有水龙头,按钮被禁用.

样式 : buttonType plain | buy | pay | continue (钱包没有变成 plain; 苹果支付 continue 用苹果绘制 <apple-pay-button>, (中文(简体) ), buttonColor black | white | white-outline (谷歌付费显示) white-outline 作为 white, (中文(简体) ), height 财务报告和审定财务报表 borderRadius px(默认54和28).

Apple Pay需要该页面域名在Primer中验证Apple Pay,如出账.

条纹融合

这个 Billing.stripe namespace 提供了一个外形- 本地取出体验—— 不需要Primer依赖 。 它支持通过"花脸元素"支付卡和通过"支付请求API"支付本地钱包(Apple Pay / Google Pay).

说明: @primer-io/checkout-web 实值 没有 用于条纹融合。 仅 @funnelfox/billing 并需要在您的 Funnelfox 账户中有一个花旗价格.


Billing.stripe.createCardForm(element, params)

将条纹元素支付表挂入 DOM 元素。 回返a { submit() } handle —— 启动付款时控制(例如点击自己的按钮).

const element = document.getElementById('card-form');

const cardForm = await Billing.stripe.createCardForm(element, {
  // Required
  priceId: 'price_123',
  externalId: 'user_456',

  // Optional
  orgId: 'your-org-id',
  email: '[email protected]',
  countryCode: 'US',
  showWallets: false, // show Apple Pay / Google Pay inside the form
  appearance: {
    // Stripe Elements appearance API
    theme: 'stripe',
  },

  // Callbacks
  onRenderSuccess: () => {
    document.getElementById('pay-button').disabled = false;
  },
  onLoaderChange: loading => {
    document.getElementById('pay-button').disabled = loading;
  },
  onPaymentSuccess: (paymentMethod, orderId) => {
    window.location.href = '/success?order=' + orderId;
  },
  onPaymentFail: error => {
    console.error('Payment failed:', error.message);
  },
});

// Wire up your own submit button
document.getElementById('pay-button').addEventListener('click', async () => {
  await cardForm.submit();
});

关键参数:

参数类型说明
priceId字符串价格标识符
externalId字符串您的用户标识符
email绳子?客户电子邮件
orgId绳子?Org ID( 如果没有全局配置)
showWallets布尔?在条纹格式内显示 Apple Pay / Google Pay
appearance对象?条纹元素外观 API
clientMetadata对象?自定义附于命令的元数据

回返情况: Promise<{ submit: () => Promise<void> }>


Billing.stripe.getAvailableWallet(params)

检查当前设备和浏览器上是否有Apple Pay或Google Pay. 使用此选项在尝试支付前有条件显示钱包按钮 .

const wallet = await Billing.stripe.getAvailableWallet({
  priceId: 'price_123',
  externalId: 'user_456',
});

if (wallet === 'APPLE_PAY') {
  document.getElementById('apple-pay-btn').style.display = 'block';
} else if (wallet === 'GOOGLE_PAY') {
  document.getElementById('google-pay-btn').style.display = 'block';
} else {
  // No wallet available — show card form only
}

回返情况: Promise<'APPLE_PAY' | 'GOOGLE_PAY' | null>

打电话 Billing.stripe.getAvailableWallet(...) 在渲染页面时( 它决定是否显示钱包按钮 ) 。 除了检测可用性,它预发了付款单,所以 purchaseWallet(...) 点击处理器内部可以立即打开——Safari取消在点击处理器中长时间的ASync工作后打开的Apple Pay页. 传出同样的 priceId编号 :externalId编号 :email编号 :countryCode 对两个呼叫:它们共享一个检出会话,不匹配的参数创建第二个会话并跳过前置工作表.


Billing.stripe.purchaseWallet(params)

触发本地苹果支付或谷歌支付表. 在确认钱包可用后点击按钮调用此选项 getAvailableWallet。 。 。 .

document.getElementById('wallet-btn').addEventListener('click', async () => {
  await Billing.stripe.purchaseWallet({
    priceId: 'price_123',
    externalId: 'user_456',
    totalLabel: 'Premium Plan', // Label shown in the payment sheet

    onPaymentSuccess: (paymentMethod, orderId) => {
      window.location.href = '/success?order=' + orderId;
    },
    onPaymentFail: error => {
      console.error('Wallet payment failed:', error.message);
    },
    onPaymentCancel: () => {
      console.log('User cancelled');
    },
    onLoaderChange: loading => {
      document.getElementById('wallet-btn').disabled = loading;
    },
  });
});

关键参数:

参数类型说明
priceId字符串价格标识符
externalId字符串您的用户标识符
totalLabel绳子?在付款表金额旁边显示标签
email绳子?客户电子邮件
clientMetadata对象?自定义附于命令的元数据

Billing.stripe.getAvailablePaymentMethods(params)

返回当前设备可用的所有花旗支付方法。 总是包含 PAYMENT_CARD; 如果检测到钱包, 还包括钱包方法.

const methods = await Billing.stripe.getAvailablePaymentMethods({
  priceId: 'price_123',
  externalId: 'user_456',
});

// methods: ['PAYMENT_CARD', 'APPLE_PAY'] or ['PAYMENT_CARD'] etc.
console.log('Available:', methods);

回返情况: Promise<PaymentMethod[]> - 总是包含 PAYMENT_CARD,可选 APPLE_PAY 或者说 GOOGLE_PAY


组合示例:钱包检测+卡后退

建议的模式——在可用时显示钱包按钮,始终将卡片表显示为倒置:

import { Billing } from '@funnelfox/billing';

Billing.configure({ orgId: 'your-org-id' });

const params = {
  priceId: 'price_123',
  externalId: 'user_456',
  email: '[email protected]',
};

async function initCheckout() {
  // 1. Check for wallet availability
  const wallet = await Billing.stripe.getAvailableWallet(params);

  if (wallet) {
    const walletBtn = document.getElementById('wallet-btn');
    walletBtn.textContent =
      wallet === 'APPLE_PAY' ? 'Pay with Apple Pay' : 'Pay with Google Pay';
    walletBtn.style.display = 'block';

    walletBtn.addEventListener('click', () => {
      Billing.stripe.purchaseWallet({
        ...params,
        totalLabel: 'Premium Plan',
        onPaymentSuccess: (_, orderId) => {
          window.location.href = '/success?order=' + orderId;
        },
        onPaymentFail: err => alert(err.message),
        onPaymentCancel: () => console.log('Cancelled'),
      });
    });
  }

  // 2. Always mount card form as fallback
  const cardForm = await Billing.stripe.createCardForm(
    document.getElementById('card-form'),
    {
      ...params,
      onPaymentSuccess: (_, orderId) => {
        window.location.href = '/success?order=' + orderId;
      },
      onPaymentFail: err => alert(err.message),
      onLoaderChange: loading => {
        document.getElementById('pay-btn').disabled = loading;
      },
    }
  );

  document.getElementById('pay-btn').addEventListener('click', () => {
    cardForm.submit();
  });
}

initCheckout();

浏览器支持

  • 颜色60+
  • Firefox 55+ (英语)
  • 第12条+
  • 边缘79+

实例

见 示例目录 用于比较完整的示例:

许可证

麻省理工学院

版本与发布记录

@funnelfox/billing 共发布 124 个版本,首次发布于 2025/10/01;过去 12 个月发布了 122 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.14.0latest2026/11/123 天前
0.8.0-ffb-805.562026/11/096 天前
0.8.0-ffb-837.552026/11/069 天前
0.13.22026/11/0213 天前
0.8.0-ffb-758.542026/10/3016 天前
0.8.0-ffb-778.532026/10/3016 天前

其他发布通道:beta → 0.9.0-beta.7,add-release-docs → 0.8.0-add-release-docs.8,ffb-467-adyen → 0.9.0-ffb-467-adyen.25,ffb-395 → 0.8.0-ffb-395.26,ffb-apple-pay-continue-button → 0.9.3-ffb-apple-pay-continue-button.33,feat-defer-session-on-interaction → 0.9.3-feat-defer-session-on-interaction.34。

关于 @funnelfox/billing 的常见问题

@funnelfox/billing 是什么?

JavaScript SDK 用于与 Primer 集成的 Funnelfox 计费

如何安装 @funnelfox/billing?

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

@funnelfox/billing 支持 TypeScript 吗?

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

@funnelfox/billing 支持 ESM 还是 CommonJS?

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

@funnelfox/billing 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=14.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@funnelfox/billing 可以商用吗?

@funnelfox/billing 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@funnelfox/billing 的下载量有多少?

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

@funnelfox/billing 还在维护吗?

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