Question

How to know at any moment if the purchase of a product is deferred?


Userlevel 1
Badge +5

Good evening

From our understanding, on iOS, the purchase of a non-consommable or subscription can end up in the deferred state for example: permission of parent needed to complete the purchase. 

 

We would like to know at any moment the deferred or not state of a product. This way, we can reflect accordingly within our UI.

 

If this is not possible with RC, why? And what is your best practice recommendation? We believe that with SK1, we are able to check this anytime thanks to the payment queue? 

 

Cheers 


2 replies

Userlevel 6
Badge +8

Hey @philippe levieux!

Just wanted to provide this answer here since we discussed this in a support ticket. We'll send a payment pending error in the completion block of a deferred purchase. You can see this implementation in GitHub here: https://github.com/RevenueCat/purchases-ios/blob/395322ac7ef437f6b2f880b2057559b11954bdc4/Sources/Purchasing/PurchasesOrchestrator.swift#L540 

 

Badge +3

For those looking for a code sample of what cody is saying... `Purchasing/purchase` will throw a `RevenueCat.ErrorCode.paymentPendingError` when the user enters the ask to buy flow. So you will need to catch that error and handle it according to your specific needs.

If you are using async/await:

do {
let (transaction, customerInfo, userCancelled) = try await Purchases.shared.purchase(package: package)
// handle success and cancelled cases
} catch(RevenueCat.ErrorCode.paymentPendingError) {
// handle deferred case
} catch(let error) {
throw error
}

If you prefer completion blocks:

Purchases.shared.purchase(package: package, completion: { transaction, customerInfo, error, userCancelled in
if let error {
if let errorCode = error as? ErrorCode {
switch errorCode {
case .paymentPendingError:
// handle deferred case
default:
// default RevenueCat error handling
}
} else {
// default error handling
}
}
})

The purchase will now be pending until the approving party finally approves it. You can catch this event by adopting the `PurchasesDelegate` protocol in your `AppDelegate` or elsewhere, then check for the entitlement in the updated `customerInfo`

extension AppDelegate : PurchasesDelegate {
func purchases(_ purchases: Purchases, receivedUpdated customerInfo: CustomerInfo) {
if customerInfo.entitlements["your_entitlement_id"]?.isActive == true {
// user has access to "your_entitlement_id"
}
}
}

 

I do question the framework design decision of throwing an error in this situation, as the purchase did not really fail. It feels like deferred should probably be part of the should probably be part of the `PurchaseResultData` tuple, much like the `userCancelled`, but this is the way the framework is designed, and this is how to handle it.

Reply