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

  1. The end-user goes through your Zellify funnel and reaches the paywall

  2. They complete checkout via Stripe or Paddle. Zellify creates a customer in your payment provider with the user's email and sets app_user_id in metadata (Stripe) or custom data (Paddle)

  3. Stripe or Paddle sends a webhook to your backend with the transaction details, including app_user_id in the metadata

  4. Your backend creates the user account and grants entitlements, storing app_user_id as the identifier

  5. The end-user lands on your success page, which contains a Deep Link Button configured with the app_user_id

  6. The end-user taps the button, is routed to your app, and the app reads app_user_id from the deeplink

  7. Your 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_id for every funnel visitor

  • Creates a Stripe Customer or Paddle Customer with the user's email at checkout

  • Sets app_user_id in Stripe Customer/Subscription metadata or Paddle Customer/Transaction custom data

  • Provides the Deep Link Button component on the success page, which interpolates app_user_id into 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:

Scenario
Webhook Event
Why

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

circle-info

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.

circle-exclamation

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 Subscriptionsarrow-up-right

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 Overviewarrow-up-right

2. Handle the webhook in your backend

When the payment webhook fires, your backend should:

  • Extract app_user_id from the subscription/transaction metadata or custom data

  • Extract 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.

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

When 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:

  1. 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

  2. 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.


Last updated