diff --git a/content/docs/android/guides/consumable-products.mdx b/content/docs/android/guides/consumable-products.mdx new file mode 100644 index 00000000..dd773f0e --- /dev/null +++ b/content/docs/android/guides/consumable-products.mdx @@ -0,0 +1,8 @@ +--- +title: "Consumable Products" +description: "Set up consumable products for Superwall paywalls on Android." +--- + +Use consumable products when a purchase should grant a quantity that can be used up, such as credits, coins, boosts, or tokens. + +../../../shared/consumables/android.mdx diff --git a/content/docs/android/meta.json b/content/docs/android/meta.json index bf7fb351..e468fc72 100644 --- a/content/docs/android/meta.json +++ b/content/docs/android/meta.json @@ -43,6 +43,7 @@ "sdk-reference/advanced", "---Guides---", + "guides/consumable-products", "guides/test-mode", "guides/vibe-coding", "guides/using-revenuecat", diff --git a/content/docs/dashboard/charts.mdx b/content/docs/dashboard/charts.mdx deleted file mode 100644 index 892549ed..00000000 --- a/content/docs/dashboard/charts.mdx +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: "Charts" -description: "View charts detailing important metrics about your app's subscription performance, paywalls and more." ---- - -To view charts breaking down your app's performance, click the **Charts** button in the **sidebar**: - -![](/images/overview-charts.png) - - -Check out a video overview of our charts on [YouTube](https://youtu.be/7UIO99LSvTQ). - - -### Chart types -Choose between different charts by making a selection from the left sidebar: - -![](/images/chartsv2_types.jpeg) - -You can also toggle which charts are showing by using the chevrons on the left-hand side. -Currently, we offer the following charts: - -#### Revenue Charts -- **Proceeds:** Revenue after refunds, store fees, and taxes. -- **Sales:** Revenue before refunds, taxes, and fees. -- **Cohorted Proceeds:** Net proceeds cohorted by Install Date, after refunds, taxes, and fees. Usually used in comparison to Ad Spend. Only includes revenue generated by Superwall. -- **ARR:** Normalized revenue from subscriptions to an annual period until their expiration. Doesn't factor in auto renew status. -- **MRR:** Normalized revenue from subscriptions to a monthly period until their expiration. Doesn't factor in auto renew status. -- **ARPU:** Average revenue per user, cohorted by install date. -- **Realized LTV:** Realized fifetime value, cohorted by install date. - -#### Subscription Charts -- **Active Subscriptions:** Count of unexpired, paid subscriptions. Doesn't factor in auto renew status. Shows how many users have access to your product at a point in time. -- **Paid Conversion:** Percent of installs who became paying users. -- **New Trials:** Trial starts, cohorted by trial start date. -- **Trial Conversion:** Percentage of trials that converted to paid subscriptions. -- **New Subscriptions:** New subscriptions, cohorted by subscription start date. -- **Auto Renew Status:** How much of your MRR is set to renew vs churn. - -#### Paywall Charts -- **Initial Conversion:** Percent of new users who converted on a paywall. -- **Paywalled Users:** Count of unique users who opened paywalls. -- **Paywall Rate:** Percent of new users who opened paywalls. -- **Paywall Conversion:** Percent of users who converted on a paywall. -- **Conversions:** Count of paywall conversions (i.e. a completed transaction). -- **Checkout Conversion:** Percentage of users who converted after starting checkout. - -#### User Charts -- **New Users:** Count of new users. -- **Active Users:** Count of active users. - -#### Refund & Churn Charts -- **Refund Rate:** Ratio of refunds to gross proceeds, cohorted by first purchase date. - -### Filtering chart data -To filter data on a chart, **click** the **Filter** button at the top right: - -![](/images/chartsv2_filter.jpeg) - -Filter data by choosing a filter type and **clicking** on **+ Add Filter** to apply it. You can add one, or several, filters: - -![](/images/chartsv2_filterOpen.jpeg) - -When you're done, **click** on the **Apply** button, and the chart will refresh with the data filtered by your selections: - -![](/images/chartsv2_filterResult.jpeg) - -To remove an individual filter, **click** on the **trash can** icon on the trailing side of it: - -![](/images/chartsv2_remove.jpeg) - -To remove an individual component that's part of a filter (i.e. breaking down by "Application" and removing one of the included apps), **click** on the **X** button on its trailing side: - -![](/images/chartsv2_removeSingle.jpeg) - -To remove all filters, **click** on the **Clear Filters** button: - -![](/images/chartsv2_removeAll.jpeg) - -### Breaking down chart data -To break down data in a chart, **click** the **Breakdown** toggle at the top right: - -![](/images/chartsv2_breakdown.jpeg) - -The breakdowns available are tailored to the type of chart you have selected. After you apply a selection, the chart will automatically update. At the bottom of the chart, the data displayed will also be updated according to your breakdown selection: - -![](/images/chartsv2_breakdownResult.jpeg) - -Breaking down a **Proceeds** chart by **Placements** is a powerful way to directly correlate features you make (or similar things you've paywalled) to your app's revenue growth. - -### Selecting time ranges -To customize the time span and level of detail of the data displayed on the chart, use the two date toggles at the top right: - -![](/images/chartsv2_dates.jpeg) - -These controls adjust the chart's view interval and data range: - -**Display Interval (First Dropdown):** Sets the interval at which data is displayed on the chart. Choose options like Hourly, Daily, Weekly, etc., to adjust how granular the data appears. Selecting "Auto" automatically optimizes the interval based on the selected date range. - -**Data Fetch Range (Second Dropdown):** Defines the total date range from which data is fetched and displayed on the chart. Options include Yesterday, Last 7 Days, Last 30 Days, and more. The selected range determines the period of data used to populate the chart, regardless of the display interval setting. - -You can also use natural language to set a data fetch range. For example, "last month", "two weeks ago", etc. - -### Changing chart formats -Each chart type can display its data in different chart formats. To change the default display, **click** on the **Chart** button found at the top right: - -![](/images/chartsv2_typeToggle.jpeg) - -You can toggle the chart format between **Stacked Area**, **Line**, **Stacked Bar**, or **Bar**. Here is the same chart data in each format: - - - - ![](/images/chartsv2_stackedArea.jpeg) - - - ![](/images/chartsv2_line.jpeg) - - - ![](/images/chartsv2_stackedBar.jpeg) - - - ![](/images/chartsv2_bar.jpeg) - - - -Additionally, you can hover any chart element to see more details about the data point: - -![](/images/chartsv2_hover.jpeg) - -### Exporting chart data - -You can export any chart data as a `.csv` file. Just **click** the **Export** button at the bottom-right of any chart: - -![](/images/chartsv2_export.jpeg) diff --git a/content/docs/dashboard/charts/active-subscriptions.mdx b/content/docs/dashboard/charts/active-subscriptions.mdx new file mode 100644 index 00000000..3e765ba1 --- /dev/null +++ b/content/docs/dashboard/charts/active-subscriptions.mdx @@ -0,0 +1,18 @@ +--- +title: "Active Subscriptions" +description: "Learn what the Active Subscriptions chart measures and when to use it." +--- + +![Active Subscriptions chart](/images/charts/active-subscriptions.jpg) + +## What it shows + +Active Subscriptions shows the count of unexpired, paid subscriptions over time. + +## How to use it + +Use this chart to track the size of your paying subscriber base. It is one of the clearest ways to see whether new subscriptions and renewals are outpacing expirations. + +## Good to know + +Active Subscriptions does not tell the full story by itself. Pair it with MRR, Subscriber Churn, and Auto Renew Status to understand revenue and retention quality. diff --git a/content/docs/dashboard/charts/active-users.mdx b/content/docs/dashboard/charts/active-users.mdx new file mode 100644 index 00000000..689c45e5 --- /dev/null +++ b/content/docs/dashboard/charts/active-users.mdx @@ -0,0 +1,18 @@ +--- +title: "Active Users" +description: "Learn what the Active Users chart measures and when to use it." +--- + +![Active Users chart](/images/charts/active-users.jpg) + +## What it shows + +Active Users shows the count of users who were active during each period. + +## How to use it + +Use this chart to understand audience size and engagement. It helps explain changes in paywall exposure, conversion volume, and revenue that may be driven by app usage. + +## Good to know + +Active Users is a useful denominator when reviewing monetization. If revenue is flat while active users grow, look at paywall exposure and conversion rates next. diff --git a/content/docs/dashboard/charts/arr.mdx b/content/docs/dashboard/charts/arr.mdx new file mode 100644 index 00000000..90d6c179 --- /dev/null +++ b/content/docs/dashboard/charts/arr.mdx @@ -0,0 +1,18 @@ +--- +title: "ARR" +description: "Learn what the ARR chart measures and when to use it." +--- + +![ARR chart](/images/charts/arr.jpg) + +## What it shows + +ARR shows annualized recurring revenue from subscriptions. It normalizes subscription revenue into an annual view. + +## How to use it + +Use ARR to understand your recurring revenue run rate and how it changes over time. It is helpful when you want a longer-range view than monthly movement can provide. + +## Good to know + +ARR is an annualized metric, so use it for directional recurring revenue trends instead of short-term cash timing. diff --git a/content/docs/dashboard/charts/auto-renew-status.mdx b/content/docs/dashboard/charts/auto-renew-status.mdx new file mode 100644 index 00000000..6b54e824 --- /dev/null +++ b/content/docs/dashboard/charts/auto-renew-status.mdx @@ -0,0 +1,18 @@ +--- +title: "Auto Renew Status" +description: "Learn what the Auto Renew Status chart measures and when to use it." +--- + +![Auto Renew Status chart](/images/charts/auto-renew-status.jpg) + +## What it shows + +Auto Renew Status shows how much MRR is currently set to renew versus churn based on subscription renewal status. + +## How to use it + +Use this chart to understand renewal risk before subscriptions expire. It is useful for spotting periods where a larger share of recurring revenue is at risk. + +## Good to know + +Use Auto Renew Status alongside Subscriber Churn. One shows renewal intent before expiration, while the other shows subscriptions that actually expired. diff --git a/content/docs/dashboard/charts/checkout-conversion.mdx b/content/docs/dashboard/charts/checkout-conversion.mdx new file mode 100644 index 00000000..3bafdb20 --- /dev/null +++ b/content/docs/dashboard/charts/checkout-conversion.mdx @@ -0,0 +1,18 @@ +--- +title: "Checkout Conversion" +description: "Learn what the Checkout Conversion chart measures and when to use it." +--- + +![Checkout Conversion chart](/images/charts/checkout-conversion.jpg) + +## What it shows + +Checkout Conversion shows the percentage of users who converted after starting checkout. + +## How to use it + +Use this chart to spot purchase flow friction. If users start checkout but do not complete it, the issue may be closer to product selection, store purchase flow, payment state, or purchase restoration. + +## Good to know + +Compare Checkout Conversion with Paywall Conversion. A drop in checkout conversion points later in the funnel than a drop in paywall conversion. diff --git a/content/docs/dashboard/charts/cohorted-proceeds.mdx b/content/docs/dashboard/charts/cohorted-proceeds.mdx new file mode 100644 index 00000000..fdc51127 --- /dev/null +++ b/content/docs/dashboard/charts/cohorted-proceeds.mdx @@ -0,0 +1,18 @@ +--- +title: "Cohorted Proceeds" +description: "Learn what the Cohorted Proceeds chart measures and when to use it." +--- + +![Cohorted Proceeds chart](/images/charts/cohorted-proceeds.jpg) + +## What it shows + +Cohorted Proceeds shows net proceeds grouped by install date. Each cohort represents users who installed during the same period. + +## How to use it + +Use this chart to understand how install cohorts monetize over time. It is a good fit for comparing acquisition quality, measuring payback windows, and reviewing the impact of product or campaign changes by cohort. + +## Good to know + +Pair this chart with acquisition spend when you want to understand whether new cohorts are earning back what you spent to acquire them. diff --git a/content/docs/dashboard/charts/conversions.mdx b/content/docs/dashboard/charts/conversions.mdx new file mode 100644 index 00000000..86b0109c --- /dev/null +++ b/content/docs/dashboard/charts/conversions.mdx @@ -0,0 +1,18 @@ +--- +title: "Conversions" +description: "Learn what the Conversions chart measures and when to use it." +--- + +![Conversions chart](/images/charts/conversions.jpg) + +## What it shows + +Conversions shows the count of completed transactions. + +## How to use it + +Use this chart to track purchase volume over time. It is useful for reviewing campaign launches, product changes, offer changes, and seasonal traffic patterns. + +## Good to know + +Conversion count is volume, not rate. Pair it with Paywall Conversion and New Users when you need to understand whether performance changed because of traffic or efficiency. diff --git a/content/docs/dashboard/charts/index.mdx b/content/docs/dashboard/charts/index.mdx new file mode 100644 index 00000000..523c7c22 --- /dev/null +++ b/content/docs/dashboard/charts/index.mdx @@ -0,0 +1,135 @@ +--- +title: "Charts" +description: "View charts detailing important metrics about your app's subscription performance, paywalls, users, and revenue." +--- + +To view charts breaking down your app's performance, click the **Charts** button in the **sidebar**: + +![](/images/overview-charts.png) + + +Check out a video overview of our charts on [YouTube](https://youtu.be/7UIO99LSvTQ). + + +### Chart types +Choose between different charts by making a selection from the left sidebar: + +![](/images/chartsv2_types.jpeg) + +You can also toggle which charts are showing by using the chevrons on the left-hand side. + +Currently, we offer the following charts: + +#### Revenue Charts +- [**Proceeds**](/dashboard/charts/proceeds): Revenue after refunds, store fees, and taxes. +- [**Sales**](/dashboard/charts/sales): Revenue before refunds, taxes, and fees. +- [**Cohorted Proceeds**](/dashboard/charts/cohorted-proceeds): Net proceeds cohorted by install date, after refunds, taxes, and fees. +- [**ARR**](/dashboard/charts/arr): Annualized recurring revenue from subscriptions. +- [**MRR**](/dashboard/charts/mrr): Monthly recurring revenue from subscriptions. +- [**Realized LTV per new user**](/dashboard/charts/realized-ltv-per-new-user): Net proceeds per new user, cohorted by install date. +- [**Realized LTV per paid user**](/dashboard/charts/realized-ltv-per-paid-user): Net proceeds per paying user, cohorted by install date. + +#### Subscription Charts +- [**Active Subscriptions**](/dashboard/charts/active-subscriptions): Count of unexpired, paid subscriptions. +- [**Paid Conversion**](/dashboard/charts/paid-conversion): Percent of installs who became paying users. +- [**New Trials**](/dashboard/charts/new-trials): Trial starts, cohorted by trial start date. +- [**Trial Conversion**](/dashboard/charts/trial-conversion): Percentage of trials that converted to paid subscriptions. +- [**New Subscriptions**](/dashboard/charts/new-subscriptions): New subscriptions, cohorted by subscription start date. +- [**Auto Renew Status**](/dashboard/charts/auto-renew-status): How much of your MRR is set to renew versus churn. + +#### Paywall Charts +- [**Initial Conversion**](/dashboard/charts/initial-conversion): Percent of new users who converted on a paywall. +- [**Paywalled Users**](/dashboard/charts/paywalled-users): Count of unique users who opened paywalls. +- [**Paywall Rate**](/dashboard/charts/paywall-rate): Percent of new users who opened paywalls. +- [**Paywall Conversion**](/dashboard/charts/paywall-conversion): Percent of users who converted after opening a paywall. +- [**Conversions**](/dashboard/charts/conversions): Count of completed transactions. +- [**Checkout Conversion**](/dashboard/charts/checkout-conversion): Percentage of users who converted after starting checkout. + +#### User Charts +- [**New Users**](/dashboard/charts/new-users): Count of new users. +- [**Active Users**](/dashboard/charts/active-users): Count of active users. + +#### Retention & Churn Charts +- [**Subscriber Churn**](/dashboard/charts/subscriber-churn): Percentage of paid subscriptions that expired in each period. +- [**Refund Rate**](/dashboard/charts/refund-rate): Ratio of refunds to gross proceeds, cohorted by first purchase date. +- [**Subscription Retention**](/dashboard/charts/subscription-retention): Subscription retention by cohort over time. + +### Filtering chart data +To filter data on a chart, **click** the **Filter** button at the top right: + +![](/images/chartsv2_filter.jpeg) + +Filter data by choosing a filter type and **clicking** on **+ Add Filter** to apply it. You can add one, or several, filters: + +![](/images/chartsv2_filterOpen.jpeg) + +When you're done, **click** on the **Apply** button, and the chart will refresh with the data filtered by your selections: + +![](/images/chartsv2_filterResult.jpeg) + +To remove an individual filter, **click** on the **trash can** icon on the trailing side of it: + +![](/images/chartsv2_remove.jpeg) + +To remove an individual component that's part of a filter, such as breaking down by **Application** and removing one included app, **click** on the **X** button on its trailing side: + +![](/images/chartsv2_removeSingle.jpeg) + +To remove all filters, **click** on the **Clear Filters** button: + +![](/images/chartsv2_removeAll.jpeg) + +### Breaking down chart data +To break down data in a chart, **click** the **Breakdown** toggle at the top right: + +![](/images/chartsv2_breakdown.jpeg) + +The breakdowns available are tailored to the type of chart you have selected. After you apply a selection, the chart and the table below it update automatically: + +![](/images/chartsv2_breakdownResult.jpeg) + +Breaking down a **Proceeds** chart by **Placements** is a powerful way to directly correlate features you make, or similar things you've paywalled, to your app's revenue growth. + +### Selecting time ranges +To customize the time span and level of detail of the data displayed on the chart, use the two date toggles at the top right: + +![](/images/chartsv2_dates.jpeg) + +These controls adjust the chart's view interval and data range: + +**Display Interval:** Sets the interval at which data is displayed on the chart. Choose options like hourly, daily, weekly, or monthly to adjust how granular the chart appears. Selecting **Auto** automatically optimizes the interval based on the selected date range. + +**Data Fetch Range:** Defines the total date range used to populate the chart. Options include **Yesterday**, **Last 7 Days**, **Last 30 Days**, and more. + +You can also use natural language to set a data fetch range. For example, "last month", "two weeks ago", or similar ranges. + +### Changing chart formats +Each chart type can display its data in different chart formats. To change the default display, **click** on the **Chart** button found at the top right: + +![](/images/chartsv2_typeToggle.jpeg) + +You can toggle the chart format between **Stacked Area**, **Line**, **Stacked Bar**, or **Bar**. Here is the same chart data in each format: + + + + ![](/images/chartsv2_stackedArea.jpeg) + + + ![](/images/chartsv2_line.jpeg) + + + ![](/images/chartsv2_stackedBar.jpeg) + + + ![](/images/chartsv2_bar.jpeg) + + + +Additionally, you can hover any chart element to see more details about the data point: + +![](/images/chartsv2_hover.jpeg) + +### Exporting chart data +You can export any chart data as a `.csv` file. Just **click** the **Export** button at the bottom-right of any chart: + +![](/images/chartsv2_export.jpeg) diff --git a/content/docs/dashboard/charts/initial-conversion.mdx b/content/docs/dashboard/charts/initial-conversion.mdx new file mode 100644 index 00000000..a3e513e9 --- /dev/null +++ b/content/docs/dashboard/charts/initial-conversion.mdx @@ -0,0 +1,18 @@ +--- +title: "Initial Conversion" +description: "Learn what the Initial Conversion chart measures and when to use it." +--- + +![Initial Conversion chart](/images/charts/initial-conversion.jpg) + +## What it shows + +Initial Conversion shows the percentage of new users who converted on a paywall. + +## How to use it + +Use this chart to evaluate the first monetization path for new users. It is useful when you are tuning onboarding, first-session paywalls, placements, or introductory offers. + +## Good to know + +If Initial Conversion changes, compare it with New Users and Paywall Rate to make sure the shift is not just a change in acquisition volume or paywall exposure. diff --git a/content/docs/dashboard/charts/meta.json b/content/docs/dashboard/charts/meta.json new file mode 100644 index 00000000..8584b749 --- /dev/null +++ b/content/docs/dashboard/charts/meta.json @@ -0,0 +1,35 @@ +{ + "title": "Charts", + "pages": [ + "index", + "---Revenue---", + "proceeds", + "sales", + "cohorted-proceeds", + "arr", + "mrr", + "realized-ltv-per-new-user", + "realized-ltv-per-paid-user", + "---Subscriptions---", + "active-subscriptions", + "paid-conversion", + "new-trials", + "trial-conversion", + "new-subscriptions", + "auto-renew-status", + "---Paywalls---", + "initial-conversion", + "paywalled-users", + "paywall-rate", + "paywall-conversion", + "conversions", + "checkout-conversion", + "---Users---", + "new-users", + "active-users", + "---Retention & Churn---", + "subscriber-churn", + "refund-rate", + "subscription-retention" + ] +} diff --git a/content/docs/dashboard/charts/mrr.mdx b/content/docs/dashboard/charts/mrr.mdx new file mode 100644 index 00000000..62018d46 --- /dev/null +++ b/content/docs/dashboard/charts/mrr.mdx @@ -0,0 +1,18 @@ +--- +title: "MRR" +description: "Learn what the MRR chart measures and when to use it." +--- + +![MRR chart](/images/charts/mrr.jpg) + +## What it shows + +MRR shows monthly recurring revenue from subscriptions. It normalizes subscription revenue into a monthly view. + +## How to use it + +Use MRR as the baseline for subscription revenue health. It makes it easier to see whether recurring revenue is expanding, flattening, or shrinking over time. + +## Good to know + +Review MRR alongside Active Subscriptions and Auto Renew Status to separate subscriber count changes from renewal risk. diff --git a/content/docs/dashboard/charts/new-subscriptions.mdx b/content/docs/dashboard/charts/new-subscriptions.mdx new file mode 100644 index 00000000..ddb29140 --- /dev/null +++ b/content/docs/dashboard/charts/new-subscriptions.mdx @@ -0,0 +1,18 @@ +--- +title: "New Subscriptions" +description: "Learn what the New Subscriptions chart measures and when to use it." +--- + +![New Subscriptions chart](/images/charts/new-subscriptions.jpg) + +## What it shows + +New Subscriptions shows new subscription starts, grouped by subscription start date. + +## How to use it + +Use this chart to understand subscription acquisition. It is helpful for reviewing launches, pricing tests, offer changes, and campaign experiments. + +## Good to know + +Break this chart down by subscription start type when you want to separate direct paid starts from trial conversions. diff --git a/content/docs/dashboard/charts/new-trials.mdx b/content/docs/dashboard/charts/new-trials.mdx new file mode 100644 index 00000000..3b24a4a5 --- /dev/null +++ b/content/docs/dashboard/charts/new-trials.mdx @@ -0,0 +1,18 @@ +--- +title: "New Trials" +description: "Learn what the New Trials chart measures and when to use it." +--- + +![New Trials chart](/images/charts/new-trials.jpg) + +## What it shows + +New Trials shows trial starts, grouped by trial start date. + +## How to use it + +Use this chart to understand how often users are entering trials. It is especially helpful after changing offers, pricing, trial eligibility, or paywall presentation. + +## Good to know + +Trial volume is only one side of the story. Review Trial Conversion to see whether those trials are turning into paid subscriptions. diff --git a/content/docs/dashboard/charts/new-users.mdx b/content/docs/dashboard/charts/new-users.mdx new file mode 100644 index 00000000..4832ae73 --- /dev/null +++ b/content/docs/dashboard/charts/new-users.mdx @@ -0,0 +1,18 @@ +--- +title: "New Users" +description: "Learn what the New Users chart measures and when to use it." +--- + +![New Users chart](/images/charts/new-users.jpg) + +## What it shows + +New Users shows the count of users who were first seen during each period. + +## How to use it + +Use this chart to understand acquisition volume. It gives context for revenue, conversion, paywall exposure, and subscription metrics that may move because traffic changed. + +## Good to know + +When conversion or revenue rates change, check New Users to see whether the app's audience mix or volume changed at the same time. diff --git a/content/docs/dashboard/charts/paid-conversion.mdx b/content/docs/dashboard/charts/paid-conversion.mdx new file mode 100644 index 00000000..4fd5783f --- /dev/null +++ b/content/docs/dashboard/charts/paid-conversion.mdx @@ -0,0 +1,16 @@ +--- +title: "Paid Conversion" +description: "Learn what the Paid Conversion chart measures and when to use it." +--- + +## What it shows + +Paid Conversion shows the percentage of installs that became paying users. + +## How to use it + +Use this chart to connect acquisition to paid outcomes. It is useful when you want to know whether new users are becoming customers at a healthy rate. + +## Good to know + +Paid Conversion sits downstream of several steps. If it drops, compare it against Paywall Rate, Paywall Conversion, Trial Conversion, and Checkout Conversion to find where the funnel changed. diff --git a/content/docs/dashboard/charts/paywall-conversion.mdx b/content/docs/dashboard/charts/paywall-conversion.mdx new file mode 100644 index 00000000..0fb96e15 --- /dev/null +++ b/content/docs/dashboard/charts/paywall-conversion.mdx @@ -0,0 +1,18 @@ +--- +title: "Paywall Conversion" +description: "Learn what the Paywall Conversion chart measures and when to use it." +--- + +![Paywall Conversion chart](/images/charts/paywall-conversion.jpg) + +## What it shows + +Paywall Conversion shows the percentage of users who converted after opening a paywall. + +## How to use it + +Use this chart to judge how well paywalls convert the users who actually see them. It is helpful for comparing paywall designs, products, pricing, and experiments. + +## Good to know + +Review Paywall Conversion together with Paywall Rate. One tells you how often users see paywalls, and the other tells you how often exposed users convert. diff --git a/content/docs/dashboard/charts/paywall-rate.mdx b/content/docs/dashboard/charts/paywall-rate.mdx new file mode 100644 index 00000000..bd64c28a --- /dev/null +++ b/content/docs/dashboard/charts/paywall-rate.mdx @@ -0,0 +1,18 @@ +--- +title: "Paywall Rate" +description: "Learn what the Paywall Rate chart measures and when to use it." +--- + +![Paywall Rate chart](/images/charts/paywall-rate.jpg) + +## What it shows + +Paywall Rate shows the percentage of new users who opened paywalls. + +## How to use it + +Use this chart to understand how much of your new user base is reaching a monetization moment. It is useful when tuning placements, campaign audiences, onboarding flows, or feature gating. + +## Good to know + +A low Paywall Rate can hide a strong paywall. If too few users see it, pair this chart with Paywall Conversion before changing the paywall itself. diff --git a/content/docs/dashboard/charts/paywalled-users.mdx b/content/docs/dashboard/charts/paywalled-users.mdx new file mode 100644 index 00000000..7821edc2 --- /dev/null +++ b/content/docs/dashboard/charts/paywalled-users.mdx @@ -0,0 +1,18 @@ +--- +title: "Paywalled Users" +description: "Learn what the Paywalled Users chart measures and when to use it." +--- + +![Paywalled Users chart](/images/charts/paywalled-users.jpg) + +## What it shows + +Paywalled Users shows the count of unique users who opened paywalls. + +## How to use it + +Use this chart to understand paywall exposure. It helps answer whether enough users are reaching monetization moments before you judge paywall conversion performance. + +## Good to know + +Break this chart down by placement or paywall when you want to see which surfaces are driving the most paywall views. diff --git a/content/docs/dashboard/charts/proceeds.mdx b/content/docs/dashboard/charts/proceeds.mdx new file mode 100644 index 00000000..94346305 --- /dev/null +++ b/content/docs/dashboard/charts/proceeds.mdx @@ -0,0 +1,18 @@ +--- +title: "Proceeds" +description: "Learn what the Proceeds chart measures and when to use it." +--- + +![Proceeds chart](/images/charts/proceeds.jpg) + +## What it shows + +Proceeds shows the revenue your app keeps after refunds, store fees, and taxes. + +## How to use it + +Use this as your grounded revenue view. It is the chart to reach for when you want to understand how much revenue is actually making it through after deductions, instead of only looking at top-line sales. + +## Good to know + +Break proceeds down by renewal type, placement, product, or country when you need to understand which parts of the business are driving net revenue. diff --git a/content/docs/dashboard/charts/realized-ltv-per-new-user.mdx b/content/docs/dashboard/charts/realized-ltv-per-new-user.mdx new file mode 100644 index 00000000..de93d1b9 --- /dev/null +++ b/content/docs/dashboard/charts/realized-ltv-per-new-user.mdx @@ -0,0 +1,16 @@ +--- +title: "Realized LTV per new user" +description: "Learn what the Realized LTV per new user chart measures and when to use it." +--- + +## What it shows + +Realized LTV per new user shows net proceeds per new user, cohorted by install date. + +## How to use it + +Use this chart to understand how much revenue new users have actually generated so far. It is useful for evaluating acquisition quality, pricing changes, and onboarding or paywall changes across install cohorts. + +## Good to know + +Newer cohorts have had less time to generate revenue, so compare them carefully against older cohorts. diff --git a/content/docs/dashboard/charts/realized-ltv-per-paid-user.mdx b/content/docs/dashboard/charts/realized-ltv-per-paid-user.mdx new file mode 100644 index 00000000..b0be7fb1 --- /dev/null +++ b/content/docs/dashboard/charts/realized-ltv-per-paid-user.mdx @@ -0,0 +1,16 @@ +--- +title: "Realized LTV per paid user" +description: "Learn what the Realized LTV per paid user chart measures and when to use it." +--- + +## What it shows + +Realized LTV per paid user shows net proceeds per paying user, cohorted by install date. + +## How to use it + +Use this chart to understand how valuable paying users are after they convert. It helps separate paid customer quality from broader acquisition volume. + +## Good to know + +Compare this chart with Realized LTV per new user to see whether changes are coming from conversion rate, paid user value, or both. diff --git a/content/docs/dashboard/charts/refund-rate.mdx b/content/docs/dashboard/charts/refund-rate.mdx new file mode 100644 index 00000000..62acbb96 --- /dev/null +++ b/content/docs/dashboard/charts/refund-rate.mdx @@ -0,0 +1,18 @@ +--- +title: "Refund Rate" +description: "Learn what the Refund Rate chart measures and when to use it." +--- + +![Refund Rate chart](/images/charts/refund-rate.jpg) + +## What it shows + +Refund Rate shows refunds as a share of gross proceeds, cohorted by first purchase date. + +## How to use it + +Use this chart to detect refund spikes and understand whether specific purchase cohorts are refunding at a higher rate. It is helpful after offer, pricing, product, or audience changes. + +## Good to know + +If Refund Rate rises while Sales stays healthy, review the matching Proceeds chart to understand the net revenue impact. diff --git a/content/docs/dashboard/charts/sales.mdx b/content/docs/dashboard/charts/sales.mdx new file mode 100644 index 00000000..73dca5b6 --- /dev/null +++ b/content/docs/dashboard/charts/sales.mdx @@ -0,0 +1,18 @@ +--- +title: "Sales" +description: "Learn what the Sales chart measures and when to use it." +--- + +![Sales chart](/images/charts/sales.jpg) + +## What it shows + +Sales shows gross revenue before refunds, taxes, and store fees are deducted. + +## How to use it + +Use Sales to understand top-line purchase volume and demand. It is especially useful when you want to compare how much users purchased against what eventually became proceeds. + +## Good to know + +If Sales is growing but Proceeds is not, look at refunds, store fees, taxes, and product mix to understand where the gap is coming from. diff --git a/content/docs/dashboard/charts/subscriber-churn.mdx b/content/docs/dashboard/charts/subscriber-churn.mdx new file mode 100644 index 00000000..8c5e00aa --- /dev/null +++ b/content/docs/dashboard/charts/subscriber-churn.mdx @@ -0,0 +1,18 @@ +--- +title: "Subscriber Churn" +description: "Learn what the Subscriber Churn chart measures and when to use it." +--- + +![Subscriber Churn chart](/images/charts/subscriber-churn.jpg) + +## What it shows + +Subscriber Churn shows the percentage of paid subscriptions that expired during each period. + +## How to use it + +Use this chart to monitor retention risk and understand when paid subscriptions are dropping out. It is useful after pricing changes, product changes, offer changes, or onboarding updates. + +## Good to know + +Compare Subscriber Churn with Auto Renew Status to see whether renewal risk is turning into actual expired subscriptions. diff --git a/content/docs/dashboard/charts/subscription-retention.mdx b/content/docs/dashboard/charts/subscription-retention.mdx new file mode 100644 index 00000000..6f1d05bf --- /dev/null +++ b/content/docs/dashboard/charts/subscription-retention.mdx @@ -0,0 +1,18 @@ +--- +title: "Subscription Retention" +description: "Learn what the Subscription Retention chart measures and when to use it." +--- + +![Subscription Retention chart](/images/charts/subscription-retention.jpg) + +## What it shows + +Subscription Retention shows how subscription cohorts retain over time. + +## How to use it + +Use this chart to understand long-term subscriber retention by cohort. It helps you see whether newer cohorts are holding onto subscriptions better or worse than earlier cohorts. + +## Good to know + +Newer cohorts have less elapsed time, so their later retention periods may not be complete yet. Use wider date ranges when you need a more mature retention view. diff --git a/content/docs/dashboard/charts/trial-conversion.mdx b/content/docs/dashboard/charts/trial-conversion.mdx new file mode 100644 index 00000000..fabc57da --- /dev/null +++ b/content/docs/dashboard/charts/trial-conversion.mdx @@ -0,0 +1,18 @@ +--- +title: "Trial Conversion" +description: "Learn what the Trial Conversion chart measures and when to use it." +--- + +![Trial Conversion chart](/images/charts/trial-conversion.jpg) + +## What it shows + +Trial Conversion shows the percentage of trials that converted to paid subscriptions. + +## How to use it + +Use this chart to understand trial quality and renewal behavior. It helps you see whether trial starts are leading to paid outcomes after the trial period ends. + +## Good to know + +When trial conversion moves, check the trial length, offer type, onboarding experience, cancellation behavior, and product usage during the trial window. diff --git a/content/docs/dashboard/products.mdx b/content/docs/dashboard/products.mdx index 0c037dae..18817fef 100644 --- a/content/docs/dashboard/products.mdx +++ b/content/docs/dashboard/products.mdx @@ -156,11 +156,21 @@ Then, add your [RevenueCat Public API Key](https://docs.revenuecat.com/docs/auth After you've added products to an app, you're ready to start using them in paywalls. Check out our [docs](/dashboard/dashboard-creating-paywalls/paywall-editor-products) for a step-by-step guide on how to do that. ## Understanding how consumable and non-consumable products work -Superwall uses entitlements to determine access to features instead of treating purchases as a simple “subscribed/unsubscribed” status. To that end, here is how to work with consumable and non-consumable products: -- **Consumable products** (e.g., credits, tokens, energy boosts) generally _aren't_ associated with an entitlement. -- **Non-consumable products** (e.g., a lifetime unlock) _should_ be linked to an entitlement in Superwall. This ensures users who purchase them receive the appropriate access to features. +Superwall uses entitlements to determine ongoing access. Consumables and non-consumables are both one-time purchases, but they should be configured differently: -Note that on iOS, if you're using consumables you need to include `SKIncludeConsumableInAppPurchaseHistory` (Apple's docs [here](https://developer.apple.com/documentation/bundleresources/information-property-list/skincludeconsumableinapppurchasehistory)) in your `info.plist` file. Ensure its type is set to `Boolean` and its value is `YES`. Note that on pre-iOS 18 devices, StoreKit 1 will be used when this key is present. +- **Consumable products** (e.g., credits, tokens, energy boosts) should usually have **Period** set to **None (Lifetime / Consumable)** and **no entitlements** selected. Your app grants the benefit after the purchase event. +- **Non-consumable products** (e.g., a lifetime unlock) should use **Period** set to **None (Lifetime / Consumable)** and should be linked to the entitlement they unlock. + +If a product has no entitlements, purchasing it will not make the user's subscription status active. This is expected for consumables. + +Finish setup in the SDK guide for your platform: + +- [iOS consumable products](/ios/guides/consumable-products) +- [Android consumable products](/android/guides/consumable-products) +- [Flutter consumable products](/flutter/guides/consumable-products) +- [Expo consumable products](/expo/guides/consumable-products) + +On iOS, consumables also require `SKIncludeConsumableInAppPurchaseHistory` in your `Info.plist` as a Boolean set to `YES`. On pre-iOS 18 devices, StoreKit 1 will be used when this key is present. See the [iOS consumables guide](/ios/guides/consumable-products) for the exact setup. ## Understanding paid offer types Any **paid up front** or **pay as you go** product offer types will also be referenced using the `trial` variables. In Superwall, these are represented as "paid trials". For example, to reference the product's trial price of $3.99 in the image below, you'd use `products.selected.trialPeriodPrice`: diff --git a/content/docs/expo/guides/consumable-products.mdx b/content/docs/expo/guides/consumable-products.mdx new file mode 100644 index 00000000..ffc7f6eb --- /dev/null +++ b/content/docs/expo/guides/consumable-products.mdx @@ -0,0 +1,57 @@ +--- +title: "Consumable Products" +description: "Set up consumable products for Superwall paywalls in Expo apps." +--- + +Use consumable products when a purchase should grant a quantity that can be used up, such as credits, coins, boosts, or tokens. + +This guide covers the Superwall-only flow where purchases are started from paywalls and you are not using a custom purchase controller. + +The platform sections below explain the iOS and Android store requirements that still apply to Expo apps. + +## iOS + +../../../shared/consumables/ios.mdx + +## Android + +../../../shared/consumables/android.mdx + +## Expo Event Handling + +In Expo, listen for purchase events with [`useSuperwallEvents`](/expo/sdk-reference/hooks/useSuperwallEvents). On Android, the `transactionComplete` event can include `transaction.purchaseToken`; after you grant the benefit, pass that token to [`Superwall.consume(purchaseToken)`](/expo/sdk-reference/hooks/consume). + +```tsx +import { Platform } from "react-native" +import Superwall from "expo-superwall/compat" +import { useSuperwallEvents } from "expo-superwall" + +export function ConsumableEvents() { + useSuperwallEvents({ + onSuperwallEvent: async ({ event }) => { + if (event.event !== "transactionComplete" || Platform.OS !== "android") { + return + } + + if (event.product.productIdentifier !== "coins_100") { + return + } + + const purchaseToken = event.transaction?.purchaseToken + if (!purchaseToken) { + return + } + + await grantCoins({ + count: 100, + productId: event.product.productIdentifier, + purchaseToken, + }) + + await Superwall.consume(purchaseToken) + }, + }) + + return null +} +``` diff --git a/content/docs/expo/meta.json b/content/docs/expo/meta.json index 03a9b0c4..a9b82b3e 100644 --- a/content/docs/expo/meta.json +++ b/content/docs/expo/meta.json @@ -27,6 +27,7 @@ "sdk-reference/hooks", "---Guides---", + "guides/consumable-products", "guides/debugging", "guides/vibe-coding", "guides/using-revenuecat", diff --git a/content/docs/flutter/guides/consumable-products.mdx b/content/docs/flutter/guides/consumable-products.mdx new file mode 100644 index 00000000..04d8a0aa --- /dev/null +++ b/content/docs/flutter/guides/consumable-products.mdx @@ -0,0 +1,24 @@ +--- +title: "Consumable Products" +description: "Set up consumable products for Superwall paywalls in Flutter apps." +--- + +Use consumable products when a purchase should grant a quantity that can be used up, such as credits, coins, boosts, or tokens. + +This guide covers the Superwall-only flow where purchases are started from paywalls and you are not using a `PurchaseController`. + +The platform sections below explain the iOS and Android store requirements that still apply to Flutter apps. + +## iOS + +../../../shared/consumables/ios.mdx + +## Android + +../../../shared/consumables/android.mdx + +## Flutter Event Handling + +In Flutter, listen for purchase events with [`SuperwallDelegate`](/flutter/sdk-reference/SuperwallDelegate) or a [`PaywallPresentationHandler`](/flutter/sdk-reference/PaywallPresentationHandler). Android consumables still need the Google Play purchase token consumed after you grant the benefit. + +Use [`Superwall.shared.consume(purchaseToken)`](/flutter/sdk-reference/consume) for the Android consumption call. The current Flutter SDK exposes `consume()` directly, but its public `StoreTransaction` wrapper does not expose the Android `purchaseToken` from Superwall's `transactionComplete` event. If your Flutter app sells Android consumables, expose that token from your Android purchase handling path before calling `consume()`. diff --git a/content/docs/flutter/meta.json b/content/docs/flutter/meta.json index 18ee0308..bbae6b0b 100644 --- a/content/docs/flutter/meta.json +++ b/content/docs/flutter/meta.json @@ -58,6 +58,7 @@ "sdk-reference/advanced", "---Guides---", + "guides/consumable-products", "guides/vibe-coding", "guides/using-revenuecat", "guides/experimental-flags", diff --git a/content/docs/flutter/sdk-reference/consume.mdx b/content/docs/flutter/sdk-reference/consume.mdx index cec8af08..67bf688a 100644 --- a/content/docs/flutter/sdk-reference/consume.mdx +++ b/content/docs/flutter/sdk-reference/consume.mdx @@ -28,37 +28,22 @@ Returns a `Future` that resolves to the purchase token of the consumed p ## Usage -Consuming a purchase after successful purchase: +Consuming a purchase after you have granted the benefit: ```dart -try { - final purchaseToken = await Superwall.shared.consume('purchase_token_123'); - print('Purchase consumed: $purchaseToken'); -} catch (e) { - print('Failed to consume purchase: $e'); -} -``` - -Using with a PurchaseController: -```dart -class MyPurchaseController extends PurchaseController { - @override - Future purchase(Product product) async { - // Handle purchase - final transaction = await _processPurchase(product); - - // If this is a consumable product, consume it - if (product.type == ProductType.consumable) { - try { - await Superwall.shared.consume(transaction.purchaseToken); - print('Consumable purchase consumed'); - } catch (e) { - print('Failed to consume purchase: $e'); - } - } +Future consumeGrantedPurchase(String purchaseToken) async { + try { + final consumedToken = await Superwall.shared.consume(purchaseToken); + print('Purchase consumed: $consumedToken'); + } catch (e) { + print('Failed to consume purchase: $e'); } } ``` + +On Android, `purchaseToken` comes from Google Play's purchase result. The current Flutter `StoreTransaction` wrapper does not expose this token from Superwall's `transactionComplete` event. + + ## Related - [`PurchaseController`](/flutter/sdk-reference/PurchaseController) - Handles purchase logic -- `Product` - Product information used in your purchase flow +- [`Consumable Products`](/flutter/guides/consumable-products) - End-to-end setup for consumable products diff --git a/content/docs/images/charts/active-subscriptions.jpg b/content/docs/images/charts/active-subscriptions.jpg new file mode 100644 index 00000000..5ac9f300 Binary files /dev/null and b/content/docs/images/charts/active-subscriptions.jpg differ diff --git a/content/docs/images/charts/active-users.jpg b/content/docs/images/charts/active-users.jpg new file mode 100644 index 00000000..1e9cc09d Binary files /dev/null and b/content/docs/images/charts/active-users.jpg differ diff --git a/content/docs/images/charts/arr.jpg b/content/docs/images/charts/arr.jpg new file mode 100644 index 00000000..72209453 Binary files /dev/null and b/content/docs/images/charts/arr.jpg differ diff --git a/content/docs/images/charts/auto-renew-status.jpg b/content/docs/images/charts/auto-renew-status.jpg new file mode 100644 index 00000000..f0cd6bfb Binary files /dev/null and b/content/docs/images/charts/auto-renew-status.jpg differ diff --git a/content/docs/images/charts/checkout-conversion.jpg b/content/docs/images/charts/checkout-conversion.jpg new file mode 100644 index 00000000..bbfefbd8 Binary files /dev/null and b/content/docs/images/charts/checkout-conversion.jpg differ diff --git a/content/docs/images/charts/cohorted-proceeds.jpg b/content/docs/images/charts/cohorted-proceeds.jpg new file mode 100644 index 00000000..abd1ebe0 Binary files /dev/null and b/content/docs/images/charts/cohorted-proceeds.jpg differ diff --git a/content/docs/images/charts/conversions.jpg b/content/docs/images/charts/conversions.jpg new file mode 100644 index 00000000..d44c1a54 Binary files /dev/null and b/content/docs/images/charts/conversions.jpg differ diff --git a/content/docs/images/charts/initial-conversion.jpg b/content/docs/images/charts/initial-conversion.jpg new file mode 100644 index 00000000..9b787aeb Binary files /dev/null and b/content/docs/images/charts/initial-conversion.jpg differ diff --git a/content/docs/images/charts/mrr.jpg b/content/docs/images/charts/mrr.jpg new file mode 100644 index 00000000..2fcbf06e Binary files /dev/null and b/content/docs/images/charts/mrr.jpg differ diff --git a/content/docs/images/charts/new-subscriptions.jpg b/content/docs/images/charts/new-subscriptions.jpg new file mode 100644 index 00000000..f12c0e8f Binary files /dev/null and b/content/docs/images/charts/new-subscriptions.jpg differ diff --git a/content/docs/images/charts/new-trials.jpg b/content/docs/images/charts/new-trials.jpg new file mode 100644 index 00000000..7a4ca7f5 Binary files /dev/null and b/content/docs/images/charts/new-trials.jpg differ diff --git a/content/docs/images/charts/new-users.jpg b/content/docs/images/charts/new-users.jpg new file mode 100644 index 00000000..8f4033a5 Binary files /dev/null and b/content/docs/images/charts/new-users.jpg differ diff --git a/content/docs/images/charts/paid-conversion.jpg b/content/docs/images/charts/paid-conversion.jpg new file mode 100644 index 00000000..d974f058 Binary files /dev/null and b/content/docs/images/charts/paid-conversion.jpg differ diff --git a/content/docs/images/charts/paywall-conversion.jpg b/content/docs/images/charts/paywall-conversion.jpg new file mode 100644 index 00000000..677500bf Binary files /dev/null and b/content/docs/images/charts/paywall-conversion.jpg differ diff --git a/content/docs/images/charts/paywall-rate.jpg b/content/docs/images/charts/paywall-rate.jpg new file mode 100644 index 00000000..665529aa Binary files /dev/null and b/content/docs/images/charts/paywall-rate.jpg differ diff --git a/content/docs/images/charts/paywalled-users.jpg b/content/docs/images/charts/paywalled-users.jpg new file mode 100644 index 00000000..74bc5d25 Binary files /dev/null and b/content/docs/images/charts/paywalled-users.jpg differ diff --git a/content/docs/images/charts/proceeds.jpg b/content/docs/images/charts/proceeds.jpg new file mode 100644 index 00000000..7ee254ac Binary files /dev/null and b/content/docs/images/charts/proceeds.jpg differ diff --git a/content/docs/images/charts/realized-ltv-per-new-user.jpg b/content/docs/images/charts/realized-ltv-per-new-user.jpg new file mode 100644 index 00000000..e7b5408f Binary files /dev/null and b/content/docs/images/charts/realized-ltv-per-new-user.jpg differ diff --git a/content/docs/images/charts/realized-ltv-per-paid-user.jpg b/content/docs/images/charts/realized-ltv-per-paid-user.jpg new file mode 100644 index 00000000..aadcb313 Binary files /dev/null and b/content/docs/images/charts/realized-ltv-per-paid-user.jpg differ diff --git a/content/docs/images/charts/refund-rate.jpg b/content/docs/images/charts/refund-rate.jpg new file mode 100644 index 00000000..17aeefa5 Binary files /dev/null and b/content/docs/images/charts/refund-rate.jpg differ diff --git a/content/docs/images/charts/sales.jpg b/content/docs/images/charts/sales.jpg new file mode 100644 index 00000000..25aba7a5 Binary files /dev/null and b/content/docs/images/charts/sales.jpg differ diff --git a/content/docs/images/charts/subscriber-churn.jpg b/content/docs/images/charts/subscriber-churn.jpg new file mode 100644 index 00000000..4259df66 Binary files /dev/null and b/content/docs/images/charts/subscriber-churn.jpg differ diff --git a/content/docs/images/charts/subscription-retention.jpg b/content/docs/images/charts/subscription-retention.jpg new file mode 100644 index 00000000..a9902696 Binary files /dev/null and b/content/docs/images/charts/subscription-retention.jpg differ diff --git a/content/docs/images/charts/trial-conversion.jpg b/content/docs/images/charts/trial-conversion.jpg new file mode 100644 index 00000000..fa3ce4c5 Binary files /dev/null and b/content/docs/images/charts/trial-conversion.jpg differ diff --git a/content/docs/images/web2app_add_stripe.jpg b/content/docs/images/web2app_add_stripe.jpg new file mode 100644 index 00000000..b48d5abb Binary files /dev/null and b/content/docs/images/web2app_add_stripe.jpg differ diff --git a/content/docs/ios/guides/consumable-products.mdx b/content/docs/ios/guides/consumable-products.mdx new file mode 100644 index 00000000..10d4bbb8 --- /dev/null +++ b/content/docs/ios/guides/consumable-products.mdx @@ -0,0 +1,8 @@ +--- +title: "Consumable Products" +description: "Set up consumable products for Superwall paywalls on iOS." +--- + +Use consumable products when a purchase should grant a quantity that can be used up, such as credits, coins, boosts, or tokens. + +../../../shared/consumables/ios.mdx diff --git a/content/docs/ios/meta.json b/content/docs/ios/meta.json index ab1ecc07..63d332ab 100644 --- a/content/docs/ios/meta.json +++ b/content/docs/ios/meta.json @@ -55,6 +55,7 @@ "sdk-reference/advanced", "---Guides---", + "guides/consumable-products", "guides/direct-purchasing", "guides/vibe-coding", "guides/using-revenuecat", diff --git a/content/shared/consumables/android.mdx b/content/shared/consumables/android.mdx new file mode 100644 index 00000000..02adeaab --- /dev/null +++ b/content/shared/consumables/android.mdx @@ -0,0 +1,149 @@ + +This guide assumes purchases are made from Superwall paywalls and that you are not using a `PurchaseController`. + + +Consumable products are one-time purchases that users can buy repeatedly, such as credits, tokens, boosts, or packs. Non-consumable products are also one-time purchases, but they grant permanent access, such as a lifetime unlock. + +Superwall uses entitlements to decide whether a user has ongoing access. Because consumables are meant to be used up, they should usually not grant entitlements. Your app should listen for the purchase, grant the consumable benefit in your own system, and then consume the Google Play purchase token so the item can be purchased again. + +## Dashboard Setup + +1. Create the product as an in-app product in Google Play Console. +2. Add the product in Superwall from **Products**. +3. Use the Google Play product ID. +4. Set **Period** to **None (Lifetime / Consumable)**. +5. Leave **Entitlements** empty. +6. Add the product to any paywall that should sell it. + + +Do not attach an entitlement to a consumable unless the purchase should also unlock ongoing access. If a consumable has no entitlement, buying it does not make the user's subscription status active. + + +## Consume The Purchase Token + +Google Play in-app products must be consumed after you grant the benefit. If you do not consume the purchase token, the user may not be able to buy that same consumable again. + +Use the `purchaseToken` from the `TransactionComplete` event, grant the benefit, then call `Superwall.instance.consume(purchaseToken)`. + + +`Superwall.instance.consume(purchaseToken)` is available in Android SDK 2.6.2 and later. + + + +```kotlin Kotlin +import androidx.lifecycle.lifecycleScope +import com.superwall.sdk.Superwall +import com.superwall.sdk.analytics.superwall.SuperwallEvent +import com.superwall.sdk.analytics.superwall.SuperwallEventInfo +import com.superwall.sdk.delegate.SuperwallDelegate +import kotlinx.coroutines.launch + +class MainActivity : AppCompatActivity(), SuperwallDelegate { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Superwall.instance.delegate = this + } + + override fun handleSuperwallEvent(eventInfo: SuperwallEventInfo) { + val event = eventInfo.event + if (event !is SuperwallEvent.TransactionComplete) { + return + } + + if (event.product.productIdentifier != "coins_100") { + return + } + + val purchaseToken = event.transaction?.purchaseToken ?: return + + lifecycleScope.launch { + ConsumablesService.grantCoins( + count = 100, + productId = event.product.productIdentifier, + purchaseToken = purchaseToken, + ) + + Superwall.instance.consume(purchaseToken) + .onFailure { error -> + // Retry consumption after confirming the benefit was granted. + println("Failed to consume purchase: ${error.message}") + } + } + } +} +``` + +```java Java +import com.superwall.sdk.Superwall; +import com.superwall.sdk.analytics.superwall.SuperwallEvent; +import com.superwall.sdk.analytics.superwall.SuperwallEventInfo; +import com.superwall.sdk.delegate.SuperwallDelegateJava; +import com.superwall.sdk.store.abstractions.transactions.StoreTransactionType; +import kotlin.Unit; + +public class MainActivity extends AppCompatActivity implements SuperwallDelegateJava { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Superwall.getInstance().setJavaDelegate(this); + } + + @Override + public void handleSuperwallEvent(SuperwallEventInfo eventInfo) { + if (!(eventInfo.getEvent() instanceof SuperwallEvent.TransactionComplete)) { + return; + } + + SuperwallEvent.TransactionComplete event = + (SuperwallEvent.TransactionComplete) eventInfo.getEvent(); + + if (!event.getProduct().getProductIdentifier().equals("coins_100")) { + return; + } + + StoreTransactionType transaction = event.getTransaction(); + if (transaction == null) { + return; + } + + String purchaseToken = transaction.getPurchaseToken(); + + ConsumablesService.grantCoins(100, event.getProduct().getProductIdentifier(), purchaseToken); + + Superwall.getInstance().consume(purchaseToken, result -> { + // Check the Result in your app and retry if consumption fails. + return Unit.INSTANCE; + }); + } +} +``` + + + +Grant the benefit before consuming the token. If your grant call fails, leave the purchase unconsumed and retry after your app confirms the user received the benefit. + + +## Read Purchase History + +Consumable and non-consumable purchases appear in `customerInfo.nonSubscriptions`. Use `isConsumable` to distinguish consumables from lifetime purchases. + + +```kotlin Kotlin +val customerInfo = Superwall.instance.getCustomerInfo() + +val consumables = customerInfo.nonSubscriptions.filter { it.isConsumable } +for (purchase in consumables) { + println("Consumable purchased: ${purchase.productId}") +} +``` + +```java Java +CustomerInfo customerInfo = Superwall.getInstance().getCustomerInfo(); + +for (NonSubscriptionTransaction purchase : customerInfo.getNonSubscriptions()) { + if (purchase.isConsumable()) { + System.out.println("Consumable purchased: " + purchase.getProductId()); + } +} +``` + diff --git a/content/shared/consumables/ios.mdx b/content/shared/consumables/ios.mdx new file mode 100644 index 00000000..776f5d2f --- /dev/null +++ b/content/shared/consumables/ios.mdx @@ -0,0 +1,119 @@ + +This guide assumes purchases are made from Superwall paywalls and that you are not using a `PurchaseController`. + + +Consumable products are one-time purchases that users can buy repeatedly, such as credits, tokens, boosts, or packs. Non-consumable products are also one-time purchases, but they grant permanent access, such as a lifetime unlock. + +Superwall uses entitlements to decide whether a user has ongoing access. Because consumables are meant to be used up, they should usually not grant entitlements. Your app should listen for the purchase, grant the consumable benefit in your own system, and treat Superwall's purchase history as a record of what happened. + +## Dashboard Setup + +1. Create the consumable in App Store Connect. +2. Add the product in Superwall from **Products**. +3. Use the App Store product identifier. +4. Set **Period** to **None (Lifetime / Consumable)**. +5. Leave **Entitlements** empty. +6. Add the product to any paywall that should sell it. + + +Do not attach an entitlement to a consumable unless the purchase should also unlock ongoing access. If a consumable has no entitlement, buying it does not make the user's subscription status active. + + +## Include Consumables In Purchase History + +Apple excludes consumable purchases from App Store purchase history unless you opt in. Add `SKIncludeConsumableInAppPurchaseHistory` to your app's `Info.plist` as a Boolean set to `YES`. + +```xml Info.plist +SKIncludeConsumableInAppPurchaseHistory + +``` + + +When this key is present and set to `YES`, Superwall uses StoreKit 2 on iOS 18 and later. On earlier iOS versions, the SDK falls back to StoreKit 1 for purchase history support. + + +## Grant The Consumable Benefit + +Superwall does not maintain balances for consumables. Grant credits, tokens, or other benefits from your app or backend after the `transactionComplete` event. Make this operation idempotent so retries do not double-credit the user. + + +```swift Swift +import SuperwallKit + +final class SWDelegate: SuperwallDelegate { + func handleSuperwallEvent(withInfo eventInfo: SuperwallEventInfo) { + guard case let .transactionComplete(transaction, product, _, _) = eventInfo.event else { + return + } + + guard product.productIdentifier == "com.example.credits_100" else { + return + } + + Task { + await ConsumablesService.shared.grantCredits( + count: 100, + productId: product.productIdentifier, + transactionId: transaction?.storeTransactionId + ) + } + } +} + +Superwall.shared.delegate = SWDelegate() +``` + +```swift Objective-C +#import + +@interface SWDelegate : NSObject +@end + +@implementation SWDelegate + +- (void)handleSuperwallEventWithInfo:(SWKSuperwallEventInfo *)eventInfo { + if (eventInfo.event != SWKSuperwallEventTransactionComplete) { + return; + } + + NSString *productId = eventInfo.params[@"primary_product_id"]; + if (![productId isEqualToString:@"com.example.credits_100"]) { + return; + } + + NSString *transactionId = eventInfo.params[@"store_transaction_id"]; + [[ConsumablesService shared] grantCredits:100 + productId:productId + transactionId:transactionId]; +} + +@end + +[Superwall sharedInstance].delegate = [SWDelegate new]; +``` + + +## Read Purchase History + +Consumable and non-consumable purchases appear in `customerInfo.nonSubscriptions`. Use `isConsumable` to distinguish consumables from lifetime purchases. + + +```swift Swift +let customerInfo = Superwall.shared.customerInfo + +let consumables = customerInfo.nonSubscriptions.filter { $0.isConsumable } +for purchase in consumables { + print("Consumable purchased: \(purchase.productId)") +} +``` + +```swift Objective-C +SWKCustomerInfo *customerInfo = [Superwall sharedInstance].customerInfo; + +for (SWKNonSubscriptionTransaction *purchase in customerInfo.nonSubscriptions) { + if (purchase.isConsumable) { + NSLog(@"Consumable purchased: %@", purchase.productId); + } +} +``` + diff --git a/scripts/download-references.ts b/scripts/download-references.ts index 4adee9ad..c93b9290 100644 --- a/scripts/download-references.ts +++ b/scripts/download-references.ts @@ -9,7 +9,10 @@ const repositories = [ { name: "expo", url: "https://github.com/superwall/expo-superwall.git" }, { name: "react-native", url: "https://github.com/superwall/react-native-superwall.git" }, ]; -const reactNativeTestFileRelativePath = "src/__tests__/index.test.tsx"; +const removableTestFilesByRepo: Record = { + expo: ["src/__tests__/sdk.behavior.test.tsx"], + "react-native": ["src/__tests__/index.test.tsx"], +}; const referenceDir = join(process.cwd(), "reference"); @@ -36,9 +39,10 @@ for (const repo of repositories) { continue; } - if (repo.name === "react-native") { - // Restore this file before pulling so prior local deletion doesn't block updates. - await $`git -C ${repoPath} checkout -- ${reactNativeTestFileRelativePath}`; + const removableTestFiles = removableTestFilesByRepo[repo.name] ?? []; + for (const relativePath of removableTestFiles) { + // Restore these files before pulling so prior local deletion doesn't block updates. + await $`git -C ${repoPath} checkout -- ${relativePath}`; } console.log(`Updating ${repo.name}...`); @@ -51,8 +55,8 @@ for (const repo of repositories) { } } - if (repo.name === "react-native") { - const testFilePath = join(repoPath, reactNativeTestFileRelativePath); + for (const relativePath of removableTestFilesByRepo[repo.name] ?? []) { + const testFilePath = join(repoPath, relativePath); if (existsSync(testFilePath)) { try {