Check if user cancelled SKPaymentTransaction

10,024

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!
}
Share:
10,024

Related videos on Youtube

dariaa
Author by

dariaa

#SOreadytohelp

Updated on June 21, 2022

Comments

  • dariaa
    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
    dariaa over 11 years
    That'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
    Jonathan Zdziarski over 11 years
    That'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
    Stavash over 11 years
    This 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
    Nuthatch about 10 years
    You 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
    Jason Leach about 7 years
    If you don't cast it to an NSError it will remain an SKError and you can just do case SKError.unknown:; there is no need to use .rawValue.
  • Grzegorz Krukowski
    Grzegorz Krukowski about 5 years
    I think more properly you should check for error.domain instead of hard casting the object
  • jwarrent
    jwarrent about 4 years
    You must convert the transaction.error to an SKError to get the appropriate code. For example, if (transaction.error as? SKError)?.code != SKError.Code.paymentCancelled {}.
  • Robin Stewart
    Robin Stewart over 3 years
    @GrzegorzKrukowski No, this is fine because the expression (error as? SKError)?.code will just resolve to nil if the cast fails.