BuySellAds.com

Our DNA is written in Objective-C
Jump

Things I learned implementing my first InAppPurchase

Yesterday I sent my first app off to Apple containing an InAppPurchase (IAP). It’s a free app that gives the user an option to pay a dollar for a premium features. That’s what they call Freemium these days. Free to try, premium to get some more.

The possibility for Freemium was only introduced in October 2009 when Apple finally gave in to developer’s wish to be able to do away with those dreaded Lite versions which have a very low conversion rate (about 1%) anyway. Until that time IAPs where only available for paid apps.

After developing on the premium content for about two weeks I hit my first roadblock. You apparently cannot configure an app id for use in a provisioning profile it it is already configured on another developer’s account.

If you are looking for a full walkthrough, this is not the article to provide that. Troy Brant has the most complete IAP Walkthrough on his blog. Instead this is a summary of my mental notes.

Double App Identifier Trouble

For sake of simplicity I’ve been using the wildcard app identifier on all my published apps and did not have any problems with this approach. You only need a unique app identifier for apps that either use push notifications or IAPs. So far my apps did neither, so I was not missing anything.

This problem is actually a global one as a devious competitor would be able to block your change of a wildcard provisioning profile to a unique one quite simply. He would only have to inspect your IPAs as downloaded from iTunes for the app id in the info.plist and try to configure it in his program portal. If successful then his app-id squatting is successful and you are effectively prevented from ever adding IAP or Push to your existing app.

In our case it was our own mistake. My brother-in-law had configured the Identifier in his personal developer account because he thought that necessary. We published the app on the customer’s account with a wildcard app. It was only when I wanted to configure the new id that I found the problem.

So we contacted Apple Developer Support on multiple levels and heard several times that it is not possible to remove an unused app identifier, but “thank you for your suggestion, we’ll consider it for future updates” (I’m paraphrasing). So if it were for myself I would have had to submit the app as new with the different identifier. I cannot say more but hint that knowing the right people with the right corporate and parallel interests can even move a seemingly unmovable position.

Setting up in Program Portal

I configured the app id and created a development provisioning profile for it. Then I built the app with that to see if it really would work and it did. The second step was to configure the necessary IAP product which is done in iTunes Connect. Apple has improved the process quite a bit, because previously you had to submit an app with IAP only to self-reject it right away to be able to configure products.

After setting up the unique app identifier and enabling it for IAP I was able to configure a simple non-consumable product with price tier 1 and localizations for English and German. At minimum you have to have one language even though you might never display the localized title or description. Also you have to provide a screenshot which will show the reviewer what the IAP is about. This will never be seen by customers. The same is true for the “Reference Name” which is also just for your own use.

You have a choice of three variants:

  • non-consumable. Customers can purchase this feature once and download it for free on the same or other devices that use the same iTunes login.
  • consumable. Those are non-transferable and have to be paid for every time.
  • subscriptions. Like non-consumables those have to be accessible on all the user’s devices and also have to be paid for every time.

So my freemium update clearly is a non-consumable.

As Product ID you should use the parent app’s ID and append the reference name of your IAP. For example if the app has ID: com.yourcompany.app, then the IAP should have ID com.yourcompany.app.extrafeature1. Note that there seems to be a separate approval process in place for individual apps and IAP products.

When setting up the IAP product you have to approve it yourself and clear it for sale. Then you also have to decide whether it’s going to be available to an existing app or wether you will be uploading an app update containing this IAP.

The latter will be your choice if you added the feature code to the app itself and the purchase will just unlock it. The former is your option if the content comes for your server and requires no update to the app itself. Some time may have to pass until the new configuration has been replicated to the distributed servers of the iTunes cloud. But in my case the product was available within 15 minutes or so.

I also had to create a special IAP test user on iTunes Connect. For this I used my gmail address with a +iaptest appended. This allows for creating a new iTunes Connect user without having to create a new e-mail address. Note that before testing IAPs on device you need to log out of your regular iTunes user via the App Store settings pane.

Nuts & Bolts

The general program logic you will have to put in place is the following:

  1. Your program starts out with your store page disabled while you check if purchases are possible. Parental controls or enterprise settings might have disabled app downloads which at the same time prevents in-app purchases.
  2. Construct a list of IAP product identifiers and send an asynchronous query to Apple’s server.
  3. After a couple of seconds you get a delegate callback telling you which of the passed IDs are invalid and for the valid ones you get an SKProduct object containing localized title, description, price and a locale to format the price with.
  4. At that point you can enable your store and show the localized products. Apple does not provide their nifty purchase button to you (as seen on the mobile app store), so I had to create DTPurchaseButton to have a UI that the user feels familiar with.
  5. When the user has expressed his intent of purchase you add a payment transaction to the persistent payment queue. After a couple of seconds an alert view pops up asking for the user’s confirmation. If you had transferred the app to your device in debug mode then it will mention that it’s in sandbox mode, i.e. just simulating.
  6. Here the user has a choice of cancelling the purchase or OKing it. In both cases after a couple of more seconds you get a callback informing you of the status of the transaction. Note that the callback also occurs once before the confirmation to show that the transaction has gotten status purchasing.
  7. Dependent on the result you either reset your store UI or enable the purchased item. After you made certain that the feature is available you finish the transaction, because otherwise you will continue to receive the callback every time you set the delegate. This is because the user might have exited before receiving payment confirmation.
  8. It’s up to you to save the unlock somewhere. If you don’t worry about piracy then NSUserDefaults is a convenient place. If you do, then an encrypted field in the keychain is your most secure option. In your app you have to show the feature as installed so that the user knows his purchase was successful.
  9. (Optional) On the confirmation callback you get an encrypted transaction receipt which you can save on your server. This enables intra-day tracking of purchases as well as gives you an option to verify the receipt from your server against Apple’s server and remotely disable the unlock in case of fraud.

If a non-consumable IAP product has been purchased before then you get the same callback, but the user gets an additional information that the re-download is not being charged. Finally, if you are listening to Apple’s recommendation, you will implement a “Restore Purchases” button in case the app gets reinstalled on the same or a different device. This button is tied to a simple method of payment queue and will trigger the callback mentioned in 7 for each already purchased product.

I was at first confused about the “finishing of transactions”, but you can safely do that as soon as you unlocked the feature. The restore goes into Apple’s records and finds all previous transactions this user did for this app. You could also rely on re-downloads not being charged, but as soon as you have more than a single IAP this restore button is way more user-friendly. Well, even with just a single IAP you should have the button. It makes the user feel safer knowing that he can restore the value for his hard-spent dollar.

Tricks of the Trade

The StoreKit API in itself is very simple to use once you had a couple of unclarity-realted thoughts removed, either by experiment or hearing from somebody with IAP experience. But it has something of a problem that is due to the design with delegate setting and callback methods. To properly use it in your app you are best of creating a singleton shared instance that encapsulates all the unlock-tracking and communicating with StoreKit.

When certain actions occur, like the product list being available, you post notifications to which your view controllers can respond accordingly. ¬†This way you don’t have to constantly change the delegate or have your app stuck waiting on the app store server to respond.

I put all my store logic into a class I aptly named DTStore. It’s quite simple really so I added it as a free bonus to DTPurchaseButton which you can get on my Parts Store.

One question I only found the answer to when studying the documentation is how to get the appropriate price to show. You know, you don’t set a price, but a tier and Apple will give you the appropriate price to show for each store automatically. This lifts a heavy burden off your shoulders as you can never guess what the real price will be fore any of the 96 app stores.

From the documentation I not only learned that the price is there with the correct value, but also that Cocoa gives me methods to format it appropriately for my current locale. Consider the following snipped from DTStore. Knowing that there is a number formatter, a number style NSNumberFormatterCurrencyStyle and a price locale in the SKProduct allows you to do the following.

- (NSString *)localizedPriceForProductWithSKU:(NSString *)sku
{
	SKProduct *product = [self productForSKU:sku];
 
	if (product)
	{
		NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
		[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
		[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
		[numberFormatter setLocale:product.priceLocale];
		return [numberFormatter stringFromNumber:product.price];
	}
	else
	{
		NSLog(@"Invalid Product ID %@", sku);
		return nil;
	}
}

Until now it had been burnt into my brain that I could only format numbers by means of stringWithFormat: but this would not have allowed for this kind of currency formatting. So this is a good piece of code to bookmark should you ever have to format monetary values in the future.

Conclusion

If you omit the optional server-side tracking, tighter unlock-security and validation the whole affair can be finished in a single working day, provided that you don’t have to spend too much time figuring out the intricacies of StoreKit and don’t want fancy UI work which you’d have to program or buy first. The StoreKit API in itself is beautiful and simple. It does precisely what it should but it neither provides you with any code for your store user interface or tracking of what purchases you have to unlock.

Generally I have to admit that the process of adding your first freemium content as IAP is far less painful than I previously thought. I guess I’m going to add this model to many more apps in the future.


Categories: Recipes

%d bloggers like this: