Migrating from Glassfy

Glassfy has announced that they will be ceasing operations in December 2024, and we're here to support the users during the transition. Enjoy 6 months of the Qonversion completely for free, allowing you plenty of time to get comfortable with all that Qonversion has to offer.

Glassfy is closing, Qonversion special offer

To ensure a smooth transition, we have prepared a migration guide to help you move seamlessly from Glassfy to Qonversion.

First and foremost, register at Qonversion.

The registration will take you through an onboarding where you'll have to fill out information about your project, insert links - all to help you add your project to Qonversion. We highly recommend following this flow and not skipping any settings during registration. Anyway, we'll also cover the scenario if you don't fill these out.

Store Configuration


Connecting App Store

Here's what you had in Glassfy:

To configure App store in Qonversion, you will need the following fields:

  1. App-Specific Shared Secret: Simply copy this value from the Apple Shared Secret field in the App Store Configuration section in Glassfy. Check the screenshot above. Alternatively, here's a more detailed guide on how to obtain this.
  2. App Store ID: You'll find your App Store ID in the URL of your App Store Connect account. For example, if your URL is something like https://apps.apple.com/us/app/example-app/id123456789, then 123456789 is your app’s Apple ID.

Connecting Google Play Console

Here's what you saw at Glassfy:

To set up your Google Play account in Qonversion, you will need the following:

  1. Android Package Name: This is usually the applicationID in your app-level build.gradle file. You can either copy it from there or simply copy the Android Package Name field in your settings from Glassfy.
  2. Service Account Credentials JSON: Copy the value from the Google JSON Key in the screenshot above or simply attach the same file you used in Glassfy. Here's more on how you can get Google Play Service Account Key.

🚧

Can't find the fields?

If you skipped filling in these details during the onboarding, navigate to the Settings -> Stores on your Qonversion dashboard and fill out the empty fields.

Offerings, Products, and Entitlements

Once all the data is filled in and the stores are set up, it's time to configure Offerings, Products, and Entitlements.

Here's how we refer to the same things differently:

GlassfyQonversion
PermissionsEntitlements
SKUsProducts
OfferingsOfferings

Entitlements (Permissions)

Let's start with Entitlements. Similar to how you set up Permissions in Glassfy, you need to set up Entitlements in Qonversion.

Here’s how it was done in Glassfy:

Here’s how to do it in Qonversion:

Navigate to the Entitlements and click the Create Entitlement button.

Products (SKUs)

Next, you need to create Products similar to how you created SKUs in Glassfy:

To create Products in Qonversion, go to Entitelments --> Products and click Create Product.

  1. Fill in the product details.
  • Qonversion Product ID – create your unique product identifier in Qonversion that corresponds to a unique product on the Apple App Store and Google Play Store. Qonversion SDK will use it to make purchases.

  • AppStore Product ID – product identifier on Apple App Store. Learn more on how to create subscriptions on iOS.

  • Google Play Product ID – product identifier on Google Play Console. Learn more on how to set up Android in-app products .

  • Google Play Base Plan ID - identifier of the base plan for Google Play Product. This is used for subscription products. If you're creating an in-app, leave this field empty for in-apps. In Qonversion, you will set up different products for the different base plans of the same product.

  • Associated Entitlements - choose the entitlements that should be granted once this product is purchased.


Offerings

Here's what your Offering setup looked like at Glassfy:

In Qonversion, simply navigate to the Remote Config -> Offerings from your dashboard and add the offerings similarly as you did it in Glassfy.

Now, move to installing your SDK and getting your hands into the code a bit.

Install the SDK

Check out the links below for the SDK you need and follow the steps provided:

iOS SDK
Android SDK
Flutter SDK
React Native SDK
Cordova Plugin
Unity SDK
Web
Kids Mode Qonversion SDKs

Read more about Installing the SDKs.


When you're done with the setup, it's time to get into the code!

Launch SDK

Firstly, you need to initialize the SDK. Here's how you did it in Glassfy:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

  Glassfy.initialize(apiKey: "YOUR_API_KEY", watcherMode: false)

}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  [Glassfy initializeWithAPIKey:@"YOU_API_KEY" watcherMode:NO];

}
public class App extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    Glassfy.initialize(this, "YOUR_API_KEY", false, null);
  }
}
class App : Application() {
  override fun onCreate() {
    super.onCreate()

    Glassfy.initialize(this, "YOUR_API_KEY", false, null)
  }
}
try {

  await Glassfy.initialize('YOU_API_KEY',watcherMode: false);

} catch (e) {
  // error
  [...]
}
try {

  await Glassfy.initialize('YOU_API_KEY', false);

} catch (e) {
  // initialization error
  [...]
}

Here's how you'll do it for Qonversion.

import Qonversion

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    let config = Qonversion.Configuration(projectKey: "projectKey", launchMode: .subscriptionManagement)
    Qonversion.initWithConfig(config)
  	return true
}
#import "Qonversion.h"
  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    QONConfiguration *configuration = [[QONConfiguration alloc] initWithProjectKey:@"projectKey" launchMode:QONLaunchModeSubscriptionManagement];
    [Qonversion initWithConfig:configuration];
    
    return YES;
}
import com.qonversion.android.sdk.Qonversion;
import com.qonversion.android.sdk.QonversionConfig;
import com.qonversion.android.sdk.Qonversion.dto.QLaunchMode;

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        final QonversionConfig qonversionConfig = new QonversionConfig.Builder(
                this,
                "projectKey",
                QLaunchMode.SubscriptionManagement
        ).build();
        Qonversion.initialize(qonversionConfig);
    }
}
import com.qonversion.android.sdk.Qonversion
import com.qonversion.android.sdk.QonversionConfig
import com.qonversion.android.sdk.Qonversion.dto.QLaunchMode

public class App : Application {
    override fun onCreate() {
        super.onCreate()
        val qonversionConfig = QonversionConfig.Builder(
            this,
            "projectKey",
            QLaunchMode.SubscriptionManagement
        ).build()
        Qonversion.initialize(qonversionConfig)
    }
}
import 'package:qonversion_flutter/qonversion_flutter.dart';

final config = new QonversionConfigBuilder(
  'projectKey',
  QLaunchMode.subscriptionManagement
).build();
Qonversion.initialize(config);
import Qonversion, {
  QonversionConfigBuilder,
  LaunchMode,
} from 'react-native-qonversion';

const config = new QonversionConfigBuilder(
  'projectKey',
  LaunchMode.SUBSCRIPTION_MANAGEMENT
).build();
Qonversion.initialize(config);
using QonversionUnity;

private void Start()
{
    QonversionConfig config = new QonversionConfigBuilder(
            "projectKey",
            LaunchMode.SubscriptionManagement
        ).Build();
    Qonversion.Initialize(config);
}
const config = new Qonversion.ConfigBuilder(
  'projectKey',
  Qonversion.LaunchMode.SUBSCRIPTION_MANAGEMENT,
).build();
Qonversion.initialize(config);

Displaying products

Glassfy:

Glassfy.offerings { (offerings, err) in
    if let offering = offerings?["premium"] {
        // display your offering's skus
        for sku in offering.skus {
            // sku.extravars
            // sku.product.localizedTitle
            // sku.product.localizedDescription
            // sku.product.price
        }
    }
}
[Glassfy offeringsWithCompletion:^(GYOfferings *offers, NSError *err) {
    GYOffering *offering = offers[@"premium"];
    if (offering) {
        // display your offering's skus
        for (GYSku *sku in offering.skus) {
            // sku.extravars
            // sku.product.localizedTitle
            // sku.product.localizedDescription
            // sku.product.price
        }
    }
}];
Glassfy.offerings() { offers, err ->
    offers?.all
        ?.firstOrNull { it.offeringId == "premium" }
        ?.also {
            // display your offering's skus
            for (sku in it.skus) {
                // sku.extravars
                // sku.product.title
                // sku.product.description
                // sku.product.price
            }
        }
}
Glassfy.offerings(new OfferingsCallback() {
    @Override
    public void onResult(@Nullable Offerings offers, @Nullable GlassfyError err) {
        Offering offering = null;
        if (offers != null) {
            for (Offering o : offers.getAll()) {
                if (o.getOfferingId().equals("premium")) {
                    offering = o;
                }
            }
        }
        if (offering != null) {
            // display your offering's skus
            for (Sku sku : offering.getSkus()) {
                // sku.getExtravars();
                // sku.getProduct().getTitle();
                // sku.getProduct().getDescription();
                // sku.getProduct().getPrice();
            }
        }
    }
});
try {
    var offerings = await Glassfy.offerings();
    var offering = offerings.all
        ?.singleWhere((offering) => offering.offeringId == 'premium');

    offering?.skus?.forEach((sku) {
        // sku.product.description
        // sku.product.price
    });
} catch (e) {
  // initialization error
  [...]
}
try {
    let offering = await Glassfy.offerings().all.find((o) => o.offeringId === 'premium');

    offering?.skus.forEach((sku) => {
      // sku.extravars
      // sku.product.description;
      // sku.product.price
    });
} catch (e) {
  [...]
}

Qonversion:

Qonversion.shared().offerings { (offerings, error) in
  if let products = offerings?.main?.products {
    // Display products for sale
  }
}
[[Qonversion sharedInstance] offerings:^(QONOfferings * _Nullable offerings, NSError * _Nullable error) {
  if (offerings.main.products.count > 0) {
    // Display products for sale
  }
}];
Qonversion.getSharedInstance().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.shared.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.getSharedInstance().offerings();
  final List<QProduct> products = offerings.main.products;
  if (products.isNotEmpty) {
    // Display your products
  }
} catch (e) {
  print(e);
}
try {
  const offerings = await Qonversion.getSharedInstance().offerings();
  if (offerings.main != null && offerings.main.products.length > 0) {
    // Display products for sale
  }
} catch (e) {
  // handle error here
}
Qonversion.GetSharedInstance().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());
   }
});
try {
  const offerings = await Qonversion.getSharedInstance().offerings();
  if (offerings.main != null && offerings.main.products.length > 0) {
    // Display products for sale
  }
} catch (e) {
  // handle error here
}

Making purchases

Glassfy:

Glassfy.purchase(sku: premiumSku) { (transaction, e) in
    // update app status accordingly
    if let p = transaction?.permissions["aPermission"] {
        if p.isValid {
            // unlock aFeature
        } else {
            // lock aFeature
        }
    }
}
[Glassfy purchaseSku:premiumSku completion:^(GYTransaction *transaction, NSError *err) {
    GYPermissions *permissions = transaction.permissions;
    if (permissions) {
        GYPermission *p = permissions[@"aPermission"];
        if (p.isValid) {
            // unlock aFeature
        }
        else {
            // lock aFeature
        }
    }
}];
Glassfy.purchase(activity, sku, new PurchaseCallback() {
    @Override
    public void onResult(@Nullable Transaction t, @Nullable GlassfyError err) {
        // update app status accordingly
        Permission permission = null;
        if (t != null) {
            for (Permission p : t.getPermissions().getAll()) {
                if (p.getPermissionId().equals("aPermission")) {
                    permission = p;
                }
            }
        }
        if (permission != null) {
            if (permission.isValid()) {
              // unlock aFeature
            } else {
              // lock aFeature
            }
        }
    }
});
Glassfy.purchase(activity, sku) { transaction, err ->
    // update app status accordingly
    transaction?.permissions
        ?.all
        ?.firstOrNull { it.permissionId == "aPermission" }
        ?.also {
            if (it.isValid) {
                // unlock aFeature
            } else {
                // lock aFeature
            }
        }
}
try {
    var transaction = await Glassfy.purchaseSku(sku);

    var p = transaction.permissions?.all?.singleWhere((permission) => permission.permissionId == 'premium');
    if (p?.isValid==true) {
        // unlock aFeature
    }
    else {
        // lock aFeature
    }
} catch (e) {
  // initialization error
  [...]
}
try {
    const transaction = await Glassfy.purchaseSku(premiumSku );
    const permission = transaction.permissions.all.find((p) => p.permissionId === "aPermission");
    if (permission && permission.isValid) {
        // unlock aFeature
    }
} catch (e) {
  // initialization error
  [...]
}

Qonversion:

Qonversion.shared().purchaseProduct(product) { (entitlements, error, isCancelled) in  
  if let premium: Qonversion.Entitlement = entitlements["premium"], premium.isActive {
    // Flow for success state
  }
}
[[Qonversion sharedInstance] purchaseProduct:product completion:^(NSDictionary<NSString *,QONEntitlement *> * _Nonnull entitlements,
                                                                  NSError * _Nullable error,
                                                                  BOOL cancelled) {
  QONEntitlement *premiumEntitlement = entitlements[@"premium"];
  
  if (premiumEntitlement && premiumEntitlement.isActive) {
    // Flow for success state
  }
}];
final QPurchaseModel purchaseModel = product.toPurchaseModel();
Qonversion.getSharedInstance().purchase(this, purchaseModel, new QonversionEntitlementsCallback() {
    @Override
    public void onSuccess(@NotNull Map<String, QEntitlement> entitlements) {
        QEntitlement premiumEntitlement = entitlements.get("premium");

        if (premiumEntitlement != null && premiumEntitlement.isActive()) {
            // Handle active entitlement here
        }
    }

    @Override
    public void onError(@NotNull QonversionError error) {
        // Handle error here
        if (error.getCode() == QonversionErrorCode.CanceledPurchase) {
            // Purchase canceled by the user  
        }
    }
});
val purchaseModel = product.toPurchaseModel()
Qonversion.shared.purchase(this, purchaseModel, callback = object: QonversionEntitlementsCallback {
    override fun onSuccess(entitlements: Map<String, QEntitlement>) {
        val premiumEntitlement = entitlements["premium"]
        if (premiumEntitlement != null && premiumEntitlement.isActive) {
            // Handle active entitlement here
        }
    }

    override fun onError(error: QonversionError) {
        // Handle error here
        if (error.code === QonversionErrorCode.CanceledPurchase) {
            // Purchase canceled by the user
        }
    }
})
try {
  final QPurchaseModel purchaseModel = product.toPurchaseModel();
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchase(purchaseModel);
} on QPurchaseException catch (e) {
  if (e.isUserCancelled) {
    // Purchase canceled by the user
  }
  print(e);
}
try {
  const purchaseModel: PurchaseModel = product.toPurchaseModel();
  const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().purchase(purchaseModel);
} catch (e) {
  if (e.userCanceled) {
    // Purchase canceled by the user
  }
  console.log(e);
}
PurchaseModel purchaseModel = product.ToPurchaseModel();
Qonversion.GetSharedInstance().Purchase(purchaseModel, (entitlements, error, isCancelled) =>
{
      if (error == null)
      {
          if (entitlements.TryGetValue("premium", out Entitlement premium) && premium.IsActive)
          {
             // Handle the active entitlement here
          }
      }
      else
      {
        // Handle the error  
        Debug.Log("Error" + error.ToString());
      }
});
try {
  const purchaseModel: PurchaseModel = product.toPurchaseModel();
  const entitlements = await Qonversion.getSharedInstance().purchase(purchaseModel);
} catch (e) {
  if (e.userCanceled) {
    // Purchase canceled by the user
  }
  console.log(e);
}

The "product" here is the Qonversion Product you created earlier.


Subscription status

To check the subscription status in Qonversion, you will use Entitlements. In Glassfy, you used Permissions.

Glassfy:

Glassfy.permissions { permissions, err in
    guard let permissions = permissions else { return }
    for p in permissions.all {
        switch (p.permissionId) {
        case "aPermission":
            if (p.isValid) {
                // unlock aFeature
            }
            break;
        default:
            print("Permission not handled");
            break;
        }
    }
}
[Glassfy permissionsWithCompletion:^(GYPermissions *permission, NSError *err) {
    NSArray<GYPermission*> *permissions = permission.all;
    if (permissions) {
        for (GYPermission *p in permissions) {
            switch (p.permissionId) {
            case @"premium":
                if (p.isValid) {
                    // unlock aFeature
                }
                
                break;
            default:
                NSLog(@"Permission not handled");
                break;
            }
        }
    }
}];
Glassfy.permissions { permission, err ->
    // update app status accordingly
    permission?.all?.forEach {
        when (it.permissionId) {
            "premium" ->
                if (it.isValid) {
                    // unlock aFeature
                } 
            else -> println("Permission not handled");
        }
    }
}
Glassfy.permissions(new PermissionsCallback() {
    @Override
    public void onResult(@Nullable Permissions permission, @Nullable GlassfyError error) {
        // update app status accondingly
        if (permission != null) {
            for (Permission p: permission.getAll()) {
                switch (p.getPermissionId()) {
                case "premium":
                    if (p.isValid()) {
                        // unlock aFeature
                    } 
                    break;
                default:
                    Log.d(TAG, "Permission not handled");
                }
            }
        }
    }
});
try {
var permission = await Glassfy.permissions();
      permission.all?.forEach((p)=> {
        if (p.permissionId == "premium" && p.isValid==true) {
          // unlock aFeature
        }
      });
} catch (e) {
  // initialization error
  [...]
}
try {
    const permissions = await Glassfy.permissions();
    permissions.forEach((p)=>{
        switch (p.permissionId) {
            case "premium":
                if (permission.isValid) {
                    // unlock 
                }
                break;
        
            default:
                break;
        }
    });

} catch (e) {
  // initialization error
  [...]
}

Qonversion:

Qonversion.shared().checkEntitlements { (entitlements, error) in
  if let error = error {
    // handle error
    return
  }
  
  if let premium: Qonversion.Entitlement = entitlements["premium"], premium.isActive {
    // unlock feature
  }
}
[[Qonversion sharedInstance] checkEntitlements:^(NSDictionary<NSString *, QONEntitlement *> * _Nonnull entitlements,
                                NSError * _Nullable error) {
    QONEntitlement *premiumEntitlement = entitlements[@"premium"];
    if (premiumEntitlement && premiumEntitlement.isActive) {
      // unlock feature
    }
}];
Qonversion.getSharedInstance().checkEntitlements(new QonversionEntitlementsCallback() {
    @Override
    public void onSuccess(@NotNull Map<String, QEntitlement> entitlements) {
        final QEntitlement premiumEntitlement = entitlements.get("premium");

        if (premiumEntitlement != null && premiumEntitlement.isActive()) {
            // unlock feature
        }
    }

    @Override
    public void onError(@NotNull QonversionError error) {
        // handle error here
    }
});
Qonversion.shared.checkEntitlements(object: QonversionEntitlementsCallback {
    override fun onSuccess(entitlements: Map<String, QEntitlement>) {
        val premiumEntitlement = entitlements["premium"]
        if (premiumEntitlement != null && premiumEntitlement.isActive) {
            // unlock feature
        }
    }

    override fun onError(error: QonversionError) {
        // handle error here
    }
})
try {
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().checkEntitlements();
  final premium = entitlements['premium'];
  if (premium != null && premium.isActive) {
    // unlock feature
  }
} catch (e) {
  print(e);
}
try {
  const entitlements = await Qonversion.getSharedInstance().checkEntitlements();

  const premiumEntitlement = entitlements.get('premium');
  if (premiumEntitlement != null) {
    // unlock feature
  }
} catch (e) {
  // handle error here
}
Qonversion.GetSharedInstance().CheckEntitlements((entitlements, error) =>
{
    if (error == null)
    {
        if (entitlements.TryGetValue("premium", out Entitlement premium) && premium.IsActive)
        {
            // unlock feature
        }
    }
    else
    {
        // Handle the error  
        Debug.Log("Error" + error.ToString());
    }
});
try {
    const entitlements = await Qonversion.getSharedInstance().checkEntitlements();

    const premiumEntitlement = entitlements.get('premium');
    if (premiumEntitlement != null) {
        // unlock feature
    }
} catch (e) {
    // handle error here
}