3 Steps Tutorial for PhoneGap (2.x) In-App Purchase on iOS

Those instructions are valid for PhoneGap 2.x, if you’re looking for an up-to-date version of this tutorial, check out HERE.

Nowadays, smartphone users are accustomed to download apps with the lowest possible price-tag: FREE. For us it’s allright, as long as you have other ways to monetize for our living. Thats why In-App Purchases (IAP) quickly became so popular.

To implement In-App Purchases, Apple provides an Objective-C API found in a framework called StoreKit. Of course, there is no javascript binding to it, but you’re lucky: there is a PhoneGap plugin!

So here we go, let’s integrate In-App Purchases into your PhoneGap (Cordova) application using PhoneGap-InAppPurchase-iOS!

Step 1: Configure iTunes Connect

You’ll have to:


Create an App ID
from your iOS Developer Interface

Create an Application
from iTunes Connect

Define your In-App Purchases
also from iTunes Connect

App ID

The first step is to create an App ID, if you didn’t already did so. Log onto your iOS developer account, select the Identifiers options, and click the Plus (+) button. For this tutorial, I chose cc.fovea.inapptutorial as my Bundle Identifier.

Create Application ID

Now we’ll use this to create a new Application on iTunes Connect.

iTunes Application

Log onto iTunes Connect, click “Manage Your Applications”, and then “Add New App”. Enter in an App Name, SKU number, and the Bundle Identifier you just created. Notice you have to fill every single fields, you can just put placeholders for now and change the informations later.

In App Purchases

Now that our Application is created, we can enter the “Manage In App Purchase” section of iTunes Connect, for this Application.

Manage In App Purchases

Inside the In-App Purchase Manager, click “Create New”, then select the appropriate type of In-App Purchase. “Consumable” and “Non-Consumable” are the most common option. For virtual currencies, you’ll select “Consumable”. To unlock a feature, you’ll pick “Non-Consumable”.

Follow the steps, it’s pretty straightforward: Name, price, descriptions and so on. The important part for now is the Product ID, you’ll need this in your code!

Step 2: Install PhoneGap-InAppPurchase-iOS

I’ll assume you already have a PhoneGap iOS project setup. If not, check out the cordova documentation.

To install plugins the easy way, we’ll use “plugman” (which in turn depends upon Node’s “npm” tool). You can download Node here, then install plugman using:

npm install -g plugman

Now, enter your project directory and type:

plugman --platform ios --project <directory> --plugin git://github.com/j3k0/PhoneGap-InAppPurchase-iOS.git

This should add a few Objective-C files and the StoreKit framework to your XCode project, copy the Javascript in the www/ directory and register it so it’s automatically loaded by Cordova.

Step 3: Write the code

There are 4 operations you’ll have to implement:


Request product data
from Apple

Display IAPs
with a bit of HTML

Purchase
a product

Restore
previous purchases

Request product data

Say we have 2 In-App Purchases:

  • 10 coins, product identifier cc.fovea.coins10
  • 100 coins, product identifier cc.fovea.coins100

We’ll put the In-App Purchase related code into singleton object IAP.

Let’s declare them into our code:

var IAP = {
  list: [ "cc.fovea.coins10", "cc.fovea.coins100" ]
};

Then we need to make sure storekit is available and initialized. Here’s the full code:

IAP.load = function () {
  // Check availability of the storekit plugin
  if (!window.storekit) {
    console.log("In-App Purchases not available");
    return;
  }

  // Initialize
  storekit.init({
    ready:    IAP.onReady,
    purchase: IAP.onPurchase,
    restore:  IAP.onRestore,
    error:    IAP.onError
  });
};

// StoreKit's callbacks (we'll talk about them later)
IAP.onReady = function () {};
IAP.onPurchase = function () {};
IAP.onRestore = function () {};
IAP.onError = function () {};

To get the details about an In-App Purchase, Apple requires that you retrieve the information using StoreKit.

To do so, we’ll use:

storekit.load(productIds, callback)
IAP.onReady = function () {
    // Once setup is done, load all product data.
    storekit.load(IAP.list, function (products, invalidIds) {
      IAP.products = products;
      IAP.loaded = true;
      for (var i = 0; i < invalidIds.length; ++i) {
        console.log("Error: could not load " + invalidIds[i]);
      }
  });
};

In this method we simply save the data for later, and call the success callback when all in-app purchases are loaded.

That’s it, we can now use the products data to display the list of in-app purchases available in our application. Here’s a sample code doing just that.

var renderIAPs = function (el) {
  if (IAP.loaded) {
    var coins10  = IAP.products["cc.fovea.coins10"];
    var coins100 = IAP.products["cc.fovea.coins100"];
    var html = "<ul>";
    for (var id in IAP.products) {
      var prod = IAP.products[id];
      html += "<li>" + 
       "<h3>" + prod.title + "</h3>" +
       "<p>" + prod.description + "</p>" +
       "<button type='button' " +
       "onclick='IAP.buy(\"" + id + "\")'>" +
       prod.price + "</button>" +
       "</li>";
    }
    html += "</ul>";
    el.innerHTML = html;
  }
  else {
    el.innerHTML = "In-App Purchases not available.";
  }
};

renderIAPs(document.getElementById('in-app-purchase-list'));

Make a Purchase

Before we implement the “IAP.buy” method, we need to implement a few callbacks.

IAP.onPurchase = function (transactionId, productId, receipt) {
  if (productId === 'cc.fovea.coins10')
    Coins.add(10);
  if (productId === 'cc.fovea.coins100')
    Coins.add(100);
  alert('Congratulation, you know own ' +
    Coins.get() + ' coins');
};

IAP.onError = function (errorCode, errorMessage) {
  alert('Error: ' + errorMessage);
};

The hard part is done, now we just have to initiate the purchase from IAP.buy:

IAP.buy = function (productId) {
  storekit.purchase(productId);
};

This code relies on a pseudo Coins object, that manages the number of remaining coins the player has, and most importantly the persistance of this number from session to session. Users really would be pissed off if they lose the coins they bought, right?

Restore previous purchases

This part applies to non-consumable types of In-App Purchases, when a user paid to unlock the full version of a free game for instance. If he uninstalls then reinstall the game, he expects his purchases (ie the full version) to still be available.

I’ll skip the creation of this new In-App Purchase from iTunes Connect (just follow step one again if you forgot how to do that). The code is then pretty simple.

We’ll implement our last callback.

IAP.onRestore = function (transactionId, productId, transactionReceipt) {
  // Pseudo code that unlocks the full version.
  if (productId === 'cc.fovea.unlockfullversion') {
    FeatureManager.unlockEverything();
  }
};
IAP.restore = function () {
  storekit.restore();
};

You can now add a “Restore Purchases” button in your interface which will call IAP.restore().

Resource

That’s all about it, please find bellow a collection of interesting resources, starting by some working code related to this tutorial, and a bunch of links:

I’m a consultant and developer for Mobile, Web, Games and Apps projects. I co-founded Fovea 8 years ago.

Tagged with: , ,
Posted in Blog
34 comments on “3 Steps Tutorial for PhoneGap (2.x) In-App Purchase on iOS
  1. Sebastian says:

    Nice article! The plugins is also much more better than others found…

    However, i setted up everything whitout plugman. Since the first begining, my js could not find window.storekit (I loaded inAppPurchase.js in html, add .m and .h to plugins directory, declared them in config.xml).
    Do you have any idea of this issue (maybe missing a basic thing)?

    • Hi Sebastian, and thanks!

      Two things to check:
      – did you try add the InAppPurchase.js in your index.html or in the cordova_plugins.json? (one of the 2 is necessary)
      – are you trying to access the plugin before the deviceready event?

      • Sebastian says:

        Thanks for that fast answer. I added InAppPurchase.js in the html header. For the access, i am doing it on deviceready…

        • Got it, the plugin needs to be defined in cordova_plugins.json (in your www directory). Like that:
          [{“file”:”InAppPurchase.js”,”id”:”com.phonegap.plugins.inapppurchase.InAppPurchase”,”clobbers”:[“storekit”]}]

          (this is assuming you did put InAppPurchase.js at the root of the www/ directory, if not, just specify the path in the “file” property)

          plugman would have done it for you, I need to write that in the doc! I hope it will work.

          • Sebastian says:

            I switch to plugman implementation and it is still not getting storekit, i guess i am missing a basic step.
            Do we have to include the plugin.xml? Is it just by adding the framework suffisant or need to refer it somewhere?
            Could you provide a basic project that I can add to xCode to try it?

  2. Dave Alden says:

    Hi Jean-Christophe, I’m having trouble getting the plugin to work. I’ve manually set up my Phonegap (2.9.0) project (without plugman) and while I get a successful load response containing the InApp Purchase data from Apple, the Objective-C part of the plugin is not calling the Javascript callback. I’ve posted details in a question on stackoverflow and really hope you can help. Thanks! Here’s the link: http://stackoverflow.com/questions/18123398/inapppurchase-plugin-failing-to-execute-js-callback-from-objective-c

    • Hi Dave, that’s a strange problem.

      I downloaded your XCode and reproduced it but couldn’t figure out what’s wrong. I will not be able to dig further for some time, sorry about that.

      Please share if you find a solution.

      • Dave Alden says:

        Thanks for your reply. I’m glad you could reproduce the problem and it’s not just me going mad! :-) Do you have an existing XCode project containing a working example of the plugin? That would be useful to compare to my project… I will look at this again next week and if I find the cause of the problem I will let you know.

        • Actually I don’t have a sample project, but I do have a commercial project using it successfully. I’ll be traveling for a while, but I’ll try to make this sample project as soon as possible, as it really would make the tutorial more useful.

          • Dave Alden says:

            Hi Jean-Christophe,
            just to let you know I’ve had another look at this today and the results are even more unexpected: when I ran my test project for the first time on an iPad 2 with a fresh installation of iOS 6.1.3, the JS callback was successfully invoked. But every time after, the Objective-C failed to invoke the JS callback!

            I’d be very grateful if you’re able to supply an example project where the plugin doesn’t exhibit this behaviour so I can try to track down the cause of the problem, because this bug is holding up deployment of my app, which is otherwise finished.

            Here’s the link to my latest stackoverflow question regarding this issue: http://stackoverflow.com/questions/18618463/js-plugin-callback-function-only-executed-on-first-app-run-then-fails-phonegap

          • Dave Alden says:

            Hi again Jean-Christophe,

            I found the problem was due to 2 things: first, the log output is not shown right away because the Objective-C callback function “didReceiveResponse” comes back on a different thread – pressing the power button to pause the app flushes the buffered log content to the console.

            From here, I was able to track the problem down to a reference to an undefined JS variable – the JS was failing silently so this was not obvious.

            Thanks for you help with this.

          • Dave, sorry I was too busy to be really useful. Please let me know if there is anything that could be added to the tutorial so people don’t suffer as you did.

          • Tony says:

            Hi David and John-Christophe,

            I am having this same issue – everything looks fine in Objective-C but my JavaScript callbacks are never invoked.

            One interesting point, when I called requestProductData() for my product there was no response to JS, but using requestProductsData(), with a single product ID in the array, worked fine.

            However makePurchase() has the same issue, I see the transaction come back in C but no callback. I can’t work around it in the same way.

            David, can you elaborate on your what your solution was (and maybe also respond to your StackOverflow question)?

            Thanks for any help. I’ve put a lot of effort in to my app, but if I can’t get IAP to work it’ll all be for naught…

          • Mohammed Mosayed says:

            Dave I am having the same problem as well. Can you please tell us your solution? Which variable is undefined?

          • Dave Alden says:

            Guys, sorry the delay in replying – I didn’t look at this page for a long time. I will post a link to my working Xcode project ASAP. I’m in the process of extending the plugin so it includes the option to download non-consumable packages hosted on the Apple servers. When I have that working too, I’ll post a link. Thanks again to Jean-Christophe who really helped me get started with this :-)

  3. Ji says:

    I met some this error message
    “Uncaught ReferenceError: module is not defined”
    in InAppPurchase.js:209 (module.exports = new InAppPurchase();)
    and I already add js file in index.html..
    Can you help me about this problem/..?

  4. Hey,

    I loaded the plugin with plugman, and I keep getting an error in the log “Plugin ‘InAppPurchase’ not found, or is not a CDVPlugin. Check your plugin mapping in config.xml.” The config.xml does have the plugin set:

    So I’m kind of at a loss. Any ideas?

    Thanks!

    • Ah, found it. Apparently I needed to use a feature tag rather than a plugin tag.

      More specifically:

      [feature name=”InAppPurchase”]
      [param name=”ios-package” value=”InAppPurchase”/]
      [/feature]

      With < rather than [, of course.

  5. Mohammed Mosayed says:

    The plugin is not working for Phonegap 3.0

    Has anyone found an updated plugin that will work with Phonegap 3.0?

  6. Mathew says:

    Hi Jean,

    I really appreciate this tutorial. Unfortunately I’m getting an error building my code in xcode. I’m using Phonegap 3.0, and I made sure to use the tag in the config file and closely followed the latest README on github. This is the error:

    ‘release’ is unavailable: not available in automatic reference counting mode

    This error is popping up in both .m files in the objective-c code. As a javascript developer, this stuff is a black box to me unfortunately. Any ideas what’s wrong?

  7. Jeff says:

    Hi,

    I used the PhoneGap 3.0 CLI to install this plugin. I cannot get it to recognize the window.storekit. I’m running this from a Sencha Touch 2.3 project. I noticed when Phonegap creates a new project, it puts all of the files in cordova_plugins.js NOT cordova_plugins.json. I just want to make sure that somehow phonegap is including the cordova_plugins.js file.

    The cordova_plugins.js has code like this:

    cordova.define(‘cordova/plugin_list’, function(require, exports, module) {
    module.exports = [
    {
    “file”: “plugins/com.phonegap.plugins.inapppurchase/InAppPurchase.js”,
    “id”: “com.phonegap.plugins.inapppurchase.InAppPurchase”,
    “clobbers”: [
    “storekit”
    ]
    }
    ]});

    I’ve tried all sorts of ways to make it recognize window.storekit, but I’m having no luck. Any sugestions?

  8. Two questions:
    1) Should this work on iOS Simulator?
    2) Console output: Could not load {my app id}. So, my in-app purchase status is Ready to Submit, is this enough for testing? I can’t submit it without my app and it’s not yet ready. What to do? Thanks a lot for tutorial and support! :)

  9. Jiang says:

    Thanks man! I’ve tested it with phonegap 3.1.0 and it works perfectly so far…

  10. moshe says:

    Can I have an example of how to use the Sample file
    I installed everything but I can not figure out how to run the functions

    In-App Purchase Working Sample
    http://fovea.cc/blog/wp-content/uploads/2013/06/iap.js

    Thanks!!

  11. Pattel says:

    I got following error message after following your guide:

    “Uncaught ReferenceError: module is not defined”
    in InAppPurchase.js:209 (module.exports = new InAppPurchase();)

    Can you help me about this problem?
    Thanks a lot.

  12. DanielG says:

    Hi,

    I´ve installed your plugin with plugman, but it´s throwing me always the same error in JavaScript: ” In-App Purchase not available”.
    I have no idea where is the problem and i spent almost 3 hours trying to fix the problem, but i don´t know where to look at.
    I´ve added the InAppPurchase.js to my index.html and cordova_plugins.js too (just in case) and both are in the root of the www folder.

    The only thing i can think of is the base SDK on XCode, it says 7, but in my iPhone i have 6.1. I tried to add the StoreKit for 6.1 but i can´t as the base SDK is 7 and it just give me the option of iOS 7…

    I´m using phonegap 2.3.0 and XCode 5.0.

    What else can i check? i´m lost and this looks like the only decent plugin to manage in-app purchases through phonegap…

    Thanks!

  13. DanielG says:

    Hello again,

    Everytime i try to install the version of this plugin for Phonegap 2.x, it installs me the version 3.1.0 which i think is not the right one for Phonegap 2.x…. am i right?

    I found on your Github 2 releases, one for 3.x and another one for 2.x, but in your installation guide, both point to the same version (which is 3.x)
    https://github.com/j3k0/PhoneGap-InAppPurchase-iOS/releases

    I will really appreciate if you could reply me.

    Thanks

    Daniel

1 Pings/Trackbacks for "3 Steps Tutorial for PhoneGap (2.x) In-App Purchase on iOS"
  1. […] The tutorial can be find here. […]

Leave a Reply

Your email address will not be published. Required fields are marked *

*