> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.qonversion.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Funnel — Sell subscriptions on the web

> Build a web checkout funnel with the No-Code Builder and let users pay on the web (Stripe) and unlock the subscription in your mobile app.

A **Web Funnel** is a No-Code screen served on the web. Users open it in a browser (from ads, emails, or influencer links), pay with Stripe, and then unlock the subscription inside your mobile app through a redemption link.

Unlike Mobile Paywall / Mobile Onboarding screens (which render inside your app), a Web Funnel is hosted by Qonversion at `screens.qonversion.io` and checkout runs through **Stripe**.

## Prerequisites

* **Stripe connected** to your project: **Project Settings → Stores → Stripe → Connect Stripe** (Stripe OAuth — no API keys to paste). Web Funnels require Stripe; the Publish button stays disabled until Stripe is connected.
* A **product** mapped to a **Stripe Price ID** — set up below.
* A **Stripe webhook** with its signing secret added on the Stripe store card — see [Set up the Stripe webhook](#set-up-the-stripe-webhook).

### Map a product to a Stripe Price

The checkout charges a Stripe **Price** (`price_...`), not a Product (`prod_...`). Connect one to a Qonversion product:

1. In the Stripe Dashboard of your **connected** Stripe account (live mode), open **Product catalog → Add product**, set a **recurring** price, and save. (An existing product works too — every price lives on a product's page, in the **Pricing** section.)
2. Copy the **Price ID** — it looks like `price_...`.
3. In the Qonversion dashboard, open **Products** in the left menu and open the product you want to sell on the web (or create one — for subscriptions pick the matching duration).
4. Paste the Price ID into the **Stripe Price ID** field and save.

<Info>
  The price must live in the same Stripe account you connected to Qonversion — prices from any other account can't be resolved at checkout. Mobile store IDs (App Store / Play) on the same product are unrelated and can stay as they are.
</Info>

**Why a Price ID, when the Stripe integration never needed one?** With the classic Stripe integration *your own* checkout creates the subscription and picks the price — Qonversion only matches incoming webhook events to a product, and a `prod_...` id is enough for that. A Web Funnel flips this: **Qonversion runs the checkout for you**, and Stripe requires an exact Price to start a checkout session. A Stripe product can carry many prices (monthly, annual, currencies), so you pin the one to sell in the **Stripe Price ID** field.

## Set up the Stripe webhook

Qonversion records your web purchases by listening to events from your Stripe account — payment confirmations, subscription renewals, refunds. Every incoming event is verified with a **webhook signing secret** unique to your endpoint. Without it, purchase events can't be verified, and Web Funnel purchases won't reach your users.

Stripe keeps live and test traffic fully separate: a live-mode endpoint and a test-mode endpoint are **two different endpoints with two different signing secrets**. Qonversion mirrors this — the Stripe store card has a **Production webhook signing secret** field and, once you connect Stripe in sandbox, a separate **Sandbox webhook signing secret** field. This section sets up the production one; the sandbox secret is covered in [Test with a sandbox purchase](#test-with-a-sandbox-purchase).

You never share Stripe API keys (`sk_...`) with Qonversion — account access is granted through Stripe Connect, and the signing secret can only be used to verify that events are authentic. Anyone with access to the **Developers** section of your Stripe Dashboard can set this up.

### Step 1 — Create the webhook endpoint in Stripe

1. Open your [Stripe Dashboard](https://dashboard.stripe.com) → **Developers → Webhooks → Add endpoint** (in the newer Workbench UI: **Add destination → Webhook endpoint**).
2. If asked for the destination scope, choose **Your account** (not *Connected accounts*).
3. Set the endpoint URL to `https://api.qonversion.io/v1/stripe-s2s/`.
4. Select the following events:
   * `checkout.session.completed` — confirms the purchase *(required)*
   * `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted` — subscription lifecycle *(required)*
   * `invoice.paid` — renewals and trial conversions *(required)*
   * `charge.refunded` — refunds
   * `charge.dispute.created`, `charge.dispute.funds_withdrawn`, `charge.dispute.closed` — chargebacks
5. Create the endpoint.

<Info>
  Create this endpoint in **live mode** (Test mode toggle off) — it handles your production purchases. For sandbox testing you'll add a separate **test-mode** endpoint; see [Test with a sandbox purchase](#test-with-a-sandbox-purchase).
</Info>

### Step 2 — Add the signing secret to Qonversion

1. On the endpoint page in Stripe, click **Reveal** under **Signing secret** and copy the `whsec_...` value.
2. In the Qonversion dashboard, open **Project Settings → Stores** and on the **Stripe** card paste it into the **Production webhook signing secret** field, then save.

Qonversion validates the secret and checks that your Stripe connection is active. If you later roll the secret in Stripe, the dashboard shows a banner asking you to paste the refreshed value.

## 1. Create a Web Funnel

1. Go to **No-Codes → Create Screen**.
2. Choose **Web Funnel** as the screen type.
3. Design the screen in the Builder as usual (headings, products, buttons).
4. Add a **Product** component to the screen, then bind it: open **Settings → Products** in the Builder and pick your Stripe-priced product from the dropdown for the screen's product slot. This is what the checkout charges — a funnel without a Stripe-priced product cannot check out.
5. Set the purchase button's action to **Make a purchase**. On the web this opens the Stripe checkout for the bound product.
6. Add a **success page** the buyer sees after paying (this is also where you can remind them to open the redemption email).

<Info>
  A Web Funnel is served on the web, so a few in-app-only actions don't apply:

  * Navigation actions (`Navigate to the screen`) are no-ops on the web — use **Go to page** to move between funnel steps.
  * `Restore` is not supported on the web.
</Info>

## 2. Publish

Open the **Publish** dialog. The readiness checklist confirms the funnel is ready:

* **Stripe connected** — required (blocking).
* Success page and other checks — recommended.

On publish, Qonversion generates a **production key** (`prod_key`) and the public checkout URL:

```
https://screens.qonversion.io/flows/{prod_key}
```

Share this URL in your ads, emails, or landing pages.

## 3. The purchase → redemption flow

1. A user opens the funnel URL and completes the Stripe checkout. The **email field on the Stripe payment page** is where the buyer's address is collected — there is no separate email step; the redemption email goes to that address.
2. Qonversion emails them a one-time **redemption link** (`https://screens.qonversion.io/r/{project_uid}/{token}`).
3. The user taps the link on their phone → your app opens and the purchase is redeemed, activating the entitlement.

To handle the redemption link in your app, see **[Web Funnel — Redeem web purchases in your app](web-funnel-redemption)**.

## The redemption email

After a successful payment, Qonversion automatically sends the buyer a redemption email — you don't send anything yourself.

* **Trigger:** sent once, right after the payment is confirmed. The buyer enters their email during checkout.
* **Contents:** a call-to-action button and a fallback link to `https://screens.qonversion.io/r/{project_uid}/{token}`.
* **One-time & time-limited:** the link works **once** and expires **30 days** after purchase. After it's used (or expires), it can't be reused.
* **Lost or expired link:** the buyer can request a fresh one from your app, which sends a new email with a new link and a new 30-day window. See the [redemption guide](web-funnel-redemption#reissue-a-redemption-link).

<Info>
  **Sender & branding.** The email is currently sent from Qonversion. Per-merchant branding (your app's name / sender) is on the roadmap. If a buyer doesn't see the email, ask them to check spam.
</Info>

## Testing

### Test with a sandbox purchase

You can run the whole funnel end to end in Stripe **test mode** — no real charge — before going live.

1. **Connect Stripe in sandbox.** In **Project Settings → Stores** on the **Stripe** card, click **Connect Sandbox**. This is separate from your live connection and links your Stripe test account.
2. **Add a test-mode Stripe webhook.** Stripe keeps live and test events separate, so your live webhook won't receive sandbox purchases. In your [Stripe Dashboard](https://dashboard.stripe.com) with **Test mode on**, add a second endpoint at `https://api.qonversion.io/v1/stripe-s2s/` with the same events as your live endpoint (Step 1 above).
3. **Add the sandbox signing secret.** The test-mode endpoint has its own `whsec_...` signing secret — your production secret won't verify sandbox events. Reveal and copy it from the endpoint page in Stripe (Test mode on), then in **Project Settings → Stores** paste it into the **Sandbox webhook signing secret** field on the **Stripe** card. Without it, sandbox purchase events fail signature verification and test purchases won't reach your users.
4. **Open the sandbox link.** In the **Publish** dialog, below the production link, you'll find a **Sandbox** link:

   ```
   https://screens.qonversion.io/flows/sandbox/{sandbox_key}
   ```

   The same published funnel serves both URLs, so there's nothing extra to publish.
5. **Check out with a Stripe test card** — for example `4242 4242 4242 4242` (see [Stripe's test cards](https://docs.stripe.com/testing)). The checkout runs in Stripe test mode against your sandbox account, so no real money is charged.

A test purchase grants a **sandbox** entitlement, so your app must be running in the **sandbox** environment (`QEnvironment.Sandbox`) to see it after redeeming. Sandbox purchases never appear in your production revenue or analytics.

<Warning>
  Don't share the sandbox link with real customers — it only processes test payments and grants sandbox entitlements.
</Warning>

### Testing the redemption link on Android

You can test redemption with a debug build from Android Studio — on a real device or an emulator with Google Play services:

1. **Register the debug signing key.** Debug builds are signed with the debug keystore, and Android verifies App Links against the certificate of the installed build. Get the fingerprint with `./gradlew signingReport` (the `SHA-256` line under `Variant: debug`) and add it in **Connect Apps** — the fingerprints field accepts multiple values, so keep both your debug and release/Play App Signing keys there.
2. **Match the application ID.** If your debug build uses `applicationIdSuffix` (e.g. `.debug`), register the suffixed ID — otherwise verification won't match the installed package.
3. **Wait for the manifest, then (re)install.** After you add or change an app in Connect Apps, allow a few minutes for `https://screens.qonversion.io/.well-known/assetlinks.json` to refresh, then reinstall the app — Android checks App Links at install time. To force a re-check without reinstalling:

```bash theme={null}
adb shell pm verify-app-links --re-verify <your.package>
adb shell pm get-app-links <your.package>   # expect: screens.qonversion.io: verified
```

4. **Open a redemption link without the email.** You can fire the link straight from the terminal to check that it opens your app (not the browser):

```bash theme={null}
adb shell am start -a android.intent.action.VIEW \
  -d "https://screens.qonversion.io/r/{project_uid}/{token}"
```
