Displaying Products

Manage promoted in-app purchases in your app without releasing a new app version

Qonversion SDK provides flexible cross-platform in-app purchase management for your app. You don't need to save App Stores product IDs and respective prices on the client-side.
To be able to manage in-app products promoted in your app from the Qonversion dashboard, you need to configure Products, Offerings, and Permissions in Qonversion.

Discover Product-Center Concept
Create Permissions
Create Offerings
Create Products

Qonversion provides two ways of displaying products:

  • with Qonversion Offerings (recommended)
  • with Qonversion Product directly

We strongly recommend using Qonversion Offerings. This allows to:

  • Change products offered to your users without app release
  • Run A/B tests
  • Store products and experiment results in one place with aggregated data on them

Qonversion Offerings are available for the following SDK versions:

SDK

Version

iOS

2.7.0+

Android

2.3.0+

React Native

2.2.0+

Flutter

2.2.0+

Unity

2.1.0+

👍

Local cache

Qonversion SDK caches the data on products, offerings, and permissions. This data is available in cases the internet connection is lost or any server-side delays. Every time your application is launched, SDK requests actual SkProduct/SkuDetails from the App Store or Google Play to get the most up-to-date product data.

Displaying Products

Use the offerings method to get the wrapper object Offerings:

Qonversion.offerings { (offerings, error) in
  if let products = offerings?.main?.products {
    // Display products for sale
  }
}
[Qonversion offerings:^(QNOfferings * _Nullable offerings, NSError * _Nullable error) {
  if (offerings.main.products.count > 0) {
    // Display products for sale
  }
}];
Qonversion.offerings(new QonversionOfferingsCallback() {
    @Override
    public void onSuccess(@NotNull QOfferings offerings) {
        if (offerings.getMain() != null && !offerings.getMain().getProducts().isEmpty()) {
            // Display products for sale
        }
    }
    @Override
    public void onError(@NotNull QonversionError error) {
        // handle error here
    }
});
Qonversion.offerings(object: QonversionOfferingsCallback {
    override fun onSuccess(offerings: QOfferings) {
        val mainOffering = offerings.main
        if (mainOffering != null && mainOffering.products.isNotEmpty()) {
            // Display products for sale
        }
    }
    override fun onError(error: QonversionError) {
        // handle error here
    }
})
try {
  final QOfferings offerings = await Qonversion.offerings();
  final List<QProduct> products = offerings.main.products;
  if (products.isNotEmpty) {
    // Display your products
  }
} catch (e) {
  print(e);
}
try {
  const offerings = await Qonversion.offerings();
  if (offerings.main != null && offerings.main.products.length > 0) {
    // Display products for sale
  }
} catch (e) {
  // handle error here
}
Qonversion.Offerings((offerings, error) =>
{
   if (error == null)
   {
            Offering mainOffering = offerings.Main;
            if(mainOffering != null)
            {
                List<Product> products = mainOffering.Products;
                if (products.Any())
                {
                    // Display your products
                }
            }
   }
   else
   {
       // Handle the error  
       Debug.Log("Error" + error.ToString());
   }
});

The products are listed in the same order as they were added to the Qonversion Offerings settings.

You can set an offering as the main in Product Center setting. That makes it easier to access it in your code. You can also get an offering by ID (see below), this way it's easier to set up an AB test later on.

Qonversion OfferingsQonversion Offerings

Qonversion Offerings

Qonversion.Product contains the following data:

Property

Description

qonversionID

Qonversion product ID. For example, main

storeID

App Stores Product ID

type

Product type. It can have the following values:

  • trial– Subscription with a trial period
  • directSubscription – Auto-renewable subscription without a trial
  • oneTime – Non-recurring product

duration

Duration of a product. It can have the following values:

  • unknown – for non-renewable purchases
  • weekly
  • monthly
  • 3Months
  • 6Months
  • annual

Please note, there is no 2-months duration option since Google Play does not have that option. Qonversion Product is designed to support cross-platform in-app purchases.

trialDuration

Duration of an introductory offer. It can have the following values:

  • notAvailable - trial period is not available,
  • threeDays
  • week
  • twoWeeks
  • month
  • twoMonths
  • threeMonths
  • sixMonths
  • year
  • other - for cases when the duration is not from enum range. You can check the trial duration directly from the product's skuDetails or skProduct depending on OS.

skProduct

iOS only. Contains SKProduct object received from StoreKit.

skuDetail

Android only. Contains skuDetails object received from Google Billing Client.

prettyPrice

A localized product price with currency symbol provided by Apple or Google.
Can be displayed to a user. For example, $99.99

Get Offering by ID

You can request the specific offering using its ID:

Qonversion.offerings { (offerings, error) in
  if let offering = offerings?.offering(forIdentifier: "discount") {
    // offering is available
  }
}
[Qonversion offerings:^(QNOfferings * _Nullable offerings, NSError * _Nullable error) {
  QNOffering *offering = [offerings offeringForIdentifier:@"discount"];
  if (offering) {
    // offering is available
  }
}];
Qonversion.offerings(new QonversionOfferingsCallback() {
    @Override
    public void onSuccess(@NotNull QOfferings offerings) {
        QOffering offering = offerings.offeringForID("discount");
        if (offering != null) {
            // offering is available
        }
    }
    @Override
    public void onError(@NotNull QonversionError error) {
        // handle error here
    }
});
Qonversion.offerings(object: QonversionOfferingsCallback {
    override fun onSuccess(offerings: QOfferings) {
        val offering = offerings.offeringForID("discount")
        if (offering != null) {
            // offering is available
        }
    }
    override fun onError(error: QonversionError) {
        // handle error here
    }
})
try {
  final QOfferings offerings = await Qonversion.offerings();
  final QOffering discount = offerings.offeringForIdentifier("discount");
  if (discount != null) {
    // Offering is available
    // Display products
  }
} catch (e) {
  print(e);
}
try {
  const offerings = await Qonversion.offerings();
  const specialOffering = offerings.offeringForIdentifier('discount');
  if (specialOffering != null) {
    // offering is available
  }
} catch (e) {
  // handle error here
}
Qonversion.Offerings((offerings, error) =>
{
   if (error == null)
   {
        var discount = offerings.OfferingForID("discount");
        if (discount != null)
        {
           // Offering is available
           // Display products
        }
   }
   else
   {
       // Handle the error  
       Debug.Log("Error" + error.ToString());
   }
});

Get The List of Available Products

Use the products method to get the list of the products available:

Qonversion.products { productsList, error in
    let product = productsList["main"]
    if product?.type == .trial {
    
    }
}
[Qonversion products:^(NSDictionary<NSString *,QNProduct *> * _Nonnull productsList, NSError * _Nullable error) {
  if (error) {
    // Handle error
  }
  QNProduct *product = productsList[@"main"];
  if (product && product.type == QNProductTypeTrial) {

  }
}];
Qonversion.products(new QonversionProductsCallback() {
            @Override
            public void onSuccess(@NotNull Map<String, QProduct> productsList) {
                // handle available products here
            }

            @Override
            public void onError(@NotNull QonversionError error) {
                // handle error here
            }
});
Qonversion.products(callback = object: QonversionProductsCallback {
            override fun onSuccess(products: Map<String, QProduct>) {
                // handle available products here
            }

            override fun onError(error: QonversionError) {
                // handle error here
            }
})
try {
  final Map<String, QProduct> products = await Qonversion.products();
} catch (e) {
  print(e);
}
const products: Map<string, Product> = await Qonversion.products();
Qonversion.Products((products, error) =>
{
   if (error == null)
   {
       // Display products
   }
   else
   {
       // Handle the error  
       Debug.Log("Error" + error.ToString());
   }
});

The completion block returns the following two vars:

  • productsList – dictionary with available products
  • error - error

Qonversion Product IDs are the keys to the productsList dictionary objects.
The values are the objects of Qonversion.Product class.

Product trial or intro eligibility

You can check if a user is eligible for an introductory offer, including a free trial. Users who have not previously used an introductory offer for any products in the same subscription group are eligible for an introductory offer.
See more info here

Sample Apps

iOS, Android, Flutter and React Native sample apps demonstrating in-app subscriptions implementation with Qonversion Product Center.


Did this page help you?