Custom Registration Component
The Problem#
You need to register users in your own system after they complete payment through a Zellify funnel. Standard components don't support calling your backend API with custom logic. You want to send payment and user data to your backend, receive credentials (like a token), and then route the user to your app.
How It Works#
- The end-user goes through the Zellify funnel and lands on the paywall page
- They select a product or click buy, triggering checkout initiation
- Zellify backend creates the Stripe/Paddle customer and checkout session (with
app_user_idin metadata) - The checkout form renders; the user fills it out and submits payment
- On payment success, the user is redirected to the success page containing a custom registration component
- The component reads Zellify data (email, Stripe customer ID, checkout session ID,
app_user_id) and calls your backend API - Your backend creates the user account, links it to the payment, and returns credentials
- The component redirects the user to your app with the credentials
Example#
This example shows a component that sends user data to your backend after payment, receives a token, and opens your app with the credentials.
The example focuses on logic — add your own UI (button, loading state, etc.) as needed.
1import { tracker } from "@tracker";2import { useVariable } from "@variables";3import { useFunnelRouter } from "@primitives";4import { useState } from "react";5 6export default function RegisterAndOpenApp() {7 const funnelRouter = useFunnelRouter();8 9 // Zellify variables (available after payment)10 const [email] = useVariable("email");11 const [stripeCustomerId] = useVariable("_stripeCustomerId");12 const [stripeCheckoutSessionId] = useVariable("_stripeCheckoutSessionId");13 14 // Session identifier (same as app_user_id in payment metadata)15 const appUserId = tracker.sessionId;16 17 const [loading, setLoading] = useState(false);18 19 const handleRegister = async () => {20 setLoading(true);21 22 // 1. Call your backend to create the user account23 const res = await fetch("https://api.yourapp.com/register", {24 method: "POST",25 headers: { "Content-Type": "application/json" },26 body: JSON.stringify({27 app_user_id: appUserId,28 email: email,29 stripe_customer_id: stripeCustomerId,30 stripe_checkout_session_id: stripeCheckoutSessionId,31 }),32 });33 34 const { token } = await res.json();35 36 // 2. Redirect to your app with credentials37 const params = new URLSearchParams({38 token: token,39 user_id: appUserId,40 });41 42 funnelRouter.openExternal(`https://yourapp.onelink.me/abc?${params}`);43 };44 45 return (46 <button type="button" onClick={handleRegister} disabled={loading}>47 {loading ? "Setting up..." : "Open App"}48 </button>49 );50}
tracker.sessionId may be undefined on first render — it takes up to ~200ms to initialize.Use this hook instead of reading tracker.sessionId directly:`import { useState, useEffect } from "react";
import { tracker } from "@tracker";
function useAppUserId() {
const [id, setId] = useState(tracker.sessionId);
useEffect(() => {
if (id) return;
const interval = setInterval(() => {
if (tracker.sessionId) {
setId(tracker.sessionId);
clearInterval(interval);
}
}, 100);
return () => clearInterval(interval);
}, [id]);
return id;
}
Then replace const appUserId = tracker.sessionId; with:const appUserId = useAppUserId();
`What Zellify Provides#
Your custom component has access to:
| Import | What it provides |
|---|---|
useVariable("email") | User's email address |
useVariable("_stripeCustomerId") | Stripe Customer ID (requires Stripe payment provider) |
useVariable("_stripeCheckoutSessionId") | Stripe Checkout Session ID (requires Stripe payment provider) |
useVariable("_paddleCustomerId") | Paddle Customer ID (requires Paddle payment provider) |
useVariable("_paddleTransactionId") | Paddle Transaction ID (requires Paddle payment provider) |
tracker.sessionId | Unique session ID (same as app_user_id in payment metadata) |
funnelRouter.openExternal(url) | Navigate to external URL |
What You Need to Set Up#
1. Build your backend endpoint#
Your API endpoint should:
- Accept POST requests with the user data
- Create or update the user account
- Link the account to the Stripe/Paddle customer using
app_user_id - Return credentials (token, user ID, etc.) for your app
- Handle CORS if your funnel domain differs from your API domain
2. Create the custom component in Zellify#
In the funnel builder, create a custom component where you implement your registration logic (similar to the example above). Then drag and drop that component onto your success page — any page after the paywall where paid users land.
3. Handle the redirect#
Use funnelRouter.openExternal() to send the user to your app with the credentials. For mobile apps, this is typically a deep link or universal link (AppsFlyer, Adjust, Branch, etc.).
The Linking Key#
app_user_id (from tracker.sessionId) is the same identifier that Zellify sets in Stripe/Paddle metadata at checkout. Your backend can use this to match the registration request to the payment record.