Warning: join(): Invalid arguments passed in /home/troyb/troybrant.net/blog/wp-content/themes/hybrid-hacked/hybrid-hacked/library/functions/breadcrumbs.php on line 79

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

Update: You do NOT need to submit your application binary to get IAP working. See question #4 in Apple’s FAQ http://developer.apple.com/library/ios/#technotes/tn2259/_index.html (thanks to everyone in the comments who pointed this out)

  1. Add a new product for in-app purchase
  2. Write code for fetching the product description
  3. 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

Update: This step is NOT required. See question #4 in Apple’s FAQ on IAP http://developer.apple.com/library/ios/#technotes/tn2259/_index.html (thanks to everyone in the comments who pointed this out)

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.

1,017 Responses to “In App Purchases: A Full Walkthrough”

  1. Troy:

    Love the new site. Thanks for putting in the time to provide so much detail on in-app purchases. Hopefully Apple streamline the process in upcoming SDK updates.

  2. Regarding the “Have you submitted (and optionally rejected) your application binary?” test.

    What I have noticed is that unless your app is in “Waiting for review”, “In review” or “Approved” InApp purchase even getting the productID will simply not work.

    This is a thread about this: https://devforums.apple.com/thread/36218?tstart=0

    I even submitted a binary with the mention “Do not review – InApp test phase – ” and still it got reviewed and rejected by Apple and I got the following email:

    “We have reviewed these submissions and determined they are tests. Test binaries & purchases cannot be accepted in the production environment. Please review ‘Test User Setup’ section in iTunes Connect Guide on how to test In App items in your development sandbox environment.”

  3. Hi Troy,

    thanks for this great blog post! I’m looking forward to read more from you. Maybe some time you could give advice on how to implement a subscription based product with In App Purchase.

    Regards,
    Manuel

  4. Hi, Troy,

    great post. I had done ALMOST everything right, except for one thing and you gave me the fix. By the way, timings have changed for the better. Getting the list of products was IMMEDIATE: I add one product; I click on my test app, it immediately shows up. Being able to buy it from the Sandbox environment took about 45 minutes from the time I added the product to the list in iTunes Connect.

  5. I have to disagree with Andre above: I have “Rejected” my app, and I can still do as many test purchases as I want. My app is listed as “Developer Rejected”.

  6. Just posted a thread with link to this great Walkthrough on the Developer’s Forum:

    https://devforums.apple.com/thread/37109

  7. One small change, two file names should be changed to SKProduct+LocalizedStrings.h and SKProduct+LocalizedStrings.m.

  8. Glad the post helped you, Marco! I wasn’t sure if anyone would have the patience to wade through to the end.

    The SKProduct category name did indeed have a typo. The name should be SKProduct+LocalizedPrice, and the code above has been updated.

    Thanks for the update on the turnaround time for creating new products! That’s great to hear it’s faster now.

  9. We are getting the same results as Andre. If we reject the binary, we get no results. If its “In Review” everything works fine. We’ve been pounding our heads against the wall trying to figure this out and sure enough, as soon as we upload a new binary, everything *instantly* started working.

    Thanks for the tip Andre and thanks for the great walkthrough Troy!

  10. Thanks so much Troy, this was really helpful to get my inapp stuff working again, after it had suddenly decided to stop…
    You’re spot on about Apple’s doc taking exactly the wrong approach to explaining how to actually implement the whole system.

  11. You are very welcome, Troy. I guess I am one of the lucky ones that can get everything working even with a “Developer Rejected” app. I really did NOT do anything different from what you list here. It makes much easier that I do not have to worry about uploading-rejecting every day :-)

  12. Troy,

    It looks like Apple has a bug in priceLocale. It doesn’t always work as expected. Check out this thread:

    https://devforums.apple.com/thread/31251?tstart=0

  13. Not sure if it makes a difference, but the “Status” sequence that I followed is this:

    Waiting for Upload
    Waiting for Review
    in Review
    Developer Rejected

    So it actually made it to “in Review” for a couple of hours.

  14. This post is SUPER helpful, 1000 thanks!

  15. Hi
    u give all coding details but u give sample project so i would work on it. See all views display in iphone..

    Regards
    Aman Gupta
    +91-9888229450

  16. Amazing walkthrough! Everything worked great! I am only having a problem with my UI. I’m having trouble linking it to the information returned from the app store. Somewhat new to iPhone dev so I’m sure it’s something simple I’m missing but maybe there is some kind of a walkthrough that shows even just one simple way to set up the UI and link it to the info? Again, great post!

  17. One minor nit: in the localizedPrice method, numberFormatter should be released.

  18. A couple of issues after seeing actual experiences on the Developer Forum:

    1. Step 5 is NOT required and in fact it is NOT recommended if you have a FAT binary. If you do NOT add a binary, but correctly associate your app using the Bundle ID (see below), you will be able to do all our testing WITHOUT having to upload, reject, etc. I have tested it myself and can confirm that I have one app, with no uploaded binary that properly perform In App Purchases. Several people in the Forum have reported that once they upload a binary, they need to go through a Wait For Review and in Review, before they can test. Some, but not all, report that Rejected status does not all proper testing. NOT uploading a binary allows you to totally sidestep these issues.

    2. A new step is now required after “Select your Application” (Step 5, of section 6, Add the Product). Itunes Connect presents a screen asking you to “Select the Application Bundle ID” and providing a drop down with an appropriate selection. I found that this drop-down is “smart”: even when you have NOT downloaded a binary, it finds the proper bundle ID from what you created in the Program Portal.

    3. Several developers have reported that the comment that “the Product ID does NOT need to have your App ID as a prefix” is NOT true. All have reported that IF the Product ID has the App ID as a prefix, the transactions will ALWAYS work.

    4. Under section 7, “Write Code,” it is important to emphasize that the project and target need to have selected the Developer Provisioning Profile created in Step 2, “Create a New Provisioning Profile”. Once you enter your App Bundle ID in the info.plist, Xcode will grey out all the Provisioning profiles that do not have the corresponding com.company.appname associated with it.

    5. As I previously mentioned, step 8, “Wait a Few Hours”, no longer applies: I have been able to mimic purchases just minutes after setting up a new In App Purchase for 3 separate In App Purchases.

  19. Thanks for pointing out the memory leak, Mark. I’ve updated the code with the release call.

  20. Thanks for the updates, Marco. I think Apple is constantly improving the process behind the scenes (I’m an optimist), so I can believe there have been changes made. With regards to your individual points:

    1. There really *shouldn’t* be a need to upload a binary for in-app purchases to work. I hope you’re right; I haven’t been able to test for myself just yet.

    2. I believe this step is required only if you haven’t uploaded a binary. Not a 100% on that one, but thanks for the heads-up!

    3. Check out page 68 of the iTunes Connect Developer’s Guide (https://itunesconnect.apple.com/docs/iTunesConnect_DeveloperGuide.pdf). Note that even in Apple’s example, the Bundle ID is “com.apple.forestapp.application” while the Product ID is “com.apple.tree.forestapp”. That is, the prefix of the Product ID != the Bundle ID. I’m going to defer to the docs on this one and maintain that the Product ID prefix != Bundle ID.

    4. Great additional details.

    5. Great to hear!

  21. Thank you for this excellent guide. I believe it was the only thing that kept me from blowing my brains out during the absurdly Kafkaesque process of getting an IAP to work. I am now actually past getting invalid product IDs — something that was going on for useless, soul-crushing, endless days of flailing hopelessness and pain.

    Some notes:
    - Once I was actually getting a product back, the [products firstObject] method call was throwing an unrecognized selector error. Not sure why. Replaced it with [products objectAtIndex:0] (since I only have 1 product) and it started working fine.

    - I’m curious if you (or anyone here) has found some good examples of what an in-app store might look like. For me, this would only be a single-item store that allowed a product upgrade. What are your favorite UI examples of this? (This is a layout / UI question, not a code question.)

    Thanks again.

  22. [...] 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 [...]

  23. [...] The tutorial is: In App Purchases A Full Walkthrough [...]

  24. HI ,

    I am implementing the inapp work but getting one trouble can u help me out .Actually when i click on to purchase an item than it will deduct the money for that items twice or thrice we have checked all the things but still the problem remains.

    can u help me out there?

    Thanks

  25. Thanks for the fantastic guide!

    Almost there… I get a problem with a failed transaction ‘Cannot connect to iTunes store’ in failedTransaction. It doesn’t prompt me at all for my test user details. I signed out of App Store as instructed and tried this several times.

    Any ideas?

  26. I missed something obvious. I had changed your app name to mine in the first code snippet but had missed your definition of kInAppPurchaseProUpgradeProductId!

  27. “3. Several developers have reported that the comment that “the Product ID does NOT need to have your App ID as a prefix” is NOT true. All have reported that IF the Product ID has the App ID as a prefix, the transactions will ALWAYS work.”

    I found that if I didn’t have the Product ID didn’t have the App ID as a prefix I could not get it to work as soon as I added it it worked.

  28. hi ,

    For all the above discussion about the invalid product identifier , i would like share my experience with all of you ..
    When i followed all the steps which are defined in the blog , and apple documentation , i got the same error , and now my app status is Waiting for review . if i reject the binary , i don’t get the products , i get the error ” invalid product identifier” and count of product is 0 . i haved checked this , and its confirmed that when you upload the binary and don’t reject it , you will get the products and once you reject the binary , you will not get a valid response .

    Thanks
    Yasir

  29. Nice site and awesome tutorial but i am one question.

    Could I develop a daily basis items App In Purchases App?

    I ask this question because if apple verify products individually I can’t do it.

    Please, can you give me your point of view?.

    Thank you.

  30. Hi there, firstly thank you for providing a walkthrough, where Apple provides essentially nothing.

    After having created a test app to add your code, nothing seems to work. I added your code to separate .h and .m files in a clean View-Based Application and it compiles. However, nothing is outputted to the console after launching the app (As described above, I should be seeing products listed there). Does your code need to be entered into the default ViewController.m/.h files or do i need to create extra files and place the code there?

    I have done some programming in the past, but I am quite new to the Apple iPhone SDK and Objective-C, so a layman-terms instructions for a beginner would be very handy.

    Thank you for any help you may be able to provide.

  31. Hi, thanks SO MUCH for the great tutorial, I could get part 1 (getting product id, description etc..) done in no time with your code and directions.

    I can confirm you don’t need to upload any binary at the moment to do that :)

  32. How do apps like fresh direct handle in app credit card processing. Do you know of any companies that handle in app credit card transactions without having to be redirected to a virtual terminal in a mobile web browswer?

  33. Hi,

    I was googling for this problem but not yet have I found a solution.

    I’m trying to send back to my server the receipt asynchronously. So I can’t call finishTransaction: method from within the observer’s delegate method but call it after I’ve received response from my server. However, it means that I should call finishTransaction: outside the observer’s delegate method.

    Is it safe to do this? I need to *retain the transaction* in the delegate’s method and call finishTransaction: *somewhere else* immediately after my server finish processing the receipt.

    The code will be like this.

    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray*)transactions {
    for (SKPaymentTransaction * transaction in transactions) {
    switch (transaction.transactionState) {
    ….
    case SKPaymentTransactionStatePurchased:
    self.purchasedTransaction = transaction;// a retain property
    // no call to [[SKPaymentQueue defaultQueue] finishTransaction:transaction]
    [self asynchronousSendRecipt:transaction.transactionReceipt];
    break;
    ….
    }
    }
    }

    - (void)gotResponseFromMyServer {
    [[SKPaymentQueue defalutQueue] finishTransaction:self.purchasedTransaction];
    }

    I’m going to try this tomorrow. It’s time to go home.
    Thanks in advance!!

  34. I’m having trouble getting it working. I’ve set up the InAppPurchaseManager.h and .m, and am getting a reply back from the Apple store. When I try to call [InAppPurchaseManager loadStore], my app crashes with:
    [1442] : +[InAppPurchaseManager loadStore]: unrecognized selector sent to class 0x6bf5c
    [1442] : *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘+[InAppPurchaseManager loadStore]: unrecognized selector sent to class 0x6bf5c’

  35. Nevermind… I wasn’t setting it up properly in the .m file I actually used it in. Here’s the code I used:
    myAppDelegate *appDelegate = (myAppDelegate *)[[UIApplication sharedApplication] delegate];

    [appDelegate loadStore];
    if([appDelegate canMakePurchases])
    {
    [appDelegate purchaseProUpgrade];
    }

  36. Ok, so I’ve got the purchasing working thanks to your tutorial. However, my item type is consumable… so a user should be able to purchase it over and over again. When I attempt to repurchase, it says: “You’ve already purchased this In App Purchase but it hasn’t been downloaded”. Any help?

  37. I am getting a compile error trying to compile after Step 7. The error is “fatal error: method definition not in @implementation context”.

    This is being reported on the line that reads: – (void)requestProUpgradeProductData

    What am I doing wrong?

    Can anyone help?

  38. Hi, thanks for great tutorial, I have one question. Where do I find the SKProduct+LocalizedPrice.h and SKProduct+LocalizedPrice.m. Do I need to add another file?

  39. I am getting the alert “You’ve already purchased this In App Purchase but it hasn’t been downloaded” , no matter what type of Product do I mention in the code, Anybody know how to resolve this issue, no callback was getting called… . Any help?

  40. Thanks so much for taking the time to document this. I looked all over for a writeup that made sense to me, and yours was the one of the only ones I found that didn’t just rehash what you can find in Apple’s documentation. I was almost there, but couldn’t quite get the observer/purchase portions working properly until I read this. Awesome!

  41. great experience, dude! thanks for this great post wow… it’s very wonderful report.

  42. Just great work explaining this in detail and consumable format! Why can’t people like you work for apple. This in-app purchase, certificates, profiles , app ids, itunes connect, etc etc … has been taking me almost as long as learning objective-c and writing the code for my first app all together.

    Really helpful tutorial, keep up the good work!

  43. I have an existing app and am adding IAP to it. When I request the product list, it appears to just timeout after 30 seconds and call my callback with an empty list (zero products).

    I have tried all the above steps and have still had no success.

    Sometimes it never returns, and I found a suggestion to add the following method:

    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error

    Now it always either fails by calling this method, or calls the expected delegate method:

    - (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response

    Any idea why I am getting no response at all? My App ID is correct, is configured for purchases, I added a product, have a green check mark on it, etc., etc., but it is as if the app store is completely ignoring my request.

    On another note, you wrote:

    “After saving the product, just choose the “Submit with app binary” option.”

    I don’t see this option anywhere on iTunes Connect. Where is it?

    Thanks,

    Ken

  44. Many thanks to you for this post! You saved me a lot of time!

  45. Thank you very much for the information great post, found it on Yahoo.

  46. Thanks for tutorial..

    Only one things.. I had only problem with invalidProductIdentifers, even 24 hours waiting didnt work.. I found help at Apple dev forum. I have to delete my app from phone and then build/install it again and everything starts work..

    Thanks !!

    Marek

  47. very good IAP manual in details. Very useful. Solve my big problem by following your steps.

  48. [...] 原文见:In App Purchases: A Full Walkthrough   基础, 编程   编程, In-App Purchase, 教程   [...]

  49. Great article! Thank you for your hard work.

    I’m working on a project with IAP functions. My situation is I’ll sell digital contents through IAP. My product is digital contents such as ebook. After user purchase the product, I’ll save it to my application’s document folder. And user will be able to view it in my application. My question is that do I need to provide web space to store my digital contents or Apple will just save it in App-Store?

    Thanks,

    Frank

  50. Is there a discussion somewhere related to this, or could you answer a few questions

1 2 3 21

Leave a Reply