Skip to main content

Funnel Pages

A funnel is a sequence of pages (steps). Each page has a type that determines its capabilities — whether it can accept payments, show upsells, fire conversion events, etc.

Page Types

Every node in a funnel has a type. Here are all the available types and what they unlock:
TypePurposePayment FormOrder BumpsUpsell OffersCan be EntryConversion
checkoutPayment pageYesYesYes
thankyouOrder confirmationYes
offerUpsell / downsell / cross-sellOptionalYes
landingLanding page / hero with CTAYes
customAny custom pageOptionalOptionalOptionalYes
errorError state page
externalRedirect to external URLYes
storefront_shopifyShopify product pageYes
storefront_woocommerceWooCommerce product pageYes

Why types matter

The step type controls what stepConfig options are available:
  • checkout steps can have paymentSetupConfig (card, Apple Pay, Google Pay routing), orderBumps, and payment config
  • offer steps can have upsellOffers for post-purchase offers
  • thankyou steps are marked as the conversion endpoint — analytics events like Purchase fire here
  • landing and custom steps are general-purpose — they have pixels and scripts but no payment-specific config
// Checkout step — full payment config
{
  id: 'step_checkout',
  type: 'checkout',
  config: {
    stepConfig: {
      paymentSetupConfig: { card: { enabled: true, paymentFlowId: 'flow_xxx' } },
      orderBumps: { mode: 'custom', enabledOfferIds: ['offer_1'] },
      pixels: { facebook: [...] },
    },
  },
}

// Offer step — upsell config
{
  id: 'step_upsell',
  type: 'offer',
  config: {
    stepConfig: {
      upsellOffers: [{ offerId: 'offer_2', type: 'one_click' }],
      pixels: { facebook: [...] },
    },
  },
}

// Landing step — pixels and scripts only
{
  id: 'step_landing',
  type: 'landing',
  isEntry: true,
  config: {
    stepConfig: {
      pixels: { facebook: [...] },
      scripts: [{ name: 'Hotjar', content: '...', position: 'head-end', enabled: true }],
    },
  },
}

Common funnel patterns

Landing → Checkout → Thank You                    (simple)
Landing → Checkout → Offer → Thank You            (with upsell)
Storefront (Shopify) → Checkout → Thank You       (Shopify integration)
Checkout → Offer (EU) / Offer (US) → Thank You    (geo-conditional)
Landing → Checkout → Offer → Offer → Thank You    (multi-offer)
You can combine any types in any order. The only requirement is that a funnel with payments needs at least one checkout step, and a complete flow should end with a thankyou step for conversion tracking.

Three Approaches

TagadaPay offers three ways to create funnel pages, depending on how much control you need:
Native CheckoutCustom HTMLPlugin SDK
SetupZero code — auto-injectedDeploy raw HTML/CSS/JSBuild a React or JS app
Page typescheckout + thankyou onlyAny typeAny type
ControlTagadaPay handles the UIFull control, no frameworkFull control + SDK hooks
Best forGetting live fastQuick prototypes, simple pagesProduction-grade branded experiences
PaymentsAutomaticYou wire up tokenizationSDK handles tokenization, 3DS, payments
FunnelsAutomatic navigationManual routingSDK handles step navigation
Deployment2 steps4 steps (inline deploy via API)CLI: tgd deploy from your repo
A/B testingNoVia API split configVia API or CLI
When you create a funnel with checkout and thankyou step types, TagadaPay automatically injects its built-in checkout UI. No plugin deployment, no HTML — just define the flow.

Create and activate

import Tagada from '@tagadapay/node-sdk';
const tagada = new Tagada('your-api-key');

// Create funnel — native checkout is auto-injected
const funnel = await tagada.funnels.create({
  storeId: 'store_...',
  config: {
    id: 'my-checkout',
    name: 'My Checkout',
    version: '1.0.0',
    nodes: [
      {
        id: 'step_checkout',
        name: 'Checkout',
        type: 'checkout',
        kind: 'step',
        isEntry: true,
        position: { x: 0, y: 0 },
        config: { path: '/checkout' },
      },
      {
        id: 'step_thankyou',
        name: 'Thank You',
        type: 'thankyou',
        kind: 'step',
        position: { x: 300, y: 0 },
        config: { path: '/thankyou' },
      },
    ],
    edges: [
      { id: 'e1', source: 'step_checkout', target: 'step_thankyou' },
    ],
  },
  isDefault: true,
});

// Activate — triggers routing, mounts pages to CDN
const result = await tagada.funnels.update(funnel.id, {
  storeId: 'store_...',
  config: funnel.config,
});

const funnelCheckoutUrl = result.funnel.config.nodes
  .find(n => n.id === 'step_checkout').config.url;
console.log('Funnel live at:', funnelCheckoutUrl);

// Generate a checkout link with a pre-loaded cart
const session = await tagada.checkout.createSession({
  storeId: 'store_...',
  items: [{ variantId: 'variant_...', quantity: 1 }],
  currency: 'USD',
  checkoutUrl: funnelCheckoutUrl,
});
console.log('Checkout link:', session.redirectUrl);
No pluginId or instanceId needed. TagadaPay detects that checkout and thankyou nodes have no plugin assigned and injects the native checkout automatically.The checkoutUrl parameter in createSession is critical — it tells TagadaPay to redirect customers to your funnel’s deployed page instead of the default CMS URL.

What you get

The native checkout includes:
  • Card payment form with real-time validation
  • Order summary with product details and pricing
  • Multi-currency support based on your store config
  • Mobile-responsive layout
  • Built-in thank you page with order confirmation
For the full setup including processor, store, and product creation, see the Merchant Quick Start.

Updating a Live Plugin

To update your custom checkout without downtime, deploy a new version with the same plugin name:
const updated = await tagada.plugins.deploy({
  storeId: 'store_...',
  manifest: {
    name: 'my-custom-checkout',
    version: '1.1.0',
    mode: 'spa-mode',
    pages: [
      { id: 'checkout', path: '/checkout', name: 'Checkout', isDefault: true },
      { id: 'thankyou', path: '/thankyou', name: 'Thank You' },
    ],
  },
  inlineAssets: [/* updated HTML/CSS/JS */],
  autoInstantiate: { stepType: 'checkout', isPreview: false },
});

// Point the funnel to the new instance
const config = JSON.parse(JSON.stringify(funnel.config));
for (const node of config.nodes) {
  node.config.instanceId = updated.instanceId;
  node.config.instanceVersion = '1.1.0';
}

await tagada.funnels.update(funnel.id, {
  storeId: 'store_...',
  config,
});

A/B Testing

Split traffic between two checkout designs:
const variantB = await tagada.plugins.deploy({
  storeId: 'store_...',
  manifest: {
    name: 'checkout-variant-b',
    version: '1.0.0',
    mode: 'spa-mode',
    pages: [
      { id: 'checkout', path: '/checkout', name: 'Checkout B', isDefault: true },
    ],
  },
  inlineAssets: [/* variant B HTML */],
  autoInstantiate: { stepType: 'checkout', isPreview: false },
});

// Get routeId from the activated funnel
const checkoutNode = result.funnel.config.nodes
  .find(n => n.id === 'step_checkout');
const routeId = checkoutNode.config.routeId;

// 50/50 split
await fetch(`${baseUrl}/plugins/v2/split`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    routeId,
    storeId: 'store_...',
    splitConfig: {
      type: 'weighted',
      instances: [
        { instanceId: deployment.instanceId, deploymentId: deployment.deploymentId, weight: 50 },
        { instanceId: variantB.instanceId, deploymentId: variantB.deploymentId, weight: 50 },
      ],
      stickyDuration: 3600,
    },
  }),
});

Direct Mounting (Without Funnel)

For standalone pages (landing pages, portals) outside of a checkout flow:
await tagada.plugins.mount({
  deploymentId,
  deploymentInstanceId: instanceId,
  storeId: 'store_...',
  alias: 'my-landing-page',
  matcher: '.*',
  force: true,
});
// → my-landing-page--store_xxx.cdn.tagadapay.com

Plugin Management

const plugins = await tagada.plugins.list('store_...');
const instances = await tagada.plugins.listInstances('store_...');
const details = await tagada.plugins.deploymentDetails(deploymentId, 'store_...');
await tagada.plugins.del(pluginId, 'store_...');

Next Steps

Plugin SDK Reference

Full SDK documentation — hooks, API, examples

Plugin CLI

Deploy plugins from your terminal with tgd deploy

Merchant Quick Start

Full 7-step setup from processor to live checkout

Funnel Orchestrator

Multi-step funnels with routing and analytics