Adam Howitt's Blog

Sep 08
2009

Testing iPhone StoreKit purchases

I spent a significant amount of time troubleshooting the iPhone StoreKit framework for our WalkJogRun iPhone app this weekend. I finally discovered that the documentation was slightly misleading after discovering a post on the Apple developer forums.

I had followed the StoreKit developer guide for testing which instructs you to 1. Create an account in iTunes Connect 2. Go to the Store Settings app and sign out 3. Sign in as your test account 4. Go to your app and test the purchase process

The problem comes at step 3 when you sign in. You are typically (but not consistently) told that the login has never been used in the App Store before so you'll need to review your details, which begins an account mini-interview to pick a country and enter credit card details. If you go anywhere near this process, you'll not be able to use the account when you go to your app and finally login with a series of different alerts, mainly "Your Password has Changed" or confirm your billing info.

The solution, I discovered from an Apple forum post by "Kuga", is NOT to sign in once you logout of your real account - instead just go straight to your app after you sign out in the Store Settings and use the in-app login.

Note that if you ran into the "your password has changed" error you'll find that it's damn near impossible (I couldn't work it out) how to turn an account you completed an interview for back into an account you can use for testing.

Country Specific Testing Notes

Instead of hard coding pricing into your app, if you've followed the store kit programming guide you'll pull the pricing and descriptions from iTunes Connect but initially pricing is shown in the country you were using before you logged out in the store settings.

Ben Gottlieb, author of the awesome Crosswords and Satchel iphone apps, gave me a great tip at the last NSCoder night here in Chicago for in app purchase accounts - if you use gmail you can create "throwaway aliases" to use when creating users, since each email account you use must be a unique email address that has never been used for testing and not an existing apple id. So, for example, if your email address was aardvark@gmail.com you can create fake aliases with the "+ syntax" so I would create a test user "aardvark+itcUS@gmail.com" for a US iTunes Connect test user account. The +itcUS gets ignored by Google and the email related to that account still goes into gmail for the aardvark@gmail.com account. It saves you creating a ton of email accounts for testing.

If you've created a test user for the same country as your regular account you'll have no trouble finalizing your purchase. If you've chosen a store in another country for your test user you'll be told initially that your account is only valid for that country store and you're unable to finish your purchase. This is a good thing!

Close your application and go to the store settings and you should see that you are actually logged in with that new test account for a different country. Close the store settings and reopen your app. At the point where pricing info is shown you should now see it localized to the test user country and you can complete the purchase in that language. Just be aware that the dialogs will all be in your target language so before you test the Japan store you might want to memorize the order of the buttons :-)

Verifying Receipts

I threw a simple alert in my "provide content" function to give a visual confirmation initially:

- (void) provideContent:(NSString *)productId {
    /* This is where I download the product based on the product Id to store on their phone */
    UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Purchase complete" message:@"Thanks for buying, sucka" delegate:self cancelButtonTitle:@"Done" otherButtonTitles:nil];
    [av show];
    [av release];
}

This is where I'll pull down the actual content if my purchase was successful. Since you should have your server validate receipts before delivering content you need to send the encoded receipt data over http. This piece isn't well documented but a great post on StackOverflow from JDAndrea provides the code necessary to pass the signed receipt by URL to your own server, in that case a PHP box. Since I'm a ColdFusion developer I wrote the following snippet to handle the work:


<cfset stcReceiptSend = structNew() />
<cfset stcReceiptSend["receipt-data"] = url.receipt />
<cfset receipt = serializeJSON(stcReceiptSend)>
<cfhttp method="POST" url="https://sandbox.itunes.apple.com/verifyReceipt">
    <cfhttpparam value="#receipt#" type="XML" />
</cfhttp>
<cftry>
<cfset result = deserializeJSON(cfhttp.filecontent)>
<cfmail from="mytestemail@example.com" to="mytestemail@example.com" subject="iPhone in-app Receipt Verification" type="html">
<cfdump var="#result#">
</cfmail>
<cfif result.status eq "0">
<cfcontent reset="yes"><cfoutput>0</cfoutput>
<cfelse>
<cfcontent reset="yes"><cfoutput>1</cfoutput>
</cfif>
<cfcatch>
<cfcontent reset="yes"><cfoutput>1</cfoutput>
</cfcatch>
</cftry>
It's very simple and just turns the encoded receipt data from your app into a serialized JSON packet which is posted to the itunes sandbox. Note for production releases you'll need to change that url to be the real itunes URL. If the result is a 0 you have a valid itunes transaction and I send an email dumping out the transaction keys to my email for reference before returning a 0 (success) to the code in my iPhone app. Anything other than a 0 in the result.status means something isn't right and you should pass back something other than a 0 to the iPhone app so you don't deliver the content they "unlocked".

That's it for my StoreKit testing summary - hope it helps remove some obstacles I encountered along the way. If you're looking for tips on this stuff I strongly recommend three sites which have proven critical to my forward motion: iPhoneSDK, Apple Discussions and StackOverflow. The iPhone Reference Library is a fourth but that *should* go without saying. I've found it particular useful searching for exact classnames to find the detailed API overview of properties and methods I need.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
[Add Comment] [Subscribe to Comments]
  1. Hi, I am developing a iPhone App, that has a monthly subscription feature.

    Whenever I try to test the StoreKit code in my App [on the device], I get failedtransaction in my <SKPaymentTransactionObserver> class.

    I have not uploaded any application on the appstore, nor I have associated a inApp purchase product with it.

    I am testing with a dummy product ID for buying in the app.

    Please let me know Do I need to upload the application details(with out binary) to test this?

    Thanks Sam

  2. Hi Sam, You will need to create the app on the appstore and create the associated in-app purchases for this to work. Make sure the appid matches the provisioning profile and you can't just use a wildcard provisioning profile for this. So if you usually build with the provisioning profile com.example.* that won't work for the in-app purchase stuff (I think at least or maybe I'm thinking of push notifications). Maybe try wildcard provisioning profile and if you're getting stuck go with the non-wildcard version com.example.myfullappname

    Hope that helps! Adam

  3. Thanks Adam. I am using non wildcard version for app id as well as in the provisioning profile. But as you said, we need to upload the app on the itunes store, but as the app is not ready so i guess if i check mark 'Upload Binary Later' in the app upload process, so shall I be able to create 'in app purchase product' for it and use this in the app?

    THanks Sam

  4. Hi Sam - that's right. Upload binary later will allow you to submit it when you're ready.

  5. I have a problems with a certain account, that I deleted from Test Accounts after I successfully tested the application with. Now it doesn't let me to log in with another account and seems, that it logs in automatically with this not existing account instead of letting me log in with another account. How do I reset it? How do I wipe this account from the sandbox of Store application? Thanks!

  6. Hi Nava - thanks for the comment. You need to go to Settings > Store and you should see the account is your test account. Hit Sign Out and then go back into your app. You'll be prompted to login again now.

  7. Hi Adam, Any ideas how to change the price of an active in-store purchase? Unlike the pricing of an app which is easy to change at any time on iTunes Connect, I can't seem to find a way to change the price of in-app purchases on iTunes Connect. Deleting it and creating a new one is not a good option because it would prevent someone who uninstalled the app by mistake from repurchasing that same in-app purchase at no cost.

  8. I have completed my testing for my app in terms of in app purchase in Sandbox environment. Its perfect. I have already submitted to the app store for approval. I have already changed #define SANDBOX NO But still when I am going to check,its says"Environment: Sandbox". Do i dave to make any change to switch from Sandbox environment to Production Environment? Or it will be automatically changed once it will be approved by apple.

  9. Hey Andrew - I dont' recall exactly but think it stuck around until the app went live (leaving me hard pushed to test the full thing end to end pre-release!).

[Add Comment]