Making Purchases

Make in-app purchases with Qonversion SDK

Make sure to configure Products and Entitlements in the Qonversion dashboard before you start handling purchases with Qonversion SDK:

Discover Product-Center Concept
Create Entitlements
Create Products

1. Make a purchase

When Products and Entitlements are set, you can start making purchases with purchaseProduct for iOS and purchase for Android:

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
  }
}];
Qonversion.getSharedInstance().purchase(this, product, 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  
        }
    }
});
Qonversion.shared.purchase(this, product, 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 Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchaseProduct(product);
} on QPurchaseException catch (e) {
  if (e.isUserCancelled) {
    // Purchase canceled by the user
  }
  print(e);
}
try {
  const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().purchaseProduct(product);
} catch (e) {
  if (e.userCanceled) {
    // Purchase canceled by the user
  }
  console.log(e);
}
Qonversion.GetSharedInstance().PurchaseProduct(product, (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());
      }
});

Where "product" is the Qonversion Product created in the Product Center. See the previous step to display Products.

2. Handle a purchase result

The product purchase method returns a dictionary with the user's entitlements on success (either via callback/completion block or as return value depending on the platform). If something goes wrong, the method will return an error or throw an exception with the failure description.

On iOS and Unity, there is either an isCancelled flag provided, which is true if a user cancels the purchasing process. You can check the same behaviour for Android by comparing the error code with QonversionErrorCode.CanceledPurchase. For Flutter and React-Native, there is an additional field in the thrown exception for that purpose (see the code examples above).

Entitlement IDs are the keys to the entitlements dictionary.
The values are the objects of the Qonversion.Entitlement class.

3. Update purchases (Android only)

Upgrading, downgrading, or changing a subscription on Google Play Store requires calling the updatePurchase() function.
See Google Play Documentation for more details.

Qonversion.getSharedInstance().updatePurchase(this, newProduct, "oldProductId", 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
    }
});
Qonversion.shared.updatePurchase(this, newProduct, "oldProductId", 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
    }
})
try {
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchaseWithProduct(
    newProduct: product,
    oldProductId: 'oldProductId',
  );
} catch (e) {
  print(e);
}
try {
  const entitlement: Map<string, Entitlement> = await Qonversion.getSharedInstance().updatePurchaseWithProduct(product, 'oldProductId');
} catch (e) {
  console.log(e);
}
Qonversion.GetSharedInstance().UpdatePurchaseWithProduct(newProduct, "oldProductId", (entitlements, error, isCancelled) =>
{
    if (error == null)
      {
          if (permissions.TryGetValue("premium", out Entitlement premium) && premium.IsActive)
          {
             // Handle the active entitlement here
          }
      }
      else
      {
        // Handle the error  
        Debug.Log("Error" + error.ToString());
      }
});

Alternatively, you can use Qonversion product ID for the new product (this option will be deprecated soon):

Qonversion.getSharedInstance().updatePurchase(this, "newProductId", "oldProductId", 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
    }
});
Qonversion.shared.updatePurchase(this, "newProductId", "oldProductId", 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
    }
})
try {
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().updatePurchase(
    newProductId: 'newId',
    oldProductId: 'oldId',
  );
} catch (e) {
  print(e);
}
try {
  const entitlement: Map<string, Entitlement> = await Qonversion.getSharedInstance().updatePurchase('newId', 'oldId');
} catch (e) {
  console.log(e);
}
Qonversion.GetSharedInstance().UpdatePurchase("newId", "oldId", (entitlements, error, isCancelled) =>
{
      if (error == null)
      {
         // Handle entitlements here
      }
      else
      {
        // Handle the error  
        Debug.Log("Error" + error.ToString());
      }
}, ProrationMode.ImmediateWithTimeProration);

4. Check user entitlements

Use the checkEntitlements() SDK method in case you want to check users’ entitlements separately from a purchase. Learn more here.

5. Restore purchases

Restoring purchases restores users' purchases in your app to maintain access to purchased content. Users sometimes need to restore purchased content, such as when they upgrade to a new phone.

Call the restore() method to restore purchases:

Qonversion.shared().restore { [weak self] (entitlements, error) in
  if let error = error {
    // Handle error
  }
  
  if let entitlement: Qonversion.Entitlement = entitlements["plus"], entitlement.isActive {
    // Restored and entitlement is active 
  }
}
[[Qonversion sharedInstance] restore:^(NSDictionary<NSString *, QONEntitlement *> * _Nonnull result, NSError * _Nullable error) {
  if (error) {
    // Handle error
  }
  QONEntitlement *entitlement = result[@"active"];
  if (entitlement && entitlement.isActive) {
    // Restored and entitlement is active
  }
}];
Qonversion.getSharedInstance().restore(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
    }
});
Qonversion.shared.restore(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
    }
})
try {
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().restore();
} catch (e) {
  print(e);
}
try {
  const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().restore();
 } catch (e) {
  console.log(e);
}
Qonversion.GetSharedInstance().Restore((entitlements, error) =>
{
      if (error == null)
      {
         // Handle entitlements here
      }
      else
      {
        // Handle the error  
        Debug.Log("Error" + error.ToString());
      }
});