130
votes

Does anyone have any ideas on how to reset and/or clear the iOS in-app purchase sandbox?

I have an app that I'm testing with the sandbox, and I'd like to test new purchases without having to create a new test user every time I purchase something.

If I don't do this, then I (of course) always get a message that the in-app purchase item has already been purchased when I click on my app's buy button.

8

8 Answers

83
votes

IMO there are 3 things you can do to make testing non-consumables bearable:

  1. You can have many test accounts associated to one email. Gmail for example lets you add a "plus" string to the email to create aliases for an address: so [email protected] and [email protected] both really just go to [email protected]. Probably other email hosts do the same. When you create a test account you need to introduce: first name, last name, email address, password, secret question, secret answer, date of birth, and iTunes store country. You can put exactly the same data (including password) for [email protected] and [email protected] and you will have two test accounts. Finally, in your [email protected] inbox you will receive two verification emails from Apple to confirm both test accounts.

  2. Say that you have a non-consumable with product ID @"Extra_Levels". Instead of writing @"Extra_Levels" in all methods (requestProduct, purchaseProduct, ...), just write PRODUCT_ID1 and at some header file put #define PRODUCT_ID1 @"Extra_Levels" (with no semicolon!), then the preprocessor will search PRODUCT_ID1 and substitute it for @"Extra_Levels". Then creating a new non-consumable called @"Extra_Levels_01" and changing the #define will be as good as resetting the purchases for all your test users.

  3. As appsmatics pointed out, you can test the correct behavior of your code when you buy a non-consumable IAP by first using a consumable IAP (so that test user can make as many purchases as needed) to get rid of some bugs. Of course, you should also test the code with the real non-consumable IAP after that.

34
votes

You can't do this, as far as I know. The sandbox backend works like a real account-- once it's purchased, it's purchased (and thus you can test restore). You should do most of your development with the store stuff shimmed out, and then when you get to testing it for real, just expect to create several test accounts.

11
votes

I have 2 in app purchase items. 1 for production. and the other for testing. when I need to "clear" I delete the in app item and create new one (15 seconds in itunes connect and 1 second to change the product id in code)

if i dont need to test "new user", i use the production in app item.

8
votes

Well, technically you don't need that.

If you get SKPaymentTransactionStateRestored, it is 100% equivalent to the app store verifying the user and granting him the purchase. I have a switch like:

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
  for( SKPaymentTransaction *purch in transactions )
  {
    switch( purch.transactionState )
    {
      case SKPaymentTransactionStateRestored:
        info( "PURCHASE RESTORE" ) ;
        // fall thru
      case SKPaymentTransactionStatePurchased:
        [[SKPaymentQueue defaultQueue] finishTransaction:purch];
        // Do regular changes to app state for this purchase,
        // register in keychain, etc.
        break ;

       //.. other cases
     }
  }
}

The question of having your app logic / take back the purchase is simple: if you're caching purchases in keychain, delete your keychain. If you're doing it some other how, just change your local app state to pretend like the user never purchased it before. The request to purchase dialog is still exactly the same, the only difference is when you punch YES, it gives you SKPaymentTransactionStateRestored instead of SKPaymentTransactionStatePurchased.

7
votes

Deleting your app and reinstalling works also for sandbox testing. Depends on the app obviously, but I'm testing a subscription based app that only purchases during sign up at the moment so it's been the easiest solution.

3
votes

Check out SimStoreKit. It's a "simulated version of the iPhone's StoreKit, for testing store UIs on the iPhone Simulator, or even on device without having to set up IAP in Connect."

SimStoreKit stores purchases in the user defaults under the key ILSimSKTransactions. So to clear all purchases you can do:

[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"ILSimSKTransactions"]

On the simulator, you can simply remove your app and install it again.

I've successfully used SimStoreKit to debug my app's store front before testing with the sandbox. The beauty of this library is that it can be set-up to use the same class names as the real StoreKit framework (by doing #define ILSimReplaceRealStoreKit 1 before doing #include <ILSimStoreKit.h>).

In source files where I need to access StoreKit, I include this header file:

#import <TargetConditionals.h>

#if TARGET_IPHONE_SIMULATOR
    #define kILSimAllowSimulatedStoreKit 1
    #define ILSimReplaceRealStoreKit 1
    #import <ILSimStoreKit.h>
#else
    #import <StoreKit/StoreKit.h>
#endif

This has the effect of using SimStoreKit when I run on the simulator and the real StoreKit when I run on the device.

-1
votes

alternatively to create multiple test user solution you can create multiple test in app purchases in iTunes connect then you don't need to change a user account.

-1
votes

Just keep using the same test account, restoring purchases as opposed to completing new ones. After all, whether you start a new purchase or restore an old one, YOUR APP will do the same thing (at least initially, maybe the user interface will update differently upon completion). Apple are the folks handling things differently in those different situations - don't worry about it.

Place your delivery logic in the SKPaymentTransactionStateRestored case within this method's implementation for testing:

- (void)paymentQueue:(SKPaymentQueue *)queue
 updatedTransactions:(NSArray *)transactions;

Then be sure to put that delivery logic into the SKPaymentTransactionStatePurchased case.

At the end, because most of us are obsessive-compulsive to varying degrees, do a final test with a fresh account (not a big deal to make a second one for absolute certainty).

The final thing to note: consider apple's position. If there was a problem with developers having to waste time creating tens or hundreds of accounts to test IAP thoroughly, they would have solved the problem. There is no problem.