Skip to main content
The SDK supports several advanced use-cases for more customized integration.

Callbacks instead of events

If you prefer not to use event emitters, you can supply callback functions directly in the createCheckout options for success, error, and status changes. This provides a quick way to handle outcomes without separately registering event listeners:
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}`);
  },
});
Using callbacks is equivalent to listening for the 'success', 'error', and 'status-change' events on the CheckoutInstance. Internally, the SDK will trigger these callbacks at the same time as the events. Choose the approach (callbacks vs. event listeners) that fits your application style; you can even mix them (for example, provide an onSuccess callback but also listen for 'status-change' events).

Custom card input selectors

By default, the SDK automatically generates card input elements. You can provide custom selectors if you want to use your own HTML structure:
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',
    button: '#my-submit-button',
  },
  
  // Custom payment method button containers
  paypalButtonContainer: '#my-paypal-button',
  googlePayButtonContainer: '#my-google-pay-button',
  applePayButtonContainer: '#my-apple-pay-button',
});
In the above snippet, we provide custom CSS selectors for card input fields and payment method button containers. If you don’t specify cardSelectors, FunnelFox will automatically generate the payment form for you.

Manual session creation

For full control over the checkout flow, you can manually create a client session and then use it with Primer’s Headless Checkout. This is useful if you need to perform custom steps between tokenization and finalizing payment (for example, if you want to process the payment on your backend before confirming the UI).

1. Create client session

Create a client session to get a clientToken (and orderId):
import { createClientSession } from '@funnelfox/billing';
import { Primer } from '@primer-io/checkout-web';

const session = await createClientSession({
  priceId: 'price_123',
  externalId: 'user_456',
  email: '[email protected]',
  orgId: 'your-org-id',
});
console.log('Client token:', session.clientToken);
console.log('Order ID:', session.orderId);

2. Use with Primer Headless Checkout

Use the obtained clientToken with Primer’s Headless Checkout:
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();
In this flow, you manually control when to display the checkout and what happens with the payment data. After calling createClientSession, you initialize Primer’s Headless Checkout with the clientToken. The onTokenizeSuccess callback gives you the opportunity to handle the payment token (paymentMethodTokenData.token) as needed. Once your server confirms the payment or subscription creation, you call handler.handleSuccess() to complete the flow (or call handler.handleFailure() if an error occurred). This advanced approach is useful for integrating additional custom business logic into the payment flow.

Custom payment method order

You can customize the order in which payment methods are displayed to your customers:
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'],
});
Available payment methods:
  • 'PAYMENT_CARD': Credit/debit card payment
  • 'PAYPAL': PayPal payment
  • 'GOOGLE_PAY': Google Pay payment
  • 'APPLE_PAY': Apple Pay payment
By default, payment methods are shown in the order: Card, PayPal, Google Pay, Apple Pay. You can reorder them to match your business priorities or regional preferences.

Single payment method rendering

For scenarios where you need full control over individual payment method placement and behavior, use initMethod to render a single payment method:
import { Billing, PaymentMethod } from '@funnelfox/billing';

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

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

    // Optional - API configuration
    baseUrl: 'https://custom.api',
    meta: { source: 'web' },

    // Optional - Primer configuration
    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');
    },
    onPaymentSuccess: () => {
      console.log('Payment completed!');
    },
    onPaymentFail: error => {
      console.error('Payment failed:', error.message);
    },
  }
);

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

// Trigger submit for card payments
if (paymentMethod.submit) {
  await paymentMethod.submit();
}

// Clean up when done
await paymentMethod.destroy();
method
PaymentMethod
required
Payment method to initialize: PAYMENT_CARD, PAYPAL, GOOGLE_PAY, or APPLE_PAY
element
HTMLElement
required
DOM element where the payment method will be rendered
options
InitMethodOptions
required
Configuration options for the payment method
Returns: Promise<PaymentMethodInterface> with methods:
  • setDisabled(disabled: boolean) - Enable/disable the payment method
  • submit() - Trigger form submission (available for card payments)
  • destroy() - Clean up and remove the payment method