Payment Flow
This guide walks through building a complete payment flow in a funnel — from product configuration to checkout to success page.
Overview
A payment flow has five parts:
- Configure products in funnel settings
- Add product selection UI (pricing page)
- Add the Payment Element component
- Add a submit button
- Configure the success page
Step 1: Configure Products
Products are configured in the funnel’s Settings > Products panel. Each product links a store price to a funnel-level ID that you reference in templates and click actions.
| Field | Description |
|---|---|
| ID | Internal identifier used in product variables (e.g., monthly, yearly). Must be unique within the funnel. |
| Name | Display name for the product. Accessible via products.{id}.displayName. |
| Store Price | The imported store price to charge. Determines amount, currency, and billing interval. |
| Trial Days | Optional free trial period (in days). When set, the checkout captures the card without charging. |
| Trial Price | Optional one-time charge during the trial period (paid trial). A separate store price for the trial fee. |
| Default | Whether this product is pre-selected when the funnel loads. Exactly one product should be marked as default. |
Insert image: The Funnel Settings > Products panel showing two configured products with ID, name, store price dropdown, trial days, and default toggle fields
Product IDs are used in Liquid templates: {{ products.monthly.price }}, {{ products.yearly.periodly }}. Choose short, descriptive IDs.
Product Variables
Each configured product exposes computed variables for dynamic pricing display. Access them as products.{id}.{property} or products.selected.{property} for the currently selected product.
Common properties:
| Variable | Example Output | Description |
|---|---|---|
products.monthly.price | $9.99 | Formatted price with currency symbol |
products.monthly.rawPrice | 9.99 | Numeric price without symbol |
products.monthly.period | month | Billing period label |
products.monthly.periodly | monthly | Adverbial form of the period |
products.monthly.currencySymbol | $ | Currency symbol |
products.monthly.hasTrial | true | Whether the product has a trial |
products.monthly.trialDays | 7 | Number of trial days |
products.monthly.dailyPrice | $0.33 | Price divided by period in days |
products.monthly.weeklyPrice | $2.30 | Price divided by period in weeks |
For the full list of 39 product variable properties, see the Product Variable Reference.
Step 2: Add Product Selection UI
Visitors need a way to choose which product to purchase. Build pricing cards using Stacks with a Select Product click action on each one.
- Add a Stack for each product (e.g., “Monthly” and “Yearly” cards).
- On each Stack, add a click action: Select Product with the product ID.
- Add an Active Style condition to each Stack:
products.selected.id equals monthly(oryearly). This highlights the selected plan automatically. - Inside each Stack, use Text elements with Liquid templates to display pricing.
When a visitor taps a pricing card, the selectProduct action updates the selected product. All products.selected.* variables update reactively, so pricing text elsewhere on the page reflects the new selection.
Displaying Prices
Use Liquid templates in Text elements for dynamic pricing. Examples:
{{ products.yearly.price }}/{{ products.yearly.period }}Save {{ products.yearly.rawPrice | divided_by: 12 | times: 100 | divided_by: products.monthly.rawPrice | minus: 100 | abs | round }}%{{ products.selected.trialDays }}-day free trial, then {{ products.selected.price }}/{{ products.selected.period }}Step 3: Add Payment Element
Add a Payment Element component to your checkout page. This renders the Stripe PaymentElement — a pre-built card form that supports cards, Apple Pay, Google Pay, and other payment methods.
Insert image: The editor canvas showing a checkout page with the Stripe Payment Element component rendered, displaying the card input form with a Submit button below
The Payment Element automatically uses the currently selected product to determine the payment amount and whether to create a PaymentIntent (direct charge) or SetupIntent (trial).
Trial detection is automatic. If the selected product has trial days configured, the Payment Element creates a SetupIntent to capture the card without charging. The subscription is created with the trial period after the card is saved. No extra configuration needed.
Alternative: Embedded Checkout (Stripe Managed)
Instead of the Payment Element, you can use the Embedded Checkout component. This renders Stripe’s complete checkout UI in an iframe — Stripe controls the entire payment experience.
Embedded Checkout uses Stripe Managed Payments. Stripe handles payment method selection, tax calculation, and compliance automatically. You don’t configure Apple Pay, Google Pay, or tax settings — Stripe decides what to show based on the customer’s location and device.
When to use Embedded Checkout over Payment Element:
| Payment Element | Embedded Checkout | |
|---|---|---|
| UI control | Full — you style and position everything | Stripe controls the UI |
| Payment methods | You enable/disable individually | Stripe auto-selects optimal methods |
| Tax | Optional (automaticTax flag) | Stripe handles automatically |
| Upsells | Supports purchase action (saved card) | No saved card for upsells |
| Customization | Stripe Appearance API | Limited to Stripe’s design |
Embedded Checkout does not save the card for future charges. If you need upsells after the initial purchase, use the Payment Element instead.
Step 4: Add Submit Button
Add a button (or any clickable component) with the Submit Payment Element click action. This triggers form validation and payment submission.
Typical setup:
- Add a Stack or Button component below the Payment Element.
- Add click action: Submit Payment Element.
- Optionally add an onFailure chain to handle errors (e.g., set a variable to display an error message).
Payment State Variables
During checkout, these system variables update automatically:
| Variable | Type | Description |
|---|---|---|
payment.loading | boolean | true while payment is being processed |
payment.error | string | Error message if payment fails, empty otherwise |
purchase.status | string | Current purchase status |
purchase.success | boolean | true after successful payment |
purchase.amount | number | Charged amount |
purchase.currency | string | Currency code |
Use payment.loading for button loading states and payment.error for error display via dynamic properties or Liquid templates.
Step 5: Configure Success Page
After a successful payment, the visitor needs to navigate to a success page (download, confirmation, etc.).
Two approaches:
-
Route-based — Configure the checkout page’s route to point to the success page. Add “Go to Next Page” as a click action after “Submit Payment Element” in the action chain. The route evaluates after payment succeeds.
-
Conditional navigation — Use a dynamic property on the “Go to Next Page” action, conditioned on
purchase.success == true.
Paid Trials
A paid trial charges a small upfront fee while signing the customer up for a subscription that starts after the trial period. This is implemented by combining a one-time product (the trial fee) with a subscription product (the recurring plan).
How It Works
When you configure a product with both a Store Price (the subscription) and a Trial Price (a separate one-time price), AppFunnel:
- Charges the trial price immediately as a one-time payment
- Creates the subscription with the trial period — the first renewal happens after the trial days expire
- The card is captured from the trial payment, so no additional payment form is needed for the subscription
Example: 1-Week Trial
| Field | Value |
|---|---|
| Store Price | Yearly subscription — $39.99/year |
| Trial Days | 7 |
| Trial Price | One-time price — $4.99 |
The visitor pays $4.99 upfront. After 7 days, their subscription renews at $39.99/year.
Display this in your funnel with Liquid:
{{ products.selected.trialDays }}-day trial: {{ products.selected.trialPrice }}Renews at {{ products.selected.price }}/{{ products.selected.period }}More Examples
| Offer | Trial Price | Trial Days | Subscription |
|---|---|---|---|
| 1-week trial: $4.99 (renews at $39.99/yr) | $4.99 one-time | 7 | $39.99/year |
| 3-day trial: $0.99 (renews at $9.99/mo) | $0.99 one-time | 3 | $9.99/month |
| 14-day trial: $1.99 (renews at $19.99/mo) | $1.99 one-time | 14 | $19.99/month |
Setup
Create Two Prices in Stripe
Create a recurring price for the subscription (e.g., $39.99/year) and a one-time price for the trial fee (e.g., $4.99). Import both into AppFunnel via your store.
Configure the Product
In Funnel Settings > Products, set the Store Price to the recurring price, enter the Trial Days, and set the Trial Price to the one-time price.
Display Trial Pricing
Use product variables to show the trial offer on your pricing page:
Start for just {{ products.yearly.trialPrice }} — {{ products.yearly.trialDays }}-day trial, then {{ products.yearly.price }}/{{ products.yearly.period }}The products.{id}.paidTrial variable is true when a product has a trial price configured. Use it for conditional display:
{% if products.selected.paidTrial %}
{{ products.selected.trialDays }}-day trial for {{ products.selected.trialPrice }}
{% elsif products.selected.hasTrial %}
{{ products.selected.trialDays }}-day free trial
{% endif %}Complete Example
A typical payment funnel flow:
Quiz Pages --> Pricing Page --> Checkout Page --> Success Page
| |
selectProduct submitPaymentElement
(per option) (on submit button)Pricing page shows plan cards with products.monthly.price and products.yearly.price in text elements. Each card (Stack) has a selectProduct click action and an Active Style condition to highlight the selected plan.
Checkout page contains an email Input (bound to user.email), the Payment Element, and a submit button. The submit button has two chained actions: submitPaymentElement followed by goToNextPage.
Success page shows a confirmation message using {{ products.selected.displayName }} and routes to the app download page.
The visitor must provide an email before payment. The Payment Element requires a customer email to create the Stripe Customer. Capture the email on a prior page or on the checkout page itself using an Input element bound to user.email.