We have been using RevenueCat for IAP auto-renewable subscriptions. We have properly followed RevenueCat integration guideline and able to successfully test IAP subscriptions through Sandbox testers for some devices and get failure for some devices. It’s kind of 50/50 scenario for IAP success/failure.
Its not like Sandbox test users fail for IAP, in production build also IAP fails for many times.
It would be helpful if you check code implementation we did for current app.
AppDelegate.swift
Purchases.debugLogsEnabled = true
Purchases.configure(withAPIKey: PURCHASE_APIKEY, appUserID: UIDevice.current.identifierForVendor?.uuidString)
PremiumController
-> Which displays list of offerings from which user can select particular package and subscribe for the same.
var selectPackage: Purchases.Package? // Selected package reference - the package which is selected by user
// Listing of all offerings which will be displayed to user and user will select an offering from this list
Purchases.shared.offerings { [weak self] offerings, error in
if let pack = offerings?.current?.availablePackages {
self?.packages = pack
for i in pack {
if i.packageType == .annual {
self?.selectPackage = i
break
}
}
self?.tableView.reloadData() // Products listing
}
}
// Function to purchase the package
if let package = selectPackage {
purchasePackage(package)
}
func purchasePackage(_ package: Purchases.Package) {
if Purchases.canMakePayments() {
Purchases.shared.purchasePackage(package) { [weak self] transaction, purchaserInfo, error, userCancelled in
if let error = error {
alertError(error.localizedDescription) // Error alert will be displayed
return
}
if let info = purchaserInfo?.entitlements.all["powermove.pro"], info.isActive, let date = purchaserInfo?.latestExpirationDate {
Settings.expiresDateStr = date.toUTCSubscriptionString
DispatchQueue.main.async {
let alert = UIAlertController(title: "Success", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
DispatchQueue.main.async {
self?.back() // this is kind of callback function which will be called to update UI
}
}))
self?.present(alert, animated: true)
}
}
}
}
}
We have debugged the code and found out that when
storeKitWrapper:(RCStoreKitWrapper *)storeKitWrapper updatedTransaction:(SKPaymentTransaction *)transaction
is called, transaction.transactionState
is returned as SKPaymentTransactionStateFailed
.
We are getting error -> “Cannot connect to iTunes Store”. Below are some log statements.
2020-10-19 18:06:40.657001+0530 Strong Consumer[37652:7035479] [Purchases] - DEBUG: GET /v1/subscribers/069A0745-1204-4079-8D7D-D14E95211998
2020-10-19 18:06:40.657370+0530 Strong Consumer[37652:7035479] [Purchases] - DEBUG: Offerings cache is stale, updating caches
2020-10-19 18:06:40.657607+0530 Strong Consumer[37652:7035479] [Purchases] - DEBUG: GET /v1/subscribers/069A0745-1204-4079-8D7D-D14E95211998/offerings
2020-10-19 18:06:40.663777+0530 Strong Consumer[37652:7035479] [Purchases] - DEBUG: PaymentQueue removedTransaction: monthly_5.99 AB64AECB-94AD-45CA-B836-65D0D6EC6D0C ((null) Error Domain=SKErrorDomain Code=0 "Cannot connect to iTunes Store" UserInfo={NSLocalizedDescription=Cannot connect to iTunes Store}) {
2020-10-19 18:06:40.664062+0530 Strong Consumer[37652:7035479] [Purchases] - INFO: Subscriber attributes synced successfully
From the logs, you're getting an
SKErrorUnknown
(code 0) from Apple. There's unfortunately nothing you can do on your end to resolve these in production. It's either a problem with the user's Apple account, or an internal problem with Apple.Apple docs: https://developer.apple.com/documentation/storekit/skerrorcode/skerrorunknown