Skip to main content

Initialize Checkout Session with Items

Learn how to programmatically create a new checkout session with line items using the init() function from useCheckout().
Use Case: This is useful when you want to create a checkout without a pre-existing checkoutToken, such as:
  • Landing pages that add products directly to checkout
  • BOGO funnels that start with a specific bundle
  • Custom product selectors that initialize checkout on-the-fly

Basic Example

import { useCheckout, useFunnel } from '@tagadapay/plugin-sdk/v2';
import { useEffect, useRef } from 'react';

export default function CheckoutPage() {
  const { checkout, init, error } = useCheckout({
    checkoutToken: '', // No token - we'll create one
  });
  
  const { context } = useFunnel();
  const hasInitialized = useRef(false);

  // Initialize checkout when component mounts
  useEffect(() => {
    if (!checkout && init && !hasInitialized.current && context) {
      hasInitialized.current = true;

      init({
        storeId: 'your-store-id',
        lineItems: [
          {
            variantId: 'variant_abc123',
            quantity: 1,
          },
        ],
      }).catch((err) => {
        console.error('Failed to initialize checkout:', err);
      });
    }
  }, [checkout, init, context]);

  if (!checkout) {
    return <div>Initializing checkout...</div>;
  }

  return (
    <div>
      <h1>Checkout</h1>
      <p>Total: ${(checkout.summary.total / 100).toFixed(2)}</p>
    </div>
  );
}

Complete Example with Store ID

import { useCheckout, useFunnel, usePluginConfig } from '@tagadapay/plugin-sdk/v2';
import { useEffect, useRef, useState } from 'react';

export default function CheckoutPage() {
  const { storeId } = usePluginConfig();
  const { checkout, init, error } = useCheckout({
    checkoutToken: '',
  });
  const { context, isInitialized } = useFunnel();
  
  const hasInitialized = useRef(false);
  const [initError, setInitError] = useState<string | null>(null);

  useEffect(() => {
    // Wait for funnel to be ready
    if (!isInitialized || !context) return;
    
    // Only initialize once
    if (hasInitialized.current || checkout) return;
    
    // Check we have the init function
    if (!init) return;

    hasInitialized.current = true;
    
    init({
      storeId: storeId,
      lineItems: [
        {
          variantId: 'variant_xyz789', // Replace with your variant ID
          quantity: 1,
        },
      ],
    }).catch((err) => {
      console.error('❌ Failed to initialize checkout:', err);
      setInitError(err.message || 'Failed to create checkout');
    });
  }, [checkout, init, storeId, isInitialized, context]);

  // Show error state
  if (initError || error) {
    return (
      <div className="error">
        <h2>Unable to Initialize Checkout</h2>
        <p>{initError || error?.message}</p>
      </div>
    );
  }

  // Show loading state
  if (!checkout) {
    return (
      <div className="loading">
        <div className="spinner" />
        <p>Creating your checkout...</p>
      </div>
    );
  }

  // Checkout is ready!
  return (
    <div className="checkout">
      <h1>Checkout</h1>
      <div className="summary">
        <p>Items: {checkout.summary.items.length}</p>
        <p>Total: ${(checkout.summary.total / 100).toFixed(2)}</p>
      </div>
    </div>
  );
}

With Static Resources (CRM-Configured Variants)

This example shows how to use variant IDs from funnel static resources:
import { useCheckout, useFunnel, usePluginConfig } from '@tagadapay/plugin-sdk/v2';
import { useEffect, useRef } from 'react';

export default function CheckoutPage() {
  const { storeId } = usePluginConfig();
  const { checkout, init } = useCheckout({ checkoutToken: '' });
  const { context, isInitialized } = useFunnel();
  
  const hasInitialized = useRef(false);

  // Get variant ID from static resources (configured in CRM)
  const variantId = context?.static?.variant?.id;

  useEffect(() => {
    // Wait for all dependencies
    if (!isInitialized || !context || !variantId) return;
    if (hasInitialized.current || checkout || !init) return;

    hasInitialized.current = true;
    console.log('🚀 Initializing checkout with variant:', variantId);

    init({
      storeId: storeId,
      lineItems: [
        {
          variantId: variantId,
          quantity: 1,
        },
      ],
    }).catch((err) => {
      console.error('❌ Init failed:', err);
    });
  }, [checkout, init, storeId, variantId, isInitialized, context]);

  if (!checkout) return <div>Loading...</div>;

  return <div>Checkout Ready!</div>;
}
In your plugin manifest, declare static resources:
{
  "features": [{
    "requirements": [{
      "resource": "variant",
      "from": [{ "name": "id", "type": "static" }]
    }]
  }]
}
Then merchants can configure which variant to use in the CRM Funnel Builder.

Multiple Line Items

init({
  storeId: storeId,
  lineItems: [
    {
      variantId: 'variant_main',
      quantity: 2,
    },
    {
      variantId: 'variant_addon',
      quantity: 1,
    },
  ],
});

With Specific Price ID (Subscription)

import { useProducts } from '@tagadapay/plugin-sdk/v2';

function CheckoutWithSubscription() {
  const { storeId } = usePluginConfig();
  const { checkout, init } = useCheckout({ checkoutToken: '' });
  const { getVariant } = useProducts({ 
    variantIds: ['variant_abc123'],
  });
  
  const hasInitialized = useRef(false);

  useEffect(() => {
    if (hasInitialized.current || checkout || !init) return;
    
    const variantData = getVariant('variant_abc123');
    if (!variantData) return;

    // Get subscription price (recurring: true)
    const subscriptionPrice = variantData.variant.prices?.find(
      (p: any) => p.recurring === true
    );

    if (!subscriptionPrice) return;

    hasInitialized.current = true;

    init({
      storeId: storeId,
      lineItems: [
        {
          variantId: 'variant_abc123',
          priceId: subscriptionPrice.id, // Specific price for subscription
          quantity: 1,
        },
      ],
    });
  }, [checkout, init, storeId, getVariant]);

  return <div>...</div>;
}

Key Points

Use init() when:
  • ✅ No checkoutToken is provided in the URL
  • ✅ You want to programmatically create a checkout
  • ✅ Building a landing page that adds products to cart
  • ✅ Creating custom bundle selectors
Don’t use init() when:
  • ❌ You have a checkoutToken from the URL
  • ❌ The checkout already exists (checkout data loaded)
Always use a ref to prevent multiple init calls:
const hasInitialized = useRef(false);

useEffect(() => {
  if (hasInitialized.current) return;
  hasInitialized.current = true;
  
  init({ ... });
}, []);
Make sure to wait for:
  1. Funnel to be initialized (isInitialized)
  2. Context to be available (context)
  3. Static resources to load (if using them)
if (!isInitialized || !context) return;
if (!variantId) return; // if using static resources
Always handle initialization errors:
init({ ... }).catch((err) => {
  console.error('Init failed:', err);
  // Show error UI to user
});

API Reference

init() Parameters

init({
  storeId: string;           // Required: Your store ID
  lineItems: Array<{
    variantId: string;       // Required: Product variant ID
    priceId?: string;        // Optional: Specific price ID (for subscriptions)
    quantity: number;        // Required: Quantity to add
  }>;
})

Returns

Promise<void>  // Resolves when checkout is created

Common Patterns

Pattern 1: Auto-initialize with Default Product

// Perfect for landing pages with a single product
const variantId = 'variant_default_product';

useEffect(() => {
  if (!checkout && init && !hasInitialized.current) {
    hasInitialized.current = true;
    init({ storeId, lineItems: [{ variantId, quantity: 1 }] });
  }
}, [checkout, init, storeId]);

Pattern 2: Initialize from Static Resources

// Perfect for funnels where merchant configures products
const variantId = context?.static?.variant?.id;

useEffect(() => {
  if (variantId && !checkout && init && !hasInitialized.current) {
    hasInitialized.current = true;
    init({ storeId, lineItems: [{ variantId, quantity: 1 }] });
  }
}, [variantId, checkout, init, storeId]);

Pattern 3: Initialize with User Selection

// Perfect for bundle selectors or product pickers
const [selectedVariant, setSelectedVariant] = useState<string | null>(null);

const handleSelectVariant = async (variantId: string) => {
  setSelectedVariant(variantId);
  
  await init({
    storeId,
    lineItems: [{ variantId, quantity: 1 }],
  });
};

Next Steps