> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.qonversion.io/llms.txt
> Use this file to discover all available pages before exploring further.

# How to make purchases with the Qonversion SDK

> Make in-app purchases with the Qonversion SDK: start a purchase, handle the success/cancel/pending/error result, restore purchases, and use Android offers, subscription updates, and iOS multi-quantity options.

Before you handle purchases with the Qonversion SDK, [configure Products and Entitlements](subscription-management-mode) in the Qonversion dashboard. `product` in every example below is a Qonversion Product you created in the [dashboard](https://dash.qonversion.io/entitlements/products); see [Displaying Products](displaying-products) to fetch and display them.

<span id="1-make-a-purchase" />

## Make a purchase

Start a purchase with the `purchase()` method (Swift), `purchaseWithResult:` (Objective-C), or `purchase()` on Android and the cross-platform SDKs. The method returns a purchase result object; when the purchase succeeds, look up the granted entitlement on `result.entitlements`.

<CodeGroup>
  ```swift Swift theme={null}
  Qonversion.shared().purchase(product) { (result) in
    if result.isSuccessful {
      if let premium: Qonversion.Entitlement = result.entitlements["premium"], premium.isActive {
        // Grant user access to premium features
      }
    } else if result.isCanceledByUser {
      // Handle canceled purchase
    } else if result.isPending {
      // Handle pending purchase
    } else {
      // Handle errors
    }
  }
  ```

  ```objectivec Objective-C theme={null}
  [[Qonversion sharedInstance] purchaseWithResult:product completion:^(QONPurchaseResult * _Nonnull result) {
    if (result.isSuccessful) {
      QONEntitlement *premiumEntitlement = result.entitlements[@"premium"];
      if (premiumEntitlement && premiumEntitlement.isActive) {
        // Grant user access to premium features
      }
    } else if (result.isCanceledByUser) {
      // Handle canceled purchase
    } else if (result.isPending) {
      // Handle pending purchase
    } else {
      // Handle errors
    }
  }];
  ```

  ```java Java theme={null}
  Qonversion.getSharedInstance().purchase(this, product, new QonversionPurchaseCallback() {
      @Override
      public void onResult(@NonNull QPurchaseResult result) {
          if (result.isSuccessful()) {
              QEntitlement premium = result.getEntitlements().get("premium");
              if (premium != null && premium.isActive()) {
                  // Grant user access to premium features
              }
          } else if (result.isCanceledByUser()) {
              // Handle canceled purchase
          } else if (result.isPending()) {
              // Handle pending purchase
          } else {
              // Handle errors
          }
      }
  });
  ```

  ```kotlin Kotlin theme={null}
  Qonversion.shared.purchase(requireActivity(), product, object : QonversionPurchaseCallback {
      override fun onResult(result: QPurchaseResult) {
          when {
              result.isSuccessful -> {
                  val premium = result.entitlements["premium"]
                  if (premium != null && premium.isActive) {
                      // Grant user access to premium features
                  }
              }
              result.isCanceledByUser -> {
                  // Handle canceled purchase
              }
              result.isPending -> {
                  // Handle pending purchase
              }
              else -> {
                  // Handle errors
              }
          }
      }
  })
  ```

  ```dart Flutter theme={null}
  final result = await Qonversion.getSharedInstance().purchaseWithResult(product);

  if (result.isSuccess) {
    final premium = result.entitlements?['premium'];
    if (premium != null && premium.isActive) {
      // Grant user access to premium features
    }
  } else if (result.isCanceled) {
    // Handle canceled purchase
  } else if (result.isPending) {
    // Handle pending purchase
  } else {
    // Handle errors
  }
  ```

  ```typescript React Native theme={null}
  const result: PurchaseResult = await Qonversion.getSharedInstance().purchaseWithResult(product);

  if (result.isSuccess) {
    const premium = result.entitlements?.get('premium');
    if (premium && premium.isActive) {
      // Grant user access to premium features
    }
  } else if (result.isCanceled) {
    // Handle canceled purchase
  } else if (result.isPending) {
    // Handle pending purchase
  } else {
    // Handle errors
  }
  ```

  ```csharp Unity theme={null}
  Qonversion.GetSharedInstance().Purchase(product, (result) =>
  {
      if (result.IsSuccess)
      {
          if (result.Entitlements != null &&
              result.Entitlements.TryGetValue("premium", out var premium) &&
              premium.IsActive)
          {
              // Grant user access to premium features
          }
      }
      else if (result.IsCanceled)
      {
          // Handle canceled purchase
      }
      else if (result.IsPending)
      {
          // Handle pending purchase
      }
      else
      {
          // Handle errors
      }
  });
  ```

  ```typescript Cordova theme={null}
  const result = await Qonversion.getSharedInstance().purchase(product);

  if (result.isSuccess) {
    const premium = result.entitlements?.get('premium');
    if (premium && premium.isActive) {
      // Grant user access to premium features
    }
  } else if (result.isCanceled) {
    // Handle canceled purchase
  } else if (result.isPending) {
    // Handle pending purchase
  } else {
    // Handle errors
  }
  ```

  ```typescript Capacitor theme={null}
  const result = await Qonversion.getSharedInstance().purchase(product);

  if (result.isSuccess) {
    const premium = result.entitlements?.get('premium');
    if (premium && premium.isActive) {
      // Grant user access to premium features
    }
  } else if (result.isCanceled) {
    // Handle canceled purchase
  } else if (result.isPending) {
    // Handle pending purchase
  } else {
    // Handle errors
  }
  ```
</CodeGroup>

## Handle a purchase result

The purchase method returns a single result object on every platform — it does not throw for a user cancellation or a pending purchase. Inspect the status flags and branch on all four cases:

* **Success** — `isSuccess` (`isSuccessful` on iOS/Android; `IsSuccess` on Unity). Grant access; read the granted entitlement from `result.entitlements`.
* **Canceled by the user** — `isCanceled` (`isCanceledByUser` on iOS/Android; `IsCanceled` on Unity). Do not grant access.
* **Pending** — `isPending` (`IsPending` on Unity). The transaction is queued and awaiting an external action (for example, Ask to Buy or a deferred payment); do not grant access yet.
* **Error** — none of the above. Handle the error (see [Purchase errors](#purchase-errors)).

Entitlement IDs are the keys to the `entitlements` map. The values are [Qonversion.Entitlement](check-permissions#the-entitlement-object) objects.

## Purchase errors

When a purchase fails, the SDK returns an error rather than an entitlement. The full list of purchase error codes, their iOS `QONErrorCode` names, and Android names is on the [Handling Errors](handling-errors) page. The most common purchase errors and where to fix them:

| Error                 | iOS `QONErrorCode`                 | Android name          | What it means                                                                                                                                                                                 |
| --------------------- | ---------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Purchase canceled     | `QONErrorCodePurchaseCanceled` (1) | `PurchaseCanceled`    | The user canceled the purchase request. Surfaced as the `isCanceled` flag, not an error branch, on most platforms.                                                                            |
| Product not found     | `QONErrorCodeProductNotFound` (2)  | `ProductNotFound`     | The product could not be found. See [Product not found error](troubleshooting#product-not-found-error).                                                                                       |
| Product already owned | —                                  | `ProductAlreadyOwned` | The item is already owned, so it cannot be purchased again.                                                                                                                                   |
| Purchase pending      | `QONErrorCodePurchasePending` (18) | —                     | The payment is queued and its final status is pending external action. Surfaced as the `isPending` flag.                                                                                      |
| Purchase invalid      | `QONErrorCodePurchaseInvalid` (31) | `PurchaseInvalid`     | The purchase failed — for example, a purchase with such `purchaseToken` cannot be found, or the store key belongs to another project. See the [Google Play billing setup guide](google-play). |
| Store error           | `QONErrorCodeAppleStoreError` (30) | `PlayStoreError`      | The App Store or Play Store is unavailable, times out, disconnects, or returns an unexpected response.                                                                                        |
| Fraud purchase        | `QONErrorCodeFraudPurchase` (28)   | `FraudPurchase`       | A fraudulent purchase was detected.                                                                                                                                                           |

## Restore purchases

When a user switches to a new device, call `restore()` so they keep access to premium features. Restoring returns the user's current entitlements; it does not create a new purchase or charge the user.

<CodeGroup>
  ```swift Swift theme={null}
  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
    }
  }
  ```

  ```objectivec Objective-C theme={null}
  [[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
    }
  }];
  ```

  ```java Java theme={null}
  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
      }
  });
  ```

  ```kotlin Kotlin theme={null}
  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
      }
  })
  ```

  ```dart Flutter theme={null}
  try {
    final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().restore();
  } catch (e) {
    print(e);
  }
  ```

  ```typescript React Native theme={null}
  try {
    const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().restore();
   } catch (e) {
    console.log(e);
  }
  ```

  ```csharp Unity theme={null}
  Qonversion.GetSharedInstance().Restore((entitlements, error) =>
  {
        if (error == null)
        {
           // Handle entitlements here
        }
        else
        {
          // Handle the error
          Debug.Log("Error" + error.ToString());
        }
  });
  ```

  ```typescript Cordova theme={null}
  try {
      const entitlements = await Qonversion.getSharedInstance().restore();
  } catch (e) {
      console.log(e);
  }
  ```

  ```typescript Capacitor theme={null}
  try {
    const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().restore();
   } catch (e) {
    console.log(e);
  }
  ```
</CodeGroup>

<span id="5-check-user-entitlements" />

## Check user entitlements

To check a user's entitlements separately from a purchase — for example, on app launch — use the `checkEntitlements()` method. See [Check user entitlements](check-permissions) for details.

## Consumable in-app purchases

Consumable in-app purchases are not tied to an entitlement, so when you make a consumable purchase you only look at the outcome (success or error), not at entitlements. On success, grant the bonus; on error, do not. The flow is:

1. The customer initiates the consumable in-app purchase.
2. You call the Qonversion purchase method.
3. You receive the purchase response:
   1. Successful response — grant the user the bonus.
   2. Error — do not grant any bonus.

On iOS and Android you can also read the completed store transaction from the result (`result.transaction` on iOS, `result.purchase` on Android, `result.storeTransaction` on the cross-platform SDKs):

<CodeGroup>
  ```swift Swift theme={null}
  Qonversion.shared().purchase(product) { (result) in
    if result.isSuccessful {
      // Grant coins here
      // Also check the store transaction if necessary
      if let transaction = result.transaction {

      }
    } else if result.isCanceledByUser {
      // Handle canceled purchase
    } else if result.isPending {
      // Handle pending purchase
    } else {
      // Handle errors
    }
  }
  ```

  ```objectivec Objective-C theme={null}
  [[Qonversion sharedInstance] purchaseWithResult:product completion:^(QONPurchaseResult * _Nonnull result) {
    if (result.isSuccessful) {
      // Grant coins here
      // Also check the store transaction if necessary
      if (result.transaction != nil) {

      }
    } else if (result.isCanceledByUser) {
      // Handle canceled purchase
    } else if (result.isPending) {
      // Handle pending purchase
    } else {
      // Handle errors
    }
  }];
  ```

  ```java Java theme={null}
  Qonversion.getSharedInstance().purchase(this, product, new QonversionPurchaseCallback() {
      @Override
      public void onResult(@NonNull QPurchaseResult result) {
          if (result.isSuccessful()) {
              // Grant coins here
              // Also check the store purchase if necessary
              if (result.purchase != null) {

              }
          } else if (result.isCanceledByUser()) {
              // Handle canceled purchase
          } else if (result.isPending()) {
              // Handle pending purchase
          } else {
              // Handle errors
          }
      }
  });
  ```

  ```kotlin Kotlin theme={null}
  Qonversion.shared.purchase(requireActivity(), product, object : QonversionPurchaseCallback {
      override fun onResult(result: QPurchaseResult) {
          when {
              result.isSuccessful -> {
                  // Grant coins here
                  // Also check the store purchase if necessary
                  result.purchase?.let {

                  }
              }
              result.isCanceledByUser -> {
                  // Handle canceled purchase
              }
              result.isPending -> {
                  // Handle pending purchase
              }
              else -> {
                  // Handle errors
              }
          }
      }
  })
  ```

  ```dart Flutter theme={null}
  final result = await Qonversion.getSharedInstance().purchaseWithResult(product);

  if (result.isSuccess) {
    // Grant coins here
    // Also check the store purchase if necessary
    if (result.storeTransaction != null) {

    }
  } else if (result.isCanceled) {
    // Handle canceled purchase
  } else if (result.isPending) {
    // Handle pending purchase
  } else {
    // Handle errors
  }
  ```

  ```typescript React Native theme={null}
  const result: PurchaseResult = await Qonversion.getSharedInstance().purchaseWithResult(product);

  if (result.isSuccess) {
    // Grant coins here
    // Also check the store purchase if necessary
    if (result.storeTransaction) {

    }
  } else if (result.isCanceled) {
    // Handle canceled purchase
  } else if (result.isPending) {
    // Handle pending purchase
  } else {
    // Handle errors
  }
  ```

  ```csharp Unity theme={null}
  Qonversion.GetSharedInstance().Purchase(product, (result) =>
  {
      if (result.IsSuccess)
      {
          // Grant coins here
          // Also check the store purchase if necessary
          if (result.StoreTransaction != null)
          {

          }
      }
      else if (result.IsCanceled)
      {
          // Handle canceled purchase
      }
      else if (result.IsPending)
      {
          // Handle pending purchase
      }
      else
      {
          // Handle errors
      }
  });
  ```

  ```typescript Cordova theme={null}
  const result = await Qonversion.getSharedInstance().purchase(product);

  if (result.isSuccess) {
    // Grant coins here
    // Also check the store purchase if necessary
    if (result.storeTransaction) {

    }
  } else if (result.isCanceled) {
    // Handle canceled purchase
  } else if (result.isPending) {
    // Handle pending purchase
  } else {
    // Handle errors
  }
  ```

  ```typescript Capacitor theme={null}
  const result = await Qonversion.getSharedInstance().purchase(product);

  if (result.isSuccess) {
    // Grant coins here
    // Also check the store purchase if necessary
    if (result.storeTransaction) {

    }
  } else if (result.isCanceled) {
    // Handle canceled purchase
  } else if (result.isPending) {
    // Handle pending purchase
  } else {
    // Handle errors
  }
  ```
</CodeGroup>

## Android: choose a specific offer

*Android only.* The Google Play Billing Library lets you sell a subscription with different offers. Read the available offers from `QProduct.storeDetails`, then pass the chosen offer through `QPurchaseOptions`. This is not available on iOS — App Store offers are selected by StoreKit, not by the SDK.

<CodeGroup>
  ```java Java theme={null}
  // Specify the concrete offer:
  final QProductOfferDetails productOfferDetails = ...; // Choose an offer from `storeDetails`
  final QPurchaseOptions purchaseOptions = new QPurchaseOptions.Builder()
          .setOffer(productOfferDetails)
          .build();

  // or specify only the offer ID:
  final QPurchaseOptions purchaseOptions = new QPurchaseOptions.Builder()
          .setOfferId("offer_id")
          .build();

  // and then provide created `QPurchaseOptions` to the `purchase` method:
  Qonversion.getSharedInstance().purchase(this, product, purchaseOptions, new QonversionPurchaseCallback() {
      ...
  });
  ```

  ```kotlin Kotlin theme={null}
  // Specify the concrete offer:
  val productOfferDetails = ... // Choose an offer from `storeDetails`
  val purchaseOptions = QPurchaseOptions.Builder()
      .setOffer(productOfferDetails)
      .build()

  // or specify only the offer ID:
  val purchaseOptions = QPurchaseOptions.Builder()
      .setOfferId("offer_id")
      .build()

  // and then provide created `QPurchaseOptions` to the `purchase` method:
  Qonversion.shared.purchase(this, product, purchaseOptions, callback = object: QonversionPurchaseCallback {
      ...
  })
  ```

  ```dart Flutter theme={null}
  // Specify the concrete offer:
  final productOfferDetails = ... // Choose an offer from `storeDetails`
  final purchaseOptions = QPurchaseOptionsBuilder()
      .setOffer(productOfferDetails)
      .build()

  // or specify only the offer ID:
  final purchaseOptions = QPurchaseOptionsBuilder()
      .setOfferId('offer_id')
      .build()

  // and then provide created `QPurchaseOptions` to the `purchaseWithResult` method:
  final result = await Qonversion.getSharedInstance().purchaseWithResult(
    product,
    purchaseOptions: purchaseOptions
  );
  ```

  ```typescript React Native theme={null}
  // Specify the concrete offer:
  const productOfferDetails = ...; // Choose an offer from `storeDetails`
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setOffer(productOfferDetails)
    .build();

  // or specify only the offer ID:
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setOfferId('offer_id')
    .build();

  // and then provide created `PurchaseOptions` to the `purchaseWithResult` method:
  const result: PurchaseResult = await Qonversion.getSharedInstance().purchaseWithResult(product, purchaseOptions);
  ```

  ```csharp Unity theme={null}
  // Specify the concrete offer:
  ProductOfferDetails productOfferDetails = ...; // Choose an offer from StoreDetails
  var purchaseOptions = new PurchaseOptionsBuilder()
      .SetOffer(productOfferDetails)
      .Build();

  // or specify only the offer ID:
  var purchaseOptions = new PurchaseOptionsBuilder()
      .SetOfferId("offer_id")
      .Build();

  // and then provide created PurchaseOptions to the Purchase method:
  Qonversion.GetSharedInstance().Purchase(product, purchaseOptions, (result) =>
  {
      ...
  });
  ```

  ```typescript Cordova theme={null}
  // Specify the concrete offer:
  const productOfferDetails = ...; // Choose an offer from `storeDetails`
  const purchaseOptions = new Qonversion.PurchaseOptionsBuilder()
    .setOffer(productOfferDetails)
    .build();

  // or specify only the offer ID:
  const purchaseOptions = new Qonversion.PurchaseOptionsBuilder()
    .setOfferId('offer_id')
    .build();

  // and then provide created `PurchaseOptions` to the `purchase` method:
  const result = await Qonversion.getSharedInstance().purchase(product, purchaseOptions);
  ```

  ```typescript Capacitor theme={null}
  // Specify the concrete offer:
  const productOfferDetails = ...; // Choose an offer from `storeDetails`
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setOffer(productOfferDetails)
    .build();

  // or specify only the offer ID:
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setOfferId('offer_id')
    .build();

  // and then provide created `PurchaseOptions` to the `purchase` method:
  const result = await Qonversion.getSharedInstance().purchase(product, purchaseOptions);
  ```
</CodeGroup>

If you provide an offer ID, the SDK tries to find and purchase the offer with that ID for the requested Qonversion product; if no offer with that ID exists, an error is returned. If you do not provide an offer ID for a subscription purchase of a Qonversion product with a specified base plan ID, the SDK chooses the most profitable offer for the client from all available offers — it compares all trial or intro phases and the base plan and picks the cheapest price. For old Qonversion products (no base plan ID specified) and for in-app products, the offer ID is ignored.

You can also remove any intro/trial offer from the purchase to keep only the base plan by calling `removeOffer` on the purchase options builder:

<CodeGroup>
  ```java Java theme={null}
  final QPurchaseOptions purchaseOptions = new QPurchaseOptions.Builder()
          .removeOffer()
          .build();
  ```

  ```kotlin Kotlin theme={null}
  val purchaseOptions = QPurchaseOptions.Builder()
      .removeOffer()
      .build()
  ```

  ```dart Flutter theme={null}
  final purchaseOptions = QPurchaseOptionsBuilder()
      .removeOffer()
      .build()
  ```

  ```typescript React Native theme={null}
  const purchaseOptions = new PurchaseOptionsBuilder()
    .removeOffer()
    .build();
  ```

  ```csharp Unity theme={null}
  var purchaseOptions = new PurchaseOptionsBuilder()
      .RemoveOffer()
      .Build();
  ```

  ```typescript Cordova theme={null}
  const purchaseOptions = new Qonversion.PurchaseOptionsBuilder()
    .removeOffer()
    .build();
  ```

  ```typescript Capacitor theme={null}
  const purchaseOptions = new PurchaseOptionsBuilder()
    .removeOffer()
    .build();
  ```
</CodeGroup>

<span id="3-update-purchases-android-only" />

## Android: update a subscription

*Android only.* Upgrading, downgrading, or changing a subscription on the Google Play Store requires setting the old product and, optionally, a replacement mode through the `QPurchaseOptions` builder. See the [Google Play upgrade/downgrade documentation](https://developer.android.com/google/play/billing/subscriptions#upgrade-downgrade) for details. Subscription updates through this API are not supported on iOS.

<CodeGroup>
  ```java Java theme={null}
  final QPurchaseOptions purchaseOptions = new QPurchaseOptions.Builder()
          .setOldProduct(oldProduct)
          .build();
  Qonversion.getSharedInstance().purchase(this, product, purchaseOptions, new QonversionPurchaseCallback() {
      ...
  });
  ```

  ```kotlin Kotlin theme={null}
  val purchaseOptions = QPurchaseOptions.Builder()
      .setOldProduct(oldProduct)
      .build()
  Qonversion.shared.purchase(this, product, purchaseOptions, callback = object: QonversionPurchaseCallback {
      ...
  })
  ```

  ```dart Flutter theme={null}
  final purchaseOptions = QPurchaseOptionsBuilder()
      .setOldProduct(oldProduct)
      .build();
  final result = await Qonversion.getSharedInstance().purchaseWithResult(
    product,
    purchaseOptions: purchaseOptions
  );
  ```

  ```typescript React Native theme={null}
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setOldProduct(oldProduct)
    .build();
  const result: PurchaseResult = await Qonversion.getSharedInstance().purchaseWithResult(product, purchaseOptions);
  ```

  ```csharp Unity theme={null}
  var purchaseOptions = new PurchaseOptionsBuilder()
      .SetOldProduct(oldProduct)
      .SetUpdatePolicy(PurchaseUpdatePolicy.WithTimeProration)
      .Build();
  Qonversion.GetSharedInstance().Purchase(product, purchaseOptions, (result) =>
  {
      ...
  });
  ```

  ```typescript Cordova theme={null}
  const purchaseOptions = new Qonversion.PurchaseOptionsBuilder()
    .setOldProduct(oldProduct)
    .build();
  const result = await Qonversion.getSharedInstance().purchase(product, purchaseOptions);
  ```

  ```typescript Capacitor theme={null}
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setOldProduct(oldProduct)
    .build();
  const result = await Qonversion.getSharedInstance().purchase(product, purchaseOptions);
  ```
</CodeGroup>

Qonversion supports any replacement mode for the old purchase. Provide the update policy while building the purchase options:

<CodeGroup>
  ```java Java theme={null}
  final QPurchaseOptions purchaseOptions = new QPurchaseOptions.Builder()
          .setOldProduct(oldProduct)
          .setUpdatePolicy(QPurchaseUpdatePolicy.WithTimeProration)
          .build();
  ```

  ```kotlin Kotlin theme={null}
  val purchaseOptions = QPurchaseOptions.Builder()
      .setOldProduct(oldProduct)
      .setUpdatePolicy(QPurchaseUpdatePolicy.WithTimeProration)
      .build()
  ```

  ```dart Flutter theme={null}
  final purchaseOptions = QPurchaseOptionsBuilder()
      .setOldProduct(oldProduct)
      .setUpdatePolicy(QPurchaseUpdatePolicy.withTimeProration)
      .build();
  ```

  ```typescript React Native theme={null}
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setOldProduct(oldProduct)
    .setUpdatePolicy(PurchaseUpdatePolicy.WITH_TIME_PRORATION)
    .build();
  ```

  ```csharp Unity theme={null}
  var purchaseOptions = new PurchaseOptionsBuilder()
      .SetOldProduct(oldProduct)
      .SetUpdatePolicy(PurchaseUpdatePolicy.WithTimeProration)
      .Build();
  ```

  ```typescript Cordova theme={null}
  const purchaseOptions = new Qonversion.PurchaseOptionsBuilder()
    .setOldProduct(oldProduct)
    .setUpdatePolicy(PurchaseUpdatePolicy.WITH_TIME_PRORATION)
    .build();
  ```

  ```typescript Capacitor theme={null}
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setOldProduct(oldProduct)
    .setUpdatePolicy(PurchaseUpdatePolicy.WITH_TIME_PRORATION)
    .build();
  ```
</CodeGroup>

The update policy can be one of the following values. The default is `WithTimeProration`.

| Name                  | Description                                                                                                                                                                              |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ChargeFullPrice`     | The new plan takes effect immediately, and the user is charged full price of new plan and is given a full billing cycle of subscription, plus remaining prorated time from the old plan. |
| `ChargeProratedPrice` | The new plan takes effect immediately, and the billing cycle remains the same.                                                                                                           |
| `WithTimeProration`   | The new plan takes effect immediately, and the remaining time will be prorated and credited to the user.                                                                                 |
| `Deferred`            | The new purchase takes effect immediately, the new plan will take effect when the old item expires.                                                                                      |
| `WithoutProration`    | The new plan takes effect immediately, and the new price will be charged on next recurrence time.                                                                                        |

## iOS: multi-quantity purchases

*iOS only.* For in-app products you can choose how many items to buy. On Android the quantity is adjusted directly in the purchase pop-up, so no SDK option is needed; on iOS you must set the quantity beforehand while building the purchase options:

<CodeGroup>
  ```swift Swift theme={null}
  let purchaseOptions = Qonversion.PurchaseOptions(quantity: quantity)
  Qonversion.shared().purchase(product, options: purchaseOptions) { (result) in
    ...
  }
  ```

  ```objectivec Objective-C theme={null}
  QONPurchaseOptions *purchaseOptions = [[QONPurchaseOptions alloc] initWithQuantity:3];
  [[Qonversion sharedInstance] purchaseWithResult:product
                                          options:purchaseOptions
                                       completion:^(QONPurchaseResult * _Nonnull result) {
    ...
  }];
  ```

  ```dart Flutter theme={null}
  final purchaseOptions = QPurchaseOptionsBuilder()
      .setQuantity(3)
      .build();
  final result = await Qonversion.getSharedInstance().purchaseWithResult(
    product,
    purchaseOptions: purchaseOptions
  );
  ```

  ```typescript React Native theme={null}
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setQuantity(3)
    .build();
  const result: PurchaseResult = await Qonversion.getSharedInstance().purchaseWithResult(product, purchaseOptions);
  ```

  ```csharp Unity theme={null}
  var purchaseOptions = new PurchaseOptionsBuilder()
      .SetQuantity(3)
      .Build();
  Qonversion.GetSharedInstance().Purchase(product, purchaseOptions, (result) =>
  {
      ...
  });
  ```

  ```typescript Cordova theme={null}
  const purchaseOptions = new Qonversion.PurchaseOptionsBuilder()
    .setQuantity(3)
    .build();
  const result = await Qonversion.getSharedInstance().purchase(
    product,
    purchaseOptions
  );
  ```

  ```typescript Capacitor theme={null}
  const purchaseOptions = new PurchaseOptionsBuilder()
    .setQuantity(3)
    .build();
  const result = await Qonversion.getSharedInstance().purchase(
    product,
    purchaseOptions
  );
  ```
</CodeGroup>
