3 Steps Tutorial for PhoneGap In-App Purchase on iOS

Those instructions are valid for PhoneGap 3.x, if you’re looking for a PhoneGap 2.x 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 alright, 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!

Last, you will need a “Sandbox” user account. Enter the “Manage users” section of iTunes connect to create one.

Step 2: Install PhoneGap-InAppPurchase-iOS

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

Now, enter your project directory and type:

phonegap local plugin add 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

a product

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");

  // Initialize
    debug:    true, // Enable IAP messages on the console
    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(\"" + prod.id + "\")'>" +
       prod.price + "</button>" +
    html += "</ul>";
    el.innerHTML = html;
  else {
    el.innerHTML = "In-App Purchases not available.";


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')
  if (productId === 'cc.fovea.coins100')
  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) {

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') {
IAP.restore = function () {

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


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
108 comments on “3 Steps Tutorial for PhoneGap In-App Purchase on iOS
  1. Maxon says:

    Nice effort!

    However… after just installing Phonegap and adding the IAP plugin in a fresh project, i am getting several errors in regards to ARC. It seems that the project uses ARC but the plugin doesn’t.

  2. Maxon says:

    You can fix this by adding -fno-objc-arc flag to:
    Target>Build Phases>Compile Sources>InAppPurchase.m
    Target>Build Phases>Compile Sources>SKProduct+LocalizedPrice.m

  3. Sebastien says:

    Great! It is working for me except callbacks for purchase and restore. I saw correct NSlog from InAppPurchase.m (transactionId and receipt)however inAppPurchase.js never get callbacks…

    This is a bit problematic for inApp content that should be available since success callback is catched by the js

    I have tried both simulator and real device. Do you have this issue?
    Could it be related to the sandbox environment?



  4. Max says:

    Have you submitted your plugin to phonegap build? If so, have they given you any idea if/when it will be approved?

  5. Christopher says:

    Thanks for the plugin and excellent tutorial. I’ve got everything working and I’ve downloaded hosted content – an mp3. I have no idea where to go from here – is there a response that I can access via the plugin to see the asset location? Any tips on inapp downloaded content in phonegap would be appreciated.

    • Ben says:

      Hi Christopher. How did you get on? When you say hosted content – is that hosted content at Apple? Could you post how you did that with this plugin, thanks.

  6. Hi

    Can anyone tell me where can I find a xcode project with this sample working? I got this, https://github.com/jkirkell/cordova-inapp-sample but could not get working.

    This is the sample I used http://www.legal-recorder.com/src/inappsample.zip, any help will be very gratefull.

    • The plugin itself contains a sample project.

      From within the PhoneGap-InAppPurchase-iOS/test directory, run ./test.sh

      This will generate a sample project into PhoneGap-InAppPurchase-iOS/test/v31-build

  7. Ruben says:

    I have a problem when I do storekit.purchase I get an error in xcode

    2013-11-19 13:36:42.525 flow[72488:907] Resetting plugins due to page load.
    2013-11-19 13:36:42.800 flow[72488:907] Finished load of: file:///Users/rubenalcala/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/895D879A-6F9F-4C8A-90DE-9EFCC92DC06A/flow.app/www/index.html
    2013-11-19 13:36:42.808 flow[72488:907] InAppPurchase[objc]: About to do IAP
    2013-11-19 13:36:42.809 flow[72488:907] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Invalid product identifier: (null)’
    *** First throw call stack:
    (0x100012 0x28a1e7e 0xffdeb 0x28787dc 0x3b728 0x1a396 0x19bab 0x196f9 0x198a6 0x197bf 0x28b56b0 0x149b765 0x83f3f 0x8396f 0xa6734 0xa5f44 0xa5e1b 0x3a0d7e3 0x3a0d668 0x33bffc 0x208cc 0x2085d)
    libc++abi.dylib: terminate called throwing an exception

  8. José María says:

    Hello, thank you for the tutorial and for all the comments, which are also helpful.
    I generated the example project with run.sh and works well with the sample app (cc.fovea.babygoo) but when I run my app with my IAP return: “Error: could not load myIdIAP1”
    I have submitted my IAP to iTunes Connect and it is “Waiting for Review”.
    Can anyone help me??

  9. Celestino says:


    I am having this (EXC_BAD_ACCESS) error when I test it with:

    any idea?

  10. Celestino says:

    it stops in main.m, in:

    int retVal = UIApplicationMain(argc, argv, nil, @”AppDelegate”);


    @autoreleasepool {….}

  11. Alex Wrench says:


    I have moved the files across and have got to the stage of “Request product data” in your tutorial – what files are these code snippets going into?

  12. Nikhil says:


    I need to include inApp purchase in my App, and have installed the plugin via CLI. Plugin seems to be installed correctly, but I seem to be missing something in the configuration/setup of inApp Purchase on the iTunes. Have added two products ProductID1 and ProductID2, as you can see in the log, but it seems to be returning 0 Valid products.

    Console LOG output is as follows

    [915:60b] InAppPurchase[objc]: Getting products data
    [915:60b] InAppPurchase[objc]: Set has 2 elements
    [915:60b] InAppPurchase[objc]: – ProductID2
    [915:60b] InAppPurchase[objc]: – ProductID1
    [915:60b] InAppPurchase[objc]: Starting product request…
    [915:60b] InAppPurchase[objc]: Product request started
    [915:60b] InAppPurchase[objc]: productsRequest: didReceiveResponse:
    [915:60b] InAppPurchase[objc]: Has 0 validProducts
    [915:60b] InAppPurchase[objc]: productsRequest: didReceiveResponse: sendPluginResult: (

    Any Idea that can help me achieve the same.

    Many Thanks,

  13. Andrew H says:

    I’ve been looking into using this plugin and I am part-way into implementing it. I was wondering if I could suggest an enhancement where you add success/error callbacks to the individual “buy” and “restore” methods instead of relying on the more global “onPurchase” and “onRestore” handlers. My reasoning is that it would be helpful to be able to call my own callback method if the purchase is happening in an asynchronous function call already. If there was a special reason that you implemented it the way you did, I would be interested in knowing why.

  14. Clyde Thomas says:

    I think I have almost got this plugin working. My issue was that my IPA.js was undefined as I had to install the plugin manually.

    Because of this I had to add cordova.define(“com.phonegap.plugins.inapppurchase.InAppPurchase”, function(require, exports, module) {

    to the first line of my IAP.js

    Now my “in-app-purchase-list” div displays :”In-App Purchases not available.”.

    So onward I go. If anyone has any ideas of what is causing this, please let me know. “In-App Purchases not available.” shows when I look at my index.html file in safari but not when I look on my iphone device. Any help would be appreciated.

    • Andries says:

      Try this:

      window.storekit = cordova.require(“cordova/plugin/InAppPurchase”);

      • DanielG says:

        I´m having the same problem and this solution does not work… Can anyone who has this working give a hint where the problem might be?
        I´ve been trying to make this plugin work for 2 weeks and nobody gives me any help/support/suggestions.
        It´s becoming a nightmare!!!!

  15. Ben says:

    Is it possible to use this plugin for downloading hosted content from Apple on purchase? I can’t see this in the plugin’s JS file.

  16. Andries says:

    What is the difference between the:

    purchase: IAP.onPurchase,
    finish: IAP.onFinished,

    I’m seeing references to finish as well and when I implement the onFinished, it gets called too.

  17. Aaron says:

    I’ve set up a the test project as its own xcode project, made sure the bundle id matches my records in itunes connect, made sure that the product id matches, yet absolutely nothing happens, nothing in the console, nothing in on the phone, it simply hangs on ‘loading iap’

    What am I doing wrong here? Would using the JQM framework have something to do with this?

  18. 5States says:

    Installed using Cordova CLI (using cordova 3.1.0)

    I added the ARC flags to the 2 files, but when running the test JS, I can’t get windows.storekit object. I keep getting the “In-App Purchases not available.” console log message.

    Are there any other configs I have to do so that windows.storekit is recognized?

    • 5States says:

      Or, what is the process to debug if the storekit is not recognized? Is it related to how the JS files are set up? related to the plugin setup in XCode ? Thanks!

      • DanielG says:

        Hi, i´m having the same problem. Did you fix it?
        I think our problem is on how the plugin is integrated (the JS part). the object storekit is not being created or initialized.

        • 5States says:

          Still trying. Still waiting for a reply here. I noticed there was an update to the readme.md file where they removed the flags to set on those files, but that’s about it.

          There have been more “can’t get it to work” issues here, and no reply from the developer. Perhaps this plugin is no longer supported?

  19. Danemm says:

    Hello Chris, thanks for this great plugin. However, I can’t make it working, I have installed the plugin and followed the steps for the InAppPurchase also. What happened was, I tried to use the sample generated from test/run.sh, I changed the product ID in the iap.js with mine. Then build to Xcode, the result shows only:

    Device is Ready
    Loading IAP…

    No other response. If close the app and open again, RESTORE ALL button is the thing change.

    Please help me and tell me what do you think is the issue? What other thing should I do? Thank you.

    • Clyde Thomas says:


      Thanks for this very helpful plugin and tutorial.

      I think I am having a similar problem. My In App Purchases work great on an iphone with iOS 7.1.1. But my apps keep getting rejected because the IAPrender isn’t being pulled through properly on ipad with iOS 7.1.1.

      Only the “restore all” button shows up on ipad and does not show the product or buy button.

      Any ideas would be appreciated.

      Could it be a CSS issue?

  20. Thibaut says:


    Thanks for this tutorial. I have installed IAP plugins and I have created IAP.js file where I replaced IAP.list with my own product ID.

    After doing that, I am kinda stuck, I don’t really know what to do to test my and make my IAP work.

    What I need to do with IAP is to create a single page where you can unlock access to the app features, how can I achieve that please ?

  21. mihnea says:

    Console says In-App Purchases not available and
    I get an error on line 320 of InappPurchase.js : Uncaught ReferenceError: module is not defined (anonymous function) .
    Any ideas?

  22. Pulah says:

    Hello sir,
    In my app I have 2 add-on when i buy both, & then remove app & restore it it only returns one add-on only. i dont understand why this happens. can you please help me out?

  23. mihnea says:

    Can anyone help me please ? I am getting : “Uncaught TypeError: Cannot call method ‘purchase’ of undefined ” on the console

    when calling this:

    window.storekit.purchase(“ro.digitype.bettingtipz.subscr1mo”, 1);


    product id is correct , have no errors on storekit initialization .

    I am willing to pay for someone to look over my code and find the error .

  24. John Lindor says:

    This code is an enigma for beginners.

    How to use this? How to call these functions? How to listen for callback?

    Where’s the
    window.plugins.inAppPurchaseManager.onPurchased ?

  25. Johan says:

    Hi! Thanx for an awsome tutoial!
    Everything works perfect in my test app, but when I add the code to an existing app I get a really strange behaviour.
    The IAP won’t initialize but if I quit the app by pressing the home button and reopen it again everything works fine.

    Have someone come across the same thing?

  26. Bart Lamot says:

    I think it should be:

    IAP.onPurchase = function (transactionId, productId)

    without the receipts, looking at the github code.

  27. burnick says:

    any return value on

    IAP.buy = function (productId) {

    how would i know it was successful?

  28. Henrik says:

    We have implemented iap in phonegap and it is working, but not consistently so. Most purchases are coming in with a receipt containing a transaction_id of XXXXXXXXXXXXXXX (15 ciphers)But some some are coming in with a totally different format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Even though we do receive a receipt on these we cannot see the payments actually going through in itunes connect. Anybody have an idea what is going on. Wrote Apple – but so far no reply

  29. Henrik says:

    Turns out this was probably caused by users trying to cheat our system via the Zeptolab superpower1 hack.

    It is a scandal that Apple has not closed this gaping hole in their payment security

  30. Scott DeSapio says:

    Any way to catch the “Cancel” event? Right now, the error handler is picking up with a “Can’t connect to iTunes” error message.

  31. Brian says:

    Any chance this could be submitted to phonegap build?

  32. René says:

    Hi! I have a question, do I need to submit the in-app purchases before use the api? I mean, I can connect to the store and get an array of products right now (their status is ‘ready to submit’) but I’m not able to do a in-app purchase.

    Here’s my log:

    InAppPurchase[objc]: About to do IAP
    InAppPurchase[objc]: Payment transaction updated ((null)):
    InAppPurchase[objc]: Purchasing…
    InAppPurchase[objc]: Payment transaction updated ((null)):
    InAppPurchase[objc]: Error ERR_UNKNOWN Cannot connect to iTunes Store
    InAppPurchase[objc]: Error 4983507 Cannot connect to iTunes Store
    InAppPurchase[objc]: State: PaymentTransactionStateFailed

    Not sure what is going on, any help is appreciated…

    • Sergiy says:

      I have same problem. How did you solve it?

    • ChrisP says:

      René, have you been able to resolve this issue? I’m getting the same error with mine…but it only happens when I use a live test account

      • Chris says:

        Hi René and others the experience the error:

        Error 4983507 Cannot connect to iTunes Store

        I had the same thing, I guess it had something to do with not signing with correct provisioning profile. In Xcode ‘Build settings’ I changed all ‘Code signing identity’ to ‘iPhone Developer’ – and then I was able to sign in to App Store using my sandbox test user.

    • Scott says:

      Any luck with this error?
      Error 4983507 Cannot connect to iTunes

  33. Zach Aysan says:

    When you say:

    “Create an Application from iTunes Connect”

    Does that mean you need to have the application ready to go for submittal? I’m creating my first iOS app, and I’m certainly not ready to submit, but I’m trying to figure out how to build a sandboxed version of the marketplace experience.

    Ie, I’m not ready to launch, I’m just trying to get this mostly working so I can show a client.

  34. Asfahaan Mirza says:

    Hi there.

    Our developer is saying that we cannot display the country/store specific pricing in the App unless the user logs into their App Store account via the Init function. Only once the user logs in then we can show the pricing information within the app.

    E.g. Unlock Audio – $X.XX The price can only be determined if we get the user to login using their app store id.

    Does that mean it is best not to show the price at all in the app? The user will see the price when they click on Unlock Audio and login, etc.

    What is your suggestion?


    • The price has to be retrieved from Apple’s server, which requires the user to be logged into their App Store account. It’s part of Apple’s guidelines, so an app can be refused by Apple’s reviewers for doing it differently.

      My suggestion: if price is not available when “Unlock Audio” buttons are created, then display no price and eventually refresh the button’s label once price is retrieved successfully.

  35. Senthil Murugan says:

    This in app purchase plugin is suitable for android platform?.
    I need this plugin for both android and ios.

    Please help me to find the right inapp purchse plugin for both android and ios

    Thanks in advance. Any help will be appreciated …

  36. GNR says:

    hi i have use above code in-app-puchase . but that not to right..

    plz help me..

    how to my appliction in work in-app-purchase

  37. GNR says:

    hi i have use above code in-app-puchase . but that not to right..

    plz help me..

    how to my appliction in work in-app-purchase?

  38. Rob says:

    First off, great tutorial Jean-Christophe! Very in depth and well written.

    I’ve setup my app based on your tutorial (my in app purchase id is called ‘full_version’). When I build my app my purchase comes back as invalid. Any suggestions on troubleshooting?

    2014-06-28 16:13:11.517 Phrase[6838:60b] InAppPurchase[js]: setup ok
    2014-06-28 16:13:11.519 Phrase[6838:60b] READY
    2014-06-28 16:13:11.523 Phrase[6838:60b] InAppPurchase[js]: load [“full_version”]
    2014-06-28 16:13:11.526 Phrase[6838:60b] InAppPurchase[objc]: Getting products data
    2014-06-28 16:13:11.528 Phrase[6838:60b] InAppPurchase[objc]: Set has 1 elements
    2014-06-28 16:13:11.530 Phrase[6838:60b] InAppPurchase[objc]: – full_version
    2014-06-28 16:13:11.531 Phrase[6838:60b] InAppPurchase[objc]: Starting product request…
    2014-06-28 16:13:11.536 Phrase[6838:60b] InAppPurchase[objc]: Product request started
    2014-06-28 16:13:11.539 Phrase[6838:60b] THREAD WARNING: [‘InAppPurchase’] took ‘13.447998’ ms. Plugin should use a background thread.
    2014-06-28 16:13:11.541 Phrase[6838:60b] In-App Purchases not available! [object Object]
    2014-06-28 16:13:12.055 Phrase[6838:60b] InAppPurchase[objc]: productsRequest: didReceiveResponse:
    2014-06-28 16:13:12.057 Phrase[6838:60b] InAppPurchase[objc]: Has 0 validProducts
    2014-06-28 16:13:12.059 Phrase[6838:60b] InAppPurchase[objc]: productsRequest: didReceiveResponse: sendPluginResult: (
    2014-06-28 16:13:12.064 Phrase[6838:60b] InAppPurchase[js]: load ok: { valid:[] invalid:[“full_version”] }
    2014-06-28 16:13:12.066 Phrase[6838:60b] IAPs loading done:
    2014-06-28 16:13:12.068 Phrase[6838:60b] Error: could not load full_version

    • Rob says:

      It looks to be an issue with the storekit load not working. Could my app be me missing some sort of permission?

      IAP.onReady = function () {

      // Once setup is done, load all product data.
      storekit.load(IAP.list, function (products, invalidIds) {
      console.log(‘IAPs loading done:’);

      IAP.products = products;
      IAP.loaded = true;
      for (var i = 0; i < invalidIds.length; ++i) {
      console.log("Error: could not load " + invalidIds[i]);

      • Hey Rob,

        It looks like a configuration problem, load returns successfully (which means he could communicate with Apple’s servers), however full_version is marked as invalid. Did you create it on itunesconnect? Note that a newly created item may take up to a day to become available. Also note that the iap item should be “cleared for sale” in itunesconnect. Are you using a test account? Either way, I advise you to logout from itunes in your device, create a new test account and test again… I encountered a few cases where test accounts got silently disabled (without any failure reported).

        • Rob says:

          I added the IAP, and its in ‘Waiting for Upload’ status, but is also marked as cleared for sale.

          Does it matter if I host the IAP with Apple or not? I also can’t seem to find the area where you add a test account. I see shared secret, but no test accounts. Am I missing something?

          I’ll wait another day and try again, but logging out/in again didn’t seem to do the trick.

          Thanks a ton for the help.

        • Rob says:

          Thanks for the help Jean-Christophe! I am setup with the test user, but my IAP still refuses to load. It looks like my IAP.loaded action is not firing off. Here is a link to my code on Github. I setup IAP on line 71 of this file:


          Do you see anything wrong with that code?

          I appreciate the help! I’ll give you a copy of the final app for your troubles 🙂

          • Rob says:

            Ok, I figured it out. Anyone getting the “Has 0 validProducts” error, make sure (on top of what Edward B says below) that you have all the contracts and information setup under “Contracts, Tax, and Banking” in iTunes connect. Once these are approved by Apple, you can use your IAP in your app for testing.

    • Edward B says:

      Same happened to me. You need to verify two things:
      1. You’re logged out of any account on your Store account (Settings->Store then Disconnect/Logout). Don’t add your testing account here otherwise it might get invalidated

      2. The “Has 0 validProducts” refers to the account you’re trying to connect. So either your Product hasn’t been approved… OR (most importantly), you are using a WRONG Bundle ID in your whole app.

      In my case, the incorrect bundle ID was the problem, I double checked on iTunesConnect and that’s it. It should now give you the proper quantity of products you have added.

  39. Milan says:

    I seem to be encountering the same problem as @Nikhil. This is what keeps returning:

    Jul 6 21:05:45 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[js]: setup ok
    Jul 6 21:05:45 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[js]: load [“eu.gtdomain.app.test”]
    Jul 6 21:05:45 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[objc]: Getting products data
    Jul 6 21:05:45 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[objc]: Set has 1 elements
    Jul 6 21:05:45 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[objc]: – eu.gtdomain.app.test
    Jul 6 21:05:45 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[objc]: Starting product request…
    Jul 6 21:05:45 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[objc]: Product request started
    Jul 6 21:05:46 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[objc]: productsRequest: didReceiveResponse:
    Jul 6 21:05:46 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[objc]: Has 0 validProducts
    Jul 6 21:05:46 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[objc]: productsRequest: didReceiveResponse: sendPluginResult: (
    Jul 6 21:05:46 iPhone-uzivatela-Milan GuerrillaTips[1265] : InAppPurchase[js]: load ok: { valid:[] invalid:[“eu.gtdomain.app.test”] }
    Jul 6 21:05:46 iPhone-uzivatela-Milan GuerrillaTips[1265] : Invalid Product IDs: [“eu.gtdomain.app.test”]

    Have you thought of any fix???

  40. Mz says:

    I can see the Product list, description and price – Displaying the products in my In App purchase is working. When I tap on Buy button, it ask for Apple Id and password. I put the correct password but it say Apple Id not found or incorrect password. With the same account I can login to Itunes store and appleid.apple.com. Not sure why it does not accept my apple id and password. Any suggestions will be appreciated. Thanks in advance.

  41. Tom says:

    With the IAP test account. I’ve logged out of all accounts on the iphone to test but it never asks me to login and says my product is invalid.

    InAppPurchase[js]: setup ok
    InAppPurchase[js]: load [“newpet002”]
    InAppPurchase[js]: load ok: { valid:[] invalid:[“newpet002”] }
    IAPs loading done:
    Error: could not load newpet002

    I’ve checked everything in itunes connect (the product is waiting for approval? – could that be it?)

    • Cody says:

      I have the same error too, no clue how to fix this and get it to see the product in my development version of my app.

      Do we need to do anything “special” to properly activate the in-app-purchase?

  42. E. e dev says:

    Hello, your tutorial was really helpful 🙂 I was able to get it to work faster than iI thought.

    But when calling renderIAPs(“…”) it is often faster than the IAP.onReady function which sets IAP.loaded to true within the storekit.load();

    Though I have placed the IAP.load() function at the very top of the script but it didn’t speed up the process, I ended up placing the renderIAPs(“…”) inside the IAP.onReady to load the items as fast as possible:

    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]);


    Isn't that better?

  43. Justin Noel says:

    Do you have any idea how to recover from the dreaded “You’ve already purchased this In-App Purchase but it hasn’t been downloaded” problem?

    I’m using `autoFinish` false and made a mistake with my callback. So, now, I can never “buy” this item again.

    I can’t find a way in the plugin to handle this. Shouldn’t some event trigger to attempt to download again automatically?

  44. Christian says:

    How to resume a unfinished purchase?

    I’m using noAutoFinish=true to make sure, the user get his goods before he pays for it. But when the app is not able to call storekit.finish after the purchase (e.g. connection problem), i have the problem (at consumable items), that the user can’t buy it again, because the last purchase isn’t finished.

    Is there any way to find out (after restarting the app), if there is an unfinished purchase (e.g. for a given productId)?

  45. David says:


    First of all, lots of thanks to Jean-Christophe for this great plugin + tutorial.

    In my case, the plugin works fine for a long time, but (today / after a reinstall) it seems to change its behaviour when restoring purchases:

    My init (really simple):

    debug: true,
    ready: IAP.onReady,
    error: IAP.onError,
    purchase: IAP.onPurchase,
    restore: IAP.onRestore

    The product (only 1 non-consumable to unlock the app) is loaded ok as usual, BUT after window.storekit.restore() execution, “restore” callback is never called anymore (0 logs, 0 errors), and now I found “restoreCompleted” callback is called instead. Before this issue I can do:

    IAP.onRestore = function(originalTransactionId, productId) {
    if (productId === IAP.appstore.productId) {
    // Unlock stuff!

    How can I check if the user has bought the product identified by productId if “restoreCompleted” function doesn’t receive any parameters?

    Thanks again,

  46. thomas says:

    Hi good day I am new to in-app purchase I am not sure what file to copy.

    It did say here that “copy the Javascript in the www/ directory and register it so it’s automatically loaded by Cordova.” I am not sure what javascript file should I copy.

    I did look at the plugin cc.fovea.plugins.inapppurchase folder and found InAppPurchase.js and more in the test forlder.

    Hope to hear from the author of the tutorial.


  47. Tejas Bharambr says:

    Awsome tutorial.
    Thank you very much.

  48. Tex says:


    I am using dreamweavers phonegap build service to build my app.

    Can I just paste into the xml file generated by dreamweaver and use the above code in my javascript file. All the other plugins I am using with dreamweaver work this way, but I am not sure if it will work this way with your plugin.


  49. Niko says:

    thanks for the great tutorial. I am developing using iOS7; I created my app in itunesconnect, created an in-app purchase product; and created a test user.
    On my phone, I logged out and logged back in with the sandbox text user.
    When I try to init the storeKit with the exact same method as in the article I get:

    InAppPurchase[objc]: Cant make payments, plugin disabled.

    My app is in ‘Prepare for Submission’ mode and the in-app purchase products are in ‘Ready to submit’ mode.

    What am I missing here?

  50. Elvis says:

    I need your help in the Step 3: Write the code! who call the function and what are the parameters? please

  51. krishnakumar says:

    Is there a way to use subscription with this plugin. Like once paid the user can use the feature for 3 months and that can be extended based on users payment from 6 to 12 months?

  52. Andreas says:


    Any of you guys succeed implementing this via PhoneGap Build (the cloud builder)?

    After adding into my config.xml, I can see the StoreKit object, but I always get the “Error: could not load” message for all my IAPs (although they are registered and approved in iTunes Connect).

    Do I miss something or it only works via local CLI build?

    Many thanks,

    • Hi,

      I know it was used successfully with PhoneGap Build.

      Maybe you’re not using a valid Test User? Are you testing on a real device?

      • Andreas says:

        Thanks Jean-Christophe, this gives me hope! 🙂

        Yes, I’ve compiled with Phonegap Build, and testing it on real device (iPad). Unfortunately I don’t get Apple’s standard pop-up asking for username/password. 🙁

        Storekit object is created, I can initialize it, but for some reasons it cannot load IAPs (ERROR) although they are created in itunesconnect and cleared for sale.

        After this, (no surprise) nothing happens when it comes to window.storekit.purchase

        Do you have any clue or can direct me to the person who did it with PhoneGap Build?


        • Andreas says:

          It seems like I found the problem:
          It should be storekit.purchase(“inappid1”);
          And not: storekit.purchase(“com.example.app.inappid1”);

          At least Apple’s “Sign In to iTunes Store” window pops up now!

          But I’m also getting the “The Apple ID you entered couldn’t be found or your password was incorrect. Please try again” – message, when actually both are correct. Is this test account-related?

          • Kevin says:

            Are you sure you are using a sandbox tester account? I believe this is a requirement for development testing…

  53. Daniel says:

    Could you help me with this strange error ?
    after IAP.buy(‘com.mydomain.app.item20’); i get the strange webkit error on ios

    2014-11-05 12:09:36.323 app[265:27826] InAppPurchase[js]: load ok: { valid:[{“title”:”German”,”id”:”com.mydomain.app.item20″,”price”:”€7,99″,”description”:”German language”},{“title”:”German”,”id”:”com.mydomain.app.item3″,”price”:”€2,69″,”description”:”German language”}] invalid:[] }
    2014-11-05 12:09:40.552 app[265:27826] InAppPurchase[objc]: About to do IAP
    2014-11-05 12:09:40.552 app[265:27826] InAppPurchase[objc]: Transaction updated: com.mydomain.app.joker20
    2014-11-05 12:09:40.553 app[265:27826] InAppPurchase[objc]: Purchasing…
    2014-11-05 12:09:40.553 app[265:27826] InAppPurchase[objc]: State: PaymentTransactionStatePurchasing
    2014-11-05 12:09:40.555 app[265:27826] *** WebKit discarded an uncaught exception in the webView:decidePolicyForNavigationAction:request:frame:decisionListener: delegate: Cannot finish a purchasing transaction

  54. Sean says:

    Hi, I have followed the tutorial – I have developed the app NOT using Xcode. I build the app using build.phonegap.gap from my GitHub repo. After implementing the code in my app and adding the in app purchases within itunes connect, I do not receive these within my app, any suggestions as to where I have went wrong. on the Render IAP the placeholder i defined shows “”In-App Purchases not available.”;”

  55. Kumar says:

    Hi, What are possible cases if i get error “setupFailed” error. Can i get the details on it. And what should i do to avoid this error.

  56. Jaimin says:

    load [“com.dynamicmethods.superyacht.subject1”]
    2015-03-04 11:06:27.596 jaimininApp[4138:891476] InAppPurchase[objc]: Getting products data
    2015-03-04 11:06:27.596 jaimininApp[4138:891476] InAppPurchase[objc]: Set has 1 elements
    2015-03-04 11:06:27.597 jaimininApp[4138:891476] InAppPurchase[objc]: – com.dynamicmethods.superyacht.subject1
    2015-03-04 11:06:27.598 jaimininApp[4138:891476] InAppPurchase[objc]: Starting product request…
    2015-03-04 11:06:27.599 jaimininApp[4138:891476] InAppPurchase[objc]: Product request started
    2015-03-04 11:06:33.839 jaimininApp[4138:891476] InAppPurchase[objc]: productsRequest: didReceiveResponse:
    2015-03-04 11:06:33.840 jaimininApp[4138:891476] InAppPurchase[objc]: Has 0 validProducts
    2015-03-04 11:06:33.842 jaimininApp[4138:891476] InAppPurchase[objc]: productsRequest: didReceiveResponse: sendPluginResult: (
    2015-03-04 11:06:33.865 jaimininApp[4138:891476] InAppPurchase[js]: load ok: { valid:[] invalid:[“com.dynamicmethods.superyacht.subject1”] }
    2015-03-04 11:06:33.866 jaimininApp[4138:891476] []
    2015-03-04 11:06:33.867 jaimininApp[4138:891476] Error: could not load com.dynamicmethods.superyacht.subject1

  57. elepetit says:


    thx for the job, everything works fine !

    just, how can I control if the user has bought something?
    wich is the function to verify if the in-app object has been bought ?
    and how do you store this information on the device?

    thx a lot

  58. Jahanvi says:

    storekit.purchase function is not getting called!!

    Thanks in advance