Deferred Purchases

Deferred and pending purchases

Deferred or interrupted in-app purchases may happen when SCA confirmation is needed, in the case of parental control and some other cases.
Here is what the deferred purchase flow looks like:

  • a user initiates a purchase
  • purchase is received in state deferred.
  • you get a purchase error with a deferred purchase description
Qonversion.shared().purchaseProduct(product) { (entitlements, error, isCancelled) in
  if let error = error as? NSError, error.code == Qonversion.Error.storePaymentDeferred.rawValue {
      // Purchase is pending
  }
}
  [[Qonversion sharedInstance] purchaseProduct:product completion:^(NSDictionary<NSString *,QONEntitlement *> * _Nonnull entitlements,
                                                                    NSError * _Nullable error,
                                                                    BOOL cancelled) {
    if (error && error.code == QONErrorStorePaymentDeferred) {
      // Purchase is pending
    }
  }];
final QPurchaseModel purchaseModel = product.toPurchaseModel();
Qonversion.getSharedInstance().purchase(this, purchaseModel, new QonversionEntitlementsCallback() {
    @Override
    public void onSuccess(@NotNull Map<String, QEntitlement> entitlements) {
        ...
    }

    @Override
    public void onError(@NotNull QonversionError error) {
        if (error.getCode() == QonversionErrorCode.PurchasePending) {
            // Purchase is pending
        }
    }
});
val purchaseModel = product.toPurchaseModel()
Qonversion.shared.purchase(this, purchaseModel, callback = object: QonversionEntitlementsCallback {
    override fun onSuccess(entitlements: Map<String, QEntitlement>) {
        ...
    }

    override fun onError(error: QonversionError) {
        if (error.code === QonversionErrorCode.PurchasePending) {
            // Purchase is pending
        }
    }
})
try {
  final QPurchaseModel purchaseModel = product.toPurchaseModel();
  final Map<String, QEntitlement> entitlements = await Qonversion.getSharedInstance().purchase(purchaseModel);
} on QPurchaseException catch (e) {
  if (e.code == QErrorCode.purchasePending) {
    // Purchase is pending
  }
  print(e);
}
try {
  const purchaseModel: PurchaseModel = product.toPurchaseModel();
  const entitlements: Map<string, Entitlement> = await Qonversion.getSharedInstance().purchase(purchaseModel);
} catch (e) {
  if (e.code === QonversionErrorCode.PURCHASE_PENDING) {
    // Purchase is pending 
  }
  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
  {
    if (qonversionError.Code == QErrorCode.PurchasePending)
    {
      // Purchase is pending
    }
  }
});
try {
  const purchaseModel = product.toPurchaseModel();
  const entitlements = await Qonversion.getSharedInstance().purchase(purchaseModel);
} catch (e) {
  if (e.code === QonversionErrorCode.PURCHASE_PENDING) {
    // Purchase is pending 
  }
  console.log(e);
}
  • if a transaction completes successfully later, Apple StoreKit or Google Billing Service returns a successful purchase
  • Qonversion SDK returns a successful purchase

Make sure your app handles deferred or interrupted transactions by setting a delegate and a function that is triggered when such a transaction is made.

🚧

Attention

Please note, this function is called only in case of deferred purchases. The purchase flow happens as described here for all other cases.

Set delegate (iOS only)

Set the delegate to handle pending purchases.

Qonversion.shared().setEntitlementsUpdateListener(self)
[[Qonversion sharedInstance] setEntitlementsUpdateListener:self];

Add a function to handle entitlements

Add this function to handle new entitlements when the purchase is completed.

func didReceiveUpdatedEntitlements(_ entitlements: [String : Qonversion.Entitlement]) {
    // handle updated entitlements here
}
- (void)didReceiveUpdatedEntitlements:(NSDictionary<NSString *, QONEntitlement *>  * _Nonnull)entitlements {
    // handle updated entitlements here
}
Qonversion.getSharedInstance().setEntitlementsUpdateListener(entitlements -> {
    // handle updated entitlements here
});
Qonversion.shared.setEntitlementsUpdateListener(object : QEntitlementsUpdateListener {
    override fun onEntitlementsUpdated(entitlements: Map<String, QEntitlement>) {
        // handle updated entitlements here
    }
})
StreamSubscription<Map<String, QEntitlement>> _updatedEntitlementsStream;
_updatedEntitlementsStream = Qonversion.getSharedInstance().updatedEntitlementsStream.listen((entitlements) => {
   // handle updated entitlements here
});
Qonversion.getSharedInstance().setEntitlementsUpdateListener({
    onEntitlementsUpdated(entitlements) {
        // handle updated entitlements here
    },
});
Qonversion.GetSharedInstance().UpdatedEntitlementsReceived += HandleUpdatedEntitlements;

private void HandleUpdatedEntitlements(Dictionary<string, Entitlement> entitlements)
{
    // handle updated entitlements here
}
Qonversion.getSharedInstance().setEntitlementsUpdateListener({
    onEntitlementsUpdated(entitlements) {
        // handle updated entitlements here
    },
});

You can also provide a listener during the initialization for some platforms as follows:

final QonversionConfig qonversionConfig = new QonversionConfig.Builder(
        this,
        "projectKey",
        QLaunchMode.SubscriptionManagement
)
        .setEntitlementsUpdateListener(entitlements -> {
            // handle updated entitlements here
        })
        .build();
Qonversion.initialize(qonversionConfig);
val qonversionConfig = QonversionConfig.Builder(
    this,
    "projectKey",
    QLaunchMode.SubscriptionManagement
)
    .setEntitlementsUpdateListener(object : QEntitlementsUpdateListener {
        override fun onEntitlementsUpdated(entitlements: Map<String, QEntitlement>) {
            // handle updated entitlements here
        }
    })
    .build()
Qonversion.initialize(qonversionConfig)
const config = new QonversionConfigBuilder(
  'projectKey',
  LaunchMode.SUBSCRIPTION_MANAGEMENT
)
  .setEntitlementsUpdateListener({
      onEntitlementsUpdated(entitlements) {
          // handle updated entitlements here
      },
  })
  .build();
Qonversion.initialize(config);
const config = new Qonversion.ConfigBuilder(
  'projectKey',
  Qonversion.LaunchMode.SUBSCRIPTION_MANAGEMENT
)
  .setEntitlementsUpdateListener({
      onEntitlementsUpdated(entitlements) {
          // handle updated entitlements here
      },
  })
  .build();
Qonversion.initialize(config);