Check if user cancelled SKPaymentTransaction
Solution 1
Check the SKPaymentTransaction error
property that is set.
@property(nonatomic, readonly) NSError *error
An object describing the error that occurred while processing the transaction. (read-only)
The error property is undefined except when transactionState is set to SKPaymentTransactionStateFailed. Your application can read the error property to determine why the transaction failed.
Additionally, you may want to use Apple's Reachability class to determine if Internet is available prior to initiating the transaction.
Solution 2
This code works for me:
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(@"Other error");
} else {
NSLog(@"User canceled");
}
Solution 3
Ellen's answer is perfect. Just in case someone is wondering about the other cases
switch (transaction.error.code) {
case SKErrorUnknown:
//Unknown error
break;
case SKErrorClientInvalid:
// client is not allowed to issue the request, etc.
break;
case SKErrorPaymentCancelled:
// user cancelled the request, etc.
break;
case SKErrorPaymentInvalid:
// purchase identifier was invalid, etc.
break;
case SKErrorPaymentNotAllowed:
// this device is not allowed to make the payment
break;
default:
break;
}
Solution 4
Swift version:
if let error = transaction.error as? NSError {
if error.domain == SKErrorDomain {
// handle all possible errors
switch (error.code) {
case SKError.unknown.rawValue:
print("Unknown error")
case SKError.clientInvalid.rawValue:
print("client is not allowed to issue the request")
case SKError.paymentCancelled.rawValue:
print("user cancelled the request")
case SKError.paymentInvalid.rawValue:
print("purchase identifier was invalid")
case SKError.paymentNotAllowed.rawValue:
print("this device is not allowed to make the payment")
default:
break;
}
}
}
Solution 5
Swift 4.2
if let error = transaction.error,
(error as? SKError)?.code != SKError.paymentCancelled {
// Here it is failed for sure!
}
Related videos on Youtube
Comments
-
dariaa almost 2 years
How can I check if user tapped cancel button (either when he was asked if he wants to purchase smth or perhaps if he already purchased this SKProduct whether he wants to download it)?
For now I just receive SKPaymentTransactionStateFailed in paymentQueue:updatedTransactions: method both after user taps cancel button and for instance when there is no internet. Any way to differentiate these two cases?
-
dariaa over 11 yearsThat's the weird part about it. When I press cancel button I still receive SKPaymentTransactionStateFailed in paymentQueue:updatedTransactions: method and the following error: Error Domain=SKErrorDomain Code=2 "Cannot connect to iTunes Store"
-
Jonathan Zdziarski over 11 yearsThat's unfortunate. So I'd check Internet connectivity using Apple's Reachability class then, and if you do have connectivity, then you know it was either canceled or the purchase failed with the server somehow. I would also file a bug report stating that the error property doesn't work as documented, and ask for a fix.
-
Stavash over 11 yearsThis isn't a connectivity issue - for some reason, the error is the same when canceling the transaction or when it fails for any other reason.
-
Nuthatch about 10 yearsYou need to check the error code. The description seems to always be the same. If you look at SKError.h you'll find these values:
SKErrorUnknown, SKErrorClientInvalid, SKErrorPaymentCancelled, SKErrorPaymentInvalid, SKErrorPaymentNotAllowed, SKErrorStoreProductNotAvailable.
Check and respond to each appropriately. -
Jason Leach about 7 yearsIf you don't cast it to an
NSError
it will remain anSKError
and you can just docase SKError.unknown:
; there is no need to use.rawValue
. -
Grzegorz Krukowski about 5 yearsI think more properly you should check for
error.domain
instead of hard casting the object -
jwarrent about 4 yearsYou must convert the
transaction.error
to anSKError
to get the appropriate code. For example,if (transaction.error as? SKError)?.code != SKError.Code.paymentCancelled {}
. -
Robin Stewart over 3 years@GrzegorzKrukowski No, this is fine because the expression
(error as? SKError)?.code
will just resolve tonil
if the cast fails.