diff --git a/bun.lock b/bun.lock index 5d46cd05..39ad79db 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "superwall-docs", @@ -45,6 +44,7 @@ "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.4", + "next-validate-link": "^1.6.4", "oxfmt": "^0.36.0", "oxlint": "^1.51.0", "srvx": "^0.11.8", @@ -1256,6 +1256,8 @@ "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + "next-validate-link": ["next-validate-link@1.6.4", "", { "dependencies": { "gray-matter": "^4.0.3", "picocolors": "^1.1.1", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-mdx": "^3.1.1", "tinyglobby": "^0.2.15", "unist-util-visit": "^5.0.0" } }, "sha512-wR/VKyJlaTbUT5k1uujEnk6O616YtWGt52s9FJa3tRCQ2qL2TGFTAklXJ0QdX1NTAEeP6rGFOTtHEDwveFrc2g=="], + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], diff --git a/content/docs/advanced-paywall-creation.mdx b/content/docs/advanced-paywall-creation.mdx index 16acb620..1302b544 100644 --- a/content/docs/advanced-paywall-creation.mdx +++ b/content/docs/advanced-paywall-creation.mdx @@ -4,7 +4,7 @@ title: "External Paywall Creation" The legacy editor is deprecated. Please visit the docs covering our new - [editor](/paywall-editor-overview). + [editor](/dashboard/dashboard-creating-paywalls/paywall-editor-overview). The following is only necessary if you're looking to build your own paywall website outside of Superwall. We strongly recommend using the paywall editor or templates instead. @@ -24,7 +24,7 @@ There are seven different types of data tag: | data-pw-purchase | Either primary, secondary, or tertiary. | This relates to your primary, secondary, or tertiary product set up in the Superwall dashboard respectively. When the tagged element is tapped, it tells the SDK to initialize a purchase of the specified product. | | data-pw-open-url | A valid url. E.g.` .` | Normal href links do not work in a paywall. However, attaching this tag to an element opens the provided url. | | data-pw-open-deep-link | A valid deep link url. E.g. fb://profile/33138223345. | Opens a deeplink from a button on a paywall. | -| data-pw-custom | A name for a custom action | When the tagged element is tapped, the SDK calls its delegate method handleCustomPaywallAction(withName:). The value you provide is passed to the function, from which you can perform custom logic. Check out [Custom Paywall Buttons](/docs/custom-paywall-events) for more. | +| data-pw-custom | A name for a custom action | When the tagged element is tapped, the SDK calls its delegate method handleCustomPaywallAction(withName:). The value you provide is passed to the function, from which you can perform custom logic. Check out [Custom Paywall Buttons](/sdk/guides/advanced/custom-paywall-actions) for more. | | data-pw-skip-inner-html | true | This prevents an element's text from being edited. It tells Paywall.js to keep looking into the element's children for more data-pw-var tags. This is useful when you want to hide a whole section or edit the style of a container, rather than edit its text. | Here are a few examples of what data tags look like in the webpage HTML: diff --git a/content/docs/android/guides/web-checkout/linking-membership-to-iOS-app.mdx b/content/docs/android/guides/web-checkout/linking-membership-to-iOS-app.mdx index bd9012ed..7b44df78 100644 --- a/content/docs/android/guides/web-checkout/linking-membership-to-iOS-app.mdx +++ b/content/docs/android/guides/web-checkout/linking-membership-to-iOS-app.mdx @@ -4,13 +4,13 @@ description: "Handle a deep link in your app and use the delegate methods." --- After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. -Please follow our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) guide to handle this user experience. +Please follow our [Post-Checkout Redirecting](/android/guides/web-checkout/post-checkout-redirecting) guide to handle this user experience. If you're using Superwall to handle purchases, then you don't need to do anything here. -If you're using your own `PurchaseController`, you will need to update the subscription status with the redeemed web entitlements. If you're using RevenueCat, you should follow our [Using RevenueCat](/web-checkout-using-revenuecat) guide. +If you're using your own `PurchaseController`, you will need to update the subscription status with the redeemed web entitlements. If you're using RevenueCat, you should follow our [Using RevenueCat](/android/guides/web-checkout/using-revenuecat) guide. ### Using a PurchaseController @@ -98,4 +98,4 @@ If you aren't using a Purchase Controller, the SDK will refresh the web entitlem ### Redeeming while a paywall is open -If a redeem event occurs when a paywall is open, the SDK will track that as a restore event and the paywall will close. \ No newline at end of file +If a redeem event occurs when a paywall is open, the SDK will track that as a restore event and the paywall will close. diff --git a/content/docs/android/guides/web-checkout/post-checkout-redirecting.mdx b/content/docs/android/guides/web-checkout/post-checkout-redirecting.mdx index 6bd786fe..f2f1398f 100644 --- a/content/docs/android/guides/web-checkout/post-checkout-redirecting.mdx +++ b/content/docs/android/guides/web-checkout/post-checkout-redirecting.mdx @@ -7,7 +7,7 @@ After a user completes a web purchase, Superwall needs to redirect them back to ## Post-Purchase Behavior Modes -You can configure how users are redirected after checkout in your [Application Settings](/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): +You can configure how users are redirected after checkout in your [Application Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): ### Redeem Mode (Default) @@ -49,15 +49,15 @@ You'll need to implement your own logic to handle the redirect and deep link use Whether you're showing a checkout page in a browser or using the In-App Browser, the Superwall SDK relies on deep links to redirect back to your app. #### Prerequisites -1. [Configuring Stripe Keys and Settings](/web-checkout-configuring-stripe-keys-and-settings) -2. [Deep Links](/in-app-paywall-previews) +1. [Configuring Stripe Keys and Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) +2. [Deep Links](/android/quickstart/in-app-paywall-previews) If you're not using Superwall to handle purchases, then you'll need to follow extra steps to redeem the web purchase in your app. -- [Using RevenueCat](/web-checkout-using-revenuecat) -- [Using a PurchaseController](/web-checkout-linking-membership-to-iOS-app#using-a-purchasecontroller) +- [Using RevenueCat](/android/guides/web-checkout/using-revenuecat) +- [Using a PurchaseController](/android/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller) --- @@ -134,4 +134,4 @@ class SWDelegate : SuperwallDelegate { } } } -``` \ No newline at end of file +``` diff --git a/content/docs/android/guides/web-checkout/using-revenuecat.mdx b/content/docs/android/guides/web-checkout/using-revenuecat.mdx index 9002e7de..70fd6a0d 100644 --- a/content/docs/android/guides/web-checkout/using-revenuecat.mdx +++ b/content/docs/android/guides/web-checkout/using-revenuecat.mdx @@ -3,7 +3,7 @@ title: "Using RevenueCat" description: "Handle a deep link in your app and use the delegate methods to link web checkouts with RevenueCat." --- -After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. Please follow our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) guide to handle this user experience. +After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. Please follow our [Post-Checkout Redirecting](/android/guides/web-checkout/post-checkout-redirecting) guide to handle this user experience. If you're using Superwall to handle purchases, then you don't need to do anything here. @@ -11,7 +11,7 @@ After purchasing from a web paywall, the user will be redirected to your app by You only need to use a `PurchaseController` if you want end-to-end control of the purchasing pipeline. The recommended way to use RevenueCat with Superwall is by putting it in observer mode. -If you're using your own `PurchaseController`, you should follow our [Redeeming In-App](/web-checkout-linking-membership-to-iOS-app) guide. +If you're using your own `PurchaseController`, you should follow our [Redeeming In-App](/android/guides/web-checkout/linking-membership-to-iOS-app) guide. ### Using a PurchaseController with RevenueCat @@ -136,4 +136,4 @@ class SWDelegate : SuperwallDelegate { The web entitlements will be returned along with other existing entitlements in the `CustomerInfo` object accessible via RevenueCat's SDK. -If you're logging in and out of RevenueCat, make sure to resend the Stripe subscription IDs to RevenueCat's endpoint after logging in. \ No newline at end of file +If you're logging in and out of RevenueCat, make sure to resend the Stripe subscription IDs to RevenueCat's endpoint after logging in. diff --git a/content/docs/android/quickstart/tracking-subscription-state.mdx b/content/docs/android/quickstart/tracking-subscription-state.mdx index 6d7507f8..1002c7ae 100644 --- a/content/docs/android/quickstart/tracking-subscription-state.mdx +++ b/content/docs/android/quickstart/tracking-subscription-state.mdx @@ -272,7 +272,7 @@ class MyApplication : Application() { ## Superwall checks subscription status for you -Remember that the Superwall SDK uses its [audience filters](/campaigns-audience#matching-to-entitlements) for determining when to show paywalls. You generally don't need to wrap your calls to register placements with subscription status checks: +Remember that the Superwall SDK uses its [audience filters](/dashboard/dashboard-campaigns/campaigns-audience#matching-to-entitlements) for determining when to show paywalls. You generally don't need to wrap your calls to register placements with subscription status checks: ```kotlin // ❌ Unnecessary @@ -284,4 +284,4 @@ if (Superwall.instance.subscriptionStatus.value !is SubscriptionStatus.Active) { Superwall.instance.register("campaign_trigger") ``` -In your [audience filters](/campaigns-audience#matching-to-entitlements), you can specify whether the subscription state should be considered, which keeps your codebase cleaner and puts the "Should this paywall show?" logic where it belongs—in the Superwall dashboard. +In your [audience filters](/dashboard/dashboard-campaigns/campaigns-audience#matching-to-entitlements), you can specify whether the subscription state should be considered, which keeps your codebase cleaner and puts the "Should this paywall show?" logic where it belongs—in the Superwall dashboard. diff --git a/content/docs/configuring-a-paywall.mdx b/content/docs/configuring-a-paywall.mdx index 64808ebe..3203ff13 100644 --- a/content/docs/configuring-a-paywall.mdx +++ b/content/docs/configuring-a-paywall.mdx @@ -6,7 +6,7 @@ description: "Superwall maintains a growing list of paywall templates for you to The legacy editor is deprecated. Please visit the docs covering our new - [editor](/paywall-editor-overview). + [editor](/dashboard/dashboard-creating-paywalls/paywall-editor-overview). On the Superwall dashboard under **Paywalls**, click **\+ New Paywall** and select **From Template**: diff --git a/content/docs/dashboard/dashboard-campaigns/campaign-rules.mdx b/content/docs/dashboard/dashboard-campaigns/campaign-rules.mdx index 88916445..f81b8d9b 100644 --- a/content/docs/dashboard/dashboard-campaigns/campaign-rules.mdx +++ b/content/docs/dashboard/dashboard-campaigns/campaign-rules.mdx @@ -4,7 +4,7 @@ description: "Rules allow you to decide _which users_ see a paywall." --- - This page is outdated. Please visit this [one](/campaigns-audience) for the most relevant + This page is outdated. Please visit this [one](/dashboard/dashboard-campaigns/campaigns-audience) for the most relevant information. \ No newline at end of file + diff --git a/content/docs/dashboard/guides/using-stripe-checkout-in-app.mdx b/content/docs/dashboard/guides/using-stripe-checkout-in-app.mdx index 7c41d387..956ecbcc 100644 --- a/content/docs/dashboard/guides/using-stripe-checkout-in-app.mdx +++ b/content/docs/dashboard/guides/using-stripe-checkout-in-app.mdx @@ -2,12 +2,12 @@ title: Using Stripe's New Web Checkout In-App --- -For users in the United States, you can offer Stripe checkout inside of your app's experience. For more information on how to configure Stripe checkout, review the documentation [here](/web-checkout-direct-stripe-checkout). This specific guide shows you how to use Stripe's new checkout flow. +For users in the United States, you can offer Stripe checkout inside of your app's experience. For more information on how to configure Stripe checkout, review the documentation [here](/web-checkout/web-checkout-direct-stripe-checkout). This specific guide shows you how to use Stripe's new checkout flow. Superwall will prioritize showing Apple Pay when available as the first payment option. -**App Store Review Tip:** If you're using an in-app Stripe checkout sheet, we strongly suggest showing your Stripe purchase experience to Apple during an App Review before testing with users. For recommended reviewer note language, see the [App2Web docs](/web-checkout-direct-stripe-checkout). +**App Store Review Tip:** If you're using an in-app Stripe checkout sheet, we strongly suggest showing your Stripe purchase experience to Apple during an App Review before testing with users. For recommended reviewer note language, see the [App2Web docs](/web-checkout/web-checkout-direct-stripe-checkout). @@ -24,4 +24,4 @@ For users in the United States, you can offer Stripe checkout inside of your app This process works by attaching the Stripe product to a purchase tap behavior. Once the user checks out, the [web redemption -flow](/web-checkout-direct-stripe-checkout) will occur. +flow](/web-checkout/web-checkout-direct-stripe-checkout) will occur. diff --git a/content/docs/dashboard/guides/using-superwall-for-onboarding-flows.mdx b/content/docs/dashboard/guides/using-superwall-for-onboarding-flows.mdx index d4ec1059..d669e20e 100644 --- a/content/docs/dashboard/guides/using-superwall-for-onboarding-flows.mdx +++ b/content/docs/dashboard/guides/using-superwall-for-onboarding-flows.mdx @@ -7,12 +7,12 @@ Superwall's flexible paywall system can be used for building engaging onboarding ## Creating an onboarding campaign -Start by creating a [campaign](/campaigns) specifically for onboarding: +Start by creating a [campaign](/dashboard/dashboard-campaigns/campaigns) specifically for onboarding: 1. Navigate to **Campaigns** in the dashboard sidebar 2. Click **+ New Campaign** 3. Name it something like "User Onboarding" -4. Add a [placement](/campaigns-placements) to trigger your onboarding flow +4. Add a [placement](/dashboard/dashboard-campaigns/campaigns-placements) to trigger your onboarding flow ## Triggering onboarding automatically @@ -20,9 +20,9 @@ There are two main approaches to triggering onboarding: ### Using the `app_install` placement -The [`app_install`](/campaigns-standard-placements) standard placement fires automatically when a user first installs your app. This is ideal for showing onboarding since it only fires once: +The [`app_install`](/dashboard/dashboard-campaigns/campaigns-standard-placements) standard placement fires automatically when a user first installs your app. This is ideal for showing onboarding since it only fires once: -1. Add `app_install` as a [placement](/campaigns-placements) to your onboarding campaign +1. Add `app_install` as a [placement](/dashboard/dashboard-campaigns/campaigns-placements) to your onboarding campaign 2. Optionally, in your [audience filters](/dashboard/dashboard-campaigns/campaigns-audience#using-user-properties-or-placement-parameters), add a condition like `user.totalPaywallViews` equals `0` to ensure it only shows to brand new users Since `app_install` only fires once per install, you don't need additional logic to prevent it from showing multiple times. However, if users complete onboarding and you want to track that for other purposes, you can still [set a user attribute](/sdk/quickstart/setting-user-properties): @@ -65,20 +65,20 @@ This approach lets you trigger onboarding based on specific user actions, like c ## Building multi-page onboarding paywalls -Superwall's [Navigation component](/paywall-editor-navigation-component) is perfect for creating multi-page onboarding experiences. Check out our [Simple Onboarding](https://superwall.com/templates?templateId=119147) template to see this in action. +Superwall's [Navigation component](/dashboard/dashboard-creating-paywalls/paywall-editor-navigation-component) is perfect for creating multi-page onboarding experiences. Check out our [Simple Onboarding](https://superwall.com/templates?templateId=119147) template to see this in action. To create a multi-page onboarding paywall: 1. In the paywall editor, click **+** to add a new element 2. Select **Navigation** under the "Base Elements" section -3. Add your onboarding content pages using [stacks](/paywall-editor-stacks) +3. Add your onboarding content pages using [stacks](/dashboard/dashboard-creating-paywalls/paywall-editor-stacks) 4. Add buttons with tap behaviors to navigate between pages, or use transitions like Push, Fade, or Slide -You can also use the [Slides component](/paywall-editor-slides-component) if you want gesture-driven navigation or the [Carousel component](/paywall-editor-carousel-component) if you want slides that auto-advance. +You can also use the [Slides component](/dashboard/dashboard-creating-paywalls/paywall-editor-slides-component) if you want gesture-driven navigation or the [Carousel component](/dashboard/dashboard-creating-paywalls/paywall-editor-carousel-component) if you want slides that auto-advance. ## Personalizing content with dynamic values -Use [variables](/paywall-editor-variables) and [dynamic values](/paywall-editor-dynamic-values) to show different content based on user attributes, device properties, or actions: +Use [variables](/dashboard/dashboard-creating-paywalls/paywall-editor-variables) and [dynamic values](/dashboard/dashboard-creating-paywalls/paywall-editor-dynamic-values) to show different content based on user attributes, device properties, or actions: **Show different messages based on device type:** ``` @@ -100,11 +100,11 @@ This means you can go to certain pages based off a button they tapped showing a **Adjust layout based on onboarding progress:** -Track which slide users are on using the [slides element variable](/paywall-editor-slides-component#tracking-or-updating-the-displayed-element-in-slides) and conditionally show/hide elements or change copy accordingly. +Track which slide users are on using the [slides element variable](/dashboard/dashboard-creating-paywalls/paywall-editor-slides-component#tracking-or-updating-the-displayed-element-in-slides) and conditionally show/hide elements or change copy accordingly. ## Tracking onboarding analytics -To track onboarding metrics, you can use these [Superwall events](/tracking-analytics) and can they can also be sent to your own analytics service. Additionally, you can also use [custom paywall actions](/sdk/guides/advanced/custom-paywall-actions) to trigger specific tracking events when users interact with buttons or elements in your onboarding flow, giving you detailed insights into user behavior and drop-off points. +To track onboarding metrics, you can use these [Superwall events](/sdk/guides/3rd-party-analytics/tracking-analytics) and can they can also be sent to your own analytics service. Additionally, you can also use [custom paywall actions](/sdk/guides/advanced/custom-paywall-actions) to trigger specific tracking events when users interact with buttons or elements in your onboarding flow, giving you detailed insights into user behavior and drop-off points. ## Best practices @@ -117,4 +117,4 @@ To track onboarding metrics, you can use these [Superwall events](/tracking-anal Remember, since everything is managed through the dashboard, you can iterate on your onboarding experience without shipping app updates. ## Going forward -Superwall is currently building out more tools for onboarding, such as text boxes and text entry, and will be available soon. \ No newline at end of file +Superwall is currently building out more tools for onboarding, such as text boxes and text entry, and will be available soon. diff --git a/content/docs/dashboard/index.mdx b/content/docs/dashboard/index.mdx index 6dacb070..eeb41fec 100644 --- a/content/docs/dashboard/index.mdx +++ b/content/docs/dashboard/index.mdx @@ -16,7 +16,7 @@ description: "Welcome to the Superwall Dashboard documentation" Integrate Web Checkout with your app - + Documentation for the Superwall SDK @@ -25,4 +25,4 @@ description: "Welcome to the Superwall Dashboard documentation" We are always improving our documentation! -If you have feedback on any of our docs, please leave a rating and message at the bottom of the page. \ No newline at end of file +If you have feedback on any of our docs, please leave a rating and message at the bottom of the page. diff --git a/content/docs/dashboard/overview-localization.mdx b/content/docs/dashboard/overview-localization.mdx index 17ec6e3f..3041e107 100644 --- a/content/docs/dashboard/overview-localization.mdx +++ b/content/docs/dashboard/overview-localization.mdx @@ -4,7 +4,7 @@ title: "Managing Localization Updates" If you're only dealing with one paywall, or trying to get started with localizing — read this - [doc](/paywall-editor-localization) first. + [doc](/dashboard/dashboard-creating-paywalls/paywall-editor-localization) first. When you make changes to a localized string referenced across more than one paywall, or one is changed via a localization platform provider — you can review those changes by **clicking** on the **Localization** button from the sidebar: diff --git a/content/docs/dashboard/overview-metrics.mdx b/content/docs/dashboard/overview-metrics.mdx index 65783ea8..3292b601 100644 --- a/content/docs/dashboard/overview-metrics.mdx +++ b/content/docs/dashboard/overview-metrics.mdx @@ -91,7 +91,7 @@ For each campaign, Superwall will show: time frame you've chosen from the toggles at the top of the Overview page. -Click on any campaign to dig deeper into them. If you would like to view all campaigns (active or paused), click **All Campaigns** at the top-right. Learn more about campaign [here](/campaigns). +Click on any campaign to dig deeper into them. If you would like to view all campaigns (active or paused), click **All Campaigns** at the top-right. Learn more about campaign [here](/dashboard/dashboard-campaigns/campaigns). ### Recent transactions diff --git a/content/docs/dashboard/overview-users.mdx b/content/docs/dashboard/overview-users.mdx index 87b08dd8..5c78348e 100644 --- a/content/docs/dashboard/overview-users.mdx +++ b/content/docs/dashboard/overview-users.mdx @@ -21,7 +21,7 @@ This will find users by their Superwall identifier (i.e. `$SuperwallAlias:44409A ### Filtering by event -Use the toggle at the right-hand side to toggle by a specific [placement](/campaigns-placements) or [standard placement](/campaigns-standard-placements) (such as session start, app close, app open, etc). +Use the toggle at the right-hand side to toggle by a specific [placement](/dashboard/dashboard-campaigns/campaigns-placements) or [standard placement](/dashboard/dashboard-campaigns/campaigns-standard-placements) (such as session start, app close, app open, etc). Below, Superwall displays all of the users who have opened a paywall the last 24 hours: @@ -46,7 +46,7 @@ It's divided into these sections: 1. **Overview:** Displays key user information including App User ID, Country, Total Spent, SDK Version, and user registration/last seen dates. 2. **Recent Events:** View revenue events and conversions, SDK events, and see the overall breadcrumb of actions the user has taken. You can filter or search by certain events as well. 3. **Entitlements:** Displays any active entitlements the user has attached, how long they'll be active and their corresponding identifiers. See "Granting entitlements" below for more. -4. **Aliases:** Any alias that Superwall has assigned the user will show here. Read more about how user aliases are created [here](/identity-management). +4. **Aliases:** Any alias that Superwall has assigned the user will show here. Read more about how user aliases are created [here](/sdk/quickstart/user-management). 5. **Apple Search Ads:** If you have the [Apple Search Ads](/integrations/apple-search-ads) integration activated, you'll see any A.S.A. data that relates to the user (such as the keywords used which led to install, etc). 6. **User:** This houses basic information about the user, such as their install date, user seed and more. 7. **Device:** The user's device details. All device attributes are searchable here as well. @@ -58,7 +58,7 @@ The user profile contains a wealth of information. You can search events by name The domains of events you can search and filter by are: 1. **Overview:** The default option, this shows all of the key events from today. -2. **Superwall Events:** These are [events](/tracking-analytics) automatically tracked by Superwall. +2. **Superwall Events:** These are [events](/sdk/guides/3rd-party-analytics/tracking-analytics) automatically tracked by Superwall. 3. **App Events:** Placements that you've manually added to a campaign. 4. **Subscriptions Events:** Any transactions, trial starts, and similar subscription events. 5. **All Events:** Displays every single event that's occurred today. @@ -84,5 +84,5 @@ To revoke any entitlement you've granted, **click** on the **Trash icon**: ![](/images/revokeEntitlement.jpeg) -If you are using a purchase controller, take care to follow our [implementation guide](/docs/ios/guides/advanced-configuration). For example, manually granted entitlements register in our SDK as web entitlements. If you aren't accounting for those in your purchase controller code, manually granted entitlements will not work. See the example linked above under "Complete example for iOS" for guidance. - \ No newline at end of file +If you are using a purchase controller, take care to follow our [implementation guide](/docs/sdk/guides/advanced-configuration). For example, manually granted entitlements register in our SDK as web entitlements. If you aren't accounting for those in your purchase controller code, manually granted entitlements will not work. See the example linked above under "Complete example for iOS" for guidance. + diff --git a/content/docs/dashboard/paywalls.mdx b/content/docs/dashboard/paywalls.mdx index 0ca21500..3ea92fbc 100644 --- a/content/docs/dashboard/paywalls.mdx +++ b/content/docs/dashboard/paywalls.mdx @@ -85,4 +85,4 @@ Each metric displays the data in the time frame that's selected from the date to ### Creating a new paywall -To create a new paywall, click **+ New Paywall** at the top-right. For more on creating paywalls, check out this [doc](/paywall-editor-overview#using-the-editor). +To create a new paywall, click **+ New Paywall** at the top-right. For more on creating paywalls, check out this [doc](/dashboard/dashboard-creating-paywalls/paywall-editor-overview#using-the-editor). diff --git a/content/docs/dashboard/products.mdx b/content/docs/dashboard/products.mdx index 185b80a4..6251110c 100644 --- a/content/docs/dashboard/products.mdx +++ b/content/docs/dashboard/products.mdx @@ -2,10 +2,10 @@ title: "Adding Products" --- -Add your existing products from their respective storefront, such as the App Store or the Google Play Store, to an app so they can be used in one or more paywalls. For adding Stripe products, please view [this doc](/web-checkout-adding-a-stripe-product). +Add your existing products from their respective storefront, such as the App Store or the Google Play Store, to an app so they can be used in one or more paywalls. For adding Stripe products, please view [this doc](/web-checkout/web-checkout-adding-a-stripe-product). -Before you attempt to test a paywall on iOS via TestFlight, make sure they are in the "Ready to Submit" phase if it's their initial launch. For local testing, you can use a [StoreKit configuration file](/testing-purchases) at any point. +Before you attempt to test a paywall on iOS via TestFlight, make sure they are in the "Ready to Submit" phase if it's their initial launch. For local testing, you can use a [StoreKit configuration file](/sdk/guides/testing-purchases) at any point. @@ -72,7 +72,7 @@ To edit or delete an entitlement, click on the trailing pencil icon or the trash ## Getting product identifiers -_If you use RevenueCat to handle in-app subscriptions, skip to [Using RevenueCat](/products#using-revenuecat)_ +_If you use RevenueCat to handle in-app subscriptions, skip to [Using RevenueCat](/dashboard/products#using-revenuecat)_ ### Using App Store Connect @@ -84,7 +84,7 @@ Then, copy your **Product ID**: ![3372](/images/f0c2605-Screen_Shot_2022-05-17_at_3.28.41_PM.png "Screen Shot 2022-05-17 at 3.28.41 PM.png") -Your products, whether live or pre-release, shouldn't be in the "Missing Metadata" state. If they are, you won't be able to test them on device. To fix this, make sure your products are in the "Ready to Submit" or "Approved" state. Superwall will automatically warn you if they are in this state when you have the [App Store Connect API set up](/overview-settings-revenue-tracking#app-store-connect-api): +Your products, whether live or pre-release, shouldn't be in the "Missing Metadata" state. If they are, you won't be able to test them on device. To fix this, make sure your products are in the "Ready to Submit" or "Approved" state. Superwall will automatically warn you if they are in this state when you have the [App Store Connect API set up](/dashboard/dashboard-settings/overview-settings-revenue-tracking#app-store-connect-api): ![](/images/asc_invalid_products.png) @@ -155,7 +155,7 @@ Then, add your [RevenueCat Public API Key](https://docs.revenuecat.com/docs/auth ## Using products in paywalls -After you've added products to an app, you're ready to start using them in paywalls. Check out our [docs](/paywall-editor-products) for a step-by-step guide on how to do that. +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: @@ -169,14 +169,14 @@ Any **paid up front** or **pay as you go** product offer types will also be refe ![](/images/paidOfferExample.png) -For more on setting customized text using Liquid Templating, visit this [doc](/paywall-editor-liquid). +For more on setting customized text using Liquid Templating, visit this [doc](/dashboard/dashboard-creating-paywalls/paywall-editor-liquid). ## A note on StoreKit configuration files -If you're using a StoreKit Configuration file, pricing information will come from there during local testing. Therefore, it's important to keep your StoreKit Configuration file, Superwall, and the App Store products all in sync. Follow our [Setting up StoreKit testing](/testing-purchases) guide for more information. +If you're using a StoreKit Configuration file, pricing information will come from there during local testing. Therefore, it's important to keep your StoreKit Configuration file, Superwall, and the App Store products all in sync. Follow our [Setting up StoreKit testing](/sdk/guides/testing-purchases) guide for more information. Having an issue on device with products not appearing? Run through [this - checklist](/docs/troubleshooting#my-products-arent-loading) to make sure everything is configured + checklist](/support/troubleshooting/products-not-loading) to make sure everything is configured correctly. diff --git a/content/docs/dashboard/subscription-management.mdx b/content/docs/dashboard/subscription-management.mdx index 79858ad8..c279fdf5 100644 --- a/content/docs/dashboard/subscription-management.mdx +++ b/content/docs/dashboard/subscription-management.mdx @@ -43,4 +43,4 @@ Superwall emits webhook events for every subscription lifecycle change. Connect ## SDK -Superwall's SDK tracks subscription status automatically based on your dashboard setup, so adding new products or entitlements does not require code changes. For platform-specific details, start with [Tracking Subscription State](/docs/sdk/quickstart/tracking-subscription-state). +Superwall's SDK tracks subscription status automatically based on your dashboard setup, so adding new products or entitlements does not require code changes. For platform-specific details, start with [Tracking Subscription State](/sdk/quickstart/tracking-subscription-state). diff --git a/content/docs/dashboard/surveys.mdx b/content/docs/dashboard/surveys.mdx index 3bdf23a3..e5b17cff 100644 --- a/content/docs/dashboard/surveys.mdx +++ b/content/docs/dashboard/surveys.mdx @@ -83,7 +83,7 @@ After you've selected a paywall, **click** the **Connect** button and you're all You can also attach surveys from the paywall editor itself. This is also where you specify whether - you want a close or post-purchase survey. Read how in this [doc](/paywall-editor-surveys). + you want a close or post-purchase survey. Read how in this [doc](/dashboard/dashboard-creating-paywalls/paywall-editor-surveys). ### Managing surveys @@ -126,4 +126,4 @@ Finally, if you wish to reset the survey results, **click** the **Reset Response ### Tip: showing a paywall based off of a survey response -One particularly useful technique to use with surveys is to show a paywall with a discounted price if the user indicated the pricing was too expensive in their response. You can easily do this using a [standard placement](/campaigns-standard-placements) — and we have a step-by-step guide on how to do exactly this right [here](/campaigns-standard-placements#using-the-survey-response-event). +One particularly useful technique to use with surveys is to show a paywall with a discounted price if the user indicated the pricing was too expensive in their response. You can easily do this using a [standard placement](/dashboard/dashboard-campaigns/campaigns-standard-placements) — and we have a step-by-step guide on how to do exactly this right [here](/dashboard/dashboard-campaigns/campaigns-standard-placements#using-the-survey-response-event). diff --git a/content/docs/expo/guides/web-checkout/linking-membership-to-iOS-app.mdx b/content/docs/expo/guides/web-checkout/linking-membership-to-iOS-app.mdx index 206ea370..7c2a8fb2 100644 --- a/content/docs/expo/guides/web-checkout/linking-membership-to-iOS-app.mdx +++ b/content/docs/expo/guides/web-checkout/linking-membership-to-iOS-app.mdx @@ -4,13 +4,13 @@ description: "Handle a deep link in your app and use the delegate methods." --- After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. -Please follow our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) guide to handle this user experience. +Please follow our [Post-Checkout Redirecting](/expo/guides/web-checkout/post-checkout-redirecting) guide to handle this user experience. If you're using Superwall to handle purchases, then you don't need to do anything here. -If you're using your own `PurchaseController`, you will need to update the subscription status with the redeemed web entitlements. If you're using RevenueCat, you should follow our [Using RevenueCat](/web-checkout-using-revenuecat) guide. +If you're using your own `PurchaseController`, you will need to update the subscription status with the redeemed web entitlements. If you're using RevenueCat, you should follow our [Using RevenueCat](/expo/guides/web-checkout/using-revenuecat) guide. ### Using a PurchaseController @@ -81,4 +81,4 @@ If you aren't using a Purchase Controller, the SDK will refresh the web entitlem ### Redeeming while a paywall is open -If a redeem event occurs when a paywall is open, the SDK will track that as a restore event and the paywall will close. \ No newline at end of file +If a redeem event occurs when a paywall is open, the SDK will track that as a restore event and the paywall will close. diff --git a/content/docs/expo/guides/web-checkout/post-checkout-redirecting.mdx b/content/docs/expo/guides/web-checkout/post-checkout-redirecting.mdx index 8e1ad631..721516e1 100644 --- a/content/docs/expo/guides/web-checkout/post-checkout-redirecting.mdx +++ b/content/docs/expo/guides/web-checkout/post-checkout-redirecting.mdx @@ -7,7 +7,7 @@ After a user completes a web purchase, Superwall needs to redirect them back to ## Post-Purchase Behavior Modes -You can configure how users are redirected after checkout in your [Application Settings](/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): +You can configure how users are redirected after checkout in your [Application Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): ### Redeem Mode (Default) @@ -65,15 +65,15 @@ You'll need to implement your own logic to handle the redirect and deep link use Whether you're showing a checkout page in a browser or using the In-App Browser, the Superwall SDK relies on deep links to redirect back to your app. #### Prerequisites -1. [Configuring Stripe Keys and Settings](/web-checkout-configuring-stripe-keys-and-settings) -2. [Deep Links](/in-app-paywall-previews) +1. [Configuring Stripe Keys and Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) +2. [Deep Links](/expo/quickstart/in-app-paywall-previews) If you're not using Superwall to handle purchases, then you'll need to follow extra steps to redeem the web purchase in your app. -- [Using RevenueCat](/web-checkout-using-revenuecat) -- [Using a PurchaseController](/web-checkout-linking-membership-to-iOS-app#using-a-purchasecontroller) +- [Using RevenueCat](/expo/guides/web-checkout/using-revenuecat) +- [Using a PurchaseController](/expo/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller) --- diff --git a/content/docs/expo/guides/web-checkout/using-revenuecat.mdx b/content/docs/expo/guides/web-checkout/using-revenuecat.mdx index ca0a867b..3c2a2efa 100644 --- a/content/docs/expo/guides/web-checkout/using-revenuecat.mdx +++ b/content/docs/expo/guides/web-checkout/using-revenuecat.mdx @@ -3,7 +3,7 @@ title: "Using RevenueCat" description: "Handle a deep link in your app and use the delegate methods to link web checkouts with RevenueCat." --- -After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. Please follow our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) guide to handle this user experience. +After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. Please follow our [Post-Checkout Redirecting](/expo/guides/web-checkout/post-checkout-redirecting) guide to handle this user experience. If you're using Superwall to handle purchases, then you don't need to do anything here. @@ -11,7 +11,7 @@ After purchasing from a web paywall, the user will be redirected to your app by You only need to use a `PurchaseController` if you want end-to-end control of the purchasing pipeline. The recommended way to use RevenueCat with Superwall is by putting it in observer mode. -If you're using your own `PurchaseController`, you should follow our [Redeeming In-App](/web-checkout-linking-membership-to-iOS-app) guide. +If you're using your own `PurchaseController`, you should follow our [Redeeming In-App](/expo/guides/web-checkout/linking-membership-to-iOS-app) guide. ### Using a PurchaseController with RevenueCat @@ -117,4 +117,4 @@ export class SWDelegate extends SuperwallDelegate { The web entitlements will be returned along with other existing entitlements in the `CustomerInfo` object accessible via RevenueCat's SDK. -If you're logging in and out of RevenueCat, make sure to resend the Stripe subscription IDs to RevenueCat's endpoint after logging in. \ No newline at end of file +If you're logging in and out of RevenueCat, make sure to resend the Stripe subscription IDs to RevenueCat's endpoint after logging in. diff --git a/content/docs/expo/quickstart/present-first-paywall.mdx b/content/docs/expo/quickstart/present-first-paywall.mdx index 73b2b7aa..add7f988 100644 --- a/content/docs/expo/quickstart/present-first-paywall.mdx +++ b/content/docs/expo/quickstart/present-first-paywall.mdx @@ -5,7 +5,7 @@ description: Learn how to present paywalls in your app. ## Placements -With Superwall, you present paywalls by registering a [Placement](/campaigns-placements). Placements are the configurable entry points to show (or not show) paywalls based on your [Campaigns](/campaigns) as setup in your Superwall dashboard. +With Superwall, you present paywalls by registering a [Placement](/dashboard/dashboard-campaigns/campaigns-placements). Placements are the configurable entry points to show (or not show) paywalls based on your [Campaigns](/dashboard/dashboard-campaigns/campaigns) as setup in your Superwall dashboard. The placement `campaign_trigger` is set to show an example paywall by default. diff --git a/content/docs/expo/quickstart/tracking-subscription-state.mdx b/content/docs/expo/quickstart/tracking-subscription-state.mdx index 336c9636..f4cb919d 100644 --- a/content/docs/expo/quickstart/tracking-subscription-state.mdx +++ b/content/docs/expo/quickstart/tracking-subscription-state.mdx @@ -186,7 +186,7 @@ function SubscriptionListener() { ## Superwall checks subscription status for you -Remember that the Superwall SDK uses its [audience filters](/campaigns-audience#matching-to-entitlements) for determining when to show paywalls. You generally don't need to wrap your calls to register placements with subscription status checks: +Remember that the Superwall SDK uses its [audience filters](/dashboard/dashboard-campaigns/campaigns-audience#matching-to-entitlements) for determining when to show paywalls. You generally don't need to wrap your calls to register placements with subscription status checks: ```tsx // ❌ Unnecessary @@ -198,4 +198,4 @@ if (subscriptionStatus?.status !== "ACTIVE") { await Superwall.shared.register({ placement: "campaign_trigger" }); ``` -In your [audience filters](/campaigns-audience#matching-to-entitlements), you can specify whether the subscription state should be considered, which keeps your codebase cleaner and puts the "Should this paywall show?" logic where it belongs—in the Superwall dashboard. +In your [audience filters](/dashboard/dashboard-campaigns/campaigns-audience#matching-to-entitlements), you can specify whether the subscription state should be considered, which keeps your codebase cleaner and puts the "Should this paywall show?" logic where it belongs—in the Superwall dashboard. diff --git a/content/docs/flutter/guides/web-checkout/linking-membership-to-iOS-app.mdx b/content/docs/flutter/guides/web-checkout/linking-membership-to-iOS-app.mdx index 0b0630d9..88efebb8 100644 --- a/content/docs/flutter/guides/web-checkout/linking-membership-to-iOS-app.mdx +++ b/content/docs/flutter/guides/web-checkout/linking-membership-to-iOS-app.mdx @@ -4,13 +4,13 @@ description: "Handle a deep link in your app and use the delegate methods." --- After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. -Please follow our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) guide to handle this user experience. +Please follow our [Post-Checkout Redirecting](/flutter/guides/web-checkout/post-checkout-redirecting) guide to handle this user experience. If you're using Superwall to handle purchases, then you don't need to do anything here. -If you're using your own `PurchaseController`, you will need to update the subscription status with the redeemed web entitlements. If you're using RevenueCat, you should follow our [Using RevenueCat](/web-checkout-using-revenuecat) guide. +If you're using your own `PurchaseController`, you will need to update the subscription status with the redeemed web entitlements. If you're using RevenueCat, you should follow our [Using RevenueCat](/flutter/guides/web-checkout/using-revenuecat) guide. ### Using a PurchaseController @@ -90,4 +90,4 @@ If you aren't using a Purchase Controller, the SDK will refresh the web entitlem ### Redeeming while a paywall is open -If a redeem event occurs when a paywall is open, the SDK will track that as a restore event and the paywall will close. \ No newline at end of file +If a redeem event occurs when a paywall is open, the SDK will track that as a restore event and the paywall will close. diff --git a/content/docs/flutter/guides/web-checkout/post-checkout-redirecting.mdx b/content/docs/flutter/guides/web-checkout/post-checkout-redirecting.mdx index baf1c6b0..8d4272c2 100644 --- a/content/docs/flutter/guides/web-checkout/post-checkout-redirecting.mdx +++ b/content/docs/flutter/guides/web-checkout/post-checkout-redirecting.mdx @@ -7,7 +7,7 @@ After a user completes a web purchase, Superwall needs to redirect them back to ## Post-Purchase Behavior Modes -You can configure how users are redirected after checkout in your [Application Settings](/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): +You can configure how users are redirected after checkout in your [Application Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): ### Redeem Mode (Default) @@ -49,15 +49,15 @@ You'll need to implement your own logic to handle the redirect and deep link use Whether you're showing a checkout page in a browser or using the In-App Browser, the Superwall SDK relies on deep links to redirect back to your app. #### Prerequisites -1. [Configuring Stripe Keys and Settings](/web-checkout-configuring-stripe-keys-and-settings) -2. [Deep Links](/in-app-paywall-previews) +1. [Configuring Stripe Keys and Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) +2. [Deep Links](/flutter/quickstart/in-app-paywall-previews) If you're not using Superwall to handle purchases, then you'll need to follow extra steps to redeem the web purchase in your app. -- [Using RevenueCat](/web-checkout-using-revenuecat) -- [Using a PurchaseController](/web-checkout-linking-membership-to-iOS-app#using-a-purchasecontroller) +- [Using RevenueCat](/flutter/guides/web-checkout/using-revenuecat) +- [Using a PurchaseController](/flutter/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller) --- @@ -189,4 +189,4 @@ class _MyAppState extends State { ); } } -``` \ No newline at end of file +``` diff --git a/content/docs/flutter/guides/web-checkout/using-revenuecat.mdx b/content/docs/flutter/guides/web-checkout/using-revenuecat.mdx index 1d268dd6..19eb70d1 100644 --- a/content/docs/flutter/guides/web-checkout/using-revenuecat.mdx +++ b/content/docs/flutter/guides/web-checkout/using-revenuecat.mdx @@ -3,7 +3,7 @@ title: "Using RevenueCat" description: "Handle a deep link in your app and use the delegate methods to link web checkouts with RevenueCat in Flutter." --- -After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. Please follow our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) guide to handle this user experience. +After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. Please follow our [Post-Checkout Redirecting](/flutter/guides/web-checkout/post-checkout-redirecting) guide to handle this user experience. If you're using Superwall to handle purchases, then you don't need to do anything here. @@ -11,7 +11,7 @@ After purchasing from a web paywall, the user will be redirected to your app by You only need to use a `PurchaseController` if you want end-to-end control of the purchasing pipeline. The recommended way to use RevenueCat with Superwall is by putting it in observer mode. -If you're using your own `PurchaseController`, you should follow our [Redeeming In-App](/web-checkout-linking-membership-to-iOS-app) guide. +If you're using your own `PurchaseController`, you should follow our [Redeeming In-App](/flutter/guides/web-checkout/linking-membership-to-iOS-app) guide. ### Using a PurchaseController with RevenueCat diff --git a/content/docs/flutter/quickstart/tracking-subscription-state.mdx b/content/docs/flutter/quickstart/tracking-subscription-state.mdx index 3a5b9783..f59c79c2 100644 --- a/content/docs/flutter/quickstart/tracking-subscription-state.mdx +++ b/content/docs/flutter/quickstart/tracking-subscription-state.mdx @@ -297,7 +297,7 @@ Future checkSubscriptionExpiry() async { ## Superwall checks subscription status for you -Remember that the Superwall SDK uses its [audience filters](/campaigns-audience#matching-to-entitlements) for determining when to show paywalls. You generally don't need to wrap your calls to register placements with subscription status checks: +Remember that the Superwall SDK uses its [audience filters](/dashboard/dashboard-campaigns/campaigns-audience#matching-to-entitlements) for determining when to show paywalls. You generally don't need to wrap your calls to register placements with subscription status checks: ```dart // ❌ Unnecessary @@ -311,4 +311,4 @@ final subscription = Superwall.shared.subscriptionStatus.listen((status) { Superwall.shared.registerPlacement('campaign_trigger'); ``` -In your [audience filters](/campaigns-audience#matching-to-entitlements), you can specify whether the subscription state should be considered, which keeps your codebase cleaner and puts the "Should this paywall show?" logic where it belongs—in the Superwall dashboard. +In your [audience filters](/dashboard/dashboard-campaigns/campaigns-audience#matching-to-entitlements), you can specify whether the subscription state should be considered, which keeps your codebase cleaner and puts the "Should this paywall show?" logic where it belongs—in the Superwall dashboard. diff --git a/content/docs/getting-started-with-our-sdks.mdx b/content/docs/getting-started-with-our-sdks.mdx index a64f37f7..a6801fea 100644 --- a/content/docs/getting-started-with-our-sdks.mdx +++ b/content/docs/getting-started-with-our-sdks.mdx @@ -12,14 +12,14 @@ With our latest iOS SDK (version 4) and updates for Android, Flutter, and React To see migration information, check out these guides for all of our SDKs: -- [iOS](/migrating-to-v4) -- [Android](/migrating-to-v2-android) -- [React Native](/migrating-to-v2-react-native) -- [Flutter](/migrating-to-v2-flutter) +- [iOS](/ios/guides/migrations/migrating-to-v4) +- [Android](/android/guides/migrations/migrating-to-v2) +- [React Native](/expo/guides/migrating-react-native) +- [Flutter](/flutter/guides/migrations/migrating-to-v2) ### Entitlements -Products are now attached to an entitlement. By default, we provide an entitlement out of the box — and products can use one or more of them. If you are not using a [purchase controller](/advanced-configuration) or tiered services, then you don't have to think much about them. From an SDK standpoint, tracking subscription state worked similar to this: +Products are now attached to an entitlement. By default, we provide an entitlement out of the box — and products can use one or more of them. If you are not using a [purchase controller](/sdk/guides/advanced-configuration) or tiered services, then you don't have to think much about them. From an SDK standpoint, tracking subscription state worked similar to this: ```swift if Superwall.shared.subscriptionState == .active/inactive { @@ -49,7 +49,7 @@ if Superwall.shared.subscriptionStatus.isActive{ } ``` -Also, common [delegate](/using-superwall-delegate) methods have changed as well. As the migration guides above call out, `event` has been renamed to `placement`, so you'll see that reflected across our product and SDKs. +Also, common [delegate](/sdk/guides/using-superwall-delegate) methods have changed as well. As the migration guides above call out, `event` has been renamed to `placement`, so you'll see that reflected across our product and SDKs. With entitlements, the paywall editor setting "Present Paywall" is now deprecated. Entitlements @@ -70,7 +70,7 @@ As mentioned above, products are now associated to an entitlement. When adding p ![](/images/productEntitlement.png) -For more, check out our docs on [adding products](/products). +For more, check out our docs on [adding products](/dashboard/products). ### StoreKit 2 @@ -87,7 +87,7 @@ let result = await Superwall.shared.purchase(product) let result = await Superwall.shared.purchase(storeProduct) ``` -The ability to make purchases directly along with [observer mode](/observer-mode) means that you can: +The ability to make purchases directly along with [observer mode](/sdk/guides/advanced/observer-mode) means that you can: - Have Superwall handle all purchase logic. - Track your revenue diff --git a/content/docs/integrations/apple-search-ads.mdx b/content/docs/integrations/apple-search-ads.mdx index 525e7df6..a5f8935e 100644 --- a/content/docs/integrations/apple-search-ads.mdx +++ b/content/docs/integrations/apple-search-ads.mdx @@ -18,7 +18,7 @@ If you're only using basic search ads, **click** the toggle next to **Basic Appl ![](/images/overview-settings-asa-basic.jpeg) -That's it, you're all set. With basic Apple Search Ads enabled, you'll be to see users acquired via search ads in the [users page](/overview-users). +That's it, you're all set. With basic Apple Search Ads enabled, you'll be to see users acquired via search ads in the [users page](/dashboard/overview-users). To see what you can do with advanced search ads data, skip down to the [use cases](#use-cases) section. @@ -79,7 +79,7 @@ If you're using advanced search ads, you get significantly more capabilities: #### Viewing users acquired via Apple Search Ads -If any user was acquired via a search ad, you'll see that data in the [users page](/overview-users). This can be useful for understanding the quality of users acquired from search ads: +If any user was acquired via a search ad, you'll see that data in the [users page](/dashboard/overview-users). This can be useful for understanding the quality of users acquired from search ads: ![](/images/overivew-settings-asa-user.png) @@ -104,7 +104,7 @@ Here's a breakdown of the attributes you'll see: #### Using search ad data in campaigns -Using the table above, you can turn around and use any of those values to create [campaign filters](/campaigns-audience#filters): +Using the table above, you can turn around and use any of those values to create [campaign filters](/dashboard/dashboard-campaigns/campaigns-audience#filters): ![](/images/overview-settings-asa-filters.png) @@ -116,7 +116,7 @@ Using the table above, you can turn around and use any of those values to create #### Charts -Use data from Apple Search Ads in our [charts](/charts) as a breakdown and filter: +Use data from Apple Search Ads in our [charts](/dashboard/charts) as a breakdown and filter: ![](/images/asa-chart-breakdowns.png) diff --git a/content/docs/integrations/webhooks/index.mdx b/content/docs/integrations/webhooks/index.mdx index 8740749a..1edcc12a 100644 --- a/content/docs/integrations/webhooks/index.mdx +++ b/content/docs/integrations/webhooks/index.mdx @@ -602,7 +602,7 @@ The `originalTransactionId` is Apple's terminology that acts like a subscription ### User Attributes -Any attributes you set using the [User Attributes API](/docs/ios/quickstart/setting-user-properties) are automatically included in your webhook payloads. This lets you correlate webhook events with your own user data. +Any attributes you set using the [User Attributes API](/docs/sdk/quickstart/setting-user-properties) are automatically included in your webhook payloads. This lets you correlate webhook events with your own user data. For example, you could identify which user made a purchase or had a subscription expire. diff --git a/content/docs/interactive-paywall-preview.mdx b/content/docs/interactive-paywall-preview.mdx index ad84fd7a..71ef6976 100644 --- a/content/docs/interactive-paywall-preview.mdx +++ b/content/docs/interactive-paywall-preview.mdx @@ -5,7 +5,7 @@ description: "The interactive paywall preview shows how your paywall looks on ce The legacy editor is deprecated. Please visit the docs covering our new - [editor](/paywall-editor-overview). + [editor](/dashboard/dashboard-creating-paywalls/paywall-editor-overview). ![1746](/images/4.png) @@ -51,13 +51,13 @@ The following objects can be referenced and templated within your paywalls: | primary | Product information relating to the _primary product_ defined in the _Product section_ | `{{primary.trialPeriodDays}`} days free then only `{{primary.price}}` per `{{primary.period}} ` | 7 days free then only $89.99 per year | | secondary | Product information relating to the _secondary product_ defined in the _Product section_ | Subscribe for only `{{secondary.price}}` per `{{secondary.period}}` | Subscribe for only $4.99 per month | | tertiary | Product information relating to the _tertiary product_ defined in the _Product section_ | That's only `{{tertiary.weeklyPrice}}` per week! | That's only $2.49 per week! | -| user | User attributes your SDK implementation sets on the user. See [Setting User Attributes](/setting-user-properties) | Hey `{{user.firstName}}`! FitnessAI offers tons of `{{user.fitnessGoal}}` workouts to help you reach your goals :) | Hey Sam! FitnessAI offers tons of calorie burning workouts to help you reach your goals :) | -| params | Parameters defined when triggering a paywall. See [Showing Paywalls](/feature-gating). | Oh no, you lost! The secret word was `{{params.gameAnswer}}`. Start a free trial to play again! | Oh no, you lost! The secret word was MONEY. Start a free trial to play again! | +| user | User attributes your SDK implementation sets on the user. See [Setting User Attributes](/sdk/quickstart/setting-user-properties) | Hey `{{user.firstName}}`! FitnessAI offers tons of `{{user.fitnessGoal}}` workouts to help you reach your goals :) | Hey Sam! FitnessAI offers tons of calorie burning workouts to help you reach your goals :) | +| params | Parameters defined when triggering a paywall. See [Showing Paywalls](/sdk/quickstart/feature-gating). | Oh no, you lost! The secret word was `{{params.gameAnswer}}`. Start a free trial to play again! | Oh no, you lost! The secret word was MONEY. Start a free trial to play again! | | device | Device attributes automatically created on device. | Compatible with your `{{ device.deviceModel }`} | Compatible with your iPhone 14 Pro | You can do complex math on these variables using liquid. [Take a look at their documentation](https://shopify.github.io/liquid) for more on how to do that. -Additionally, you can use the following device properties: `device.minutesSince_X`, `device.hoursSince_X`, `device.daysSince_X`, `device.monthsSince_X`, and `device.yearsSince_X`, where X is the name of an event that you've [registered](/feature-gating) or a [Superwall event](/tracking-analytics). This gives you the days etc since the last occurrence of the event that you specify, excluding the event that triggered the paywall. For example, a paywall presented via an `app_open` event and the text It has been `{{ device.daysSince_app_open}}` since you last opened the app will show `{{ It has been 2 days since you last opened the app }}`. +Additionally, you can use the following device properties: `device.minutesSince_X`, `device.hoursSince_X`, `device.daysSince_X`, `device.monthsSince_X`, and `device.yearsSince_X`, where X is the name of an event that you've [registered](/sdk/quickstart/feature-gating) or a [Superwall event](/sdk/guides/3rd-party-analytics/tracking-analytics). This gives you the days etc since the last occurrence of the event that you specify, excluding the event that triggered the paywall. For example, a paywall presented via an `app_open` event and the text It has been `{{ device.daysSince_app_open}}` since you last opened the app will show `{{ It has been 2 days since you last opened the app }}`. ### AI Powered Suggestions @@ -76,12 +76,12 @@ You choose from the following types of click behavior: | Click Behavior | Functionality | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Do nothing | This won't do anything if the user taps on it. | -| Open deep link | This will open a deep link. After selecting this you can specify the link to use. We recommend sending deep links to the SDK: [Deep Links & In-App Previews](/in-app-paywall-previews). | +| Open deep link | This will open a deep link. After selecting this you can specify the link to use. We recommend sending deep links to the SDK: [Deep Links & In-App Previews](/sdk/quickstart/in-app-paywall-previews). | | Open URL | This will open a URL from within the paywall. | | Open URL externally | This will open the provided URL in the user's browser. | | Close the paywall | Closes the paywall. | -| Restore | Restores purchases via the restorePurchases(completion:) [delegate method](/configuring-the-sdk#conforming-to-the-delegate). | -| Custom action | Sends the custom action name you provide to the SDK's delegate method handleCustomPaywallAction(withName:). You can use this to perform custom logic from your app as discussed in [Custom Paywall Actions](/custom-paywall-events). | +| Restore | Restores purchases via the restorePurchases(completion:) [delegate method](/sdk/quickstart/configure#conforming-to-the-delegate). | +| Custom action | Sends the custom action name you provide to the SDK's delegate method handleCustomPaywallAction(withName:). You can use this to perform custom logic from your app as discussed in [Custom Paywall Actions](/sdk/guides/advanced/custom-paywall-actions). | | Purchase primary | Initiates the delegate method purchase(product:) with your primary product identifier. | | Purchase secondary | Initiates the delegate method purchase(product:) with your secondary product identifier. | | Purchase tertiary | Initiates the delegate method purchase(product:) with your tertiary product identifier. | diff --git a/content/docs/ios/guides/intro-offer-eligibility-override.mdx b/content/docs/ios/guides/intro-offer-eligibility-override.mdx index 762dfc09..195964db 100644 --- a/content/docs/ios/guides/intro-offer-eligibility-override.mdx +++ b/content/docs/ios/guides/intro-offer-eligibility-override.mdx @@ -14,7 +14,7 @@ This feature is configured entirely through the Paywall Editor in the Superwall - **iOS SDK:** Version 4.11.0 or later - **Platform:** iOS 18.2+ only (App Store products) - **Xcode Version:** 16.3+ -- You must have set up the [App Store Connect API](/overview-settings-revenue-tracking#app-store-connect-api) and the [In App Purchase Configuration](/overview-settings-revenue-tracking#in-app-purchase-configuration). +- You must have set up the [App Store Connect API](/dashboard/dashboard-settings/overview-settings-revenue-tracking#app-store-connect-api) and the [In App Purchase Configuration](/dashboard/dashboard-settings/overview-settings-revenue-tracking#in-app-purchase-configuration). ## If you're using a `PurchaseController` diff --git a/content/docs/ios/guides/migrations/migrating-to-v3.mdx b/content/docs/ios/guides/migrations/migrating-to-v3.mdx index e7dea79d..d3b47e4d 100644 --- a/content/docs/ios/guides/migrations/migrating-to-v3.mdx +++ b/content/docs/ios/guides/migrations/migrating-to-v3.mdx @@ -65,7 +65,7 @@ You'll see errors saying `Cannot find 'Paywall' in scope`. This is because the m Previously you'd use `Paywall.track(...)` to implicitly trigger a paywall, and `Paywall.trigger(...)` to explicitly trigger a paywall. This was confusing as they essentially did the same thing. `Paywall.track` provided completion blocks for what happened on the paywall when really you needed to know what to do next. We wanted to make this simpler so at the heart of this release is `Superwall.shared.register(event:params:handler:feature:)`. This allows you to register an event to access a feature that may or may not be paywalled later in time. It also allows you to choose whether the user can access the feature even if they don't make a purchase. -You can read our docs on [how register works](/docs/feature-gating) to learn more. +You can read our docs on [how register works](/ios/quickstart/feature-gating) to learn more. Given the low cost nature of how register works, we strongly recommend wrapping all core functionality in a register `feature` block in order to remotely configure which features you want to gate – without an app update. @@ -85,7 +85,7 @@ This has a `SuperwallEventInfo` parameter. This has a `params` dictionary and an SuperwallKit now handles all subscription-related logic by default making integration super easy. We track the user's subscription status for you and expose the published property `Superwall.shared.subscriptionStatus`. This means that if you were previously using StoreKit you can simply delete that code and let SuperwallKit handle it. -However, if you're using RevenueCat or still want to keep control over subscription-related logic, you'll need to conform to the `PurchaseController` protocol. This is a protocol that handles purchasing and restoring, much like the `PaywallDelegate` did in v2.x of the SDK. You set the purchase controller when you configure the SDK. You can read more about that in our [Purchases and Subscription Status](/docs/advanced-configuration) guide. +However, if you're using RevenueCat or still want to keep control over subscription-related logic, you'll need to conform to the `PurchaseController` protocol. This is a protocol that handles purchasing and restoring, much like the `PaywallDelegate` did in v2.x of the SDK. You set the purchase controller when you configure the SDK. You can read more about that in our [Purchases and Subscription Status](/ios/guides/advanced-configuration) guide. The following methods were previously in the `PaywallDelegate` but are now in the `PurchaseController` and have changed slightly: @@ -113,7 +113,7 @@ This has changed to an async function that returns the result of restoring a pur `isUserSubscribed()` has been removed in favor of a `subscriptionStatus` variable which you **must** set every time the user's subscription status changes. On first app install this starts off as `.unknown` until you determine the user's subscription status and set it to `.active` when they have an active subscription, or `.inactive` when they don't. Paywalls will not show until the user's subscription status is set. -You can [check out our docs](/docs/advanced-configuration) for detailed info about implementing the `PurchaseController`. +You can [check out our docs](/ios/guides/advanced-configuration) for detailed info about implementing the `PurchaseController`. ### 2.5 Rename `PaywallOptions` to `SuperwallOptions` @@ -129,7 +129,7 @@ When configuring the API, you now no longer provide a userId or delegate. To use the optional delegate, set `Superwall.shared.delegate`. To identify a user, use `Superwall.shared.identify(userId:options:)`. -You can [read more](/docs/identity-management) about identity management in our docs. +You can [read more](/ios/quickstart/user-management) about identity management in our docs. ## 3\. Check out the full change log diff --git a/content/docs/ios/guides/test-mode.mdx b/content/docs/ios/guides/test-mode.mdx index fae6eb6b..61643813 100644 --- a/content/docs/ios/guides/test-mode.mdx +++ b/content/docs/ios/guides/test-mode.mdx @@ -85,4 +85,4 @@ When a restore is triggered while test mode is active, a drawer appears letting | **Transactions** | Simulated via UI drawer | Real StoreKit transactions in a sandbox | | **Best for** | End-to-end paywall flow testing, verifying entitlement gating, testing without App Store Connect setup | Testing real StoreKit behavior, receipt validation, subscription lifecycle | -Test mode is ideal for quickly validating your paywall presentation, purchase flows, and entitlement gating without any StoreKit setup. For testing actual StoreKit behavior, use [StoreKit testing in Xcode](/testing-purchases) instead. +Test mode is ideal for quickly validating your paywall presentation, purchase flows, and entitlement gating without any StoreKit setup. For testing actual StoreKit behavior, use [StoreKit testing in Xcode](/ios/guides/testing-purchases) instead. diff --git a/content/docs/layout-tab.mdx b/content/docs/layout-tab.mdx index 9bc3e039..f6f58c0c 100644 --- a/content/docs/layout-tab.mdx +++ b/content/docs/layout-tab.mdx @@ -4,7 +4,7 @@ title: "Layout Tab" The legacy editor is deprecated. Please visit the docs covering our new - [editor](/paywall-editor-overview). + [editor](/dashboard/dashboard-creating-paywalls/paywall-editor-overview). If you want to customise the layout of your paywall, you can use the Layout tab: diff --git a/content/docs/legacy/legacy_cohorting-in-3rd-party-tools.mdx b/content/docs/legacy/legacy_cohorting-in-3rd-party-tools.mdx index 6f47e770..32130292 100644 --- a/content/docs/legacy/legacy_cohorting-in-3rd-party-tools.mdx +++ b/content/docs/legacy/legacy_cohorting-in-3rd-party-tools.mdx @@ -86,7 +86,7 @@ For example, in the paywall below, perhaps you're interested in tracking when pe ![](/../images/3pa_cp_2.jpeg) -You could create a custom placement [tap behavior](/paywall-editor-styling-elements#tap-behaviors) which fires when a segment is tapped: +You could create a custom placement [tap behavior](/dashboard/dashboard-creating-paywalls/paywall-editor-styling-elements#tap-behaviors) which fires when a segment is tapped: ![](/../images/3pa_cp_1.jpeg) diff --git a/content/docs/legacy/legacy_feature-gating.mdx b/content/docs/legacy/legacy_feature-gating.mdx index d0f2f822..4249659e 100644 --- a/content/docs/legacy/legacy_feature-gating.mdx +++ b/content/docs/legacy/legacy_feature-gating.mdx @@ -4,7 +4,7 @@ description: "At the heart of Superwall's SDK lies `Superwall.shared.register(ev sidebarTitle: "Overview" --- -This allows you to register a [placement](/campaigns-placements) to access a feature that may or may not be paywalled later in time. It also allows you to choose whether the user can access the feature even if they don't make a purchase. +This allows you to register a [placement](/dashboard/dashboard-campaigns/campaigns-placements) to access a feature that may or may not be paywalled later in time. It also allows you to choose whether the user can access the feature even if they don't make a purchase. Here's an example. @@ -153,7 +153,7 @@ function pressedWorkoutButton() { ### How it works: -You can configure `"StartWorkout"` to present a paywall by [creating a campaign, adding the placement, and adding a rule](/campaigns) in the dashboard. +You can configure `"StartWorkout"` to present a paywall by [creating a campaign, adding the placement, and adding a rule](/dashboard/dashboard-campaigns/campaigns) in the dashboard. 1. The SDK retrieves your campaign settings from the dashboard on app launch. 2. When a placement is called that belongs to a campaign, rules are evaluated _**on device**_ and the user enters an experiment — this means there's no delay between registering a placement and presenting a paywall. @@ -302,19 +302,19 @@ Superwall.shared.register('caffeineLogged', -The `via` parameter could now be used all throughout Superwall. You could create a new [audience](/campaigns-audience) which has filters for each place users logged caffeine from, and unique paywalls for each of them. +The `via` parameter could now be used all throughout Superwall. You could create a new [audience](/dashboard/dashboard-campaigns/campaigns-audience) which has filters for each place users logged caffeine from, and unique paywalls for each of them. Parameter placements can be used in three primary ways: 1. **Audience Filtering:** As mentioned above, you can filter against parameters when creating audiences. Following our example, you'd create a **placement parameter** named **via** and then choose how to filter off of the parameter's value: ![](/../images/placementParamVia.png) -2. **Templating in Text:** Parameters are available in our [paywall editor](/paywall-editor-overview), so you can easily use them in text components too: +2. **Templating in Text:** Parameters are available in our [paywall editor](/dashboard/dashboard-creating-paywalls/paywall-editor-overview), so you can easily use them in text components too: ``` Hey {{user.firstName}}! FitnessAI offers tons of {{user.fitnessGoal}} workouts to help you reach your goals :) ``` -3. **Interfacing with Analytics:** Another common scenario is cohorting with your own analytics. See this [doc](/cohorting-in-3rd-party-tools) for more. +3. **Interfacing with Analytics:** Another common scenario is cohorting with your own analytics. See this [doc](/sdk/guides/3rd-party-analytics/cohorting-in-3rd-party-tools) for more. ### Feature Gating from the Paywall Editor @@ -524,11 +524,11 @@ Superwall.shared.register("campaign_trigger", undefined, handler).then(() => { -Wanting to see which product was just purchased from a paywall? Use the [SuperwallDelegate](/3rd-party-analytics#using-events-to-see-purchased-products). +Wanting to see which product was just purchased from a paywall? Use the [SuperwallDelegate](/legacy/legacy_3rd-party-analytics#using-events-to-see-purchased-products). ### Automatically Registered Placements -The SDK [automatically registers](/docs/tracking-analytics) some internal placements which can be used to present paywalls: +The SDK [automatically registers](/legacy/legacy_tracking-analytics) some internal placements which can be used to present paywalls: - `app_install` - `app_launch` diff --git a/content/docs/legacy/legacy_identity-management.mdx b/content/docs/legacy/legacy_identity-management.mdx index ab83dfd1..ffdc4bc2 100644 --- a/content/docs/legacy/legacy_identity-management.mdx +++ b/content/docs/legacy/legacy_identity-management.mdx @@ -83,7 +83,7 @@ On iOS, Superwall will set the value from `identify(userId:options:)` as the `ap - + Begin showing paywalls! diff --git a/content/docs/legacy/legacy_in-app-paywall-previews.mdx b/content/docs/legacy/legacy_in-app-paywall-previews.mdx index f90c0bdb..9d1cc67a 100644 --- a/content/docs/legacy/legacy_in-app-paywall-previews.mdx +++ b/content/docs/legacy/legacy_in-app-paywall-previews.mdx @@ -5,7 +5,7 @@ description: "It's important to tell Superwall when a deep link has been opened. --- 1. Previewing paywalls on your device before going live. -2. Deep linking to specific [campaigns](/campaigns). +2. Deep linking to specific [campaigns](/dashboard/dashboard-campaigns/campaigns). diff --git a/content/docs/legacy/legacy_pre-launch-checklist.mdx b/content/docs/legacy/legacy_pre-launch-checklist.mdx index dce12657..0548421c 100644 --- a/content/docs/legacy/legacy_pre-launch-checklist.mdx +++ b/content/docs/legacy/legacy_pre-launch-checklist.mdx @@ -13,7 +13,7 @@ description: "Ready to ship your app with Superwall? Here is a last minute check Set up your products in their respective storefront first, whether that's App Store Connect or Google Play. Once you've done that, add them into Superwall. All of their respective identifiers should match what they are in each storefront. For more details, refer to this - [page](/products). + [page](/dashboard/products). Each paywall should display one or more of those previously added products. You can associate @@ -49,7 +49,7 @@ These aren't essential, but they are good to think about to make sure you're lev No matter how much you optimize flows, designs or copy — the truth is that, statistically speaking, the majority of users will not convert. Finding out why is key, and you can do that with our surveys that you can attach to any paywall. - Once a user closes a paywall, we'll present the survey attached to it. See how to set them up [here](/surveys). + Once a user closes a paywall, we'll present the survey attached to it. See how to set them up [here](/dashboard/surveys).
diff --git a/content/docs/legacy/legacy_presenting-paywalls-from-one-another.mdx b/content/docs/legacy/legacy_presenting-paywalls-from-one-another.mdx index e0cbac5a..d15fa639 100644 --- a/content/docs/legacy/legacy_presenting-paywalls-from-one-another.mdx +++ b/content/docs/legacy/legacy_presenting-paywalls-from-one-another.mdx @@ -6,7 +6,7 @@ description: "Learn how to present a different paywall from one that's already p It's possible to present another paywall from one already showing. This can be useful if you want to highlight a special discount, offer, or emphasize another feature more effectively using a different paywall. Check out the example here: -- A [placement](/campaigns-placements) is evaluated when the button is tapped. Superwall sees that the user isn't subscribed, so a paywall is shown. +- A [placement](/dashboard/dashboard-campaigns/campaigns-placements) is evaluated when the button is tapped. Superwall sees that the user isn't subscribed, so a paywall is shown. - Next, the user taps the "Custom Icons Too 👀" button. - The current paywall dismisses, and then presents the icons-centric paywall. @@ -15,10 +15,10 @@ It's possible to present another paywall from one already showing. This can be u You can extend this technique to be used with several other interesting standard placements. For example, presenting a paywall when the user abandons a transaction, responds to a survey and more. - Check out the examples [here](/campaigns-standard-placements#standard-placements). + Check out the examples [here](/dashboard/dashboard-campaigns/campaigns-standard-placements#standard-placements). -There are two different ways you can do this, with [custom placements](/paywall-editor-styling-elements#tap-behaviors) or by using [deep links](/legacy/legacy_in-app-paywall-previews). We recommend using custom placements, as the setup is a little easier. +There are two different ways you can do this, with [custom placements](/dashboard/dashboard-creating-paywalls/paywall-editor-styling-elements#tap-behaviors) or by using [deep links](/legacy/legacy_in-app-paywall-previews). We recommend using custom placements, as the setup is a little easier. Custom placements minimum SDK requirements are 3.7.3 for iOS, 1.2.4 for Android, 1.2.2 for @@ -30,7 +30,7 @@ They both have the same idea, though. You create a new campaign specifically for While it's not _required_ to make a new campaign, it is best practice. Then, if you later have other paywalls you wish to open in the same manner, you can simply add a new - [audience](/campaigns-audience) for them in the campaign you make from the steps below. + [audience](/dashboard/dashboard-campaigns/campaigns-audience) for them in the campaign you make from the steps below. ### Use Custom Placements @@ -45,13 +45,13 @@ They both have the same idea, though. You create a new campaign specifically for
- Create a new [campaign](/campaigns) specifically for this purpose, here — it's called "Custom Placement Example": + Create a new [campaign](/dashboard/dashboard-campaigns/campaigns) specifically for this purpose, here — it's called "Custom Placement Example": ![](/../images/customPlacementCampaign.png) - In your new campaign, [add a new placement](/campaigns-placements#adding-a-placement) that matches the name of your custom action you added in step one. For us, that's `showIconPaywall`: + In your new campaign, [add a new placement](/dashboard/dashboard-campaigns/campaigns-placements#adding-a-placement) that matches the name of your custom action you added in step one. For us, that's `showIconPaywall`:
![](/../images/customPlacementCreate.png)
@@ -87,13 +87,13 @@ They both have the same idea, though. You create a new campaign specifically for
- Create a new [campaign](/campaigns) specifically for this purpose, here — it's called "Deeplink Example": + Create a new [campaign](/dashboard/dashboard-campaigns/campaigns) specifically for this purpose, here — it's called "Deeplink Example": ![](/../images/openPaywallCampaign.png) - In your new campaign, [add a placement](/campaigns-placements#adding-a-placement) for the `deepLink_open` standard placement: + In your new campaign, [add a placement](/dashboard/dashboard-campaigns/campaigns-placements#adding-a-placement) for the `deepLink_open` standard placement:
![](/../images/openPaywallPlacement.png)
diff --git a/content/docs/legacy/legacy_setting-user-properties.mdx b/content/docs/legacy/legacy_setting-user-properties.mdx index ab2bd42a..93d931e9 100644 --- a/content/docs/legacy/legacy_setting-user-properties.mdx +++ b/content/docs/legacy/legacy_setting-user-properties.mdx @@ -1,7 +1,7 @@ --- title: "Setting User Attributes (Legacy)" sidebarTitle: "Setting User Attributes" -description: "By setting user attributes, you can display information about the user on the paywall. You can also define [audiences](/campaigns-audience) in a campaign to determine which paywall to show to a user, based on their user attributes." +description: "By setting user attributes, you can display information about the user on the paywall. You can also define [audiences](/dashboard/dashboard-campaigns/campaigns-audience) in a campaign to determine which paywall to show to a user, based on their user attributes." --- You do this by passing a `[String: Any?]` dictionary of attributes to `Superwall.shared.setUserAttributes(_:)`: @@ -74,13 +74,13 @@ already has a value for a given property, the old value is overwritten. Other existing properties will not be affected. To unset/delete a value, you can pass `nil` for the value. -You can reference user attributes in [campaign filters](/campaigns-audience) to help decide when to display your paywall. When you configure your paywall, you can also reference the user attributes in its text variables. For more information on how to that, see [Configuring a Paywall](/paywall-editor-overview). +You can reference user attributes in [campaign filters](/dashboard/dashboard-campaigns/campaigns-audience) to help decide when to display your paywall. When you configure your paywall, you can also reference the user attributes in its text variables. For more information on how to that, see [Configuring a Paywall](/dashboard/dashboard-creating-paywalls/paywall-editor-overview). In the future, you'll be able to use user attributes to email/notify users about discounts. - + Begin showing paywalls! diff --git a/content/docs/legacy/legacy_tracking-analytics.mdx b/content/docs/legacy/legacy_tracking-analytics.mdx index b0da9ff7..36b4bf60 100644 --- a/content/docs/legacy/legacy_tracking-analytics.mdx +++ b/content/docs/legacy/legacy_tracking-analytics.mdx @@ -17,7 +17,7 @@ The following Superwall events can be used as triggers to present paywalls: - `transaction_abandon` - `survey_response` -For more info about how to use these, check out [how to add them using a Placement](/campaigns-placements#adding-a-placement). +For more info about how to use these, check out [how to add them using a Placement](/dashboard/dashboard-campaigns/campaigns-placements#adding-a-placement). The full list of events is as follows: @@ -38,7 +38,7 @@ The full list of events is as follows: | transaction_restore | When the user successfully restores their purchases | Same as subscription_start | | transaction_complete | When the user completes checkout in the payment sheet and any product was "purchased" | Same as subscription_start \+ \["web_order_line_item_id": String, "app_bundle_id": String, "config_request_id": String, "state": String, "subscription_group_id": String, "is_upgraded": String, "expiration_date": String, "trigger_session_id": String, "original_transaction_identifier": String, "id": String, "transaction_date": String, "is_superwall": true, "store_transaction_id": String, "original_transaction_date": String, "app_session_id": String\] | | paywall_close | When a paywall is closed (either by user interaction or do to a transaction succeeding) | \["paywall_webview_load_complete_time": String?, "paywall_url": String, "paywall_response_load_start_time": String?, "paywall_products_load_fail_time": String?, "secondary_product_id": String, "feature_gating": Int, "paywall_response_load_complete_time": String?, "is_free_trial_available": Bool, "is_superwall": true, "presented_by": String, "paywall_name": String, "paywall_response_load_duration": String?, "paywall_identifier": String, "paywall_webview_load_start_time": String?, "paywall_products_load_complete_time": String?, "paywall_product_ids": String, "tertiary_product_id": String, "paywall_id": String, "app_session_id": String, "paywall_products_load_start_time": String?, "primary_product_id": String, "survey_attached": Bool, "survey_presentation": String?\] | -| [paywall_decline](/campaigns-placements#implicit-placements) | When a user manually dismisses a paywall. | Same as paywall_close | +| [paywall_decline](/dashboard/dashboard-campaigns/campaigns-placements#implicit-placements) | When a user manually dismisses a paywall. | Same as paywall_close | | paywall_open | When a paywall is opened | Same as paywall_close | | paywallWebviewLoad_start | When a paywall's URL begins to load | Same as paywall_close | | paywallWebviewLoad_fail | When a paywall's URL fails to load | Same as paywall_close | @@ -55,7 +55,7 @@ The full list of events is as follows: | user_attributes | When the user attributes are set. | \["aliasId": String, "seed": Int, "app_session_id": String, "applicationInstalledAt": String, "is_superwall": true, "application_installed_at": String\] \+ provided attributes | | subscriptionStatus_didChange | When the user's subscription status changes | \["is_superwall": true, "app_session_id": String, "subscription_status": String\] | | paywallPresentationRequest | When something happened during the paywall presentation, whether a success or failure. | \["source_event_name": String, "status": String, "is_superwall": true, "app_session_id": String, "pipeline_type": String, "status_reason": String\] | -| [deepLink_open](/campaigns-placements#implicit-placements) | When a user opens the app via a deep link. | \["url": String, "path": String", "pathExtension": String, "lastPathComponent": String, "host": String, "query": String, "fragment": String\] \+ any query parameters in the deep link URL | +| [deepLink_open](/dashboard/dashboard-campaigns/campaigns-placements#implicit-placements) | When a user opens the app via a deep link. | \["url": String, "path": String", "pathExtension": String, "lastPathComponent": String, "host": String, "query": String, "fragment": String\] \+ any query parameters in the deep link URL | | [survey_response](/using-implicit-events#survey%5Fresponse) | When the response to a paywall survey as been recorded. | Same as subscription_start \+ \["survey_selected_option_title": String, "survey_custom_response": String, "survey_id": String, "survey_assignment_key": String, "survey_selected_option_id": String\] | | touches_began | When the user touches the app's UIWindow for the first time. This is only tracked if there is an active touches_began trigger in a campaign. | Same as app_install | | device_attributes | When device attributes are sent to the backend every session. | \["app_session_id": String, "is_superwall": Bool, "publicApiKey": String, "platform": String, "appUserId": String, "aliases":[String], "vendorId": String, "appVersion": String, "osVersion": String, "deviceModel": String, "deviceLocale": String, "deviceLanguageCode": String, "deviceCurrencyCode": String, "deviceCurrencySymbol": String, "interfaceType": String, "timezoneOffset": Int, "radioType": String, "interfaceStyle": String, isLowPowerModeEnabled: Bool, "bundleId": String, "appInstallDate": String, "isMac": Bool, "daysSinceInstall": Int, "minutesSinceInstall": Int, "daysSinceLastPaywallView": Int?, "minutesSinceLastPaywallView": Int?, "totalPaywallViews": Int, "utcDate": String, "localDate": String, "utcTime": String, "localTime": String, "utcDateTime": String, "localDateTime": String, "isSandbox": String, "subscriptionStatus": String, "isFirstAppOpen": Bool, "sdkVersion": String, "sdkVersionPadded": String, "appBuildString": String, "appBuildStringNumber": Int?\] | diff --git a/content/docs/legacy/legacy_troubleshooting.mdx b/content/docs/legacy/legacy_troubleshooting.mdx index df372916..7fdfd0ca 100644 --- a/content/docs/legacy/legacy_troubleshooting.mdx +++ b/content/docs/legacy/legacy_troubleshooting.mdx @@ -9,7 +9,7 @@ If you are seeing a paywall when you think you shouldn't or vice versa, we recom 1. If you're [implementing subscription-related logic yourself](/legacy/legacy_advanced-configuration), check when you're setting `Superwall.shared.subscriptionStatus`. It's important that the variable here is kept in sync with the subscription status of the user. If it isn't, the paywall won't display when it is supposed to. If the status is `.unknown` or `.active`, the paywall won't show (unless you're specifically overriding that). [See here](/legacy/legacy_advanced-configuration) for more information about that. 2. Check your device. If you have already purchased a subscription on your device, your paywall wouldn't show again. If on iOS and you're using a local StoreKit file in your app for testing purposes, deleting and reinstalling your app will reset your device's subscription status. -3. Check your [campaign](/campaigns). Are you sending the necessary properties along with your trigger to match the rule? Is your trigger event name spelt correctly? If you have a holdout group in your campaign double check that this isn't the reason your paywall isn't displaying. +3. Check your [campaign](/dashboard/dashboard-campaigns/campaigns). Are you sending the necessary properties along with your trigger to match the rule? Is your trigger event name spelt correctly? If you have a holdout group in your campaign double check that this isn't the reason your paywall isn't displaying. 4. Check that the products on your paywall are available and their identifiers are correct. If not, you'll see an error printed to the console. On iOS, if you're using a StoreKit configuration file to test, make sure to add your products before trying to present the paywall. ### How can I debug paywall presentation in production? diff --git a/content/docs/legacy/legacy_using-superwalloptions.mdx b/content/docs/legacy/legacy_using-superwalloptions.mdx index 7c93c3bc..cec537d9 100644 --- a/content/docs/legacy/legacy_using-superwalloptions.mdx +++ b/content/docs/legacy/legacy_using-superwalloptions.mdx @@ -107,7 +107,7 @@ Superwall.instance.logLevel = LogLevel.Warn ### Preloading Paywalls -Paywalls are preloaded by default when the app is launched from a cold start. The paywalls that are preloaded are determined by the list of events that result in a paywall for the user when [registered](/docs/feature-gating). Preloading is smart, only preloading paywalls that belong to rules that could be matched. +Paywalls are preloaded by default when the app is launched from a cold start. The paywalls that are preloaded are determined by the list of events that result in a paywall for the user when [registered](/legacy/legacy_feature-gating). Preloading is smart, only preloading paywalls that belong to rules that could be matched. Paywalls are cached by default, which means after they load once, they don't need to be reloaded from the network unless you make a change to them on the dashboard. However, if you have a lot of paywalls, preloading may increase network usage of your app on first load of the paywalls and result in slower loading times overall. @@ -687,6 +687,6 @@ For a list of locales that are available on iOS, take a look at [this list](http ### Game Controller -If you're using a game controller, you can enable this in `SuperwallOptions` too. Check out our [Game Controller Support](/ios/guides/advanced/game-controller-support) article. +If you're using a game controller, you can enable this in `SuperwallOptions` too. Check out our [Game Controller Support](/sdk/guides/advanced/game-controller-support) article. Take a look at [SuperwallOptions](https://sdk.superwall.me/documentation/superwallkit/superwalloptions) in our SDK reference for more info. diff --git a/content/docs/localization.mdx b/content/docs/localization.mdx index 9f0272c3..629a4954 100644 --- a/content/docs/localization.mdx +++ b/content/docs/localization.mdx @@ -4,7 +4,7 @@ title: "Localization" The legacy editor is deprecated. Please visit the docs covering our new - [editor](/paywall-editor-overview). + [editor](/dashboard/dashboard-creating-paywalls/paywall-editor-overview). You can localize paywalls for specific locales. The SDK will select the appropriate paywall locale based on the device's locale settings. If the chosen locale is not available, the English version will be displayed. @@ -17,7 +17,7 @@ Click **Download Template** to download the strings file for all of your paywall ![](/images/8e8f90f-Screenshot_2023-03-03_at_11.59.38.png) -This file follows a `"original text" = "translated text"` format for each of your paywall text elements. You should translate all the strings in the file into your desired language. [Variables](/paywall-editor-variables) inside text fields will be replaced with `{}` in the original text. +This file follows a `"original text" = "translated text"` format for each of your paywall text elements. You should translate all the strings in the file into your desired language. [Variables](/dashboard/dashboard-creating-paywalls/paywall-editor-variables) inside text fields will be replaced with `{}` in the original text. > 🚧 > @@ -43,7 +43,7 @@ Click the `**<- Paywall Editor**` button to head back to your paywall. You can p ![](/images/8a40add-Screenshot_2023-03-03_at_12.44.52.png) -You can also preview it on device using the [In-App Previews](/docs/in-app-paywall-previews). +You can also preview it on device using the [In-App Previews](/sdk/quickstart/in-app-paywall-previews). ### Third Party Localization diff --git a/content/docs/paywall-settings-sidebar.mdx b/content/docs/paywall-settings-sidebar.mdx index 82fb1a2d..89552632 100644 --- a/content/docs/paywall-settings-sidebar.mdx +++ b/content/docs/paywall-settings-sidebar.mdx @@ -5,12 +5,12 @@ description: "The settings sidebar is split into 3 sections: Products; Design; a The legacy editor is deprecated. Please visit the docs covering our new - [editor](/paywall-editor-overview). + [editor](/dashboard/dashboard-creating-paywalls/paywall-editor-overview). ### Products -In this section, you choose your products to display on your paywall. You'll need to set up your products via Settings as detailed [here](/docs/products). +In this section, you choose your products to display on your paywall. You'll need to set up your products via Settings as detailed [here](/dashboard/products). ![](/images/8cf8e67-Screenshot_2023-06-14_at_16.53.22.png) @@ -43,7 +43,7 @@ Feature gating allows your team to retroactively decide if the paywall is _Gated | **Non Gated** (default) | Show Paywall → Execute Feature | When "Sign Up" button is pressed, show a paywall, but continue onboarding after it closes. | | **Gated** | Show Paywall → Is user paying?If Yes → Execute FeatureIf No → Do Nothing | When "Start Workout" button is pressed, show a paywall. Only continue if paying. | -Remember, the [register feature block](/docs/feature-gating) is always executed if: +Remember, the [register feature block](/sdk/quickstart/feature-gating) is always executed if: 1. No campaign is configured for the event 2. The user is already paying diff --git a/content/docs/react-native/vibe-coding-guide.mdx b/content/docs/react-native/vibe-coding-guide.mdx index 5718055c..dd09ee05 100644 --- a/content/docs/react-native/vibe-coding-guide.mdx +++ b/content/docs/react-native/vibe-coding-guide.mdx @@ -6,7 +6,7 @@ includeSource: false This is an all-in-one prompt to get the Superwall SDK set up in your app. ## Prerequisites -You must have access to the Superwall Docs MCP. If you don't have it, ask me to add it, and refer me to the docs: https://superwall.com/docs/sdk/guides/vibe-coding +You must have access to the Superwall Docs MCP. If you don't have it, ask me to add it, and refer me to [the docs](/ios/guides/vibe-coding#superwall-docs-mcp). ## Integration Steps 1. Install the Superwall SDK @@ -23,4 +23,4 @@ Implement each step one at a time. For each step, do the following: - Then, help me implement the step as described in the Superwall Docs MCP response, - If needed, ask me for project-specific clarification. - Finally, test the step or instruct me on how to test it. -- Explain to me what you did, what step is complete, and what step is next. \ No newline at end of file +- Explain to me what you did, what step is complete, and what step is next. diff --git a/content/docs/recipes.mdx b/content/docs/recipes.mdx index e86f4fa5..a64422aa 100644 --- a/content/docs/recipes.mdx +++ b/content/docs/recipes.mdx @@ -10,7 +10,7 @@ mode: "wide" Using [audiences](/dashboard/dashboard-campaigns/campaigns-audience) within a campaign, you can: 1. Show unique paywalls for each one. - 2. And, within an audience, you can show multiple paywalls based on a [percentage](/campaigns-audience#paywalls). + 2. And, within an audience, you can show multiple paywalls based on a [percentage](/dashboard/dashboard-campaigns/campaigns-audience#paywalls). **The Why**
Our data clearly demonstrates that showing the right paywall, to the right user, and at the right time dramatically affects revenue. There is rarely a one-size-fits all paywall, so you should be testing different variations of them often. diff --git a/content/docs/support/troubleshooting/3920414585-why-aren-t-my-revenuecat-transactions-attributing-to-a-placement-paywall.mdx b/content/docs/support/troubleshooting/3920414585-why-aren-t-my-revenuecat-transactions-attributing-to-a-placement-paywall.mdx index 3435d8b7..e01656bc 100644 --- a/content/docs/support/troubleshooting/3920414585-why-aren-t-my-revenuecat-transactions-attributing-to-a-placement-paywall.mdx +++ b/content/docs/support/troubleshooting/3920414585-why-aren-t-my-revenuecat-transactions-attributing-to-a-placement-paywall.mdx @@ -26,7 +26,7 @@ Debugging Steps * Check that you have Revenue Tracking properly set up, and you see events in Settings -* Check that your PurchaseController code matches our example code [here](https://superwall.com/docs/sdk/guides/using-revenuecat) +* Check that your PurchaseController code matches our example code [here](/sdk/guides/using-revenuecat) ### If Using RevenueCat: diff --git a/content/docs/support/troubleshooting/6022066375-troubleshooting.mdx b/content/docs/support/troubleshooting/6022066375-troubleshooting.mdx index 840b229a..799b3ef2 100644 --- a/content/docs/support/troubleshooting/6022066375-troubleshooting.mdx +++ b/content/docs/support/troubleshooting/6022066375-troubleshooting.mdx @@ -8,11 +8,11 @@ title: "Troubleshooting" If you are seeing a paywall when you think you shouldn't or vice versa, we recommend running through the following list to debug: -1\. If you're [implementing subscription-related logic yourself](https://superwall.com/docs/advanced-configuration), check when you're setting `Superwall.shared.subscriptionStatus`. It's important that the variable here is kept in sync with the subscription status of the user. If it isn't, the paywall won't display when it is supposed to. If the status is `.unknown` or `.active(_)`, the paywall won't show (unless you're specifically overriding that). [See here](https://superwall.com/docs/advanced-configuration) for more information about that. +1\. If you're [implementing subscription-related logic yourself](/sdk/guides/advanced-configuration), check when you're setting `Superwall.shared.subscriptionStatus`. It's important that the variable here is kept in sync with the subscription status of the user. If it isn't, the paywall won't display when it is supposed to. If the status is `.unknown` or `.active(_)`, the paywall won't show (unless you're specifically overriding that). [See here](/sdk/guides/advanced-configuration) for more information about that. 2\. Check your device. If you have already purchased a subscription on your device, your paywall wouldn't show again. If on iOS and you're using a local StoreKit file in your app for testing purposes, deleting and reinstalling your app will reset your device's subscription status. -3\. Check your [campaign](https://superwall.com/docs/campaigns). Are you sending the necessary properties along with your placement to match the audience filter? Is your placement name spelt correctly? If you have a holdout group in your campaign double check that this isn't the reason your paywall isn't displaying. +3\. Check your [campaign](/dashboard/dashboard-campaigns/campaigns). Are you sending the necessary properties along with your placement to match the audience filter? Is your placement name spelt correctly? If you have a holdout group in your campaign double check that this isn't the reason your paywall isn't displaying. 4\. Check that the products on your paywall are available and their identifiers are correct. If not, you'll see an error printed to the console. On iOS, if you're using a StoreKit configuration file to test, make sure to add your products before trying to present the paywall. diff --git a/content/docs/support/troubleshooting/8569105587-why-does-my-subscription-status-remain-inactive-after-successful-purchase.mdx b/content/docs/support/troubleshooting/8569105587-why-does-my-subscription-status-remain-inactive-after-successful-purchase.mdx index 68db1cbf..2e023c45 100644 --- a/content/docs/support/troubleshooting/8569105587-why-does-my-subscription-status-remain-inactive-after-successful-purchase.mdx +++ b/content/docs/support/troubleshooting/8569105587-why-does-my-subscription-status-remain-inactive-after-successful-purchase.mdx @@ -20,11 +20,11 @@ If you are using Superwall for purchase handling: * Add Entitlements to all of your products -If you are **not** using Superwall for purchase handling, and you're using a [PurchaseController](https://superwall.com/docs/using-revenuecat#using-revenuecat): +If you are **not** using Superwall for purchase handling, and you're using a [PurchaseController](/sdk/guides/using-revenuecat#using-revenuecat): * Your PurchaseController is in charge of setting the user's subscription status - * Check that your implementation matches our [example code](https://superwall.com/docs/using-revenuecat#using-revenuecat) + * Check that your implementation matches our [example code](/sdk/guides/using-revenuecat#using-revenuecat) * Check that syncSubscriptionStatus is called on launch diff --git a/content/docs/support/troubleshooting/troubleshooting-unexpected-paywall-behavior.mdx b/content/docs/support/troubleshooting/troubleshooting-unexpected-paywall-behavior.mdx index 82c8ec2a..bf10e7e7 100644 --- a/content/docs/support/troubleshooting/troubleshooting-unexpected-paywall-behavior.mdx +++ b/content/docs/support/troubleshooting/troubleshooting-unexpected-paywall-behavior.mdx @@ -6,7 +6,7 @@ description: "Step-by-step checklist when a paywall shows up when it shouldn’t If you're seeing a paywall when you think you shouldn't—or not seeing one when you should—run through this checklist: 1. **Check your subscription status logic.** - If you're [implementing subscription-related logic yourself](/advanced-configuration), make sure `Superwall.shared.subscriptionStatus` is kept in sync with the actual subscription status. If it's `.unknown` or `.active(_)`, the paywall won’t show unless explicitly overridden. + If you're [implementing subscription-related logic yourself](/sdk/guides/advanced-configuration), make sure `Superwall.shared.subscriptionStatus` is kept in sync with the actual subscription status. If it's `.unknown` or `.active(_)`, the paywall won’t show unless explicitly overridden. 2. **Test device considerations.** If you’ve previously purchased a subscription, the paywall won’t show again. On iOS, if you’re using a local StoreKit config file, delete and reinstall the app to reset the device’s subscription state. diff --git a/content/docs/support/troubleshooting/why-is-my-paywall-not-updating-after-publishing.mdx b/content/docs/support/troubleshooting/why-is-my-paywall-not-updating-after-publishing.mdx index 8fadeb65..46a82771 100644 --- a/content/docs/support/troubleshooting/why-is-my-paywall-not-updating-after-publishing.mdx +++ b/content/docs/support/troubleshooting/why-is-my-paywall-not-updating-after-publishing.mdx @@ -149,8 +149,8 @@ When testing paywall updates, follow this process: ## Related Documentation -- [Publishing Paywalls](https://superwall.com/docs/dashboard/dashboard-creating-paywalls/paywall-editor-publishing) -- [A/B Testing and Experiments](https://superwall.com/docs/campaigns/campaigns-starting-an-experiment) -- [Dynamic Values](https://superwall.com/docs/dashboard/dashboard-creating-paywalls/paywall-editor-dynamic-values) -- [Variables Reference](https://superwall.com/docs/dashboard/dashboard-creating-paywalls/paywall-editor-variables) -- [Campaign Audiences](https://superwall.com/docs/campaigns/campaigns-audience) +- [Publishing Paywalls](/dashboard/dashboard-creating-paywalls/paywall-editor-publishing) +- [A/B Testing and Experiments](/dashboard/dashboard-campaigns/campaigns-starting-an-experiment) +- [Dynamic Values](/dashboard/dashboard-creating-paywalls/paywall-editor-dynamic-values) +- [Variables Reference](/dashboard/dashboard-creating-paywalls/paywall-editor-variables) +- [Campaign Audiences](/dashboard/dashboard-campaigns/campaigns-audience) diff --git a/content/docs/support/web-checkout/web-checkout-revenue-tracking-is-automatic.mdx b/content/docs/support/web-checkout/web-checkout-revenue-tracking-is-automatic.mdx index 0c70d42a..d667eff6 100644 --- a/content/docs/support/web-checkout/web-checkout-revenue-tracking-is-automatic.mdx +++ b/content/docs/support/web-checkout/web-checkout-revenue-tracking-is-automatic.mdx @@ -19,8 +19,8 @@ With web checkout, payments flow directly through Superwall's integration with S ## What you need to do -1. **Configure your Stripe keys** in your Superwall app's [Settings](/web-checkout-configuring-stripe-keys-and-settings) -2. **Create products** in Stripe and [add them to Superwall](/web-checkout-adding-a-stripe-product) +1. **Configure your Stripe keys** in your Superwall app's [Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) +2. **Create products** in Stripe and [add them to Superwall](/web-checkout/web-checkout-adding-a-stripe-product) 3. **Add products to your paywalls** and start showing them Once a purchase completes through your web checkout paywall, conversions and revenue will appear automatically in your Superwall dashboard. @@ -40,6 +40,6 @@ If you have completed purchases in Stripe but no conversions showing in Superwal ## Related -- [Stripe Setup](/web-checkout-configuring-stripe-keys-and-settings) +- [Stripe Setup](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) - [Revenue Tracking for iOS and Android](/dashboard/dashboard-settings/overview-settings-revenue-tracking) -- [Web Checkout Overview](/web-checkout-overview) +- [Web Checkout Overview](/web-checkout/web-checkout-overview) diff --git a/content/docs/using-implicit-events.mdx b/content/docs/using-implicit-events.mdx index 7da862da..382daf6c 100644 --- a/content/docs/using-implicit-events.mdx +++ b/content/docs/using-implicit-events.mdx @@ -1,16 +1,16 @@ --- title: "Using Implicit Events" -description: "The following [Superwall Events](/docs/tracking-analytics) are automatically registered by the SDK and can be added as events in campaigns to present paywalls." +description: "The following [Superwall Events](/sdk/guides/3rd-party-analytics/tracking-analytics) are automatically registered by the SDK and can be added as events in campaigns to present paywalls." --- - This page is outdated. Please visit this [one](/campaigns-placements#implicit-placements) for more + This page is outdated. Please visit this [one](/dashboard/dashboard-campaigns/campaigns-placements#implicit-placements) for more relevant information. * `app_install` * `app_launch` * `deepLink_open` * `session_start` * `paywall_decline` * `transaction_fail` * `transaction_abandon` * `survey_response` * `touches_began` -Visit [Superwall Events](/docs/tracking-analytics) to see a full list of parameters that you can use with these events. +Visit [Superwall Events](/sdk/guides/3rd-party-analytics/tracking-analytics) to see a full list of parameters that you can use with these events. ### `paywall_decline` @@ -26,7 +26,7 @@ Note that you can't reference parameters that you've passed in to your original ### `survey_response` -This is registered when a response to a paywall survey as been recorded. First, you need to make sure your paywall [has a survey attached](/docs/surveys). +This is registered when a response to a paywall survey as been recorded. First, you need to make sure your paywall [has a survey attached](/dashboard/surveys). You can combine this with rules to show a paywall whenever a survey response is recorded or when the user gives a specific response: @@ -36,7 +36,7 @@ Here if the user selects an option named `Too Expensive`, they will see another ### `deepLink_open` -This is registered when a user opens the app via a deep link. First, you need to make sure to [tell Superwall when a deep link has been opened](/docs/in-app-paywall-previews). +This is registered when a user opens the app via a deep link. First, you need to make sure to [tell Superwall when a deep link has been opened](/sdk/quickstart/in-app-paywall-previews). You can use the URL parameters of the deep link within your rules: diff --git a/content/docs/using-placement-parameters.mdx b/content/docs/using-placement-parameters.mdx index c6ce22c0..96fb78da 100644 --- a/content/docs/using-placement-parameters.mdx +++ b/content/docs/using-placement-parameters.mdx @@ -18,9 +18,9 @@ Notes: Platform guides: -- iOS: [`Presenting paywalls`](/ios/guides/advanced/presenting-paywalls) -- Android: [`Presenting paywalls`](/android/guides/advanced/presenting-paywalls) -- Flutter: [`Presenting paywalls`](/flutter/guides/advanced/presenting-paywalls) -- Expo: [`Presenting paywalls`](/expo/guides/advanced/presenting-paywalls) +- iOS: [`Presenting paywalls`](/sdk/guides/advanced/presenting-paywalls) +- Android: [`Presenting paywalls`](/sdk/guides/advanced/presenting-paywalls) +- Flutter: [`Presenting paywalls`](/sdk/guides/advanced/presenting-paywalls) +- Expo: [`Presenting paywalls`](/sdk/guides/advanced/presenting-paywalls) diff --git a/content/docs/using-referral-or-promo-codes-with-superwall.mdx b/content/docs/using-referral-or-promo-codes-with-superwall.mdx index ab60477a..99f46933 100644 --- a/content/docs/using-referral-or-promo-codes-with-superwall.mdx +++ b/content/docs/using-referral-or-promo-codes-with-superwall.mdx @@ -5,8 +5,8 @@ description: "Learn how to use referral or promo codes with Superwall." There are two primary ways to use referral codes or promo codes with Superwall: -1. **Using Superwall's Mobile SDKs**: By using [custom actions](/custom-paywall-events) along with a [campaign](/campaigns) for referrals, you can create a flow to handle referral codes and see their resulting conversions and other data. You create the products that each referral should unlock within the respective app storefront. -2. **Web Checkout**: Here, you can use Superwall's [web checkout](/web-checkout-overview) feature to easily offer referrals. With this approach, you could create a [checkout link](/web-checkout-creating-campaigns-to-show-paywalls) for each referral you need. Unlike the previous option, you create the [products in Stripe](/web-checkout-adding-a-stripe-product). +1. **Using Superwall's Mobile SDKs**: By using [custom actions](/sdk/guides/advanced/custom-paywall-actions) along with a [campaign](/dashboard/dashboard-campaigns/campaigns) for referrals, you can create a flow to handle referral codes and see their resulting conversions and other data. You create the products that each referral should unlock within the respective app storefront. +2. **Web Checkout**: Here, you can use Superwall's [web checkout](/web-checkout/web-checkout-overview) feature to easily offer referrals. With this approach, you could create a [checkout link](/web-checkout/web-checkout-creating-campaigns-to-show-paywalls) for each referral you need. Unlike the previous option, you create the [products in Stripe](/web-checkout/web-checkout-adding-a-stripe-product). ### Understanding Superwall's role Before you continue, it's critical to understand Superwall's role in this process. Most referral flows usually call for two things: @@ -46,7 +46,7 @@ Now, let's go through each step in detail. We'll assume that you're triggering t ![](/images/rc_1.jpg) - This app has a [`SuperwallDelegate`](/using-superwall-delegate): + This app has a [`SuperwallDelegate`](/sdk/guides/using-superwall-delegate): ```swift @main @@ -66,7 +66,7 @@ Now, let's go through each step in detail. We'll assume that you're triggering t } } ``` - Using the [`SuperwallDelegate`](/using-superwall-delegate), the app responds to the custom action. It presents a customized referral redemption interface _on top_ of the existing paywall. This is optional, but presenting over the paywall means that the user will be taken back to it if the redemption code entry fails: + Using the [`SuperwallDelegate`](/sdk/guides/using-superwall-delegate), the app responds to the custom action. It presents a customized referral redemption interface _on top_ of the existing paywall. This is optional, but presenting over the paywall means that the user will be taken back to it if the redemption code entry fails: @@ -153,7 +153,7 @@ Now, let's go through each step in detail. We'll assume that you're triggering t Create a campaign for your referrals. Here, you'd add the discounted product(s) you've made to a paywall that were created in Stripe. You can add in as many placements as you need. Maybe there's one for each influencer, or seasonal discount offer, etc. - Next, simply send out the [web checkout](/web-checkout-creating-campaigns-to-show-paywalls) links to your users. You can create a link for each referral code, or you can use the same link for all of them. + Next, simply send out the [web checkout](/web-checkout/web-checkout-creating-campaigns-to-show-paywalls) links to your users. You can create a link for each referral code, or you can use the same link for all of them. Remember that with Superwall's web checkout feature, each placement you add becomes its own web checkout link. diff --git a/content/docs/using-revenuecat.mdx b/content/docs/using-revenuecat.mdx index b7423aa8..b8efca82 100644 --- a/content/docs/using-revenuecat.mdx +++ b/content/docs/using-revenuecat.mdx @@ -894,7 +894,7 @@ export class RCPurchaseController extends PurchaseController { -As discussed in [Purchases and Subscription Status](/docs/advanced-configuration), this `PurchaseController` is responsible for handling the subscription-related logic. Take a few moments to look through the code to understand how it does this. +As discussed in [Purchases and Subscription Status](/sdk/guides/advanced-configuration), this `PurchaseController` is responsible for handling the subscription-related logic. Take a few moments to look through the code to understand how it does this. #### 2. Configure Superwall diff --git a/content/docs/using-the-dashboard.mdx b/content/docs/using-the-dashboard.mdx index 69fd1e4e..78cfa258 100644 --- a/content/docs/using-the-dashboard.mdx +++ b/content/docs/using-the-dashboard.mdx @@ -18,7 +18,7 @@ As you complete the steps to integrate Superwall these items will check off auto > > Superwall has built hundreds of paywalls for clients and has a unique vantage point on the industry. As a token of gratitude for signing up, we create a paywall for each customer, using best practices we've picked up along the way. Check your email for updates :) -The [Paywalls](/paywall-editor-overview) section of the dashboard is where you'll configure and manage all your paywalls. When you first log in, you'll see an **Example Paywall** built out for you. +The [Paywalls](/dashboard/dashboard-creating-paywalls/paywall-editor-overview) section of the dashboard is where you'll configure and manage all your paywalls. When you first log in, you'll see an **Example Paywall** built out for you. Here's what your Paywalls section might look like at scale. @@ -28,7 +28,7 @@ You can toggle its interface style to view analytics too. ### Campaigns -The [Campaigns](/docs/campaigns) section of the dashboard is where you configure _when_ and _who_ to show a paywall to, using triggers and rules. In the image below, Campaign 2 is triggered when the SDK fires either a `pressed_continue_welcome_screen` or `load_feed` event. Campaign 1's trigger is switched _off_, indicated by a cross through its icon and lack of background color. This means that the campaign won't be triggered for a `viewed_welcome_screen` event: +The [Campaigns](/dashboard/dashboard-campaigns/campaigns) section of the dashboard is where you configure _when_ and _who_ to show a paywall to, using triggers and rules. In the image below, Campaign 2 is triggered when the SDK fires either a `pressed_continue_welcome_screen` or `load_feed` event. Campaign 1's trigger is switched _off_, indicated by a cross through its icon and lack of background color. This means that the campaign won't be triggered for a `viewed_welcome_screen` event: ![](/images/db38070-Screen_Shot_2022-05-17_at_2.28.22_PM.png "Screen Shot 2022-05-17 at 2.28.22 PM.png") diff --git a/content/docs/using-the-presentation-handler.mdx b/content/docs/using-the-presentation-handler.mdx index 118de18a..75053be4 100644 --- a/content/docs/using-the-presentation-handler.mdx +++ b/content/docs/using-the-presentation-handler.mdx @@ -5,8 +5,8 @@ description: "When and how to use per-presentation handlers for paywalls, and ho Use a presentation handler when you need fine‑grained callbacks for a single paywall presentation. For global events across your app, use the platform’s delegate instead. -- iOS: see [`PaywallPresentationHandler`](/ios/sdk-reference/PaywallPresentationHandler) and [`SuperwallDelegate`](/ios/sdk-reference/SuperwallDelegate) -- Flutter: see [`PaywallPresentationHandler`](/flutter/sdk-reference/PaywallPresentationHandler) and [`SuperwallDelegate`](/flutter/sdk-reference/SuperwallDelegate) +- iOS: see [`PaywallPresentationHandler`](/sdk/sdk-reference/PaywallPresentationHandler) and [`SuperwallDelegate`](/sdk/sdk-reference/SuperwallDelegate) +- Flutter: see [`PaywallPresentationHandler`](/sdk/sdk-reference/PaywallPresentationHandler) and [`SuperwallDelegate`](/sdk/sdk-reference/SuperwallDelegate) Typical use cases: @@ -16,9 +16,9 @@ Typical use cases: For end‑to‑end presenting patterns (registering or retrieving a paywall yourself), see: -- iOS: [`Presenting paywalls`](/ios/guides/advanced/presenting-paywalls) -- Android: [`Presenting paywalls`](/android/guides/advanced/presenting-paywalls) -- Flutter: [`Presenting paywalls`](/flutter/guides/advanced/presenting-paywalls) -- Expo: [`Presenting paywalls`](/expo/guides/advanced/presenting-paywalls) +- iOS: [`Presenting paywalls`](/sdk/guides/advanced/presenting-paywalls) +- Android: [`Presenting paywalls`](/sdk/guides/advanced/presenting-paywalls) +- Flutter: [`Presenting paywalls`](/sdk/guides/advanced/presenting-paywalls) +- Expo: [`Presenting paywalls`](/sdk/guides/advanced/presenting-paywalls) diff --git a/content/docs/web-checkout/web-checkout-adding-a-stripe-product.mdx b/content/docs/web-checkout/web-checkout-adding-a-stripe-product.mdx index e4526e44..10d0225d 100644 --- a/content/docs/web-checkout/web-checkout-adding-a-stripe-product.mdx +++ b/content/docs/web-checkout/web-checkout-adding-a-stripe-product.mdx @@ -67,7 +67,7 @@ Be sure to associate the correct entitlement to the product as well. ### Adding products to paywalls -Adding Stripe products to web paywalls works the exact same way as it does for mobile paywalls. Check out the docs [here](/paywall-editor-products). For a quick overview: +Adding Stripe products to web paywalls works the exact same way as it does for mobile paywalls. Check out the docs [here](/dashboard/dashboard-creating-paywalls/paywall-editor-products). For a quick overview: 1. Open the paywall editor. 2. On the left sidebar click on **Products**. diff --git a/content/docs/web-checkout/web-checkout-configuring-stripe-keys-and-settings.mdx b/content/docs/web-checkout/web-checkout-configuring-stripe-keys-and-settings.mdx index 2564a386..513e8f7c 100644 --- a/content/docs/web-checkout/web-checkout-configuring-stripe-keys-and-settings.mdx +++ b/content/docs/web-checkout/web-checkout-configuring-stripe-keys-and-settings.mdx @@ -3,7 +3,7 @@ title: "Stripe Setup" description: "Connect Superwall to Stripe using the official Stripe app and configure your settings." --- -Once you've created a [Stripe app](/web-checkout-creating-an-app), you'll need to connect it with Stripe and fill in a few settings. This is a one-time setup that connects Superwall to Stripe. The easiest way to get started is to click on the link in your overview page, which will take you to your app's [Settings](/overview-settings) page: +Once you've created a [Stripe app](/web-checkout/web-checkout-creating-an-app), you'll need to connect it with Stripe and fill in a few settings. This is a one-time setup that connects Superwall to Stripe. The easiest way to get started is to click on the link in your overview page, which will take you to your app's [Settings](/dashboard/dashboard-settings/overview-settings) page: ![](/images/web2app_prompt.jpeg) @@ -139,7 +139,7 @@ Superwall uses the details here to handle deep links back to your app after a pu ![](/images/web-checkout-ios-config.png) -1. **Apple Custom URL Scheme:** Add your app's custom URL scheme. If you haven't set on up, read [here for instructions](/in-app-paywall-previews). +1. **Apple Custom URL Scheme:** Add your app's custom URL scheme. If you haven't set on up, read [here for instructions](/sdk/quickstart/in-app-paywall-previews). 2. **Apple App ID:** Your iOS app's ID. If you're unsure of your app's ID, you find it in **[App Store Connect](https://appstoreconnect.apple.com) -> Select your App -> General -> App Information -> Apple ID**: ![](/images/web2app_app_id.png) @@ -156,4 +156,4 @@ Once you've filled out all of these fields, you should see **Configured** for ea ![](/images/web2app_keys.jpeg) -Next, you'll need to create some products in Stripe. \ No newline at end of file +Next, you'll need to create some products in Stripe. diff --git a/content/docs/web-checkout/web-checkout-creating-an-app.mdx b/content/docs/web-checkout/web-checkout-creating-an-app.mdx index 79b946de..7625aa3a 100644 --- a/content/docs/web-checkout/web-checkout-creating-an-app.mdx +++ b/content/docs/web-checkout/web-checkout-creating-an-app.mdx @@ -18,4 +18,4 @@ You'll be presented with three fields to fill out: ![](/images/web2app_add_new_app.jpeg) -Once you've filled these out, **click** on **Add App ->**. You'll automatically be taken to your app's [overview](/overview-metrics) page. Next, it's time to [configure your app with Stripe](/web-checkout-configuring-stripe-keys-and-settings). \ No newline at end of file +Once you've filled these out, **click** on **Add App ->**. You'll automatically be taken to your app's [overview](/dashboard/overview-metrics) page. Next, it's time to [configure your app with Stripe](/web-checkout/web-checkout-configuring-stripe-keys-and-settings). \ No newline at end of file diff --git a/content/docs/web-checkout/web-checkout-creating-campaigns-to-show-paywalls.mdx b/content/docs/web-checkout/web-checkout-creating-campaigns-to-show-paywalls.mdx index e25c7367..03d26e28 100644 --- a/content/docs/web-checkout/web-checkout-creating-campaigns-to-show-paywalls.mdx +++ b/content/docs/web-checkout/web-checkout-creating-campaigns-to-show-paywalls.mdx @@ -6,7 +6,7 @@ description: "Learn how to use campaigns and placements to present web paywalls Once you've [created a Stripe app](/web-checkout/web-checkout-creating-an-app), [configured Stripe with Superwall](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) via the [Superwall Stripe app](https://marketplace.stripe.com/apps/superwall) and have [created Stripe products](/web-checkout/web-checkout-adding-a-stripe-product) — you're ready to configure a campaign to show a web paywall. -Before you proceed, recall that web checkout has all of the advantages of the Superwall platform. If you are unfamiliar with how to create campaigns or what a placement is — we recommend you read through the [introduction](/home) documentation and [campaigns doc](/campaigns) first. +Before you proceed, recall that web checkout has all of the advantages of the Superwall platform. If you are unfamiliar with how to create campaigns or what a placement is — we recommend you read through the [introduction](/using-the-dashboard) documentation and [campaigns doc](/dashboard/dashboard-campaigns/campaigns) first. ### Understanding placements in web checkout @@ -19,7 +19,7 @@ Other than that, everything operates as a normal Superwall campaign would. For e ![](/images/web2app_make_campaign.png) -Here, the placement `black-friday-promo` presents a paywall. If the app's URL in [settings](/web-checkout-configuring-stripe-keys-and-settings) is `caffeinepal`, then the URL for this placement would be `https://caffeinepal.superwall.app/black-friday-promo`. Visiting that web checkout link presents a paywall: +Here, the placement `black-friday-promo` presents a paywall. If the app's URL in [settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) is `caffeinepal`, then the URL for this placement would be `https://caffeinepal.superwall.app/black-friday-promo`. Visiting that web checkout link presents a paywall: ![](/images/web2app_showing_paywall.png) @@ -81,7 +81,7 @@ Of course, this is a simplistic example — but this is useful for personalizati ### Automatically populating user emails in checkout flows - This section is Web2App only (starting from a web link). For App2Web (starting from an iOS paywall), see [App2Web](/web-checkout-direct-stripe-checkout#prefill-email). + This section is Web2App only (starting from a web link). For App2Web (starting from an iOS paywall), see [App2Web](/web-checkout/web-checkout-direct-stripe-checkout#prefill-email). There is a special query string parameter you can use to automatically populate the user's email in the checkout flow. This is useful for pre-filling the email field in the checkout form, so that users don't have to enter it manually. Simply add `email` and set the value to the user's email address: diff --git a/content/docs/web-checkout/web-checkout-direct-stripe-checkout.mdx b/content/docs/web-checkout/web-checkout-direct-stripe-checkout.mdx index 0cc138cb..a7d84687 100644 --- a/content/docs/web-checkout/web-checkout-direct-stripe-checkout.mdx +++ b/content/docs/web-checkout/web-checkout-direct-stripe-checkout.mdx @@ -9,7 +9,7 @@ For customers in the United States, you can offer Stripe products directly from - First, follow the [web checkout setup guide](/web-checkout-overview#getting-setup) to create a Stripe app and configure your web checkout settings. Specifically, you'll need to complete the first three steps. This includes installing the [Superwall Stripe app](https://marketplace.stripe.com/apps/superwall) and setting up your app's settings. + First, follow the [web checkout setup guide](/web-checkout/web-checkout-overview#getting-setup) to create a Stripe app and configure your web checkout settings. Specifically, you'll need to complete the first three steps. This includes installing the [Superwall Stripe app](https://marketplace.stripe.com/apps/superwall) and setting up your app's settings. Select a paywall and add a Stripe product to it. This will allow users to purchase the product directly from the paywall. Stripe products are prepended with "stripe" in the product selector: @@ -33,13 +33,13 @@ For customers in the United States, you can offer Stripe products directly from ![](/images/web-checkout-app2web-campaign-filter.png) - From there, the flow works the same way as it would for web checkout. Once the payment succeeds, the [Superwall delegate](/ios/guides/using-superwall-delegate) functions `willRedeemLink()` and `didRedeemLink(result:)` will be called. You can use these functions to handle the deep link in your app if you need to show any specific UI as described in our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) docs. + From there, the flow works the same way as it would for web checkout. Once the payment succeeds, the [Superwall delegate](/sdk/guides/using-superwall-delegate) functions `willRedeemLink()` and `didRedeemLink(result:)` will be called. You can use these functions to handle the deep link in your app if you need to show any specific UI as described in our [Post-Checkout Redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) docs. - Additionally, the subscription status will be updated automatically and the delegate callback `func subscriptionStatusDidChange(from oldValue: SubscriptionStatus, to newValue: SubscriptionStatus)` will be called. If you're using a `PurchaseController`, refer to [the docs here](/web-checkout-linking-membership-to-iOS-app#using-a-purchasecontroller). + Additionally, the subscription status will be updated automatically and the delegate callback `func subscriptionStatusDidChange(from oldValue: SubscriptionStatus, to newValue: SubscriptionStatus)` will be called. If you're using a `PurchaseController`, refer to [the docs here](/sdk/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller). -If you need to test checkout, learn how [here](/web-checkout-testing-purchases). +If you need to test checkout, learn how [here](/web-checkout/web-checkout-testing-purchases). ### Prefill customer information diff --git a/content/docs/web-checkout/web-checkout-faq.mdx b/content/docs/web-checkout/web-checkout-faq.mdx index 25b03968..ae18257c 100644 --- a/content/docs/web-checkout/web-checkout-faq.mdx +++ b/content/docs/web-checkout/web-checkout-faq.mdx @@ -7,7 +7,7 @@ description: "Frequently asked questions about web checkout." When the user taps on the restore link in the paywall, we'll do the normal restore flow for on-device subscriptions. However, if you've enabled web checkout and the restored entitlements don't match the entitlements belonging to the products on the paywall, we'll present an alert asking the user if they'd like to check for subscriptions on the web. This will -take them out of your app to the [plan management screen](/web-checkout-managing-memberships) where they can get a redemption link to restore their subscriptions. +take them out of your app to the [plan management screen](/web-checkout/web-checkout-managing-memberships) where they can get a redemption link to restore their subscriptions. ### Does Superwall email customers after checkout? @@ -38,7 +38,7 @@ This system ensures flexibility while protecting against unauthorized sharing of ### How do I associate a web checkout purchase with a user in my app? -The short answer — use Superwall's [user identification APIs](/identity-management#identified-users). When you configure Superwall, or a user signs in or out, you can always associate their login status to Superwall's SDK: +The short answer — use Superwall's [user identification APIs](/sdk/quickstart/user-management#identified-users). When you configure Superwall, or a user signs in or out, you can always associate their login status to Superwall's SDK: ```swift Superwall.shared.identify(userId: user.id) @@ -63,7 +63,7 @@ Use **Redeem mode** (the default) when: - You don't need custom post-purchase logic - You want the simplest integration -Most apps should use Redeem mode. You can always switch between modes in your [Application Settings](/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior). +Most apps should use Redeem mode. You can always switch between modes in your [Application Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior). ### What data is passed to my redirect URL in Redirect mode? diff --git a/content/docs/web-checkout/web-checkout-managing-memberships.mdx b/content/docs/web-checkout/web-checkout-managing-memberships.mdx index 40095f04..bcd97a1d 100644 --- a/content/docs/web-checkout/web-checkout-managing-memberships.mdx +++ b/content/docs/web-checkout/web-checkout-managing-memberships.mdx @@ -23,4 +23,4 @@ For the above example, the URL would be `https://caffeinepal.superwall.app/manag ![](/images/web2app_manage_details.png) -For situations where a user needs to restore their purchases, check out the answer in this [F.A.Q](/web-checkout-faq). +For situations where a user needs to restore their purchases, check out the answer in this [F.A.Q](/web-checkout/web-checkout-faq). diff --git a/content/docs/web-checkout/web-checkout-overview.mdx b/content/docs/web-checkout/web-checkout-overview.mdx index 27c9320d..d2361791 100644 --- a/content/docs/web-checkout/web-checkout-overview.mdx +++ b/content/docs/web-checkout/web-checkout-overview.mdx @@ -3,7 +3,7 @@ title: "Overview" description: "Let customers purchase products online via Stripe, then link them to your iOS app with one seamless flow. No authentication required." --- -Superwall's web checkout integration makes it easy to set up purchasing funnels for your app via the web. Web checkout is powered by Stripe. Once an online purchase is complete, the customer will be redirected back to your app with a deep link that can be used to unlock content or features in your app via any associated [entitlement](/products#entitlements). +Superwall's web checkout integration makes it easy to set up purchasing funnels for your app via the web. Web checkout is powered by Stripe. Once an online purchase is complete, the customer will be redirected back to your app with a deep link that can be used to unlock content or features in your app via any associated [entitlement](/dashboard/products#entitlements). Web checkout requires the Superwall iOS SDK 4.2.0 or later. @@ -13,7 +13,7 @@ Web checkout requires the Superwall iOS SDK 4.2.0 or later. ## How it works -Superwall presents paywalls via the concept of [campaigns](/campaigns), and each campaign has one or more [placements](/campaigns-placements). A paywall is shown in a campaign when a placement is triggered after your [audience filters](/campaigns-audience) are evaluated. This setup is Superwall's foundation, and the web checkout flow works the exact same way. +Superwall presents paywalls via the concept of [campaigns](/dashboard/dashboard-campaigns/campaigns), and each campaign has one or more [placements](/dashboard/dashboard-campaigns/campaigns-placements). A paywall is shown in a campaign when a placement is triggered after your [audience filters](/dashboard/dashboard-campaigns/campaigns-audience) are evaluated. This setup is Superwall's foundation, and the web checkout flow works the exact same way. The core difference? Each placement becomes a unique URL that you can share, send or email to present a user with a paywall that leads to a Stripe checkout flow. And just like with Superwall on apps, you can create experiments, try out different paywalls, run price tests and more. @@ -29,38 +29,38 @@ Refer to the individual pages below to get started, but for a quick, high-level 4. Products are created _in_ your payment provider, and imported into Superwall. 5. Within a campaign (a default one is provided), you attach those products to a paywall. 6. A user visits a placement URL, and performs the checkout flow. -7. After a successful purchase, the user is redirected based on your [post-purchase behavior setting](/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): +7. After a successful purchase, the user is redirected based on your [post-purchase behavior setting](/web-checkout/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): - **Redeem mode** (default): User is directed to download the app and click the redemption link - **Redirect mode**: User is redirected to your custom URL with purchase data 8. For Redeem mode: _On the device that they downloaded the app_, they click the redemption link. -9. Your iOS app is opened via a deep link (which means it must be set up with Superwall deep links, [docs here](/in-app-paywall-previews)). +9. Your iOS app is opened via a deep link (which means it must be set up with Superwall deep links, [docs here](/sdk/quickstart/in-app-paywall-previews)). 10. In the `SuperwallDelegate`, `willRedeemLink()` is called, and then once it's fetched — `didRedeemLink(result:)` is called with the result of the redemption. -11. Finally, this user's account and details are managed via a link they find in their [email receipt or by visiting a URL manually](/web-checkout-managing-memberships). +11. Finally, this user's account and details are managed via a link they find in their [email receipt or by visiting a URL manually](/web-checkout/web-checkout-managing-memberships). ## Getting setup Before you start, you'll need to have a Superwall account and a Stripe account. You can create a Stripe account [here](https://dashboard.stripe.com/register). -1. **[Creating an app](/web-checkout-creating-an-app):** First, you'll add a Web Checkout app to an existing project within Superwall. -2. **[Stripe setup](/web-checkout-configuring-stripe-keys-and-settings):** Install the [Superwall Stripe app](https://marketplace.stripe.com/apps/superwall) for automatic configuration. -3. **[Managing products](/web-checkout-adding-a-stripe-product):** Create or import products to add to your web paywalls. +1. **[Creating an app](/web-checkout/web-checkout-creating-an-app):** First, you'll add a Web Checkout app to an existing project within Superwall. +2. **[Stripe setup](/web-checkout/web-checkout-configuring-stripe-keys-and-settings):** Install the [Superwall Stripe app](https://marketplace.stripe.com/apps/superwall) for automatic configuration. +3. **[Managing products](/web-checkout/web-checkout-adding-a-stripe-product):** Create or import products to add to your web paywalls. ### Creating paywalls and campaigns -4. **[Presenting paywalls](/web-checkout-creating-campaigns-to-show-paywalls):** Set up a campaign, create some placements and add paywalls to begin showing them to customers. +4. **[Presenting paywalls](/web-checkout/web-checkout-creating-campaigns-to-show-paywalls):** Set up a campaign, create some placements and add paywalls to begin showing them to customers. ### Associating entitlements to your iOS apps -5. **[Linking purchases to your iOS app](/web-checkout-linking-membership-to-iOS-app):** Once a purchase occurs, the user will be prompted to download your app and click on a redemption link. -6. **[Managing memberships](/web-checkout-managing-memberships):** Users can cancel, update or manage their memberships via Stripe. +5. **[Linking purchases to your iOS app](/sdk/guides/web-checkout/linking-membership-to-iOS-app):** Once a purchase occurs, the user will be prompted to download your app and click on a redemption link. +6. **[Managing memberships](/web-checkout/web-checkout-managing-memberships):** Users can cancel, update or manage their memberships via Stripe. ### Testing purchases -7. **[Testing purchases](/web-checkout-testing-purchases):** Test your web checkout flow with test purchases. +7. **[Testing purchases](/web-checkout/web-checkout-testing-purchases):** Test your web checkout flow with test purchases. ### App to Web -8. **[App to Web Checkout](/web-checkout-direct-stripe-checkout):** For customers in the United States, you can offer Stripe products directly from your iOS paywalls. +8. **[App to Web Checkout](/web-checkout/web-checkout-direct-stripe-checkout):** For customers in the United States, you can offer Stripe products directly from your iOS paywalls. ## Troubleshooting diff --git a/content/docs/web-checkout/web-checkout-sdk-setup.mdx b/content/docs/web-checkout/web-checkout-sdk-setup.mdx index bb19606e..d626de3e 100644 --- a/content/docs/web-checkout/web-checkout-sdk-setup.mdx +++ b/content/docs/web-checkout/web-checkout-sdk-setup.mdx @@ -6,16 +6,16 @@ description: "Platform-specific setup guides for Web Checkout implementation" Choose your platform below to get started with Web Checkout in your app: - + Setup Web Checkout for iOS apps with Swift - + Setup Web Checkout for Android apps with Kotlin/Java - + Setup Web Checkout for Flutter apps - + Setup Web Checkout for Expo apps diff --git a/content/docs/web-checkout/web-checkout-testing-purchases.mdx b/content/docs/web-checkout/web-checkout-testing-purchases.mdx index 47bbf0ea..af4fe353 100644 --- a/content/docs/web-checkout/web-checkout-testing-purchases.mdx +++ b/content/docs/web-checkout/web-checkout-testing-purchases.mdx @@ -26,17 +26,17 @@ Users should click the redemption link _on the device where the app is installed
-From there, the [redemption flow](/web-checkout-linking-membership-to-iOS-app) occurs. +From there, the [redemption flow](/sdk/guides/web-checkout/linking-membership-to-iOS-app) occurs. ### Testing a purchase To test a purchase: -1. Add a [sandbox product](/web-checkout-adding-a-stripe-product#creating-sandbox-products-to-test-with) to a paywall. +1. Add a [sandbox product](/web-checkout/web-checkout-adding-a-stripe-product#creating-sandbox-products-to-test-with) to a paywall. 2. Visit the paywall URL and checkout. 3. Choose "Card" for the payment method. 3. For the card number, use `4242 4242 4242 4242` with any expiration date later than today, any CVC and fill out the name and zip code. ![](/images/web2app_test_card.png) -This will allow you to checkout and go through the entire flow to debug issues, test it out on a device and more. \ No newline at end of file +This will allow you to checkout and go through the entire flow to debug issues, test it out on a device and more. diff --git a/content/docs/web-checkout/web-checkout-web-only.mdx b/content/docs/web-checkout/web-checkout-web-only.mdx index 312b3ef2..0921a98f 100644 --- a/content/docs/web-checkout/web-checkout-web-only.mdx +++ b/content/docs/web-checkout/web-checkout-web-only.mdx @@ -26,13 +26,13 @@ https://amazingwebproduct.com/welcome? ### 1. Create a Superwall Web App -Create a new app in Superwall for your web product in a new or existing project. Follow the steps in [Creating an App](/web-checkout-creating-an-app). +Create a new app in Superwall for your web product in a new or existing project. Follow the steps in [Creating an App](/web-checkout/web-checkout-creating-an-app). ![](/images/webcheckout-noapp-project.png) ### 2. Configure Your Payment Provider -Set up Stripe by following the [Stripe Setup](/web-checkout-configuring-stripe-keys-and-settings) guide. +Set up Stripe by following the [Stripe Setup](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) guide. You can skip any iOS/Android related configuration sections since you won't be using a mobile app. @@ -68,8 +68,8 @@ When users land on your redirect URL, use the query parameters to: ## What's Next -- [Creating Products](/web-checkout-adding-a-stripe-product) to add products to your checkout. -- [Creating Campaigns](/web-checkout-creating-campaigns-to-show-paywalls) to show paywalls via web checkout links. Remember, you use these URLs in any way you wish, but the checkout link itself is derived from two things: +- [Creating Products](/web-checkout/web-checkout-adding-a-stripe-product) to add products to your checkout. +- [Creating Campaigns](/web-checkout/web-checkout-creating-campaigns-to-show-paywalls) to show paywalls via web checkout links. Remember, you use these URLs in any way you wish, but the checkout link itself is derived from two things: 1) The web paywall domain you created for the web app (found in Settings -> General). 2) And, the placement which is housed in a campaign. diff --git a/content/shared/3rd-party-analytics/custom-paywall-analytics.mdx b/content/shared/3rd-party-analytics/custom-paywall-analytics.mdx index 6a0dd23c..fb40f9e3 100644 --- a/content/shared/3rd-party-analytics/custom-paywall-analytics.mdx +++ b/content/shared/3rd-party-analytics/custom-paywall-analytics.mdx @@ -3,13 +3,13 @@ title: "Custom Paywall Analytics" description: "Learn how to log events from paywalls, such as a button tap or product change, to forward to your analytics service." --- -You can create customized analytics tracking for any paywall event by using custom placements. With them, you can get callbacks for actions such as interacting with an element on a paywall sent to your [Superwall delegate](/using-superwall-delegate). This can be useful for tracking how users interact with your paywall and how that affects their behavior in other areas of your app. +You can create customized analytics tracking for any paywall event by using custom placements. With them, you can get callbacks for actions such as interacting with an element on a paywall sent to your [Superwall delegate](/sdk/guides/using-superwall-delegate). This can be useful for tracking how users interact with your paywall and how that affects their behavior in other areas of your app. For example, in the paywall below, perhaps you're interested in tracking when people switch the plan from "Standard" and "Pro": ![](/images/3pa_cp_2.jpeg) -You could create a custom placement [tap behavior](/paywall-editor-styling-elements#tap-behaviors) which fires when a segment is tapped: +You could create a custom placement [tap behavior](/dashboard/dashboard-creating-paywalls/paywall-editor-styling-elements#tap-behaviors) which fires when a segment is tapped: ![](/images/3pa_cp_1.jpeg) @@ -30,4 +30,4 @@ extension SuperwallService: SuperwallDelegate { } ``` -For a walkthrough example, check out this [video on YouTube](https://youtu.be/4rM1rGRqDL0). \ No newline at end of file +For a walkthrough example, check out this [video on YouTube](https://youtu.be/4rM1rGRqDL0). diff --git a/content/shared/3rd-party-analytics/index.mdx b/content/shared/3rd-party-analytics/index.mdx index 39195d83..bec6d811 100644 --- a/content/shared/3rd-party-analytics/index.mdx +++ b/content/shared/3rd-party-analytics/index.mdx @@ -6,7 +6,7 @@ description: "Superwall can easily be integrated with 3rd party analytics tools. ### Hooking up Superwall events to 3rd party tools -SuperwallKit automatically tracks some internal events. You can [view the list of events here](/tracking-analytics). We encourage you to also track them in your own analytics by implementing the [Superwall delegate](/using-superwall-delegate). Using the `handleSuperwallEvent(withInfo:)` function, you can forward events to your analytics service: +SuperwallKit automatically tracks some internal events. You can [view the list of events here](/sdk/guides/3rd-party-analytics/tracking-analytics). We encourage you to also track them in your own analytics by implementing the [Superwall delegate](/sdk/guides/using-superwall-delegate). Using the `handleSuperwallEvent(withInfo:)` function, you can forward events to your analytics service: :::ios @@ -105,8 +105,8 @@ handleSuperwallEvent(eventInfo: SuperwallEventInfo) {
- You might also want to set user attribute to allow for [Cohorting in 3rd Party - Tools](/cohorting-in-3rd-party-tools) + You might also want to set user attribute to allow for + [Cohorting in 3rd Party Tools](/sdk/guides/3rd-party-analytics/cohorting-in-3rd-party-tools). Alternatively, if you want typed versions of all these events with associated values, you can access them via `eventInfo.event`: @@ -340,5 +340,5 @@ handleSuperwallEvent(eventInfo: SuperwallEventInfo) { Wanting to use events to see which product was purchased on a paywall? Check out this - [doc](/viewing-purchased-products). + [doc](/sdk/guides/advanced/viewing-purchased-products). diff --git a/content/shared/3rd-party-analytics/tracking-analytics.mdx b/content/shared/3rd-party-analytics/tracking-analytics.mdx index f9ab302a..2a1d496d 100644 --- a/content/shared/3rd-party-analytics/tracking-analytics.mdx +++ b/content/shared/3rd-party-analytics/tracking-analytics.mdx @@ -3,7 +3,7 @@ title: "Superwall Events" description: "The SDK automatically tracks some events, which power the charts in the dashboard. " --- -We encourage you to track them in your own analytics as described in [3rd Party Analytics](/3rd-party-analytics). +We encourage you to track them in your own analytics as described in [3rd Party Analytics](..). The following Superwall events can be used as placements to present paywalls: @@ -16,7 +16,7 @@ The following Superwall events can be used as placements to present paywalls: - `transaction_abandon` - `survey_response` -For more info about how to use these, check out [how to add them using a Placement](/campaigns-placements#adding-a-placement). +For more info about how to use these, check out [how to add them using a Placement](/dashboard/dashboard-campaigns/campaigns-placements#adding-a-placement). The full list of events is as follows: @@ -35,14 +35,14 @@ The full list of events is as follows: | `configRefresh` | When the Superwall configuration is refreshed. | None | | `confirmAllAssignments` | When all experiment assignments are confirmed. | None | | `customPlacement` | When the user taps on an element in the paywall that has a `custom_placement` action. | `["name": String, "params": [String: Any], "paywallInfo": PaywallInfo]` | -| [`deepLink_open`](/campaigns-standard-placements#using-the-deeplink-open-event) | When a user opens the app via a deep link. | `["url": String, "path": String", "pathExtension": String, "lastPathComponent": String, "host": String, "query": String, "fragment": String]` + any query parameters in the deep link URL | +| [`deepLink_open`](/dashboard/dashboard-campaigns/campaigns-standard-placements#using-the-deeplink-open-event) | When a user opens the app via a deep link. | `["url": String, "path": String", "pathExtension": String, "lastPathComponent": String, "host": String, "query": String, "fragment": String]` + any query parameters in the deep link URL | | `device_attributes` | When device attributes are sent to the backend every session. | Includes `app_session_id`, `app_version`, `os_version`, `device_model`, `device_locale`, and various hardware/software details. | | `first_seen` | When the user is first seen in the app, regardless of login status. | Same as `app_install` | | `freeTrial_start` | When a user completes a transaction for a subscription product with an introductory offer. | Same as `subscription_start` | | `identityAlias` | When the user's identity aliases after calling `identify`. | None | | `nonRecurringProduct_purchase` | When the user purchases a non-recurring product. | Same as `subscription_start` | | `paywall_close` | When a paywall is closed (either manually or after a transaction succeeds). | [“paywall_webview_load_complete_time”: String?, “paywall_url”: String, “paywall_response_load_start_time”: String?, “paywall_products_load_fail_time”: String?, “secondary_product_id”: String, “feature_gating”: Int, “paywall_response_load_complete_time”: String?, “is_free_trial_available”: Bool, “is_superwall”: true, “presented_by”: String, “paywall_name”: String, “paywall_response_load_duration”: String?, “paywall_identifier”: String, “paywall_webview_load_start_time”: String?, “paywall_products_load_complete_time”: String?, “paywall_product_ids”: String, “tertiary_product_id”: String, “paywall_id”: String, “app_session_id”: String, “paywall_products_load_start_time”: String?, “primary_product_id”: String, “survey_attached”: Bool, “survey_presentation”: String?] | -| [`paywall_decline`](/campaigns-standard-placements#using-the-paywall-decline-event) | When a user manually dismisses a paywall. | Same as `paywall_close` | +| [`paywall_decline`](/dashboard/dashboard-campaigns/campaigns-standard-placements#using-the-paywall-decline-event) | When a user manually dismisses a paywall. | Same as `paywall_close` | | `paywall_open` | When a paywall is opened. | Same as `paywall_close` | | `paywallPresentationRequest` | When something happened during the paywall presentation, whether a success or failure. | `[“source_event_name”: String, “status”: String, “is_superwall”: true, “app_session_id”: String, “pipeline_type”: String, “status_reason”: String]` | | `paywallProductsLoad_complete` | When the request to load a paywall's products completes. | Same as `paywallResponseLoad_start` | @@ -68,7 +68,7 @@ The full list of events is as follows: | `subscription_start` | When a user completes a transaction for a subscription product without an introductory offer. | [“product_period_days”: String, “product_price”: String, “presentation_source_type”: String?, “paywall_response_load_complete_time”: String?, “product_language_code”: String, “product_trial_period_monthly_price”: String, “paywall_products_load_duration”: String?, “product_currency_symbol”: String, “is_superwall”: true, “app_session_id”: String, “product_period_months”: String, “presented_by_event_id”: String?, “product_id”: String, “trigger_session_id”: String, “paywall_webview_load_complete_time”: String?, “paywall_response_load_start_time”: String?, “product_raw_trial_period_price”: String, “feature_gating”: Int, “paywall_id”: String, “product_trial_period_daily_price”: String, “product_period_years”: String, “presented_by”: String, “product_period”: String, “paywall_url”: String, “paywall_name”: String, “paywall_identifier”: String, “paywall_products_load_start_time”: String?, “product_trial_period_months”: String, “product_currency_code”: String, “product_period_weeks”: String, “product_periodly”: String, “product_trial_period_text”: String, “paywall_webview_load_start_time”: String?, “paywall_products_load_complete_time”: String?, “primary_product_id”: String, “product_trial_period_yearly_price”: String, “paywalljs_version”: String?, “product_trial_period_years”: String, “tertiary_product_id”: String, “paywall_products_load_fail_time”: String?, “product_trial_period_end_date”: String, “product_weekly_price”: String, “variant_id”: String, “presented_by_event_timestamp”: String?, “paywall_response_load_duration”: String?, “secondary_product_id”: String, “product_trial_period_days”: String, “product_monthly_price”: String, “paywall_product_ids”: String, “product_locale”: String, “product_daily_price”: String, “product_raw_price”: String, “product_yearly_price”: String, “product_trial_period_price”: String, “product_localized_period”: String, “product_identifier”: String, “experiment_id”: String, “is_free_trial_available”: Bool, “product_trial_period_weeks”: String, “paywall_webview_load_duration”: String?, “product_period_alt”: String, “product_trial_period_weekly_price”: String, “presented_by_event_name”: String?] | | `subscriptionStatus_didChange` | When a user's subscription status changes. | `["is_superwall": true, "app_session_id": String, "subscription_status": String]` | | `surveyClose` | When the user chooses to close a survey instead of responding. | None | -| [`survey_response`](/campaigns-standard-placements#using-the-survey-response-event) | When a user responds to a paywall survey. | `["survey_selected_option_title": String, "survey_custom_response": String, "survey_id": String, "survey_assignment_key": String, "survey_selected_option_id": String]` | +| [`survey_response`](/dashboard/dashboard-campaigns/campaigns-standard-placements#using-the-survey-response-event) | When a user responds to a paywall survey. | `["survey_selected_option_title": String, "survey_custom_response": String, "survey_id": String, "survey_assignment_key": String, "survey_selected_option_id": String]` | | `touches_began` | When the user touches the app's UIWindow for the first time (if tracked by a campaign). | Same as `app_install` | | `transaction_abandon` | When the user cancels a transaction. | Same as `subscription_start` | | `transaction_complete` | When the user completes checkout and any product is purchased. | Same as subscription_start + [“web_order_line_item_id”: String, “app_bundle_id”: String, “config_request_id”: String, “state”: String, “subscription_group_id”: String, “is_upgraded”: String, “expiration_date”: String, “trigger_session_id”: String, “original_transaction_identifier”: String, “id”: String, “transaction_date”: String, “is_superwall”: true, “store_transaction_id”: String, “original_transaction_date”: String, “app_session_id”: String] | diff --git a/content/shared/advanced-configuration.mdx b/content/shared/advanced-configuration.mdx index 06e2ca4a..6bdb91ba 100644 --- a/content/shared/advanced-configuration.mdx +++ b/content/shared/advanced-configuration.mdx @@ -535,7 +535,7 @@ Superwall.shared.subscriptionStatusEmitter.addListener("change", (status) => { ``` ::: -You can do similar tasks with the `SuperwallDelegate`, such as [viewing which product was purchased from a paywall](/3rd-party-analytics#using-events-to-see-purchased-products). +You can do similar tasks with the `SuperwallDelegate`, such as [viewing which product was purchased from a paywall](/sdk/guides/3rd-party-analytics#using-events-to-see-purchased-products). ### Product Overrides diff --git a/content/shared/configuring-the-sdk.mdx b/content/shared/configuring-the-sdk.mdx index 7055e5de..af89b343 100644 --- a/content/shared/configuring-the-sdk.mdx +++ b/content/shared/configuring-the-sdk.mdx @@ -127,7 +127,7 @@ This configures a shared instance of `Superwall`, the primary class for interact greater control over this process (e.g. if you’re using RevenueCat), you’ll want to pass in a `PurchaseController` to your configuration call and manually set the `subscriptionStatus`. You can also pass in `SuperwallOptions` to customize the appearance and behavior of the SDK. See - [Purchases and Subscription Status](/advanced-configuration) for more. + [Purchases and Subscription Status](/sdk/guides/advanced-configuration) for more.
You've now configured Superwall! @@ -146,4 +146,4 @@ For further help, check out our [Flutter example apps](https://github.com/superw :::expo For further help, check out our [React Native example apps](https://github.com/superwall/react-native-superwall/tree/main/example) for working examples of implementing the Superwall SDK. -::: \ No newline at end of file +::: diff --git a/content/shared/configuring/using-superwalloptions.mdx b/content/shared/configuring/using-superwalloptions.mdx index 54071329..0c12eb85 100644 --- a/content/shared/configuring/using-superwalloptions.mdx +++ b/content/shared/configuring/using-superwalloptions.mdx @@ -122,7 +122,7 @@ await Superwall.shared.setLogLevel(LogLevel.Warn) ### Preloading Paywalls -Paywalls are preloaded by default when the app is launched from a cold start. The paywalls that are preloaded are determined by the list of placements that result in a paywall for the user when [registered](/docs/feature-gating). Preloading is smart, only preloading paywalls that belong to audiences that could be matched. +Paywalls are preloaded by default when the app is launched from a cold start. The paywalls that are preloaded are determined by the list of placements that result in a paywall for the user when [registered](/sdk/quickstart/feature-gating). Preloading is smart, only preloading paywalls that belong to audiences that could be matched. Paywalls are cached by default, which means after they load once, they don't need to be reloaded from the network unless you make a change to them on the dashboard. However, if you have a lot of paywalls, preloading may increase network usage of your app on first load of the paywalls and result in slower loading times overall. @@ -957,10 +957,10 @@ Superwall.configure( ::: -For a list of locales that are available on iOS, take a look at [this list](https://gist.github.com/jacobbubu/1836273). You can also preview your paywall in different locales using [In-App Previews](/docs/in-app-paywall-previews). +For a list of locales that are available on iOS, take a look at [this list](https://gist.github.com/jacobbubu/1836273). You can also preview your paywall in different locales using [In-App Previews](/sdk/quickstart/in-app-paywall-previews). ### Game Controller -If you're using a game controller, you can enable this in `SuperwallOptions` too. Check out our [Game Controller Support](/docs/game-controller-support) article. +If you're using a game controller, you can enable this in `SuperwallOptions` too. Check out our [Game Controller Support](/sdk/guides/advanced/game-controller-support) article. Take a look at [SuperwallOptions](https://sdk.superwall.me/documentation/superwallkit/superwalloptions) in our SDK reference for more info. diff --git a/content/shared/custom-paywall-events.mdx b/content/shared/custom-paywall-events.mdx index 6a832b47..756af9a0 100644 --- a/content/shared/custom-paywall-events.mdx +++ b/content/shared/custom-paywall-events.mdx @@ -58,5 +58,5 @@ handleCustomPaywallAction(name: string) {
- Remember to set `Superwall.shared.delegate`! For implementation details, see the [Superwall Delegate](/using-superwall-delegate) guide. + Remember to set `Superwall.shared.delegate`! For implementation details, see the [Superwall Delegate](/sdk/guides/using-superwall-delegate) guide. diff --git a/content/shared/handling-deep-links.mdx b/content/shared/handling-deep-links.mdx index 3e3599fb..a2719cc0 100644 --- a/content/shared/handling-deep-links.mdx +++ b/content/shared/handling-deep-links.mdx @@ -5,7 +5,7 @@ description: "Use handleDeepLink and campaign rules to present paywalls from dee When your app receives a deep link, you might be tempted to write a switch statement that maps each URL to a specific placement and calls `register`. This works, but it means every time you add a new link or change which paywall shows, you have to ship an app update. -A better approach is to pass the URL to `handleDeepLink` and let Superwall's [`deepLink_open`](/campaigns-standard-placements#deeplink_open) standard placement handle the rest. The SDK extracts the URL's path, query parameters, and other components, then fires `deepLink_open` as a placement. You write campaign rules on the dashboard to decide which paywall to show, which means there is no app update required. +A better approach is to pass the URL to `handleDeepLink` and let Superwall's [`deepLink_open`](/dashboard/dashboard-campaigns/campaigns-standard-placements#deeplink_open) standard placement handle the rest. The SDK extracts the URL's path, query parameters, and other components, then fires `deepLink_open` as a placement. You write campaign rules on the dashboard to decide which paywall to show, which means there is no app update required. ## The problem @@ -139,17 +139,17 @@ Once `handleDeepLink` is wired up, the `deepLink_open` placement fires every tim - On the Superwall dashboard, create a new [campaign](/campaigns) — for example, "Deep Link Paywalls". + On the Superwall dashboard, create a new [campaign](/dashboard/dashboard-campaigns/campaigns) — for example, "Deep Link Paywalls". - In your campaign, [add a placement](/campaigns-placements#adding-a-placement) and select `deepLink_open` from the standard placements list. + In your campaign, [add a placement](/dashboard/dashboard-campaigns/campaigns-placements#adding-a-placement) and select `deepLink_open` from the standard placements list. Edit the default audience and add filters that match the URL components you care about. For example, if your deep link is `myapp://promo?offer=summer`: - Set `params.path` **is** `promo` to match the path. - Set `params.offer` **is** `summer` to match the query parameter. - See [`deepLink_open` parameters](/campaigns-standard-placements#deeplink_open) for the full list of available fields. + See [`deepLink_open` parameters](/dashboard/dashboard-campaigns/campaigns-standard-placements#deeplink_open) for the full list of available fields. Click **Paywalls** at the top of the campaign and choose which paywall to present when the filters match. @@ -175,31 +175,31 @@ Each audience evaluates independently. When you need to add a new route, create To use `handleDeepLink`, your app needs deep link handling set up first. If you haven't done that yet, follow the setup guide: :::ios -- [Deep link setup](/ios/quickstart/in-app-paywall-previews) +- [Deep link setup](/sdk/quickstart/in-app-paywall-previews) ::: :::android -- [Deep link setup](/android/quickstart/in-app-paywall-previews) +- [Deep link setup](/sdk/quickstart/in-app-paywall-previews) ::: :::flutter -- [Deep link setup](/flutter/quickstart/in-app-paywall-previews) +- [Deep link setup](/sdk/quickstart/in-app-paywall-previews) ::: :::expo -- [Deep link setup](/expo/quickstart/in-app-paywall-previews) +- [Deep link setup](/sdk/quickstart/in-app-paywall-previews) ::: ## Related deep link guides :::ios -- [Deep Link Setup](/ios/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. -- [Using Superwall Deep Links](/ios/guides/superwall-deep-links) — Trigger paywalls or custom in-app behavior using Superwall-hosted URLs at `*.superwall.app/app-link/...`. +- [Deep Link Setup](/sdk/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. +- [Using Superwall Deep Links](/sdk/guides/superwall-deep-links) — Trigger paywalls or custom in-app behavior using Superwall-hosted URLs at `*.superwall.app/app-link/...`. ::: :::android -- [Deep Link Setup](/android/quickstart/in-app-paywall-previews) — Configure URL schemes and wire `handleDeepLink` into your app so Superwall can respond to incoming links. +- [Deep Link Setup](/sdk/quickstart/in-app-paywall-previews) — Configure URL schemes and wire `handleDeepLink` into your app so Superwall can respond to incoming links. ::: :::flutter -- [Deep Link Setup](/flutter/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. -- [Using Superwall Deep Links](/flutter/guides/superwall-deep-links) — Trigger paywalls or custom in-app behavior using Superwall-hosted URLs at `*.superwall.app/app-link/...`. +- [Deep Link Setup](/sdk/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. +- [Using Superwall Deep Links](/sdk/guides/superwall-deep-links) — Trigger paywalls or custom in-app behavior using Superwall-hosted URLs at `*.superwall.app/app-link/...`. ::: :::expo -- [Deep Link Setup](/expo/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. +- [Deep Link Setup](/sdk/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. ::: diff --git a/content/shared/in-app-paywall-previews.mdx b/content/shared/in-app-paywall-previews.mdx index fa732503..7c938b02 100644 --- a/content/shared/in-app-paywall-previews.mdx +++ b/content/shared/in-app-paywall-previews.mdx @@ -4,8 +4,19 @@ description: "It's important to tell Superwall when a deep link has been opened. --- 1. Previewing paywalls on your device before going live. -2. Deep linking to specific [campaigns](/campaigns). -3. Web Checkout [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) +2. Deep linking to specific [campaigns](/dashboard/dashboard-campaigns/campaigns). +:::ios +3. Web Checkout [Post-Checkout Redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) +::: +:::android +3. Web Checkout [Post-Checkout Redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) +::: +:::flutter +3. Web Checkout [Post-Checkout Redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) +::: +:::expo +3. Web Checkout [Post-Checkout Redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) +::: ## Setup @@ -104,10 +115,10 @@ This configuration allows your app to open in response to a deep link with the f :::ios ### Adding a Universal Link -Only required for [Web Checkout](/web-checkout), otherwise you can skip this step. +Only required for [Web Checkout](/web-checkout/web-checkout-overview), otherwise you can skip this step. -Before configuring in your app, first [create](/web-checkout-creating-an-app) and [configure](/web-checkout-configuring-stripe-keys-and-settings) your Stripe app on the Superwall Dashboard. +Before configuring in your app, first [create](/web-checkout/web-checkout-creating-an-app) and [configure](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) your Stripe app on the Superwall Dashboard. #### Add a new capability in Xcode Select your target in Xcode, then select the **Signing & Capabilities** tab. Click on the **+ Capability** button and select **Associated Domains**. This will add a new capability to your app. @@ -135,10 +146,10 @@ You can verify that your universal links are working a few different ways. Keep :::flutter ### Adding a Universal Link (iOS only) -Only required for [Web Checkout](/web-checkout), otherwise you can skip this step. +Only required for [Web Checkout](/web-checkout/web-checkout-overview), otherwise you can skip this step. -Before configuring in your app, first [create](/web-checkout-creating-an-app) and [configure](/web-checkout-configuring-stripe-keys-and-settings) your Stripe app on the Superwall Dashboard. +Before configuring in your app, first [create](/web-checkout/web-checkout-creating-an-app) and [configure](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) your Stripe app on the Superwall Dashboard. #### Add a new capability in Xcode Select your target in Xcode, then select the **Signing & Capabilities** tab. Click on the **+ Capability** button and select **Associated Domains**. This will add a new capability to your app. @@ -166,10 +177,10 @@ You can verify that your universal links are working a few different ways. Keep :::expo ### Adding a Universal Link (iOS only) -Only required for [Web Checkout](/web-checkout), otherwise you can skip this step. +Only required for [Web Checkout](/web-checkout/web-checkout-overview), otherwise you can skip this step. -Before configuring in your app, first [create](/web-checkout-creating-an-app) and [configure](/web-checkout-configuring-stripe-keys-and-settings) your Stripe app on the Superwall Dashboard. +Before configuring in your app, first [create](/web-checkout/web-checkout-creating-an-app) and [configure](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) your Stripe app on the Superwall Dashboard. #### Add a new capability in Xcode Select your target in Xcode, then select the **Signing & Capabilities** tab. Click on the **+ Capability** button and select **Associated Domains**. This will add a new capability to your app. @@ -602,21 +613,21 @@ On your device, scan this QR code. You can do this via Apple's Camera app. This ## Using Deep Links to Present Paywalls -Deep links can also be used as a placement in a campaign to present paywalls. Simply add `deepLink_open` as an placement, and the URL parameters of the deep link can be used as parameters! You can also use custom placements for this purpose. [Read this doc](/presenting-paywalls-from-one-another) for examples of both. +Deep links can also be used as a placement in a campaign to present paywalls. Simply add `deepLink_open` as an placement, and the URL parameters of the deep link can be used as parameters! You can also use custom placements for this purpose. [Read this doc](/dashboard/guides/presenting-paywalls-from-one-another) for examples of both. ## Related deep link guides :::ios -- [Handling Deep Links](/ios/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from deep links, without hardcoding routing logic in your app. -- [Using Superwall Deep Links](/ios/guides/superwall-deep-links) — Trigger paywalls or custom in-app behavior using Superwall-hosted URLs at `*.superwall.app/app-link/...`. +- [Handling Deep Links](/sdk/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from deep links, without hardcoding routing logic in your app. +- [Using Superwall Deep Links](/sdk/guides/superwall-deep-links) — Trigger paywalls or custom in-app behavior using Superwall-hosted URLs at `*.superwall.app/app-link/...`. ::: :::android -- [Handling Deep Links](/android/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from deep links, without hardcoding routing logic in your app. +- [Handling Deep Links](/sdk/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from deep links, without hardcoding routing logic in your app. ::: :::flutter -- [Handling Deep Links](/flutter/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from deep links, without hardcoding routing logic in your app. -- [Using Superwall Deep Links](/flutter/guides/superwall-deep-links) — Trigger paywalls or custom in-app behavior using Superwall-hosted URLs at `*.superwall.app/app-link/...`. +- [Handling Deep Links](/sdk/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from deep links, without hardcoding routing logic in your app. +- [Using Superwall Deep Links](/sdk/guides/superwall-deep-links) — Trigger paywalls or custom in-app behavior using Superwall-hosted URLs at `*.superwall.app/app-link/...`. ::: :::expo -- [Handling Deep Links](/expo/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from deep links, without hardcoding routing logic in your app. +- [Handling Deep Links](/sdk/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from deep links, without hardcoding routing logic in your app. ::: diff --git a/content/shared/local-resources.mdx b/content/shared/local-resources.mdx index f7cc882c..c1a4b54e 100644 --- a/content/shared/local-resources.mdx +++ b/content/shared/local-resources.mdx @@ -22,7 +22,7 @@ Local resources let your paywalls load bundled assets directly from the device i Choose a stable resource ID for each asset you want to serve locally. That same ID is what you'll select in the [paywall editor](/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources) when configuring image or video components. :::ios -On iOS, local resources are configured on `SuperwallOptions.localResources` before calling [`configure()`](/ios/sdk-reference/configure). +On iOS, local resources are configured on `SuperwallOptions.localResources` before calling [`configure()`](/sdk/sdk-reference/configure). ```swift Swift let options = SuperwallOptions() @@ -44,7 +44,7 @@ Superwall.configure( ::: :::android -On Android, local resources are configured on `Superwall.instance.localResources` after calling [`configure()`](/android/sdk-reference/configure), and before presenting paywalls that depend on those assets. +On Android, local resources are configured on `Superwall.instance.localResources` after calling [`configure()`](/sdk/sdk-reference/configure), and before presenting paywalls that depend on those assets. ```kotlin Kotlin import android.app.Application @@ -129,5 +129,6 @@ If a resource ID does not appear in the editor or fails to load: ## Related -- [`localResources`](/sdk/sdk-reference/localResources): SDK reference for the property on supported platforms. +- [iOS `localResources`](/sdk/sdk-reference/localResources): SDK reference for the iOS property. +- [Android `localResources`](/sdk/sdk-reference/localResources): SDK reference for the Android property. - [Paywall Editor: Local Resources](/dashboard/dashboard-creating-paywalls/paywall-editor-local-resources): How to assign local resource IDs in the dashboard. diff --git a/content/shared/observer-mode.mdx b/content/shared/observer-mode.mdx index a630601b..b9860f3d 100644 --- a/content/shared/observer-mode.mdx +++ b/content/shared/observer-mode.mdx @@ -67,4 +67,4 @@ There are a few things to keep in mind when using observer mode: 1. On iOS, if you're using StoreKit 2, then Superwall solely reports transaction completions. If you're using StoreKit 1, then Superwall will report transaction starts, abandons, and completions. 2. When using observer mode, you can't make purchases using our SDK — such as `Superwall.shared.purchase(aProduct)`. -For more on setting up revenue tracking, check out this [doc](/overview-settings-revenue-tracking). +For more on setting up revenue tracking, check out this [doc](/dashboard/dashboard-settings/overview-settings-revenue-tracking). diff --git a/content/shared/showing-paywalls/feature-gating.mdx b/content/shared/showing-paywalls/feature-gating.mdx index e3e7ba6b..31a318e8 100644 --- a/content/shared/showing-paywalls/feature-gating.mdx +++ b/content/shared/showing-paywalls/feature-gating.mdx @@ -4,7 +4,7 @@ description: "At the heart of Superwall's SDK lies `Superwall.shared.register(pl sidebarTitle: "Overview" --- -This allows you to register a [placement](/campaigns-placements) to access a feature that may or may not be paywalled later in time. It also allows you to choose whether the user can access the feature even if they don't make a purchase. +This allows you to register a [placement](/dashboard/dashboard-campaigns/campaigns-placements) to access a feature that may or may not be paywalled later in time. It also allows you to choose whether the user can access the feature even if they don't make a purchase. Here's an example. @@ -175,7 +175,7 @@ function pressedWorkoutButton() { ### How registering placements presents paywalls -You can configure `"StartWorkout"` to present a paywall by [creating a campaign, adding the placement, and adding a paywall to an audience](/campaigns) in the dashboard. +You can configure `"StartWorkout"` to present a paywall by [creating a campaign, adding the placement, and adding a paywall to an audience](/dashboard/dashboard-campaigns/campaigns) in the dashboard. 1. The SDK retrieves your campaign settings from the dashboard on app launch. 2. When a placement is called that belongs to a campaign, audiences are evaluated _**on device**_ and the user enters an experiment — this means there's no delay between registering a placement and presenting a paywall. @@ -300,7 +300,7 @@ function WorkoutButton() { ### Automatically Registered Placements -The SDK [automatically registers](/tracking-analytics) some internal placements which can be used to present paywalls: +The SDK [automatically registers](/sdk/guides/3rd-party-analytics/tracking-analytics) some internal placements which can be used to present paywalls: ### Register. Everything. @@ -373,4 +373,4 @@ Task { } } ``` -::: \ No newline at end of file +::: diff --git a/content/shared/showing-paywalls/presenting.mdx b/content/shared/showing-paywalls/presenting.mdx index 8ca880a4..211bd579 100644 --- a/content/shared/showing-paywalls/presenting.mdx +++ b/content/shared/showing-paywalls/presenting.mdx @@ -3,7 +3,7 @@ title: "Retrieving and Presenting a Paywall Yourself" description: "Use this technique to get an instance of a paywall manually, using either UIKit, SwiftUI, or Jetpack Compose." --- -If you want complete control over the paywall presentation process, you can use `getPaywall(forPlacement:params:paywallOverrides:delegate:)`. This returns the `UIViewController` subclass `PaywallViewController`, which you can then present however you like. Or, you can use a SwiftUI `View` via `PaywallView`. The following is code is how you'd mimic [register](/docs/feature-gating): +If you want complete control over the paywall presentation process, you can use `getPaywall(forPlacement:params:paywallOverrides:delegate:)`. This returns the `UIViewController` subclass `PaywallViewController`, which you can then present however you like. Or, you can use a SwiftUI `View` via `PaywallView`. The following is code is how you'd mimic [register](/sdk/quickstart/feature-gating): @@ -294,4 +294,4 @@ let stateSub = paywall.$loadingState.sink { state in print(state) } ``` -::: \ No newline at end of file +::: diff --git a/content/shared/showing-paywalls/using-placement-parameters.mdx b/content/shared/showing-paywalls/using-placement-parameters.mdx index 3b8e6019..a38c508b 100644 --- a/content/shared/showing-paywalls/using-placement-parameters.mdx +++ b/content/shared/showing-paywalls/using-placement-parameters.mdx @@ -54,7 +54,7 @@ Superwall.shared.register({ -The `via` parameter could now be used all throughout Superwall. You could create a new [audience](/campaigns-audience) which has filters for each place users logged caffeine from, and unique paywalls for each of them. +The `via` parameter could now be used all throughout Superwall. You could create a new [audience](/dashboard/dashboard-campaigns/campaigns-audience) which has filters for each place users logged caffeine from, and unique paywalls for each of them. Parameter placements can be used in four primary ways: @@ -62,16 +62,16 @@ Parameter placements can be used in four primary ways: ![](/images/placementParamVia.png) -2. **Templating in Text:** Parameters are available in our [paywall editor](/paywall-editor-overview), so you can easily use them in text components too: +2. **Templating in Text:** Parameters are available in our [paywall editor](/dashboard/dashboard-creating-paywalls/paywall-editor-overview), so you can easily use them in text components too: ``` Hey {{user.firstName}}! FitnessAI offers tons of {{user.fitnessGoal}} workouts to help you reach your goals :) ``` -3. **Interfacing with Analytics:** Another common scenario is cohorting with your own analytics. See this [doc](/cohorting-in-3rd-party-tools) for more. +3. **Interfacing with Analytics:** Another common scenario is cohorting with your own analytics. See [iOS](/sdk/guides/3rd-party-analytics/cohorting-in-3rd-party-tools), [Android](/sdk/guides/3rd-party-analytics/cohorting-in-3rd-party-tools), [Flutter](/sdk/guides/3rd-party-analytics/cohorting-in-3rd-party-tools), or [Expo](/sdk/guides/3rd-party-analytics/cohorting-in-3rd-party-tools) for more. -4. **Reference them on Paywalls:** Display dynamic images by constructing URLs based on placement parameters, change copy on a paywall to emphasize a feature, and more. For more, check out the docs over [creating custom variables](/paywall-editor-variables#custom-variables). +4. **Reference them on Paywalls:** Display dynamic images by constructing URLs based on placement parameters, change copy on a paywall to emphasize a feature, and more. For more, check out the docs over [creating custom variables](/dashboard/dashboard-creating-paywalls/paywall-editor-variables#custom-variables). To see an example of using placement parameters in a paywall, see this video: - \ No newline at end of file + diff --git a/content/shared/showing-paywalls/using-the-presentation-handler.mdx b/content/shared/showing-paywalls/using-the-presentation-handler.mdx index 27954b8f..9935b67f 100644 --- a/content/shared/showing-paywalls/using-the-presentation-handler.mdx +++ b/content/shared/showing-paywalls/using-the-presentation-handler.mdx @@ -231,5 +231,5 @@ function PaywallButton() { Wanting to see which product was just purchased from a paywall? Use `onDismiss` and the `result` parameter. Or, you can use the - [SuperwallDelegate](/3rd-party-analytics#using-events-to-see-purchased-products). - \ No newline at end of file + [SuperwallDelegate](/sdk/guides/3rd-party-analytics#using-events-to-see-purchased-products). + diff --git a/content/shared/showing-paywalls/viewing-purchased-products.mdx b/content/shared/showing-paywalls/viewing-purchased-products.mdx index 2b7dadae..e663fd51 100644 --- a/content/shared/showing-paywalls/viewing-purchased-products.mdx +++ b/content/shared/showing-paywalls/viewing-purchased-products.mdx @@ -161,7 +161,7 @@ const Home = () => { ### Use `SuperwallDelegate` -Next, the [SuperwallDelegate](/using-superwall-delegate) offers up much more information, and can inform you of virtually any Superwall event that occurred: +Next, the [SuperwallDelegate](/sdk/guides/using-superwall-delegate) offers up much more information, and can inform you of virtually any Superwall event that occurred: ```swift Swift @@ -384,7 +384,7 @@ export default function App() { ### Use a purchase controller -If you are controlling the purchasing pipeline yourself via a [purchase controller](/advanced-configuration), then naturally the purchased product is available: +If you are controlling the purchasing pipeline yourself via a [purchase controller](/sdk/guides/advanced-configuration), then naturally the purchased product is available: ```swift Swift diff --git a/content/shared/superwall-deep-links.mdx b/content/shared/superwall-deep-links.mdx index caaf6de4..1f6b628c 100644 --- a/content/shared/superwall-deep-links.mdx +++ b/content/shared/superwall-deep-links.mdx @@ -8,10 +8,10 @@ A Superwall Deep Link is a URL hosted at `https://.superwall.app/app- ## Prerequisites :::ios -1. Set up [deep link handling](/ios/quickstart/in-app-paywall-previews) +1. Set up [deep link handling](/sdk/quickstart/in-app-paywall-previews) ::: :::flutter -1. Set up [deep link handling](/flutter/quickstart/in-app-paywall-previews) +1. Set up [deep link handling](/sdk/quickstart/in-app-paywall-previews) ::: 2. Create a [Web Checkout app](/web-checkout/web-checkout-creating-an-app), even if you do not plan to charge through Web Checkout, this provisions the `*.superwall.app` domain that powers Superwall Deep Links. @@ -118,10 +118,10 @@ Keep your own routing logic in place for non-Superwall URLs and for any addition ## Related deep link guides :::ios -- [Deep Link Setup](/ios/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. -- [Handling Deep Links](/ios/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from your own deep links, without hardcoding routing logic. +- [Deep Link Setup](/sdk/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. +- [Handling Deep Links](/sdk/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from your own deep links, without hardcoding routing logic. ::: :::flutter -- [Deep Link Setup](/flutter/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. -- [Handling Deep Links](/flutter/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from your own deep links, without hardcoding routing logic. +- [Deep Link Setup](/sdk/quickstart/in-app-paywall-previews) — Configure URL schemes, universal links, and wire `handleDeepLink` into your app so Superwall can respond to incoming links. +- [Handling Deep Links](/sdk/guides/handling-deep-links) — Use `handleDeepLink` with the `deepLink_open` standard placement and dashboard campaign rules to present paywalls from your own deep links, without hardcoding routing logic. ::: diff --git a/content/shared/tracking-subscription-state.mdx b/content/shared/tracking-subscription-state.mdx index 0c883db5..932d611f 100644 --- a/content/shared/tracking-subscription-state.mdx +++ b/content/shared/tracking-subscription-state.mdx @@ -29,7 +29,7 @@ case .unknown: } ``` -One natural way to tie the logic of your model together with Superwall's subscription status is by having your own model conform to the [Superwall Delegate](/using-superwall-delegate): +One natural way to tie the logic of your model together with Superwall's subscription status is by having your own model conform to the [Superwall Delegate](/sdk/guides/using-superwall-delegate): ```swift @Observable @@ -187,7 +187,7 @@ function App() { ::: ### Superwall checks subscription status for you -Remember that the Superwall SDK uses its [audience filters](/campaigns-audience#matching-to-entitlements) for a similar purpose. You generally don't need to wrap your calls registering placements around `if` statements checking if a user is on a paid plan, like this: +Remember that the Superwall SDK uses its [audience filters](/dashboard/dashboard-campaigns/campaigns-audience#matching-to-entitlements) for a similar purpose. You generally don't need to wrap your calls registering placements around `if` statements checking if a user is on a paid plan, like this: ```swift // Unnecessary @@ -200,4 +200,4 @@ In your audience filters, you can specify whether or not the subscription state ![](/images/entitlementCheck.png) -...which eliminates the needs for code like the above. This keeps you code base cleaner, and the responsibility of "Should this paywall show" within the Superwall campaign platform as it was designed. \ No newline at end of file +...which eliminates the needs for code like the above. This keeps you code base cleaner, and the responsibility of "Should this paywall show" within the Superwall campaign platform as it was designed. diff --git a/content/shared/user-management/setting-user-properties.mdx b/content/shared/user-management/setting-user-properties.mdx index 99fbe04c..46f66083 100644 --- a/content/shared/user-management/setting-user-properties.mdx +++ b/content/shared/user-management/setting-user-properties.mdx @@ -3,7 +3,7 @@ title: "Setting User Attributes" description: "Customize paywalls and target users by setting user attributes" --- -By setting user attributes, you can display information about the user on the paywall. You can also define [audiences](/campaigns-audience) in a campaign to determine which paywall to show to a user, based on their user attributes. +By setting user attributes, you can display information about the user on the paywall. You can also define [audiences](/dashboard/dashboard-campaigns/campaigns-audience) in a campaign to determine which paywall to show to a user, based on their user attributes. If a paywall uses the **Set user attributes** action, the merged attributes are sent back to your app via `SuperwallDelegate.userAttributesDidChange(newAttributes:)`. @@ -111,4 +111,4 @@ already has a value for a given property, the old value is overwritten. Other existing properties will not be affected. To unset/delete a value, you can pass `nil` for the value. -You can reference user attributes in [audience filters](/campaigns-audience) to help decide when to display your paywall. When you configure your paywall, you can also reference the user attributes in its text variables. For more information on how to that, see [Configuring a Paywall](/paywall-editor-overview). +You can reference user attributes in [audience filters](/dashboard/dashboard-campaigns/campaigns-audience) to help decide when to display your paywall. When you configure your paywall, you can also reference the user attributes in its text variables. For more information on how to that, see [Configuring a Paywall](/dashboard/dashboard-creating-paywalls/paywall-editor-overview). diff --git a/content/shared/using-revenuecat.mdx b/content/shared/using-revenuecat.mdx index ce59eab6..3c5b2cb2 100644 --- a/content/shared/using-revenuecat.mdx +++ b/content/shared/using-revenuecat.mdx @@ -755,7 +755,7 @@ export default function App() { ``` ::: -As discussed in [Purchases and Subscription Status](/docs/advanced-configuration), this `CustomPurchaseControllerProvider` is responsible for handling the subscription-related logic using the modern hooks-based approach. +As discussed in [Purchases and Subscription Status](/sdk/guides/advanced-configuration), this `CustomPurchaseControllerProvider` is responsible for handling the subscription-related logic using the modern hooks-based approach. ### 2. Configure Superwall (Continued) @@ -1007,7 +1007,7 @@ export class RCPurchaseController extends PurchaseController { ``` ::: -As discussed in [Purchases and Subscription Status](/docs/advanced-configuration), this `PurchaseController` is responsible for handling the subscription-related logic. Take a few moments to look through the code to understand how it does this. +As discussed in [Purchases and Subscription Status](/sdk/guides/advanced-configuration), this `PurchaseController` is responsible for handling the subscription-related logic. Take a few moments to look through the code to understand how it does this. ### 2. Configure Superwall diff --git a/content/shared/using-superwall-delegate.mdx b/content/shared/using-superwall-delegate.mdx index 6fd79933..c2b2a361 100644 --- a/content/shared/using-superwall-delegate.mdx +++ b/content/shared/using-superwall-delegate.mdx @@ -96,9 +96,9 @@ The new hooks-based SDK uses `useSuperwallEvents()` instead of the delegate patt Some common use cases for using the Superwall delegate include: -- **Custom actions:** [Respond to custom tap actions from a paywall.](/custom-paywall-events#custom-paywall-actions) -- **Respond to purchases:** [See which product was purchased from the presented paywall.](/viewing-purchased-products) -- **Analytics:** [Forward events from Superwall to your own analytics.](/3rd-party-analytics) +- **Custom actions:** [Respond to custom tap actions from a paywall.](/sdk/guides/advanced/custom-paywall-actions#custom-paywall-actions) +- **Respond to purchases:** [See which product was purchased from the presented paywall.](/sdk/guides/advanced/viewing-purchased-products) +- **Analytics:** [Forward events from Superwall to your own analytics.](/sdk/guides/3rd-party-analytics) Below are some commonly used implementations when using the delegate. @@ -224,7 +224,7 @@ export class MySuperwallDelegate extends SuperwallDelegate { ### Paywall Custom Actions -Using the [custom tap action](/custom-paywall-events), you can respond to any arbitrary event from a paywall: +Using the [custom tap action](/sdk/guides/advanced/custom-paywall-actions#custom-paywall-actions), you can respond to any arbitrary event from a paywall: :::ios @@ -315,7 +315,7 @@ function MyApp() { ### Subscription status changes -You can be informed of subscription status changes using the delegate. If you need to set or handle the status on your own, use a [purchase controller](/advanced-configuration) — this function is only for informational, tracking or similar purposes: +You can be informed of subscription status changes using the delegate. If you need to set or handle the status on your own, use a [purchase controller](/sdk/guides/advanced-configuration) — this function is only for informational, tracking or similar purposes: :::ios diff --git a/content/shared/vibe-coding-guide-base.mdx b/content/shared/vibe-coding-guide-base.mdx index 1843d7df..fe2ebfb7 100644 --- a/content/shared/vibe-coding-guide-base.mdx +++ b/content/shared/vibe-coding-guide-base.mdx @@ -1,7 +1,7 @@ This is an all-in-one prompt to get the Superwall SDK set up in your app. ## Prerequisites -We recommend setting up the Superwall Docs MCP for any further setup/questions, but it is not required. If you want it, ask me to add it and refer me to the docs: https://superwall.com/docs/sdk/guides/vibe-coding +We recommend setting up the Superwall Docs MCP for any further setup/questions, but it is not required. If you want it, ask me to add it and refer me to [the docs](/sdk/guides/vibe-coding#superwall-docs-mcp). ## Integration Steps 1. Install the Superwall SDK diff --git a/content/shared/web-checkout/index.mdx b/content/shared/web-checkout/index.mdx index d3c69101..f91732c6 100644 --- a/content/shared/web-checkout/index.mdx +++ b/content/shared/web-checkout/index.mdx @@ -8,10 +8,33 @@ description: "Integrate Web Checkout into your app with the Superwall SDK" 2. [Add web products to your paywall](/web-checkout/web-checkout-direct-stripe-checkout) ## SDK Setup +:::ios 1. [Set up deep links](/sdk/quickstart/in-app-paywall-previews) 2. [Handle Post-Checkout redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) 3. **Only if you're using RevenueCat:** [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) 4. **Only if you're using your own PurchaseController:** [Redeeming In-App](/sdk/guides/web-checkout/linking-membership-to-iOS-app) +::: + +:::android +1. [Set up deep links](/sdk/quickstart/in-app-paywall-previews) +2. [Handle Post-Checkout redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) +3. **Only if you're using RevenueCat:** [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) +4. **Only if you're using your own PurchaseController:** [Redeeming In-App](/sdk/guides/web-checkout/linking-membership-to-iOS-app) +::: + +:::flutter +1. [Set up deep links](/sdk/quickstart/in-app-paywall-previews) +2. [Handle Post-Checkout redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) +3. **Only if you're using RevenueCat:** [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) +4. **Only if you're using your own PurchaseController:** [Redeeming In-App](/sdk/guides/web-checkout/linking-membership-to-iOS-app) +::: + +:::expo +1. [Set up deep links](/sdk/quickstart/in-app-paywall-previews) +2. [Handle Post-Checkout redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) +3. **Only if you're using RevenueCat:** [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) +4. **Only if you're using your own PurchaseController:** [Redeeming In-App](/sdk/guides/web-checkout/linking-membership-to-iOS-app) +::: ## Testing 1. [Testing Purchases](/web-checkout/web-checkout-testing-purchases) @@ -23,4 +46,4 @@ If a user has issues accessing their subscription in your app after paying via w For example: `http://yourapp.superwall.app/manage` ## FAQ -[Web Checkout FAQ](/web-checkout/web-checkout-faq) \ No newline at end of file +[Web Checkout FAQ](/web-checkout/web-checkout-faq) diff --git a/content/shared/web-checkout/linking-membership-to-iOS-app.mdx b/content/shared/web-checkout/linking-membership-to-iOS-app.mdx index 8049358e..75d672f4 100644 --- a/content/shared/web-checkout/linking-membership-to-iOS-app.mdx +++ b/content/shared/web-checkout/linking-membership-to-iOS-app.mdx @@ -4,13 +4,13 @@ description: "Handle a deep link in your app and use the delegate methods." --- After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. -Please follow our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) guide to handle this user experience. +Please follow our [Post-Checkout Redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) guide to handle this user experience. If you're using Superwall to handle purchases, then you don't need to do anything here. -If you're using your own `PurchaseController`, you will need to update the subscription status with the redeemed web entitlements. If you're using RevenueCat, you should follow our [Using RevenueCat](/web-checkout-using-revenuecat) guide. +If you're using your own `PurchaseController`, you will need to update the subscription status with the redeemed web entitlements. If you're using RevenueCat, you should follow our [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) guide. ### Using a PurchaseController diff --git a/content/shared/web-checkout/post-checkout-redirecting.mdx b/content/shared/web-checkout/post-checkout-redirecting.mdx index 4bc717f3..1911b0f6 100644 --- a/content/shared/web-checkout/post-checkout-redirecting.mdx +++ b/content/shared/web-checkout/post-checkout-redirecting.mdx @@ -7,7 +7,7 @@ After a user completes a web purchase, Superwall needs to redirect them back to ## Post-Purchase Behavior Modes -You can configure how users are redirected after checkout in your [Application Settings](/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): +You can configure how users are redirected after checkout in your [Application Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior): ### Redeem Mode (Default) @@ -49,15 +49,46 @@ You'll need to implement your own logic to handle the redirect and deep link use Whether you're showing a checkout page in Safari or using the In-App Browser, the Superwall SDK relies on deep links to redirect back to your app. #### Prerequisites -1. [Configuring Stripe Keys and Settings](/web-checkout-configuring-stripe-keys-and-settings) -2. [Deep Links](/in-app-paywall-previews) +1. [Configuring Stripe Keys and Settings](/web-checkout/web-checkout-configuring-stripe-keys-and-settings) +:::ios +2. [Deep Links](/sdk/quickstart/in-app-paywall-previews) +::: + +:::android +2. [Deep Links](/sdk/quickstart/in-app-paywall-previews) +::: + +:::flutter +2. [Deep Links](/sdk/quickstart/in-app-paywall-previews) +::: + +:::expo +2. [Deep Links](/sdk/quickstart/in-app-paywall-previews) +::: If you're not using Superwall to handle purchases, then you'll need to follow extra steps to redeem the web purchase in your app. -- [Using RevenueCat](/web-checkout-using-revenuecat) -- [Using a PurchaseController](/web-checkout-linking-membership-to-iOS-app#using-a-purchasecontroller) +:::ios +- [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) +- [Using a PurchaseController](/sdk/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller) +::: + +:::android +- [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) +- [Using a PurchaseController](/sdk/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller) +::: + +:::flutter +- [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) +- [Using a PurchaseController](/sdk/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller) +::: + +:::expo +- [Using RevenueCat](/sdk/guides/web-checkout/using-revenuecat) +- [Using a PurchaseController](/sdk/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller) +::: --- @@ -123,4 +154,4 @@ func didRedeemLink(result: RedemptionResult) { break } } -``` \ No newline at end of file +``` diff --git a/content/shared/web-checkout/using-revenuecat.mdx b/content/shared/web-checkout/using-revenuecat.mdx index a1dbee5c..f9608527 100644 --- a/content/shared/web-checkout/using-revenuecat.mdx +++ b/content/shared/web-checkout/using-revenuecat.mdx @@ -3,7 +3,7 @@ title: "Using RevenueCat" description: "Handle a deep link in your app and use the delegate methods to link web checkouts with RevenueCat." --- -After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. Please follow our [Post-Checkout Redirecting](/web-checkout-post-checkout-redirecting) guide to handle this user experience. +After purchasing from a web paywall, the user will be redirected to your app by a deep link to redeem their purchase on device. Please follow our [Post-Checkout Redirecting](/sdk/guides/web-checkout/post-checkout-redirecting) guide to handle this user experience. If you're using Superwall to handle purchases, then you don't need to do anything here. @@ -11,7 +11,7 @@ After purchasing from a web paywall, the user will be redirected to your app by You only need to use a `PurchaseController` if you want end-to-end control of the purchasing pipeline. The recommended way to use RevenueCat with Superwall is by putting it in observer mode. -If you're using your own `PurchaseController`, you should follow our [Redeeming In-App](/web-checkout-linking-membership-to-iOS-app) guide. +If you're using your own `PurchaseController`, you should follow our [Redeeming In-App](/sdk/guides/web-checkout/linking-membership-to-iOS-app) guide. ### Using a PurchaseController with RevenueCat diff --git a/package.json b/package.json index 6b269d35..ac2eb69f 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "generate:changelog": "bun run scripts/generate-changelog.ts", "download:references": "bun run scripts/download-references.ts", + "check:links:source": "bun run scripts/check-source-links.ts", "test": "bun test", "predev": "bun run scripts/copy-docs-images.cjs", "dev": "vite dev", @@ -68,6 +69,7 @@ "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.4", + "next-validate-link": "^1.6.4", "oxfmt": "^0.36.0", "oxlint": "^1.51.0", "srvx": "^0.11.8", diff --git a/scripts/check-source-links.ts b/scripts/check-source-links.ts new file mode 100644 index 00000000..1d7e0647 --- /dev/null +++ b/scripts/check-source-links.ts @@ -0,0 +1,47 @@ +import path from "node:path"; + +import { printErrors, readFiles, scanURLs, validateFiles } from "next-validate-link"; + +const docsRoot = path.resolve("content/docs"); + +function pathToUrl(filePath: string) { + const relativePath = path.relative(docsRoot, path.resolve(filePath)); + if (relativePath.startsWith("..")) return; + + const normalized = relativePath.replaceAll(path.sep, "/"); + const withoutExtension = normalized.replace(/\.mdx?$/, ""); + const urlPath = withoutExtension.replace(/\/index$/, ""); + + return urlPath.length === 0 ? "/" : `/${urlPath}`; +} + +const docsFiles = await readFiles("content/docs/**/*.mdx", { pathToUrl }); +const sharedFiles = await readFiles("content/shared/**/*.mdx"); + +const basePages = docsFiles.flatMap((file) => { + if (!file.url) return []; + return file.url === "/" ? ["", "docs"] : [file.url.slice(1), `docs/${file.url.slice(1)}`]; +}); + +const pages = Array.from( + new Set([...basePages, "ai", "sdk", "docs/sdk", "sdk/[[..._splat]]", "docs/sdk/[[..._splat]]"]), +).filter(Boolean); + +const scanned = await scanURLs({ + preset: "tanstack-start", + pages, +}); + +const docsResults = await validateFiles(docsFiles, { + scanned, + pathToUrl, + ignoreFragment: true, +}); + +const sharedResults = await validateFiles(sharedFiles, { + scanned, + checkRelativeUrls: false, + ignoreFragment: true, +}); + +printErrors([...docsResults, ...sharedResults], true); diff --git a/src/components/SdkSelectorPage.tsx b/src/components/SdkSelectorPage.tsx new file mode 100644 index 00000000..0983fe03 --- /dev/null +++ b/src/components/SdkSelectorPage.tsx @@ -0,0 +1,143 @@ +import { Link, useRouterState } from "@tanstack/react-router"; +import { HomeLayout } from "fumadocs-ui/layouts/home"; +import type { ReactNode, SVGProps } from "react"; +import { buildCanonicalUrl, SITE_NAME } from "@/lib/metadata"; +import { baseOptions } from "@/lib/layout.shared"; +import { SDK_SELECTOR_SLUGS, type SdkSlug, replaceSdkPlaceholder } from "@/lib/sdk-navigation"; +import { buildDocsPath, toRouterPath } from "@/lib/url-base"; + +function AppleIcon(props: SVGProps) { + return ( + + + + + ); +} + +function AndroidIcon(props: SVGProps) { + return ( + + + + ); +} + +function FlutterIcon(props: SVGProps) { + return ( + + + + ); +} + +function ExpoIcon(props: SVGProps) { + return ( + + + + ); +} + +const SDK_OPTION_META: Record< + (typeof SDK_SELECTOR_SLUGS)[number], + { + title: string; + icon: ReactNode; + } +> = { + ios: { + title: "iOS", + icon: , + }, + android: { + title: "Android", + icon: , + }, + flutter: { + title: "Flutter", + icon: , + }, + expo: { + title: "Expo", + icon: , + }, +}; + +function buildCardTarget(requestedPath: string, sdk: SdkSlug, suffix: string): string { + const concretePath = replaceSdkPlaceholder(requestedPath, sdk); + return `${concretePath}${suffix}`; +} + +export function buildSdkSelectorMeta(requestedPath: string) { + const title = "Choose your SDK"; + const description = + requestedPath === buildDocsPath("sdk") + ? "SDK docs are shared across platforms. Pick your SDK to continue." + : "SDK docs are shared across platforms. Pick your SDK to open this page for the platform you use."; + const canonicalUrl = buildCanonicalUrl(requestedPath); + + return { + meta: [ + { title: `${title} | ${SITE_NAME}` }, + { name: "description", content: description }, + { property: "og:title", content: `${title} | ${SITE_NAME}` }, + { property: "og:description", content: description }, + { property: "og:url", content: canonicalUrl }, + { name: "twitter:title", content: `${title} | ${SITE_NAME}` }, + { name: "twitter:description", content: description }, + ], + links: [{ rel: "canonical", href: canonicalUrl }], + }; +} + +export function SdkSelectorPage({ requestedPath }: { requestedPath: string }) { + const { searchStr, hash } = useRouterState({ + select: (state) => ({ + searchStr: state.location.searchStr, + hash: state.location.hash, + }), + }); + + const suffix = `${searchStr}${hash ? `#${hash}` : ""}`; + + return ( + +
+
+

+ Choose your SDK +

+
+ +
+ {SDK_SELECTOR_SLUGS.map((sdk) => { + const option = SDK_OPTION_META[sdk]; + const href = buildCardTarget(requestedPath, sdk, suffix); + + return ( + +
+
+ {option.icon} +
+

{option.title}

+
+ + ); + })} +
+
+
+ ); +} diff --git a/src/lib/sdk-navigation.test.ts b/src/lib/sdk-navigation.test.ts new file mode 100644 index 00000000..a3ac33a4 --- /dev/null +++ b/src/lib/sdk-navigation.test.ts @@ -0,0 +1,99 @@ +import assert from "node:assert/strict"; +import { describe, test } from "node:test"; +import { + getSdkContextFromRouterPath, + isSdkPlaceholderPath, + replaceSdkPlaceholder, + resolveSdkAwareDocsHref, +} from "./sdk-navigation"; + +describe("sdk-navigation helpers", () => { + test("detects sdk context from router paths", () => { + assert.equal(getSdkContextFromRouterPath("/ios/guides/using-superwall-delegate"), "ios"); + assert.equal(getSdkContextFromRouterPath("/docs/flutter/quickstart/install"), "flutter"); + assert.equal( + getSdkContextFromRouterPath("/sdk/guides/advanced/viewing-purchased-products"), + null, + ); + assert.equal(getSdkContextFromRouterPath("/dashboard"), null); + }); + + test("identifies and replaces sdk placeholder paths", () => { + assert.equal( + isSdkPlaceholderPath("/docs/sdk/guides/advanced/viewing-purchased-products"), + true, + ); + assert.equal(isSdkPlaceholderPath("/sdk/guides/advanced/viewing-purchased-products"), true); + assert.equal( + replaceSdkPlaceholder("/docs/sdk/guides/advanced/viewing-purchased-products", "ios"), + "/docs/ios/guides/advanced/viewing-purchased-products", + ); + assert.equal( + replaceSdkPlaceholder("/sdk/guides/advanced/viewing-purchased-products", "expo"), + "/expo/guides/advanced/viewing-purchased-products", + ); + }); + + test("resolves sdk placeholder links to the current sdk context", () => { + const href = "/docs/sdk/guides/advanced/viewing-purchased-products"; + + assert.equal( + resolveSdkAwareDocsHref(href, "/ios/guides/using-superwall-delegate"), + "/docs/ios/guides/advanced/viewing-purchased-products", + ); + assert.equal( + resolveSdkAwareDocsHref(href, "/android/guides/web-checkout/post-checkout-redirecting"), + "/docs/android/guides/advanced/viewing-purchased-products", + ); + assert.equal( + resolveSdkAwareDocsHref(href, "/flutter/quickstart/install"), + "/docs/flutter/guides/advanced/viewing-purchased-products", + ); + assert.equal( + resolveSdkAwareDocsHref(href, "/expo/quickstart/install"), + "/docs/expo/guides/advanced/viewing-purchased-products", + ); + assert.equal( + resolveSdkAwareDocsHref(href, "/react-native/vibe-coding-guide"), + "/docs/react-native/guides/advanced/viewing-purchased-products", + ); + }); + + test("leaves sdk placeholder links alone outside an sdk section", () => { + const href = "/docs/sdk/guides/advanced/viewing-purchased-products"; + + assert.equal(resolveSdkAwareDocsHref(href, "/dashboard/guides/getting-started"), href); + assert.equal(resolveSdkAwareDocsHref(href, "/support/troubleshooting"), href); + }); + + test("preserves query strings and hashes when resolving sdk placeholder links", () => { + assert.equal( + resolveSdkAwareDocsHref( + "/docs/sdk/guides/advanced/viewing-purchased-products?tab=code#purchase-controller", + "/ios/guides/using-superwall-delegate", + ), + "/docs/ios/guides/advanced/viewing-purchased-products?tab=code#purchase-controller", + ); + }); + + test("keeps non-sdk, external, and hash-only links unchanged", () => { + assert.equal( + resolveSdkAwareDocsHref( + "/docs/dashboard/dashboard-campaigns/campaigns", + "/ios/guides/test-mode", + ), + "/docs/dashboard/dashboard-campaigns/campaigns", + ); + assert.equal( + resolveSdkAwareDocsHref( + "https://superwall.com/docs/sdk/guides/advanced/viewing-purchased-products", + "/ios/guides/test-mode", + ), + "https://superwall.com/docs/sdk/guides/advanced/viewing-purchased-products", + ); + assert.equal( + resolveSdkAwareDocsHref("#purchase-controller", "/ios/guides/test-mode"), + "#purchase-controller", + ); + }); +}); diff --git a/src/lib/sdk-navigation.ts b/src/lib/sdk-navigation.ts new file mode 100644 index 00000000..7d4fd30a --- /dev/null +++ b/src/lib/sdk-navigation.ts @@ -0,0 +1,83 @@ +import { DOCS_BASE } from "./url-base"; + +export type SdkSlug = "ios" | "android" | "flutter" | "expo" | "react-native"; + +export const SDK_CONTEXT_SLUGS = [ + "ios", + "android", + "flutter", + "expo", + "react-native", +] as const satisfies readonly SdkSlug[]; + +export const SDK_SELECTOR_SLUGS = [ + "ios", + "android", + "flutter", + "expo", +] as const satisfies readonly Exclude[]; + +const SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:/; +const SDK_CONTEXT_SET = new Set(SDK_CONTEXT_SLUGS); +const SDK_PLACEHOLDER_PATTERN = /^(\/docs)?\/sdk(?=\/|$)/; + +function splitHref(input: string) { + const [, pathname = "", suffix = ""] = input.match(/^([^?#]*)([?#].*)?$/) ?? []; + return { pathname, suffix }; +} + +function stripDocsBase(pathname: string): string { + if (pathname === DOCS_BASE) return "/"; + if (pathname.startsWith(`${DOCS_BASE}/`)) { + return pathname.slice(DOCS_BASE.length) || "/"; + } + return pathname; +} + +export function getSdkContextFromRouterPath(pathname: string): SdkSlug | null { + if (!pathname) return null; + + const docsRelativePath = stripDocsBase(pathname); + const [firstSegment = ""] = docsRelativePath.replace(/^\/+/, "").split("/", 1); + + if (!SDK_CONTEXT_SET.has(firstSegment)) { + return null; + } + + return firstSegment as SdkSlug; +} + +export function isSdkPlaceholderPath(path: string): boolean { + return SDK_PLACEHOLDER_PATTERN.test(path); +} + +export function replaceSdkPlaceholder(path: string, sdk: SdkSlug): string { + if (!isSdkPlaceholderPath(path)) return path; + return path.replace(SDK_PLACEHOLDER_PATTERN, (match) => + match.startsWith(DOCS_BASE) ? `${DOCS_BASE}/${sdk}` : `/${sdk}`, + ); +} + +export function resolveSdkAwareDocsHref(href: string, currentRouterPath?: string | null): string { + const trimmed = href.trim(); + if ( + !trimmed || + trimmed.startsWith("#") || + trimmed.startsWith("//") || + SCHEME_PATTERN.test(trimmed) + ) { + return href; + } + + const { pathname, suffix } = splitHref(trimmed); + if (!isSdkPlaceholderPath(pathname)) { + return href; + } + + const currentSdk = getSdkContextFromRouterPath(currentRouterPath ?? ""); + if (!currentSdk) { + return href; + } + + return `${replaceSdkPlaceholder(pathname, currentSdk)}${suffix}`; +} diff --git a/src/mdx-components.tsx b/src/mdx-components.tsx index a4878c24..145e3aa5 100644 --- a/src/mdx-components.tsx +++ b/src/mdx-components.tsx @@ -28,7 +28,7 @@ import { Wrench, Zap, } from "lucide-react"; -import { Link } from "@tanstack/react-router"; +import { Link, useRouterState } from "@tanstack/react-router"; const LUCIDE_ICON_MAP: Record> = { AlertCircle, @@ -65,6 +65,7 @@ import { GithubInfo as GithubInfoComponent } from "fumadocs-ui/components/github import { TypeTable } from "./components/type-table"; import { Mermaid } from "./components/Mermaid"; import { normalizeDocsInternalHref } from "./lib/docs-url"; +import { resolveSdkAwareDocsHref } from "./lib/sdk-navigation"; import { toRouterPath } from "./lib/url-base"; const Callout = ({ @@ -241,10 +242,15 @@ const Card = ({ } } + const currentRouterPath = useRouterState({ + select: (state) => state.location.pathname, + }); const normalizedHref = normalizeDocsInternalHref(href); - const isExternal = /^https?:\/\//.test(normalizedHref); + const resolvedHref = resolveSdkAwareDocsHref(normalizedHref, currentRouterPath); + const isExternal = /^https?:\/\//.test(resolvedHref); - const className = "px-5 py-4 relative flex flex-col rounded-[var(--radius-lg)] border border-white/10 transition-colors hover:border-white/20 no-underline"; + const className = + "px-5 py-4 relative flex flex-col rounded-[var(--radius-lg)] border border-white/10 transition-colors hover:border-white/20 no-underline"; const content = ( <> @@ -269,14 +275,14 @@ const Card = ({ if (isExternal) { return ( - + {content} ); } return ( - + {content} ); diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index cc79350a..d58db003 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -17,6 +17,8 @@ import { Route as LlmsFullDottxtRouteImport } from './routes/llms-full[.]txt' import { Route as LlmsFullChar123sectionChar125DottxtRouteImport } from './routes/llms-full-{$section}[.]txt' import { Route as SplatRouteImport } from './routes/$' import { Route as IndexRouteImport } from './routes/index' +import { Route as SdkIndexRouteImport } from './routes/sdk/index' +import { Route as SdkSplatRouteImport } from './routes/sdk/$' import { Route as ApiSearchRouteImport } from './routes/api/search' import { Route as ApiFeedbackRouteImport } from './routes/api/feedback' import { Route as Api404ReportRouteImport } from './routes/api/404-report' @@ -65,6 +67,16 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) +const SdkIndexRoute = SdkIndexRouteImport.update({ + id: '/sdk/', + path: '/sdk/', + getParentRoute: () => rootRouteImport, +} as any) +const SdkSplatRoute = SdkSplatRouteImport.update({ + id: '/sdk/$', + path: '/sdk/$', + getParentRoute: () => rootRouteImport, +} as any) const ApiSearchRoute = ApiSearchRouteImport.update({ id: '/api/search', path: '/api/search', @@ -103,6 +115,8 @@ export interface FileRoutesByFullPath { '/api/404-report': typeof Api404ReportRoute '/api/feedback': typeof ApiFeedbackRoute '/api/search': typeof ApiSearchRoute + '/sdk/$': typeof SdkSplatRoute + '/sdk/': typeof SdkIndexRoute '/api/raw/$': typeof ApiRawSplatRoute '/llms.mdx/docs/$': typeof LlmsDotmdxDocsSplatRoute } @@ -118,6 +132,8 @@ export interface FileRoutesByTo { '/api/404-report': typeof Api404ReportRoute '/api/feedback': typeof ApiFeedbackRoute '/api/search': typeof ApiSearchRoute + '/sdk/$': typeof SdkSplatRoute + '/sdk': typeof SdkIndexRoute '/api/raw/$': typeof ApiRawSplatRoute '/llms.mdx/docs/$': typeof LlmsDotmdxDocsSplatRoute } @@ -134,6 +150,8 @@ export interface FileRoutesById { '/api/404-report': typeof Api404ReportRoute '/api/feedback': typeof ApiFeedbackRoute '/api/search': typeof ApiSearchRoute + '/sdk/$': typeof SdkSplatRoute + '/sdk/': typeof SdkIndexRoute '/api/raw/$': typeof ApiRawSplatRoute '/llms.mdx/docs/$': typeof LlmsDotmdxDocsSplatRoute } @@ -151,6 +169,8 @@ export interface FileRouteTypes { | '/api/404-report' | '/api/feedback' | '/api/search' + | '/sdk/$' + | '/sdk/' | '/api/raw/$' | '/llms.mdx/docs/$' fileRoutesByTo: FileRoutesByTo @@ -166,6 +186,8 @@ export interface FileRouteTypes { | '/api/404-report' | '/api/feedback' | '/api/search' + | '/sdk/$' + | '/sdk' | '/api/raw/$' | '/llms.mdx/docs/$' id: @@ -181,6 +203,8 @@ export interface FileRouteTypes { | '/api/404-report' | '/api/feedback' | '/api/search' + | '/sdk/$' + | '/sdk/' | '/api/raw/$' | '/llms.mdx/docs/$' fileRoutesById: FileRoutesById @@ -197,6 +221,8 @@ export interface RootRouteChildren { Api404ReportRoute: typeof Api404ReportRoute ApiFeedbackRoute: typeof ApiFeedbackRoute ApiSearchRoute: typeof ApiSearchRoute + SdkSplatRoute: typeof SdkSplatRoute + SdkIndexRoute: typeof SdkIndexRoute ApiRawSplatRoute: typeof ApiRawSplatRoute LlmsDotmdxDocsSplatRoute: typeof LlmsDotmdxDocsSplatRoute } @@ -259,6 +285,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/sdk/': { + id: '/sdk/' + path: '/sdk' + fullPath: '/sdk/' + preLoaderRoute: typeof SdkIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/sdk/$': { + id: '/sdk/$' + path: '/sdk/$' + fullPath: '/sdk/$' + preLoaderRoute: typeof SdkSplatRouteImport + parentRoute: typeof rootRouteImport + } '/api/search': { id: '/api/search' path: '/api/search' @@ -310,6 +350,8 @@ const rootRouteChildren: RootRouteChildren = { Api404ReportRoute: Api404ReportRoute, ApiFeedbackRoute: ApiFeedbackRoute, ApiSearchRoute: ApiSearchRoute, + SdkSplatRoute: SdkSplatRoute, + SdkIndexRoute: SdkIndexRoute, ApiRawSplatRoute: ApiRawSplatRoute, LlmsDotmdxDocsSplatRoute: LlmsDotmdxDocsSplatRoute, } diff --git a/src/routes/$.tsx b/src/routes/$.tsx index ce460f5e..74cc4f33 100644 --- a/src/routes/$.tsx +++ b/src/routes/$.tsx @@ -19,11 +19,11 @@ import { SITE_NAME, TWITTER_HANDLE, } from "@/lib/metadata"; +import { getSdkContextFromRouterPath, SDK_CONTEXT_SLUGS } from "@/lib/sdk-navigation"; import { buildDocsApiPath } from "@/lib/url-base"; import { staticCacheMiddleware } from "@/lib/static-cache-middleware"; - type DocsLoaderData = { url: string; path: string; @@ -146,17 +146,17 @@ const clientLoader = browserCollections.docs.createClientLoader({ ); }, }); - -const SDK_PREFIXES = new Set(["ios", "android", "flutter", "expo", "react-native"]); const NAVBAR_ONLY_TABS = new Set(["integrations", "support", "changelog"]); +const SDK_CONTEXT_SET = new Set(SDK_CONTEXT_SLUGS); function parseSDKSubPath(url: string): string | null { - const withoutBase = url.replace(/^\/docs\//, ""); - const slashIdx = withoutBase.indexOf("/"); - if (slashIdx === -1) return null; - const prefix = withoutBase.slice(0, slashIdx); - if (!SDK_PREFIXES.has(prefix)) return null; - return withoutBase.slice(slashIdx + 1); + const withoutBase = url.replace(/^\/docs/, "") || "/"; + const currentSdk = getSdkContextFromRouterPath(withoutBase); + if (!currentSdk) return null; + + const sdkPrefix = `/${currentSdk}/`; + if (!withoutBase.startsWith(sdkPrefix)) return null; + return withoutBase.slice(sdkPrefix.length); } function Page() { @@ -179,7 +179,7 @@ function Page() { let { url } = option; if (currentSubPath && option.urls) { - if (SDK_PREFIXES.has(tabPrefix)) { + if (SDK_CONTEXT_SET.has(tabPrefix)) { const candidate = `/docs/${tabPrefix}/${currentSubPath}`; if (option.urls.has(candidate)) { url = candidate; diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index d549f408..6428ed8d 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -21,6 +21,8 @@ import { SITE_NAME, TWITTER_HANDLE, } from "@/lib/metadata"; +import { normalizeDocsInternalHref } from "@/lib/docs-url"; +import { resolveSdkAwareDocsHref } from "@/lib/sdk-navigation"; import { DOCS_BASE, toRouterPath } from "@/lib/url-base"; type RootProviderLinkProps = React.ComponentProps<"a"> & { @@ -49,16 +51,21 @@ const docsFramework = { }, useRouter() { const router = useTanstackRouter(); + const pathname = useRouterState({ + select: (state) => state.location.pathname, + }); return useMemo( () => ({ push(url: string) { - router.navigate({ to: toRouterPath(url) }); + const normalizedHref = normalizeDocsInternalHref(url); + const resolvedHref = resolveSdkAwareDocsHref(normalizedHref, pathname); + router.navigate({ to: toRouterPath(resolvedHref) }); }, refresh() { router.invalidate(); }, }), - [router], + [pathname, router], ); }, useParams() { @@ -67,11 +74,17 @@ const docsFramework = { }; function DocsLink({ href, prefetch = true, ...props }: RootProviderLinkProps) { + const currentRouterPath = useRouterState({ + select: (state) => state.location.pathname, + }); + if (!href) { return ; } - const normalizedTo = toRouterPath(href); + const normalizedHref = normalizeDocsInternalHref(href); + const resolvedHref = resolveSdkAwareDocsHref(normalizedHref, currentRouterPath); + const normalizedTo = toRouterPath(resolvedHref); if ( !normalizedTo || @@ -79,7 +92,7 @@ function DocsLink({ href, prefetch = true, ...props }: RootProviderLinkProps) { normalizedTo.startsWith("//") || /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(normalizedTo) ) { - return ; + return ; } return ( diff --git a/src/routes/index.tsx b/src/routes/index.tsx index fd35f715..075d14ca 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,4 +1,4 @@ -import { createFileRoute, Link } from "@tanstack/react-router"; +import { createFileRoute, Link, useRouterState } from "@tanstack/react-router"; import { HomeLayout } from "fumadocs-ui/layouts/home"; import { baseOptions } from "@/lib/layout.shared"; import { @@ -18,6 +18,8 @@ import { SITE_NAME, TWITTER_HANDLE, } from "@/lib/metadata"; +import { normalizeDocsInternalHref } from "@/lib/docs-url"; +import { resolveSdkAwareDocsHref } from "@/lib/sdk-navigation"; import { buildDocsPath, toRouterPath } from "@/lib/url-base"; export const Route = createFileRoute("/")({ @@ -168,9 +170,15 @@ const sdkCards: DocCard[] = [ ]; function OverviewCard({ title, description, href, icon }: DocCard) { + const currentRouterPath = useRouterState({ + select: (state) => state.location.pathname, + }); + const normalizedHref = normalizeDocsInternalHref(href); + const resolvedHref = resolveSdkAwareDocsHref(normalizedHref, currentRouterPath); + return (
diff --git a/src/routes/sdk/$.tsx b/src/routes/sdk/$.tsx new file mode 100644 index 00000000..b6ecbc04 --- /dev/null +++ b/src/routes/sdk/$.tsx @@ -0,0 +1,17 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { SdkSelectorPage, buildSdkSelectorMeta } from "@/components/SdkSelectorPage"; +import { buildDocsPath } from "@/lib/url-base"; + +export const Route = createFileRoute("/sdk/$")({ + component: RequestedSdkSelectorRoute, + head: ({ params }) => + buildSdkSelectorMeta(buildDocsPath(params._splat ? `sdk/${params._splat}` : "sdk")), +}); + +function RequestedSdkSelectorRoute() { + const params = Route.useParams(); + const requestedSubpath = params._splat ?? null; + const requestedPath = buildDocsPath(requestedSubpath ? `sdk/${requestedSubpath}` : "sdk"); + + return ; +} diff --git a/src/routes/sdk/index.tsx b/src/routes/sdk/index.tsx new file mode 100644 index 00000000..142b7540 --- /dev/null +++ b/src/routes/sdk/index.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { SdkSelectorPage, buildSdkSelectorMeta } from "@/components/SdkSelectorPage"; +import { buildDocsPath } from "@/lib/url-base"; + +export const Route = createFileRoute("/sdk/")({ + component: SdkSelectorRoute, + head: () => buildSdkSelectorMeta(buildDocsPath("sdk")), +}); + +function SdkSelectorRoute() { + return ; +} diff --git a/src/start.test.ts b/src/start.test.ts new file mode 100644 index 00000000..040c195d --- /dev/null +++ b/src/start.test.ts @@ -0,0 +1,22 @@ +import assert from "node:assert/strict"; +import { describe, test } from "node:test"; +import { resolveLegacyRedirect } from "./start"; + +describe("resolveLegacyRedirect", () => { + test("does not redirect sdk placeholder routes", () => { + const requestUrl = new URL( + "https://example.com/docs/sdk/guides/advanced/viewing-purchased-products", + ); + + assert.equal(resolveLegacyRedirect(requestUrl), undefined); + }); + + test("continues redirecting legacy docs routes from the redirect map", () => { + const requestUrl = new URL("https://example.com/docs/troubleshooting"); + + assert.equal( + resolveLegacyRedirect(requestUrl), + "https://support.superwall.com/collections/6437438776-troubleshooting", + ); + }); +}); diff --git a/src/start.ts b/src/start.ts index 9bc3bcce..9171d035 100644 --- a/src/start.ts +++ b/src/start.ts @@ -74,8 +74,7 @@ export function resolveLLMPath(request: Request): string | undefined { const url = new URL(request.url); if (shouldBypassLLMRewrite(url.pathname)) return undefined; - const directRewrite = - rewriteLLMMarkdown(url.pathname) || rewriteLLMMdx(url.pathname); + const directRewrite = rewriteLLMMarkdown(url.pathname) || rewriteLLMMdx(url.pathname); if (directRewrite) return directRewrite; if (!isMarkdownPreferred(request)) return undefined; @@ -83,27 +82,10 @@ export function resolveLLMPath(request: Request): string | undefined { return rewriteLLMPreferred(url.pathname) || undefined; } -function resolveDeprecatedSdkPath(normalizedPath: string): string | undefined { - if (normalizedPath === `${DOCS_BASE}/sdk`) { - return `${DOCS_BASE}/ios`; - } - - if (normalizedPath.startsWith(`${DOCS_BASE}/sdk/`)) { - return `${DOCS_BASE}/ios/${normalizedPath.slice(`${DOCS_BASE}/sdk/`.length)}`; - } - - return undefined; -} - -function resolveLegacyRedirect(requestUrl: URL): string | undefined { +export function resolveLegacyRedirect(requestUrl: URL): string | undefined { if (shouldBypassLegacyRedirect(requestUrl.pathname)) return undefined; const normalizedPath = normalizePathname(requestUrl.pathname); - const sdkFallback = resolveDeprecatedSdkPath(normalizedPath); - if (sdkFallback && !isSelfRedirect(requestUrl, sdkFallback)) { - return sdkFallback; - } - const direct = legacyRedirectRules[normalizedPath]?.redirect.to; if (direct && !isSelfRedirect(requestUrl, direct)) return direct; diff --git a/vite.config.ts b/vite.config.ts index a10df5d3..9f06c7a7 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -64,6 +64,14 @@ export default defineConfig({ prerender: { enabled: true, concurrency: 3, + filter: (page) => { + return !( + page.path === "/sdk" || + page.path.startsWith("/sdk/") || + page.path === "/docs/sdk" || + page.path.startsWith("/docs/sdk/") + ); + }, onSuccess: ({ page }) => { console.log(`Rendered ${page.path}!`); },