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

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 the endpoint in **live mode** (Test mode toggle off) — Web Funnel checkout runs against your live Stripe connection.
</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 **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

### Payments are live-only

Web Funnel checkout always runs against your **live** Stripe connection — there is no sandbox / test-mode checkout yet, so Stripe **test cards will not work**, and connecting Stripe in Sandbox mode is not enough to check out. To validate the end-to-end flow, use a small product price and refund the charge in your Stripe dashboard afterwards.

<Info>
  A dedicated sandbox/test-mode checkout (parity with mobile, Stripe and Paddle purchases) is planned so you can test with Stripe test cards without a real charge.
</Info>

### 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}"
```
