Quick Start
Quick start guide walks you through setting up and using Qonversion
This quick start guide will help you install and set up Qonversion SDK to manage your in-app purchases and user access. Please note, if you want to use Qonversion for analytics and integrations only, and do not want to change anything in your existing in-app purchase flow, you can jump to the Observer Mode guide.
1. Create a project and register your app
Navigate to your Qonversion account and click the New Project option from the dropdown menu. Provide all of the required information to set up the project. (Check the creating a project guide if needed). One app in Qonversion supports iOS, Android, and web app.
2. Configure Products & Permissions (Entitlements)
-
You should have your in-app purchases created and configured on the App Store Connect and/or Google Play Console.
-
Once your products are created on Apple and/or Google platforms, you should create products in Qonversion and link them to the products on the app stores. Learn more about Qonversion Products here.
3. Configure Offerings
An offering is a group of products that you can offer to a user on a given paywall based on your business logic. For example, you can offer one set of products on a paywall immediately after onboarding and another set of products with discounts later on if a user has not converted.
Qonversion offerings are not required to use Qonversion's Infrastructure Mode, but they enable the following features:
- Run A/B tests
- Change products offered to your users without app release
- Store products and experiment results in one place with aggregated data on them
Learn more about Qonversion Offerings here.
4. Install the SDKs
→ iOS SDK
→ Android SDK
→ Flutter SDK
→ React Native SDK
→ Unity
5. Using the SDKs
Launching
Import the Qonversion SDK in your module that is responsible for launching third-party dependencies:
import Qonversion
#import "Qonversion.h"
import com.qonversion.android.sdk.Qonversion;
import com.qonversion.android.sdk.Qonversion
import 'package:qonversion_flutter/qonversion_flutter.dart';
import Qonversion from 'react-native-qonversion';
using QonversionUnity;
Initialize the SDK:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Qonversion.launch(withKey: "projectKey")
return true
}
#import "Qonversion.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Qonversion launchWithKey:@"projectKey"];
return YES;
}
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
Qonversion.launch(this, "projectKey", false);
}
}
public class App : Application {
override fun onCreate() {
super.onCreate()
Qonversion.launch(this, "projectKey", false)
}
}
Qonversion.launch('projectKey', isObserveMode: false);
Qonversion.launchWithKey('projectKey', false);
private void Start()
{
Qonversion.Launch("projectKey", false);
}
Set User ID (Optional)
To match user events from Qonversion to the same users on other platforms, you need to set the same user ID on both of them:
Qonversion.setProperty(.userID, value: "yourSideUserId")
[Qonversion setProperty:QNPropertyUserID value:@"yourSideUserId"];
Qonversion.setProperty(QUserProperties.CustomUserId, "yourSideUserID");
Qonversion.setProperty(QUserProperties.CustomUserId, "yourSideUserID")
Qonversion.setProperty(QUserProperty.customUserId, 'yourSideUserId');
Qonversion.setProperty(Property.CUSTOM_USER_ID, 'yourSideUserId');
Qonversion.SetProperty(UserProperty.CustomUserId, "yourSideUserId");
Qonversion.setUserId('yourSideUserID');
Learn more about User ID here.
Display products
Use the offerings
method to get the wrapper object Offerings
. 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());
}
});
The products are listed in the same order as they were added to the Qonversion Offerings settings. Learn more about displaying products here.
Make purchases
When Products and Permissions are set, you can start making purchases with purchaseProduct
for iOS and purchase
for Android:
Qonversion.purchaseProduct(product) { (permissions, error, isCancelled) in
if let premium: Qonversion.Permission = permissions["premium"], premium.isActive {
// Flow for success state
}
}
[Qonversion purchaseProduct:product completion:^(NSDictionary<NSString *,QNPermission *> * _Nonnull permissions,
NSError * _Nullable error,
BOOL cancelled) {
QNPermission *premiumPermission = permissions[@"premium"];
if (premiumPermission && premiumPermission.isActive) {
// Flow for success state
}
}];
Qonversion.purchase(this, product, new QonversionPermissionsCallback() {
@Override
public void onSuccess(@NotNull Map<String, QPermission> permissions) {
QPermission premiumPermission = permissions.get("premium");
if (premiumPermission != null && premiumPermission.isActive()) {
// handle active permission here
}
}
@Override
public void onError(@NotNull QonversionError error) {
// handle error here
}
});
Qonversion.purchase(this, product, callback = object: QonversionPermissionsCallback {
override fun onSuccess(permissions: Map<String, QPermission>) {
val premiumPermission = permissions["premium"]
if (premiumPermission != null && premiumPermission.isActive()) {
// handle active permission here
}
}
override fun onError(error: QonversionError) {
// handle error here
}
})
try {
final Map<String, QPermission> permissions = await Qonversion.purchaseProduct(product);
} on QPurchaseException catch (e) {
print(e);
}
// purchaseProduct method and A/B testing is not yet available on React Native.
// You can use purchase method and Qonversion product ID:
try {
const permissions: Map<string, Permission> = await Qonversion.purchase('main');
} catch (e) {
if (e.userCanceled) {
// purchase canceled by the user
}
console.log(e);
}
Qonversion.PurchaseProduct(product, (permissions, error) =>
{
if (error == null)
{
if (permissions.TryGetValue("premium", out Permission premium) && premium.IsActive)
{
// Handle the active permission here
}
}
else
{
// Handle the error
Debug.Log("Error" + error.ToString());
}
});
Where "product" is the Qonversion Product created in the Product Center. Learn more about purchasing products here.
Check subscription status and user permissions (entitlements)
To check user permissions (entitlements) to the premium content, as well as to get the in-app subscription status, use checkPermissions
method:
Note please that the permission object will be available only in case the user made any purchase. In case there were no purchases you will receive an empty result.
Qonversion.checkPermissions { (permissions, error) in
if let error = error {
// handle error
return
}
if let premium: Qonversion.Permission = permissions["premium"], premium.isActive {
switch premium.renewState {
case .willRenew, .nonRenewable:
// .willRenew is the state of an auto-renewable subscription
// .nonRenewable is the state of consumable/non-consumable IAPs that could unlock lifetime access
break
case .billingIssue:
// Grace period: permission is active, but there was some billing issue.
// Prompt the user to update the payment method.
break
case .cancelled:
// The user has turned off auto-renewal for the subscription, but the subscription has not expired yet.
// Prompt the user to resubscribe with a special offer.
break
default: break
}
}
}
[Qonversion checkPermissions:^(NSDictionary<NSString *,QNPermission *> * _Nonnull permissions,
NSError * _Nullable error) {
QNPermission *premiumPermission = permissions[@"premium"];
if (premiumPermission && premiumPermission.isActive) {
switch (premiumPermission.renewState) {
case QNPermissionRenewStateWillRenew:
case QNPermissionRenewStateNonRenewable:
// QNPermissionRenewStateWillRenew is state for auto-renewable purchases
// QNPermissionRenewStateNonRenewable is state for in-app purchases that unlock the permission lifetime
break;
case QNPermissionRenewStateBillingIssue:
// Grace period: permission is active, but there was some billing issue.
// Prompt the user to update the payment method.
break;
case QNPermissionRenewStateCancelled:
// The user canceled the subscription, but the subscription has not expired yet.
// Prompt the user to resubscribe with some special offer.
break;
default:
break;
}
}
}];
Qonversion.checkPermissions(new QonversionPermissionsCallback() {
@Override
public void onSuccess(@NotNull Map<String, QPermission> permissions) {
QPermission premiumPermission = permissions.get("premium");
if (premiumPermission != null && premiumPermission.isActive()) {
switch (premiumPermission.getRenewState()) {
case NonRenewable:
case WillRenew:
// WillRenew is the state of an auto-renewable subscription
// NonRenewable is the state of consumable/non-consumable IAPs that could unlock lifetime access
break;
case BillingIssue:
// Prompt the user to update the payment method.
break;
case Canceled:
// The user has turned off auto-renewal for the subscription, but the subscription has not expired yet.
// Prompt the user to resubscribe with a special offer.
break;
default: break;
}
}
@Override
public void onError(@NotNull QonversionError error) {
// handle error here
}
});
Qonversion.checkPermissions(object: QonversionPermissionsCallback {
override fun onSuccess(permissions: Map<String, QPermission>) {
val premiumPermission = permissions["premium"]
if (premiumPermission != null && premiumPermission.isActive()) {
// handle active permission here
// also you can check renew state if needed
// for example to check if user has canceled subscription and offer him a discount
when (premiumPermission.renewState) {
QProductRenewState.NonRenewable,
QProductRenewState.WillRenew ->
// WillRenew is the state of an auto-renewable subscription
// NonRenewable is the state of consumable/non-consumable IAPs that could unlock lifetime access
QProductRenewState.BillingIssue ->
// Prompt the user to update the payment method.
QProductRenewState.Canceled ->
// The user has turned off auto-renewal for the subscription, but the subscription has not expired yet.
// Prompt the user to resubscribe with a special offer.
}
}
}
override fun onError(error: QonversionError) {
// handle error here
}
})
try {
final Map<String, QPermission> permissions = await Qonversion.checkPermissions();
final main = permissions['main'];
if (main != null && main.isActive) {
switch (main.renewState) {
case QProductRenewState.willRenew:
case QProductRenewState.nonRenewable:
// .willRenew is the state of an auto-renewable subscription
// .nonRenewable is the state of consumable/non-consumable IAPs that could unlock lifetime access
break;
case QProductRenewState.billingIssue:
// Grace period: permission is active, but there was some billing issue.
// Prompt the user to update the payment method.
break;
case QProductRenewState.canceled:
// The user has turned off auto-renewal for the subscription, but the subscription has not expired yet.
// Prompt the user to resubscribe with a special offer.
break;
default:
break;
}
}
} catch (e) {
print(e);
}
const permissions: Map<string, Permission> = await Qonversion.checkPermissions();
const premiumPermission = permissions.get('premium');
if (mainPermission != null) {
switch (premiumPermission.renewState) {
case RenewState.NON_RENEWABLE:
// NON_RENEWABLE is the state of consumable/non-consumable IAPs that could unlock lifetime access
break;
case RenewState.WILL_RENEW:
// WILL_RENEW is the state of an auto-renewable subscription
break;
case RenewState.CANCELED:
// The user has turned off auto-renewal for the subscription, but the subscription has not expired yet.
// Prompt the user to resubscribe with a special offer.
break;
case RenewState.BILLING_ISSUE:
// Grace period: permission is active, but there was some billing issue.
// Prompt the user to update the payment method.
break;
}
}
Qonversion.CheckPermissions((permissions, error) =>
{
if (error == null)
{
if (permissions.TryGetValue("premium", out Permission premium) && premium.IsActive)
{
switch(premium.RenewState)
{
case QProductRenewState.WillRenew:
case QProductRenewState.NonRenewable:
// .willRenew is the state of an auto-renewable subscription
// .nonRenewable is the state of consumable/non-consumable IAPs that could unlock lifetime access
break;
case QProductRenewState.BillingIssue:
// Grace period: permission is active, but there was some billing issue.
// Prompt the user to update the payment method.
break;
case QProductRenewState.Canceled:
// The user has turned off auto-renewal for the subscription, but the subscription has not expired yet.
// Prompt the user to resubscribe with a special offer.
break;
default:
break;
}
}
}
else
{
// Handle the error
Debug.Log("Error" + error.ToString());
}
});
Learn more about checking a subscription state and permissions here.
Restore previous purchase
To restore user's previous purchases use the restore
method:
Qonversion.restore { [weak self] (permissions, error) in
if let error = error {
// Handle error
}
if let permission: Qonversion.Permission = permissions["plus"], permission.isActive {
// Restored and permission is active
}
}
[Qonversion restoreWithCompletion:^(NSDictionary<NSString *,QNPermission *> * _Nonnull result, NSError * _Nullable error) {
if (error) {
// Handle error
}
QNPermission *permission = result[@"active"];
if (permission && permission.isActive) {
// Restored and permission is active
}
}];
Qonversion.restore(new QonversionPermissionsCallback() {
@Override
public void onSuccess(@NotNull Map<String, QPermission> permissions) {
QPermission premiumPermission = permissions.get("premium");
if (premiumPermission != null && premiumPermission.isActive()) {
// handle active permission here
}
}
@Override
public void onError(@NotNull QonversionError error) {
// handle error here
}
});
Qonversion.restore(object : QonversionPermissionsCallback {
override fun onSuccess(permissions: Map<String, QPermission>) {
val premiumPermission = permissions["premium"]
if (premiumPermission != null && premiumPermission.isActive()) {
// handle active permission here
}
}
override fun onError(error: QonversionError) {
// handle error here
}
})
try {
final Map<String, QPermission> permissions = await Qonversion.restore();
} catch (e) {
print(e);
}
try {
const permissions: Map<string, Permission> = await Qonversion.restore();
} catch (e) {
console.log(e);
}
Qonversion.Restore((permissions, error) =>
{
if (error == null)
{
// Handle permissions here
}
else
{
// Handle the error
Debug.Log("Error" + error.ToString());
}
});
Next Steps
→ Check out the Sample Apps
→ Learn the details of Qonversion SDKs
→ Tips on testing
→ Troubleshooting
→ Migrating In-App Subscriptions
Updated 2 months ago