In App Purchases: A Full Walkthrough

At first glance, adding in-app purchases seems like it would be a walk in the park. Apple provides plenty of documentation that should get developers up and running in no time.

So, why is adding in-app purchases such a royal pain in the arse?

Because, inevitably, something will go wrong. And when that moment arrives, you’re screwed. Apple provides a beastly amount of documentation on in-app purchases, but they don’t provide the right kind of documentation. Nowhere is there mention of the setup steps you have to take to get in-app purchases to work. Nowhere is there a checklist you can reference if your StoreKit integration doesn’t work. Nowhere is there an NSError object that tell you exactly why your product ID is invalid.

You are left to flounder and flail like a wet noodle as you exhaustively try every possible solution on the web.

Losing days of productivity on this is ridiculous. To save you the pain and suffering I went through, this post details every step you need to take to implement in-app purchases. It’s detailed. It’s long. It’s probably overly-detailed and overly-long. But, unlike the Apple docs, it contains every single step necessary for any developer to implement in-app purchases.

Without further ado, let’s get started.

Overview

Ok, folks, here’s the secret to getting in-app purchases working: break it into two distinct steps:

  1. Create and fetch a product description
  2. Purchase a product

The first step is where you will likely run into problems. Once you can successfully fetch a product description in code, writing the code to purchase the product is cake.

We’ll tackle the product description step first.

Create and Fetch a Product Description

Here is a (very) rough overview of each step required to create a new product and fetch its description:

  1. Create a unique App ID
  2. Generate and install a new provisioning profile
  3. Update the bundle ID and code signing profile in Xcode
  4. If you haven’t already, submit your application metadata in iTunes Connect
  5. If you haven’t already, submit your application binary in iTunes Connect
  6. Add a new product for in-app purchase
  7. Write code for fetching the product description
  8. Wait a few hours

The code for fetching a product description is really simple. The setup steps, on the other hand, are rife with peril.

NOTE: You do NOT need to create an in-app test user in iTunes Connect to fetch a product description.

1. Create a Unique App ID

To support in-app purchases, your App ID cannot include a wildcard character (“*”). To see if your App ID contains a wildcard, log in to http://developer.apple.com/iphone, and navigate to the iPhone Developer Program Portal. Select “App IDs” from the menu on the left, and look for your App ID.

This is a unique App ID:

    7DW89RZKLY.com.runmonster.runmonsterfree

This is not a unique App ID:

    7DW89RZKLY.com.runmonster.*

If you don’t have a unique App ID, create one as follows:

  1. On the App IDs tab in the developer portal, select “New App ID”
  2. Fill in the following information:
    • Display name: Pick a different App ID name than you were using before. You can’t edit or delete old App IDs, so just give your App ID a new name to avoid confusion.
    • Prefix: Generate a new one, or choose an existing one if your app is one of a suite of apps that can share data via the Keychain Services API
    • Suffix: com.companyname.appname (this is the usual format – note lack of wildcard)
  3. Click “Save”
  4. Click the “Configure” link next to your App ID
  5. Check box next to “Enable In App Purchase”
  6. Click “Done”

2. Create a New Provisioning Profile

Now that you have a new App ID, you need to generate a new provisioning profile that points to the App ID.

Here’s the painfully detailed step-by-step for generating and installing a new provisioning profile:

  1. In the iPhone Developer Portal, select the Provisioning tab on the left
  2. Make sure you’re on the Development tab, and click “New Profile” in the top-right corner
  3. Fill in the requested information, and point to the unique App ID you just created
  4. If see “Pending” in the Actions column, just click the Development tab title to refresh
  5. Click “Download” to pull down the new profile
  6. Drag the profile onto the Xcode icon in the Dock to install
  7. Alternatively, if you want to preserve the name of the provisioning profile on disk, you can install the profile manually as follows:
    1. In Xcode, select Window > Organizer
    2. Select “Provisioning Profiles” category on the left
    3. Ctrl-click an existing profile > Reveal in Finder
    4. Drag and drop the new profile into the profile Finder window

3. Update Xcode Settings

After the profile is installed in Xcode, you need to make a couple edits to the project to use the provisioning profile:

  1. Edit your project’s .plist file so the Bundle ID entry matches the App ID. Ignore the alphanumeric sequence at the beginning of the ID. For instance, if your App ID is “7DW89RZKLY.com.runmonster.runmonsterfree” in the Developer Portal, just enter “com.runmonster.runmonsterfree” for the Bundle ID.
  2. Edit your project’s target info to use the new provisioning profile:
    1. Select Project > Edit Active Target
    2. Select the “Build” tab at the top
    3. Select the configuration you want (usually Debug)
    4. Select your new provisioning profile for the row labeled Code Signing Identity
    5. Select your new provisioning profile for the row directly underneath the Code Signing Identity row (probably labeled Any iPhone OS Device)

4. Add your Application

If your application is already available on the App Store, you can skip this step.

Before you can add a product in iTunes Connect, you must add the application the product is for. Don’t worry if you aren’t a 100% done with your app. You can still submit your app wtih stub data and add the real details later.

NOTE: Only the SKU and version fields are permanent and cannot be changed later.

  1. Navigate to http://developer.apple.com/iphone, and log in
  2. Follow the right-hand link to iTunes Connect
    • NOTE: you MUST be logged in to developer.apple.com first, or bad things will happen
  3. On the iTunes Connect homepage, click the “Manage Your Applications” link
  4. In the top-right corner, click “Create New Application”
  5. Fill out all the requested information for your app. When asked for your application binary, check the box indicating you will upload it later.

5. Add the App Binary

This detail is not mentioned anywhere in Apple’s documentation, but is a requirement nonetheless. You must submit a binary for your application in order to successfully test in-app purchases. Even if you aren’t 100% done, you need to submit a binary. However, you can immediately reject the binary so it won’t go through the review process.

This was the crucial step I missed that caused me hours of grief and frustration. Follow these steps to add the binary:

  1. Build your application for App Store distribution
    • If you don’t know how, navigate to the iPhone Developer Portal, click on the Distribution tab on the left, and make sure you are in the “Prepare App” tab. Now, follow the instructions in the following blue links:
      • Obtaining your iPhone Distribution Certificate
      • Create and download your iPhone Distribution Provisioning Profile for App Store Distribution
      • Building your Application with Xcode for Distribution
  2. Navigate to your app’s page in iTunes Connect
  3. Select “Upload Binary”
  4. Upload your .zip compressed application
  5. If you aren’t 100% ready for your app to be reviewed, then click the “Reject Binary” link on your app homepage in iTunes Connect. The app’s status should update to “Developer Rejected”.

Have no fear. Apple will not review this version of the app once it is “Developer Rejected”. You can submit a new version of your app at any point, and having the status “Developer Rejected” doesn’t affect the wait time for your future submissions in the slightest.

6. Add the Product

After all that setup, we are finally ready to add the product itself to iTunes Connect.

  1. Make sure you are logged in to http://developer.apple.com/iphone
  2. Navigate to the iTunes Connect homepage
  3. Click the “Manage Your in App Purchases” link
  4. Click “Create New”
  5. Select your application
  6. Fill in the production information:
    • Reference Name: common name for the product. I used “Pro Upgrade”. This name is non-editable, and it will not be displayed in the App Store.
    • Product ID: unique id for your app. Typically of the form com.company.appname.product, but it can be whatever you want. It does not need to have your app’s App ID as a prefix.
    • Type: You have 3 choices:
      • Non-consumable: only pay once (use this if you want a free-to-pro-upgrade product)
      • Consumable: pay for every download
      • Subscription: recurring payment
    • Price Tier: price of the product. See the price matrix for the different tiers.
    • Cleared for Sale: check this now. If you don’t, you will get back an invalid product ID during testing.
    • Language to Add: Pick one. The following two fields will appear:
      • Displayed Name: Name of your product shown to your user. I chose “Upgrade to Pro”.
      • Description: What the product does. The text you enter here is sent along with the Displayed Name and Price when you fetch an SKProduct in code.
    • Screenshot: Your feature in action. Despite the text on the screen about the screenshot submission triggering the product review process (a very sloppy design choice, IMHO), you can safely add the screenshot now without the product being submitted for review. After saving the product, just choose the “Submit with app binary” option. This will tie the product to the app binary, so when you finally submit the 100% complete app binary, the product will be submitted as well.
  7. Click “Save”

7. Write Code

Now, we finally write the code the fetches the product information we just added in iTunes Connect. To access the product data, we need to use the StoreKit framework.

NOTE: StoreKit does not work on the Simulator. You must test on a physical device.

  1. Add the StoreKit framework to your project.
  2. Add a reference to a SKProduct to your .h file:
// InAppPurchaseManager.h

#import <StoreKit/StoreKit.h>

#define kInAppPurchaseManagerProductsFetchedNotification @"kInAppPurchaseManagerProductsFetchedNotification"

@interface InAppPurchaseManager : NSObject <SKProductsRequestDelegate>
{
    SKProduct *proUpgradeProduct;
    SKProductsRequest *productsRequest;
}

NOTE: InAppPurchaseManager is a singleton class that handles every in app purchase for our app. It’s used throughout this post as an example implementation.

  1. Request the product, and implement the delegate protocol in the corresponding .m file:
// InAppPurchaseManager.m

- (void)requestProUpgradeProductData
{
    NSSet *productIdentifiers = [NSSet setWithObject:@"com.runmonster.runmonsterfree.upgradetopro" ];
    productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
    productsRequest.delegate = self;
    [productsRequest start];
    
    // we will release the request object in the delegate callback
}

#pragma mark -
#pragma mark SKProductsRequestDelegate methods

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    NSArray *products = response.products;
    proUpgradeProduct = [products count] == 1 ? [[products firstObject] retain] : nil;
    if (proUpgradeProduct)
    {
        NSLog(@"Product title: %@" , proUpgradeProduct.localizedTitle);
        NSLog(@"Product description: %@" , proUpgradeProduct.localizedDescription);
        NSLog(@"Product price: %@" , proUpgradeProduct.price);
        NSLog(@"Product id: %@" , proUpgradeProduct.productIdentifier);
    }
    
    for (NSString *invalidProductId in response.invalidProductIdentifiers)
    {
        NSLog(@"Invalid product id: %@" , invalidProductId);
    }
    
    // finally release the reqest we alloc/init’ed in requestProUpgradeProductData
    [productsRequest release];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}

A couple notes about the code above:

  • When specifying the product identifier, you must use the full product id. For instance, “com.runmonster.runmonsterfree.upgradetopro” is used above. “upgradetopro” alone will not work.
  • If response.products is nil in productsRequest:didReceiveResponse: and your product id shows up in the response.invalidProductIdentifers array, then prepare yourself mentally for a wild goose chase. The StoreKit API offers no help, no indication as to why your identifier was invalid, just that it is. Lovely, isn’t it?
  • The SKProduct class conveniently offers localized versions of your app title and description, but not price. To handle this omission, here’s a category that will provide a localized price string for the product as well:
// SKProduct+LocalizedPrice.h

#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>

@interface SKProduct (LocalizedPrice)

@property (nonatomic, readonly) NSString *localizedPrice;

@end

// SKProduct+LocalizedPrice.m

#import "SKProduct+LocalizedPrice.h"

@implementation SKProduct (LocalizedPrice)

- (NSString *)localizedPrice
{
    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    [numberFormatter setLocale:self.priceLocale];
    NSString *formattedString = [numberFormatter stringFromNumber:self.price];
    [numberFormatter release];
    return formattedString;
}

@end

After adding all the code above, give it a shot. You should see the product information gloriously regurgitated in your console window. However, you are more than likely getting back an invalid product ID. My next post addresses exactly how to go about debugging this problem, but the very next section may in fact hold your solution.

8. Wait a Few Hours

Have you followed all the steps above to the letter, and your product is still reported as invalid? Have you painstakingly double, triple, quadruple-checked to make sure you have followed every step? Have you despaired from finding frighteningly little in-app purchase information on the web?

Then, you may just need to wait.

It takes a while for the product you added to iTunes Connect to permeate Apple’s distributed in-app sandbox environment. For me, I gave up in despair after the umpteenth time my product came back as invalid. 24 hours later, I hadn’t changed a line a code, but my IDs were coming back valid. I think it really only took a few hours for the product to propagate through Apple’s distributed network, but if you can afford to wait, you may want to give it 24 hours like I did.

Purchase a Product

At this point, you should be able to successfully fetch an SKProduct description for your product. Adding support for purchasing the product is relatively simple compared to getting the description. There are only three steps required:

  1. Write code for supporting transactions
  2. Add an in app test user in iTunes Connect
  3. Sign out of your iTunes Store account on your phone
  4. Test the purchase

We’ll start off by taking a look at the code required to support transactions.

1. Write Code for Supporting Transactions

First, a word of warning: you are responsible for developing the user interface for purchasing your product. StoreKit offers absolutely zero interface elements. If you want your purchase view to look like the App Store’s, well, you have to build it yourself.

All the code below is for the backend of the transaction process. It is a single class with a simple API that an outside class (like a view controller) can call to make the purchase. I recommend a similar approach if you are figuring out how best to integrate in app purchases in your app.

First, you need to conform to the SKPaymentTransactionObserver protocol:

// InAppPurchaseManager.h

// add a couple notifications sent out when the transaction completes
#define kInAppPurchaseManagerTransactionFailedNotification @"kInAppPurchaseManagerTransactionFailedNotification"
#define kInAppPurchaseManagerTransactionSucceededNotification @"kInAppPurchaseManagerTransactionSucceededNotification"

@interface InAppPurchaseManager : NSObject <SKProductsRequestDelegate, SKPaymentTransactionObserver>
{
    …
}

// public methods
- (void)loadStore;
- (BOOL)canMakePurchases;
- (void)purchaseProUpgrade;

@end

Above, we have defined two more notifications that will be sent out with the result of the purchase transaction. For the sake of this example, we are using the InAppPurchaseManager class again, just as we did when when fetching the product description.

// InAppPurchaseManager.m

#define kInAppPurchaseProUpgradeProductId @"com.runmonster.runmonsterfree.upgradetopro"

#pragma -
#pragma Public methods

//
// call this method once on startup
//
- (void)loadStore
{
    // restarts any purchases if they were interrupted last time the app was open
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    
    // get the product description (defined in early sections)
    [self requestProUpgradeProductData];
}

//
// call this before making a purchase
//
- (BOOL)canMakePurchases
{
    return [SKPaymentQueue canMakePayments];
}

//
// kick off the upgrade transaction
//
- (void)purchaseProUpgrade
{
    SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

#pragma -
#pragma Purchase helpers

//
// saves a record of the transaction by storing the receipt to disk
//
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
    if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseProUpgradeProductId])
    {
        // save the transaction receipt to disk
        [[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:@"proUpgradeTransactionReceipt" ];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
}

//
// enable pro features
//
- (void)provideContent:(NSString *)productId
{
    if ([productId isEqualToString:kInAppPurchaseProUpgradeProductId])
    {
        // enable the pro features
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isProUpgradePurchased" ];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
}

//
// removes the transaction from the queue and posts a notification with the transaction result
//
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{
    // remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    
    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, @"transaction" , nil];
    if (wasSuccessful)
    {
        // send out a notification that we’ve finished the transaction
        [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionSucceededNotification object:self userInfo:userInfo];
    }
    else
    {
        // send out a notification for the failed transaction
        [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
    }
}

//
// called when the transaction was successful
//
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
    [self recordTransaction:transaction];
    [self provideContent:transaction.payment.productIdentifier];
    [self finishTransaction:transaction wasSuccessful:YES];
}

//
// called when a transaction has been restored and and successfully completed
//
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
    [self recordTransaction:transaction.originalTransaction];
    [self provideContent:transaction.originalTransaction.payment.productIdentifier];
    [self finishTransaction:transaction wasSuccessful:YES];
}

//
// called when a transaction has failed
//
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
    if (transaction.error.code != SKErrorPaymentCancelled)
    {
        // error!
        [self finishTransaction:transaction wasSuccessful:NO];
    }
    else
    {
        // this is fine, the user just cancelled, so don’t notify
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
}

#pragma mark -
#pragma mark SKPaymentTransactionObserver methods

//
// called when the transaction status is updated
//
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
                break;
            default:
                break;
        }
    }
}

In order to test this jumble of new code, you will need to write the code that calls the loadStore, canMakePurchases, and purchaseProUpgrade methods as well.

As you can see, there’s a good bit of code required to support transactions. For a full explanation of the code, see the official In App Purchase Programming Guide – http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/StoreKitGuide/AddingaStoretoYourApplication/AddingaStoretoYourApplication.html#//apple_ref/doc/uid/TP40008267-CH101-SW1.

The code above has a few parts that are specific to my implementation. Most notably, in provideContent:, the @"isProUpgradePurchased" BOOL field of NSUserDefaults is set to YES. All throughout the rest of the application, this BOOL is checked to determine whether or not to enable the pro features. If you are also implementing a free to pro upgrade product, I recommend using the same approach.

2. Add a Test User

In order to try out the mess of code you just added to your project, you will need to create a user in iTunes Connect for testing in app purchases. You can use this test account to purchase a product without being charged by Apple.

To create a test user, follow these steps:

  1. Log in to http://developer.apple.com/iphone
  2. Navigate to iTunes Connect
  3. Select “Manage Users” on the iTunes Conect home page
  4. Select “In App Purchase Test User”
  5. Select “Add New User”
  6. Fill out the user information. None of the information needs to be legit. I recommend a short, fake email address and a short password since you will need to type it in your phone during testing.
  7. Select “Save”

You will enter the email and password for this user on the iPhone during your testing.

3. Sign Out On Your Device

Before you can start testing in app purchases, you must sign out of the iTunes Store on your device. To sign out, follow these steps:

  1. Open the Settings App
  2. Tap the “Store” row
  3. Tap “Sign Out”

4. Test the Purchase

Now, you are finally ready to try out an in app purchase. Testing is simple:

  1. Run your app on your device
  2. Trigger the purchase
  3. When prompted for username and password, enter your test user details

If you repeat the purchase with the same account, you will be notified that you have already made the purchase. This is fine, just click “Yes” when prompted if you want to download the product again.

That’s a Wrap

Getting in app purchases to work is a lot more painful than it should be. It took several days of blood, sweat, and tears to get it working in my own application, and hopefully this post has helped short circuit that cycle of pain and suffering for you as well.

133 Responses to “In App Purchases: A Full Walkthrough”

  1. This was awesome.

    I got in app purchasing running in less than 3 hours with this. Thank you!

    Calvin

  2. Thank you for your fantastic tutorial. Apple really needs to make easier to follow tutorials.

  3. Hi Mark,

    This tutorial has been very helpful in getting our in app purchasing code setup. I have been fortunate enough to not have the Invalid Product Identifier problem, but once purchasing I get a popup from Apple saying “An unknown error has occurred” and when I display the error given to me in the SKPaymentTransactionStateFailed the error says “Cannot connect to iTunes Store”. The only advice I have seen online is to do a hard reset on my phone, this has not worked for me either. Do you have any insight into this particular error? Or has anyone else?

    Cheers,
    Donna

  4. Excellent! Thank you very much!

    My thanks in your maps post was a mistake.

  5. I already had a certificate in keychain and one mistake I made was to re-download the certificate from the provision profile and install it. Once I did that I didn’t get an “invalid id” error. BOOM! Hope that helps someone. Thanks for this post! Helps a lot.

  6. I have solve my problem with being given “An unknown error has occurred”. I created a new test user account and signed out of the settings -> store as described above and ONLY signed in when prompted during the In App Purchase testing. My mistake had been before on my first test user account that I had signed out of the settings -> store and then thought I’ll sign in with the test user account while I’m here, and had been prompted for a whole bunch of information, including billing information. By adding this information I believe broke my test user account so that whenever I tried to purchase it gave me this unknown error message. Can I suggest in your write up (just for other people who might fall into my trap) to explicitly say NOT to sign in at the Settings -> Store after having signed out.

    I hope this helps someone else.

  7. Man, I lost hours on this thing when I finally reached this post. Thank you indeed for taking the time to put together this precious document! My In App Purchase is now working like a charm.

  8. Hi,
    I have 1 doubt, we can download rejected (App store) Application??, there is anyway to get the contents of the rejected Application, and how to download songs through In-App Purchase.

    Thanks!!

  9. This helped me so much…. Thanks a lot…. It was very easy to follow through your steps

  10. I would like to ask… i try to follow ur step part by part. at the end of part 7… i test my apps then it return no valid products or product count 0… any ideas? stuck for a whole day already.

  11. Hi Guys,
    @Troy thank you for useful article, It was helped me a lot.
    But, there was happened some thing on apple provisioning portal, I was seeing In app purchase option on App ID edit page about 2 days ago. But now a days, this option has gone for me. Therefore my developer program subscription will expire 23 days, I think thats the problem.

    I’m wondering that did someone see the same thing like me ?

  12. I too do not see any ” In App Purchase” option in the app id section. This is the same for a couple different accounts I work with. Is this something that has gone away or what?

  13. I saw on the Apple Developer forums that one guy was told that In App purchases were already enabled on his app. Anyone know if this means that this is done by default now and that configuring the App ID for In App purchases is no longer necessary?

  14. Apple have removed the checkbox for ‘In App Purchase’ in the ‘App ID’ section of the provisioning portal. As long as you have a contract for paid apps and your bank & tax details are complete then all App IDs are good for In App Purchase.

    Also don’t use a reverse domain name (ie. ‘com.mycompany.myproduct.feature’) for the Product ID in the iTunes Connect ‘Manage your In App Purchases’. Use a single word ie. ‘feature’

  15. Apple has rejected one of my apps saying that even with a valid WiFi connection, they are unable to purchase the IAP item (there is only one). I am using the InAppPurchaseManager code from above, with basically only the product ID changed.

    The screenshot they showed me is missing the price, and the only way that should happen is if the product request did not return. As soon as the response is receive the display automatically updates to show the price, but apparently that does not happen for them.

    Is there any need to perform any sort of retries with this code? I could maybe try starting the products request earlier, but that isn’t quite so easy due to my choice of delegate class. Also, I would expect that for the production App Store servers, they wouldn’t need more than a few seconds to fetch the product info, and in my app, the request should have easily had 5-7 seconds before the price screen is ever shown.

    The odd thing is that they approved this exact IAP code before, and the IAP item always appears for me. People are buying it too, so the code clearly “works”, but perhaps not under certain circumstances.

    Does anyone have any idea what those circumstances might be or know of a way to work around this?

    Thanks.

  16. Thanks for such a wonderful post..I had some doubts regarding testing the in-App purchase without actually submitting to app-store..But you pointed that we can submit and reject binary to do that..Thanks …..

  17. Thank you so much for this post! I was struggling with the invalid products issue b/c I missed step 5. This post gave me a sane way to figure out what I missed, all good now!

  18. Thank you so much for the post! I am able to get my products, my problem comes when I try to purchase.

    I am getting into updatedTransactions with one transaction, however my transaction.transactionState (and all other data for the transaction) is null. In addition, I am never asked for my iTunes info.

    Any ideas?

    Thanks again.

  19. Hi

    I am trying to implement the in-App purchase of non-consumable goods. I have implemented your code as it is.I am getting the product information from the server.I am also able to make the purchase of that product.I am implementing this in sand box environment.I also get a call back whether the purchase is successful or not.

    The problem i am facing is that when i debug the code , none of the methods in the InAppPurchaseManager are being invoked except
    - (void)requestProUpgradeProductData
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response

    So i am not able to put the code in which i want to enable the pro features if the purchase is successful.

    Thanks
    Aditya

  20. Hi Troy
    Thanks very much for this , it was very helpful.
    I wanted to ask another thing – after the user made a purchase and upgraded his app , then removed it from the device and then downloaded it again , is there a way to know it was upgraded without asking to upgrade again?
    thank so much
    Gilad

  21. There is no way to select the provisioning profile for Code Signing Identity. It only shows certificates, not Provisioning Profiles. Can you please advice on that?

  22. Great article, and very good work!

    How to download the complete codes that you typed ?

    thanks so much.
    Dan

  23. Thanks. This was very helpful.

    One thing to note is that I did not need to submit a binary. Maybe they fixed that particular issue because I had no problems testing without a binary submitted.

  24. So helpful, thank you very much. Got me through coding In-App purchase.

    I did not have to upload binary, as noted by other folks.

  25. Hello everybody,

    After three days (actually four days if you count that I waited 24 hours for the first day) and re-testing re-doing everything over again, all I did was delete some provisioning profiles that were no longer being used on my iphone and it suddenly worked. I am sure I had the right provisioning profile set in XCode. More XCode weirdness as if we did not have enough of it already. Thanks Apple!

    – Adam

  26. Good post. I try to find screenshot and exemple of In App Purchase Store in an app ?
    The sdk and guide explains that we can implement store but I dont know advantages to use or not InApp Purchase Store…

  27. Thanks for this amazing tutorial.

    I wanted to put inapp purchase in my app but always thought it would be a lot of work. With this tutorial, I got it working basically in less the 30 mins. ( Excluding 3 Hr wait after adding my inapp purchase content).

    Now only if I could figure out what to sell ;-)

    Nikhil

  28. Great tutorial! I’ve to implement IAP and this is the best resource I’ve found online. Many thanks :D

  29. Thanks for this tutorial and I’m nearly up and running with In App purchases now. My only question is do you need to create a distribution profile which has a non-wildcard App ID (in the same way that you need to create a development profile with one)? I’m presuming you do?

  30. The problem is even more annoying when testing IAP works in one app but doesn’t in another one…

    Two things on your list are not necessary.
    We were able to test IAP in our app
    * without submitting a binary first and
    * without using a dedicated provisioning profile.

    We still use Xcode’s Team Provisioning Profile -> IAP works fine.
    We created a test app in iTunes Connect but never uploaded a binary (status: Prepare for Upload) -> IAP works fine.

  31. This tuturial is simply great! It helped me alot.

  32. I know this is really boring and you’re simply skipping to the next comment, but I simply wanted to throw a big thanks – you cleared up some things personally!

  33. Hey Troy,

    you’re the best. Works like a charm for me!

    Greetings from Vienna, Austria

    Rob.

  34. i would to ask… my in app purchase work on sandbox… but after my app got approve and ready for sale with the in app purchase item also is approve by apple… when i using real itunes id… the alert view of buying didn’t come out. when i test back using sandbox, it work. i dont’t know what happen… anybody can help?

  35. I just went mad testing my second inapp purchase. My new product just didn’t show up and i couldn’t get into sandbox environment with the testuser.

    Solution: Delete App, reinstall app through XCode

    Problem: I got the app from the appstore and installed it “over” with xcode. That confused everything obviously…

  36. @ashmira; did you test with an app downloaded from appstore?

  37. I have edited code as per my requirement, i m getting responce count zero, so app does no ask for app store.

    Please help in this case, its urgent…Please :(

  38. Uploading the binary is not necessary. But this held me up for hours – I kept getting an empty list of items back from the store until I deleted the app from the device before rebuilding.

  39. Thanks for your tutorial ! I just wanted to point out that the reason why I kept getting “invalid products” messages was because I created products with id’s like “cheese” but was requesting information about the products with “com.company.appid.cheese”. THATS WRONG !!! Cost me 2 days of work time.

  40. Thanks for sharing this In App Purchase tutorial. Worked as described.

  41. [...] Any tutorials in particular? Or just documentation? This one is a bit date but still pretty good In App Purchases: A Full Walkthrough I think that you no longer need to mess with uploading then rejecting the binary in order to [...]

  42. This is a great tutorial! We are new to iPhone/iPad devel but so far this has really helped us pull the name, description and price of the additional content from the iTunes store. However, is there any specific thing we need to do to take that content and send it to the store view to display this information? So far it’s only logging the information when the app first loads. Thanks

  43. I have tried every solution that available on the internet, and nothing worked, absolutely nothing. My problem: My device was JAILBROKEN and had Cydia on it. I restored the device to factory defaults (Not jailbroken) and it worked the first time and returned the product ID no problem.

  44. Thanks – this is great – it worked after a few hours of getting everything set up and saved me days of effort! A couple notes – I am setting this up for an existing app bundle that is approved and active in the store, and luckily I hadn’t used wildcards in the id name which was just blind luck. For the inapp product – I used the full id name followed by the product id, and in the testing process didn’t need to upload a picture for the product entry to get the sandbox in-app purchase test to work. Hope it works when it goes live – need info on how to link the in-app product to a new binary version though.

  45. So after waiting several hours I finally read all the comments here and the one that made the difference was you MUST delete old version of app from your device before running the test. IT has to recompile and install the app to work. Not very intuitive so thank you for the help and I hope this helps someone else.

  46. Hi Troy,
    Really appreciated your effort for putting up this tutorial. This helped me greatly during I developed my first IAP application. Now the app is available in Apple store, and it is selling non_consumable items. However, I have certain doubts when I wanted to submit second InAppPurchase item for Apple to review.
    My current App logic is:
    1. list all products from our server.
    2.when user clicks the buy button for certain item, the app will send SKProductRequest to Apple, if get SKProduct, then proceed for purchasing. If not getting SKProduct, that App will pop alert “not available in the store”.

    My question is: How would i prepare my second IAP item for apple to review.
    1. Should I list the new product in my live App? If not, how could I get apple review team to testing the IAP item ( buy and download)?

    Many thanks.

    Cullen

  47. Thanks dude! This was an excellent resource in getting micro-transaction working in just a few hours.

  48. Hello,
    Im making my first IAP application. Im having many questions. Whether i have to design any view for it, or it directly open any other view for transaction. If yes then how should i design view. What it include. Im unaware of all this. waiting for reply…….

  49. [...] There are also a lot of tutorials online that you can find by Googline, like this one: In App Purchases: A Full Walkthrough a bit dated but still very good. You now don't need to upload and reject a binary in order to test. [...]

  50. [...] In App Purchases: A Full Walkthrough (tags: howto iOS) [...]

Leave a Reply