> Portal Navigation:
> 
> - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version.
> - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages).
> - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`).
> - Top-level index of all portals: https://dev.wix.com/docs/llms.txt
> - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt

## Resource: Tutorial | Adjust an App to Different Pricing Plans

## Article: Tutorial | Adjust an App to Different Pricing Plans

## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-adjust-an-app-to-different-pricing-plans.md

## Article Content:

# Tutorial | Adjust an App to Different Pricing Plans

> **Note:** This tutorial uses a Wix CLI app as the example. The pricing-plan logic is the same for apps built with other frameworks. Only the SDK setup and authentication calls differ.

When you publish your app to the Wix App Market, you can provide Wix users with different pricing plans. For example, you can provide a free version of your app and enable users to purchase a premium version with additional features.

If you provide different plans for your app, it’s your responsibility as the developer to code behavior that limits features for certain plans. Your app should also provide calls-to-action to upgrade to paid plans where applicable.

As an example, think of an app that provides Wix users with a site widget that they can place in their online store. When site visitors click on the widget, it randomly generates a discount that the visitor can use when purchasing products. The app also provides a dashboard page where Wix users can adjust the range of discount amounts that can be generated. By default, the app sets a range of 1-5%. Wix users who purchase a paid plan can alter this range.

The end result looks like this:

![Discount generator example widget](https://wixmp-833713b177cebf373f611808.wixmp.com/images/b39abaf836907eaa106b83385cd6d131.gif)

This tutorial focuses on the dashboard code, which provides restrictive logic that prevents free plan Wix users from changing the range while allowing paid plan Wix users to do so.

This tutorial uses the following steps to adjust an app to different pricing plans:

1. [Use the app instance to determine the plan](#step-1--determine-the-plan-using-the-app-instance).
2. [Restrict features based on Wix user plan](#step-2--restrict-features-based-on-wix-user-plan).
3. [Include CTAs to upgrade](#step-3--include-ctas-to-upgrade).

## Before you begin

- Create an app in the app dashboard.
- Select a [business model](https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/about-pricing-plans-and-business-models.md) for your app that involves multiple plans, for example a freemium model with free and paid plans.

## Step 1 | Determine the plan using the app instance

This step shows how to retrieve the app instance information to determine the Wix user's current pricing plan. At the end of this step, you'll have the instance ID and plan information needed to apply feature restrictions.

In order to know which app features to restrict, you need to know the user’s current plan. You can determine the user’s plan from the [app instance](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/about-app-instances?apiView=SDK.md).

Fetch the app instance using the [`getAppInstance()`](https://dev.wix.com/docs/api-reference/app-management/app-instance/get-app-instance?apiView=SDK.md) method. The method returns an object containing information about the instance and the site it’s installed on.

You can call `getAppInstance()` directly in your dashboard page code. From the returned object, you can extract several pieces of information:

- The app instance ID.

- The `isFree` parameter, which indicates if the user is on a free plan or not.

- The `packageName` parameter, which tells you the plan the user selected.

Here’s some sample code that extracts the instance ID and `isFree`:

```javascript
import { appInstances } from '@wix/app-management';

const Index: FC = () => {
    const [instanceId, setInstanceId] = useState<string | null>(null);
    const [isFree, setIsFree] = useState<boolean>(true);
 
    useEffect(() => {
     const initializeInstance = async () => {
      try {
        const response = await appInstances.getAppInstance();

        // Extract the instance ID and free status from the response
        const instanceId = response.instance?.instanceId;
        const isFreeStatus = response.instance?.isFree ?? true;
        
        setInstanceId(instanceId);
        setIsFree(isFreeStatus);

      } catch (error) {
        console.error('Failed to get app instance.', error);
      } finally {
        setIsLoading(false);
      }
    };

    initializeInstance();
  }, []);
}
```

Once you know which plan an app instance uses, you can decide where to apply restrictive logic in your app.

## Step 2 | Restrict features based on Wix user plan

This step demonstrates how to use the plan information to enable or disable app features. At the end of this step, you'll have implemented feature restrictions that differentiate between free and paid plan Wix users.

If you offer a free plan and a single paid plan with your app, you can apply the `isFree` parameter to limit access to certain app features.

The discount generator example sets a default range of percent discounts that can be randomly generated. We only want to allow paid plan users to alter the discount range.

One way to achieve this behavior is to disable the fields that define discount ranges for free plan users, and enable them for paid plans. If you’re building a React component, you can use Wix design system elements to do this easily. Input elements such as [NumberInput](https://www.wix-pages.com/wix-design-system/?path=/story/components-form--numberinput) let you enable or disable a field using the `disabled` boolean.

In the code below, we directly use the `isFree` state to disable the input fields and save button. If a user is using a paid plan, `isFree` is false and the elements are enabled, allowing the user to alter the discount range. In this way, we restrict the ability to change the range to paid users.

```javascript
<Card.Content>
  <Box direction="horizontal" gap="4">
    <FormField label="Min" required>
      <NumberInput
        value={currentFormData.min}
        suffix={<Input.Affix>%</Input.Affix>}
        onChange={(value) => handleInputChange('min', value || 0)}
        defaultValue="1"
        disabled={isFree}
      />
    </FormField>

    <FormField label="Max" required>
      <NumberInput
        value={currentFormData.max}
        suffix={<Input.Affix>%</Input.Affix>}
        onChange={(value) => handleInputChange('max', value || 0)}
        defaultValue="5"
        disabled={isFree}
      />
    </FormField>

    <Button
      onClick={saveFormData}
      disabled={!currentFormData.min || !currentFormData.max || isSaving || isFree}
      prefixIcon={isSaving ? <Loader size="small" /> : <Icons.Saved />}
    >
      Save
    </Button>
  </Box>
</Card.Content>
```

If you offer multiple paid plans, use the `packageName` parameter to create behavior specific to each plan.

## Step 3 | Include CTAs to upgrade

This step shows how to add upgrade prompts for Wix users on free plans. At the end of this step, you'll have clear calls to action that encourage Wix users to upgrade to paid plans.

When you limit certain features by plan, notify the Wix user and include an option for them to upgrade. The call-to-action to upgrade can take any form that suits your app’s UI. In our discount generator example, we include a text button that links to the pricing plan page.

![Discount generator example dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/8d95a68412682b28b88d0ead5ce9d5cc.png)

The CTA appears conditionally, if the `isFree` is true:

```javascript
<Card.Header 
  title="Discount range" 
  subtitle={isFree ? (
    <Box direction="horizontal" gap="1" align="center">
      <span>Upgrade to our premium plan to change the discount range.</span>
      <TextButton size="small" as="a" href="https://example.com" target="_blank">
        Upgrade
      </TextButton>
    </Box>
  ) : undefined}
/>
```

## Complete code

Below is the complete dashboard page code for this example:

```javascript
import React, { type FC, useState, useEffect } from 'react';
import { dashboard } from '@wix/dashboard';
import {
  Button,
  Card,
  FormField,
  Input,
  NumberInput,
  Page,
  Loader,
  WixDesignSystemProvider,
  MessageBoxFunctionalLayout,
  TextButton,
  Box,
} from '@wix/design-system';
import '@wix/design-system/styles.global.css';
import * as Icons from '@wix/wix-ui-icons-common';
import { appInstances } from '@wix/app-management';
import { members } from '@wix/members';

interface FormData {
  min: number;
  max: number;
}

const Index: FC = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [instanceId, setInstanceId] = useState<string | null>(null);
  const [isFree, setIsFree] = useState<boolean>(true);
  const [formDataMap, setFormDataMap] = useState<Map<string, FormData>>(new Map());
  const [currentFormData, setCurrentFormData] = useState<FormData>({ min: 1, max: 5 });
  const [isSaving, setIsSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Initialize on component mount
  useEffect(() => {
    const initializeInstance = async () => {
      try {
        setIsLoading(true);
        const response = await appInstances.getAppInstance();

        // Extract the instance ID and free status from the response
        const instanceId = response.instance?.instanceId;
        const isFreeStatus = response.instance?.isFree ?? true;
        
        setInstanceId(instanceId);
        setIsFree(isFreeStatus);

        // Load any existing form data for this instance
        await loadFormData(instanceId);

      } catch (error) {
        console.error('Failed to get app instance.', error);
      } finally {
        setIsLoading(false);
      }
    };

    initializeInstance();
  }, []);

  // Load existing form data for this instance.
  // Local storage is used here for demo purposes.
  // In production, save to your own backend with proper
  // authentication and database storage.
  const loadFormData = async (instanceId: string) => {
    try {
      const dataKey = `formData_${instanceId}`;
      const savedData = localStorage.getItem(dataKey);
      if (savedData) {
        const parsedData = JSON.parse(savedData);
        const formData = {
          min: parsedData.min || 1,
          max: parsedData.max || 5
        };
        
        // Update the map and current form data
        setFormDataMap(prev => new Map(prev).set(instanceId, formData));
        setCurrentFormData(formData);
        
        dashboard.showToast({
          message: 'Previous form data loaded',
          type: 'standard'
        });
      }
    } catch (error) {
      console.error('Failed to load form data:', error);
    }
  };

  // Save form data per app instance
  const saveFormData = async () => {
    if (!instanceId) return;

    try {
      setIsSaving(true);
      
      // Update the map with current form data
      setFormDataMap(prev => new Map(prev).set(instanceId, currentFormData));
      
      // Local storage is used here for demo purposes.
      // In production, save to your own backend with 
      // proper authentication and database storage.
      const dataKey = `formData_${instanceId}`;
      const dataToSave = {
        ...currentFormData,
        instanceId: instanceId,
        timestamp: new Date().toISOString()
      };
      
      localStorage.setItem(dataKey, JSON.stringify(dataToSave));
      
      dashboard.showToast({
        message: 'Form data saved successfully!',
        type: 'success'
      });

    } catch (error) {
      console.error('Failed to save form data:', error);
      dashboard.showToast({
        message: 'Failed to save form data',
        type: 'error'
      });
    } finally {
      setIsSaving(false);
    }
  };

  const handleInputChange = (field: keyof FormData, value: number) => {
    setCurrentFormData(prev => ({ ...prev, [field]: value }));
  };

  return (
    <WixDesignSystemProvider features={{ newColorsBranding: true }}>
      <Page>
        <Page.Header
          title="Discount Generator Settings"
        />
        <Page.Content>
          <Box width="100%">
            <Card>
              <Card.Header 
                title="Discount range" 
                subtitle={isFree ? (
                  <Box direction="horizontal" gap="1" align="center">
                    <span>Upgrade to our premium plan to change the discount range.</span>
                    <TextButton size="small" as="a" href="https://example.com" target="_blank">
                      Upgrade
                    </TextButton>
                  </Box>
                ) : undefined}
              />
              <Card.Divider />
              <Card.Content>
                <Box direction="horizontal" gap="4">
                  <FormField label="Min" required>
                    <NumberInput
                      value={currentFormData.min}
                      suffix={<Input.Affix>%</Input.Affix>}
                      onChange={(value) => handleInputChange('min', value || 0)}
                      defaultValue="1"
                      disabled={isFree}
                    />
                  </FormField>
                  
                  <FormField label="Max" required>
                    <NumberInput
                      value={currentFormData.max}
                      suffix={<Input.Affix>%</Input.Affix>}
                      onChange={(value) => handleInputChange('max', value || 0)}
                      defaultValue="5"
                      disabled={isFree}
                    />
                  </FormField>

                  <Button
                    onClick={saveFormData}
                    disabled={!currentFormData.min || !currentFormData.max || isSaving || isFree}
                    prefixIcon={isSaving ? <Loader size="small" /> : <Icons.Saved />}
                  >
                    Save
                  </Button>
                </Box>
              </Card.Content>
            </Card>
          </Box>
        </Page.Content>
      </Page>
    </WixDesignSystemProvider>
  );
};

export default Index;
```

## See also

- [About pricing plans and business models](https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/about-pricing-plans-and-business-models.md)
- [About app instances](https://dev.wix.com/docs/build-apps/develop-your-app/access/app-instances/about-app-instances.md)