Mobile App with Custom Backend
Grant entitlements in your mobile app using your own backend after a web funnel purchase through Stripe or Paddle.
The Problem
You have a mobile app with your own backend that manages user accounts and entitlements. You do not use RevenueCat. You want to acquire users through a Zellify web funnel, collect payment via Stripe or Paddle, and then get those users into your app with the correct access.
How It Works
The end-user goes through your Zellify funnel and reaches the paywall
They complete checkout via Stripe or Paddle. Zellify creates a customer in your payment provider with the user's email and sets
app_user_idin metadata (Stripe) or custom data (Paddle)Stripe or Paddle sends a webhook to your backend with the transaction details, including
app_user_idin the metadataYour backend creates the user account and grants entitlements, storing
app_user_idas the identifierThe end-user lands on your success page, which contains a Deep Link Button configured with the
app_user_idThe end-user taps the button, is routed to your app, and the app reads
app_user_idfrom the deeplinkYour app calls your backend with
app_user_id— the user's account and entitlements are already there
What Zellify Handles
Generates a unique
app_user_idfor every funnel visitorCreates a Stripe Customer or Paddle Customer with the user's email at checkout
Sets
app_user_idin Stripe Customer/Subscription metadata or Paddle Customer/Transaction custom dataProvides the Deep Link Button component on the success page, which interpolates
app_user_idinto the deeplink URL
What You Need to Set Up
1. Register a webhook endpoint in your payment provider
You need your own webhook endpoint that listens for payment events from Stripe or Paddle. This is how your backend learns that a purchase happened and who made it.
If you use Stripe:
Register a webhook endpoint in your Stripe Dashboard under Developers → Webhooks. The webhook events you need depend on what products you sell through Zellify:
One-time product
checkout.session.completed
Payment collected at checkout
Subscription (first payment)
checkout.session.completed
Payment collected at checkout
Subscription (renewals)
invoice.paid
No checkout session on renewals
Subscription schedule
customer.subscription.created
See note below about subscription schedules
Upsells (any type)
invoice.paid
Upsells use the saved payment method to create invoices directly, without a new checkout session
Recommended: If you sell multiple product types or use upsells, listen for checkout.session.completed, customer.subscription.created, and invoice.paid. Use idempotency (track processed app_user_id values) to avoid creating duplicate accounts when multiple events fire for the same purchase.
About subscription schedules: When selling subscription schedules (multi-phase subscriptions), Zellify uses Checkout in setup mode to collect the payment method. After checkout completes, Zellify creates the subscription schedule, which creates the underlying subscription and a draft invoice.
Stripe keeps this first invoice in draft status for approximately 1 hour before finalizing and charging. This means checkout.session.completed fires before payment, and invoice.paid fires ~1 hour later—too late for the deep link flow.
For subscription schedules, use customer.subscription.created to provision access immediately when the subscription is created. The payment method has already been validated during checkout. If payment fails when the invoice finalizes, handle invoice.payment_failed to revoke access.
Refer to: Stripe: Subscription Schedules — see the note about first invoice finalization timing.
The Stripe Customer object contains the user's email. The Customer and Subscription metadata will contain app_user_id.
Refer to: Stripe: Using Webhooks with Subscriptions
If you use Paddle:
Register a webhook endpoint in your Paddle Dashboard (or note that Zellify already registers one for analytics — you need your own for entitlement logic). The recommended event for granting entitlements is transaction.completed. You can also listen for subscription.activated for subscription lifecycle events.
The Paddle Customer object contains the user's email. The Customer and Transaction custom data will contain app_user_id.
Refer to: Paddle: Webhooks Overview
2. Handle the webhook in your backend
When the payment webhook fires, your backend should:
Extract
app_user_idfrom the subscription/transaction metadata or custom dataExtract the customer's email from the Customer object (Stripe) or customer data in the event (Paddle)
Create the user account (or update an existing one)
Grant the appropriate entitlements based on the purchased product
The app_user_id becomes the key you use to identify this user across systems.
3. Configure the Deep Link Button on your success page
Add a Deep Link Button component to the page that appears after your paywall. In the component's configuration panel, set the URL to your deeplink scheme and enable app_user_id interpolation.
If you use a deferred deeplink provider (Adjust, AppsFlyer, Branch, etc.), configure the URL according to that provider's format. The app_user_id parameter will be interpolated automatically.
4. Read app_user_id in your app
app_user_id in your appWhen your app opens from the deeplink, extract the app_user_id parameter and use it to authenticate or identify the user against your backend. Your backend already has the account and entitlements ready from the webhook in step 2.
The Linking Key
The flow depends on app_user_id appearing in two places:
Stripe/Paddle metadata — set by Zellify at checkout in the subscription/transaction metadata (Stripe) or custom data (Paddle), included in the webhook payload your backend receives
Deep Link Button URL — interpolated by Zellify on the success page, carried into your app via the deeplink
The customer's email is stored on the Customer object itself (not in metadata), which is created by Zellify at checkout.
Your backend stores app_user_id when processing the payment webhook. Your app sends app_user_id when requesting the user's account. The match is automatic.
Optional: Registration Webhook for Early Data
If you want to receive the user's email and quiz answers before payment (for example, to pre-create an account or for abandoned cart recovery), you can also configure the Registration Webhook. This fires when the user submits their email in the funnel, before they reach the paywall.
The Registration Webhook payload includes the same app_user_id that will later appear in Stripe/Paddle metadata, so you can link pre-payment and post-payment data.
This is entirely optional. The payment webhook from Stripe or Paddle contains everything you need (app_user_id + email) to create the account and grant entitlements.
Related
Last updated

