How to test SKPaymentTransactionStateDeferred?
I've made a little headway on this topic, so thought I'd report in. Plus, I've found an apparent bug, which I've reported to Apple.
What I've done is the following:
1) Create a parent test (sandbox) account in iTunes Connect, and create a child test account. These are really just two test accounts in iTunes Connect. Call them P and C for parent and child.
2) With both accounts, go to https://appleid.apple.com and change their year for their age. iTunes Connect doesn't let you do this. For some reason, the process with family sharing doesn't work unless ages do have a year. I've found that you have to do the selection twice on the year list at https://appleid.apple.com. Odd. I set my P as older (some adult age) and my C as younger.
3) On one apple device (my iPhone, running iOS8), I setup family sharing under Settings > iCloud for the P account. I'm signed into iTunes on this device with my real iTunes Apple Id (which has my payment info).
4) I then invite my C apple id to be part of my family, under Settings > iCloud.
5) I accept the invitation from P (on my iPad, also running iOS8), which involves signing into the iCloud on that other device as C.
6) On my iPhone, I turn on "Ask To Buy" for the C family member.
Now, I'm ready to try a test purchase as C on my sandboxed app. After I go through the regular process in my app's store I get the following alert:
when I tap on "Ask", I get the next alert:
Now, I've tried two options, with separate purchases. I've tried the "OK" option, which should send a notification to the P account. I have yet to receive such a notification on my P account (still signed into iCloud as that on my iPad).
I've also tried the "Approve in Person" option on the "child"s iPad. I use the P account, and I enter in that if on the next alert:
I get no error after that, so it would seem the Approve In Person worked, but I have yet to have that purchase convert to SKPaymentTransactionStatePurchased state. All of the deferred purchases are still in the payment queue of the app, each with the state SKPaymentTransactionStateDeferred. When I restart the app, the state of each purchase, still in the queue, is deferred.
Next, I wondered if there was some problem with the particular test iTunes account for C, so I made a second child account, call that C2, and tried to establish it as a child under P on my iPhone. However, I run into a further problem there. I get the alert (on the iPad), when I try to accept the invitation to be a family member under P for C2:
To me, this limitation on iCloud account shouldn't apply to test accounts. This is the apparent bug I've reported to Apple.
So, in summary, I'm not yet 100% convinced that my SKPaymentTransactionStateDeferred implementation is working. We shall see if Apple gets back to me.
From iOS 8.3 there is a new flag available for SKPayment:
So in case you need to test SKPaymentTransactionStateDeferred state you initialize new
SKMutablePayment and set
simulatesAskToBuyInSandbox = YES.
PS. WARNING: folks complain that this API doesn't work properly (see comments)
iOS 9.2.1, Xcode 7.2.1, ARC enabled
simulatesAskToBuyInSandbox property does not cause the payment queue observer to register the
SKPaymentTransactionStateDeferred state. Instead, it just process the payment and the observer registers the
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:productUserRequested];payment.simulatesAskToBuyInSandbox = true;[[SKPaymentQueue defaultQueue] addPayment:payment];
The only test I was able to perform was to call my method for the deferred payment when the observer registered the
SKPaymentTransactionStatePurchased state instead of my usual method and not call:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
This persists the transaction upon app. termination, and gives you the ability to test the user not completing the purchase (e.g. loss of reception during final stages of purchasing) and deferred purchase. This is assuming your purchasing method passes a deferred parameter, as described in Listing 4-2 Responding to transaction statuses in In App Purchasing Guide: Delivering Products. Here is what the method would look like:
To simulate hung up purchasing:
[self showTransactionAsInProgress:transaction deferred:NO];
To simulate deferred:
[self showTransactionAsInProgress:transaction deferred:YES];
Note: Upon app. restart, the Apple in app. purchase mechanism will ask for your credentials if you did not process any other payments for the credentials, uninstalled the app., or signed out of "iTunes and App Stores" in "Settings". Also, if you tap "Cancel" here, then the transaction will not fail, because the payment queue observer will keep registering the
I found this suggestion from Apple which might prove to be helpful, it is a fancy way of doing what I suggested:
Test an Interrupted Transaction
Set a breakpoint in your transaction queue observer’s paymentQueue:updatedTransactions: method so you can control whether it delivers the product. Then make a purchase as usual in the test environment, and use the breakpoint to temporarily ignore the transaction—for example, by returning from the method immediately using the thread return command in LLDB.
Terminate and relaunch your app. Store Kit calls the paymentQueue:updatedTransactions: method again shortly after launch; this time, let your app respond normally. Verify that your app correctly delivers the product and completes the transaction.
Hope this helps! Cheers.