Skip to Content
Headless SDKPayments

Payments

AppFunnel provides payment components that handle Stripe and Paddle integrations out of the box. You select a product with useProducts(), render a payment form, and the SDK takes care of intent creation, event tracking, and post-purchase flow.

StripePaymentForm

import { StripePaymentForm } from '@appfunnel-dev/sdk'

A drop-in Stripe payment form that supports both inline card collection and full Stripe Embedded Checkout.

Props

PropTypeDefaultDescription
variant'elements' | 'embedded''elements'elements: inline card form (PaymentElement). embedded: Stripe Embedded Checkout (iframe).
mode'checkout' | 'validate-only''checkout'checkout: charge or create subscription. validate-only: collect card without charging.
productIdstringProduct ID override. Defaults to the currently selected product from useProducts().
successPageKeystringPage key to redirect to after embedded checkout completes. Required for embedded variant.
automaticTaxbooleanfalseEnable Stripe automatic tax calculation. Embedded variant only.
managedPaymentsbooleanfalseEnable Stripe managed payments — Stripe controls tax, payment methods, and compliance. Embedded variant only.
allowPromotionCodesbooleanfalseAllow promotion/coupon codes in checkout. Embedded variant only.
onSuccess() => voidCalled on successful payment. Elements variant only.
onError(error: string) => voidCalled on failure. Elements variant only.
onReady() => voidCalled when the form is ready for interaction. Elements variant only.
appearanceAppearanceStripe Appearance override. Elements variant only.
layout'tabs' | 'accordion' | 'auto''tabs'PaymentElement layout. Elements variant only.
refRef<StripePaymentHandle>Imperative handle for programmatic control. Elements variant only.

Ref handle

Pass a ref to get access to the StripePaymentHandle (elements variant only):

import type { StripePaymentHandle } from '@appfunnel-dev/sdk'
MethodDescription
submit()Programmatically submit the form. Equivalent to the user clicking a submit button inside the Stripe form.

Behavior

  • Automatic intent detection — The component auto-detects whether to create a SetupIntent or a PaymentIntent based on whether the selected product has a trial period.
  • Product binding — Reads the currently selected product from useProducts(). Make sure a product is selected before rendering the form.
  • Tracking events — Fires checkout.start when the form mounts, checkout.payment_added when the user enters valid payment details, and purchase.complete on successful payment.
  • Error toasts — Shows a toast notification automatically on payment errors. Disable with settings.disableToasts in your config.

Elements Variant

The default variant renders an inline card form using Stripe PaymentElement. You control the submit button and handle the success callback.

import { useRef } from 'react' import { definePage, StripePaymentForm, useProducts, usePayment, useNavigation } from '@appfunnel-dev/sdk' import type { StripePaymentHandle } from '@appfunnel-dev/sdk' export const page = definePage({ name: 'Checkout', type: 'checkout', routes: [{ to: 'success' }], }) export default function Checkout() { const formRef = useRef<StripePaymentHandle>(null) const { selected } = useProducts() const { loading, error } = usePayment() const { goToNextPage } = useNavigation() return ( <div> <h1>Checkout</h1> <p>{selected?.displayName} — {selected?.price}/{selected?.period}</p> <StripePaymentForm ref={formRef} onSuccess={() => goToNextPage()} /> {error && <p className="text-red-500">{error}</p>} <button onClick={() => formRef.current?.submit()} disabled={loading}> {loading ? 'Processing...' : 'Pay Now'} </button> </div> ) }

Navigation after payment is not automatic. Call goToNextPage() in the onSuccess callback to advance to the next page.


Embedded Variant

The embedded variant renders Stripe Embedded Checkout inside an iframe. Stripe controls the entire payment UI including payment method selection, form validation, and the submit button.

After checkout completes, Stripe redirects the user to the page specified by successPageKey. The SDK automatically picks up the page from the URL — no onSuccess callback is needed.

<StripePaymentForm variant="embedded" successPageKey="download" />

With managed payments and automatic tax

<StripePaymentForm variant="embedded" successPageKey="download" managedPayments automaticTax allowPromotionCodes />

Embedded Checkout does not save the card for future off-session charges. If you need upsells after the initial purchase, use the elements variant instead.


Upsells with purchase()

After the initial payment via the elements variant, the user’s card is saved. You can charge it again without showing another payment form using the purchase() function from usePayment().

import { useProducts, usePayment, useNavigation } from '@appfunnel-dev/sdk' export default function Upsell() { const { products } = useProducts() const { loading, purchase } = usePayment() const { goToNextPage } = useNavigation() const upsellProduct = products.find((p) => p.id.startsWith('upsell')) const handleAdd = () => { if (!upsellProduct) return purchase(upsellProduct.id, { onSuccess: () => goToNextPage(), onError: (error) => console.error(error), }) } return ( <button onClick={handleAdd} disabled={loading}> {loading ? 'Processing...' : 'Add to my plan'} </button> ) }

The purchase() function:

  • Charges the card already on file for the given product
  • Handles 3DS authentication automatically if required
  • Tracks purchase.complete and subscription.created events
  • Sets loading and error state during the operation
  • Shows a toast notification on errors (disable with settings.disableToasts)
  • In dev mode, simulates a successful charge after a short delay
ParameterTypeDescription
productIdstringThe product to purchase
options.onSuccess() => voidCalled on successful purchase
options.onError(error: string) => voidCalled with error message on failure

Returns Promise<boolean>true on success, false on failure.

Upsells require the initial payment to use the elements variant. Embedded Checkout does not save cards for future off-session charges.


PaddleCheckout

import { PaddleCheckout } from '@appfunnel-dev/sdk'

A Paddle payment component that supports overlay and inline modes.

Props

PropTypeDefaultDescription
mode'overlay' | 'inline'overlay: opens the Paddle checkout in a modal overlay. inline: renders the checkout inline on the page.
onSuccess() => voidCalled on successful payment.
onError(error: Error) => voidCalled on failure.

Paddle integration is in alpha. The API surface may change in future releases.


Payment Hooks

Two hooks are commonly used alongside payment components. See the Hooks page for full documentation.

const { products, selected, select } = useProducts() const { loading, error, purchase } = usePayment()
  • useProducts() — Returns the list of available products, the currently selected product, and a select function to change the selection. Always select a product before rendering a payment form.
  • usePayment() — Returns loading, error, and the purchase() function for off-session charges.
Last updated on