Making Purchases

Make in-app purchases with Qonversion SDK

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

1. Make a purchase

When Products and Entitlements are set, you can start making purchases with thepurchaseProduct method 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());
      }
});
try {
    const entitlements = await Qonversion.getSharedInstance().purchaseProduct(product);
} catch (e) {
    if (e.userCanceled) {
        // Purchase canceled by the user
    }
    console.log(e);
}

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 returns an error or throws an exception with the failure description.

There is aisCancelled flag available, which equals true if a user cancels the purchasing process on iOS and Unity. You can check the same behaviour for Android by comparing the error code with theQonversionErrorCode.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());
      }
});
try {
    const entitlement = await Qonversion.getSharedInstance().updatePurchaseWithProduct(product, 'oldProductId');
} catch (e) {
    console.log(e);
}

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);
try {
    const entitlement = await Qonversion.getSharedInstance().updatePurchase('newId', 'oldId');
} catch (e) {
    console.log(e);
}

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

When users, for example, upgrade to a new phone, they need to restore purchases so they can keep access to your premium features.

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());
      }
});
try {
    const entitlements = await Qonversion.getSharedInstance().restore();
} catch (e) {
    console.log(e);
}