@ 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 URLconfig.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, (中文(简体) ),errororderId(字符串) - 顺序标识符(初始化后可用)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_PAYelement(HTMlement,要求) - DOM 元素,将使用支付方法options(所需方法)orgId(字符串,需要) - 您的组织标识符priceId(字符串,要求) - 价格标识符externalId(字符串, 需要) - 您的用户标识符email(字符串,可选) - 客户电子邮件baseUrl(字符串,可选) - 自定义 API URLmeta(对象,可选) - 自定义元数据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+
实例
见 示例目录 用于比较完整的示例:
- 基本报到 - 简单的离职整合
许可证
麻省理工学院