Implementation of In-app Purchases for iOS Mobile Marketplace Apps

There is a big enough list of mobile app monetization ways to consider. In this article, though, we would like to pay a very special attention to what is called in-app billing integration. Here we share with you our expertise and the code examples on how to implement an in-app subscription mechanism for iOS (and partly for Android). Hope this short iOS in-app purchase tech tutorial will serve you great for your mobile marketplace platform development.

The machine behind how in-app purchase works

Being one of the most recognized mobile app monetization methods, in-app billing functionality requires great attention to the tech details to make it effective in use. Performance is the core: there lies the answer whether the mobile marketplace platform will be viral or not.

The structure of in-app billing mechanism

You offer one of the several (subscription, freemium, one-time purchase, etc.) ways to buy your product

> users pay money to get your product and the features it offers

> your tech side of the app is responsible for checking if the user has subscribed and grants the access to the features offered after the subscription purchase.

In-app-purchases-for-ios-based-mobile-marketplace-apps-scheme

Later, in this article, we will describe the scheme in the form of code.

Creation of the in-app purchase functionality for iOS

Difficulties, challenges, what to remember about

Since we’ve dealt with the development of an app for AppStore, we would like to describe what were the ups and downs of the process, you might find useful.

There are two things needed for the mobile in-app purchases:

  • Developer’s account, where you point out the type of your product a customer can buy via your app. Here also you fill in all the required information about your app.

  • Technical implementation (or simply, the tech stack for your in-app purchase functionality which is compatible with the technologies the app is based on). In our case, it’s Cordova, Meteor and React-based app.

The challenges:

  • If you offer to purchase your own product, you can’t use any other online payment services integration apart from the one, offered by Apple (means no PayPal, Stripe, etc.). But in case with the transactions from user to user, it’s allowed.

  • AppStore withdraws 15% of the fee based on your net revenue a customer brought you by subscribing to your mobile marketplace platform app.

  • Apple offers its own price ranges to choose from (you can’t enter it manually). The description of the services is declared by Apple too. It might get quite unusual for some product owners, since the required info for AppStore might contain commercial details.

  • To debug or test the product, you have to create a separate test account which is verified with the app’s ID. If this test account is anyhow changed it gets immediately deactivated.

Other memo-s when developing in-app purchase subscription functionality for iOS:

  • Each product has its unique ID number by which you can identify your app. There are 2 types of IDs: Team ID which is generated by Apple and Bundle ID - specified by you.

  • Apple’s native payment system has its own UI which can’t be changed;

From all said above, it might seem that Apple makes everything great for the end user but the developer tools are not flexible enough.

If you're into the mobile app development, you might be interested in reading about the React Native Limitations and Best Practices to Deal With Them.

Let’s get to how to implement in-app purchase for iOS

The event-planning online marketplace app we created is JavaScript-based. The tech stack includes such technologies: Cordova, Meteor, React, Mongo In this part we’ll describe how we created the in-app billing for iOS in code.

To make IAP’s work possible, your mobile marketplace platform should communicate with StoreKit framework. Consider this element as the secured payments passage, that is responsible for payments secure authorization and processing.

StoreKit, behind the scenes:

In-app-purchases-for-ios-based-mobile-marketplace-apps-storekit

But how to make all of this work together?

Let’s explore it further in In-app purchase programming guide.

We’ve used 2 packages:

For Apple receipts validation we’ve used Apple’s Receipt Validation Programming Guide.

Let’s see what’s happening on the client’s side

Store initialization when the user has logged in the app:

function initStore() {

    if (

     window.device

     &&window.device.platform.toLowerCase() ='ios'

     &&!store.ready()

    ) {

     Meteor.subscribe('user', () => {

      if (Meteor.user()) {

       Meteor.subscribe('packages', () => {

        document.addEventListener('deviceready', initializeStore, false);

       });

      }

    });

  }

}

The process of Store initialization

function initializeStore() {

The first step is to get all packages from you database. We need to keep information about the packages in our database. Further, we’ll use them for the registration step in App Store each time the app starts

const packages = Packages.find({}).fetch(); packages.forEach((p) => {

Later, we can get information about the products with store.products

store.register({
    id: p.iapId,
    alias: `${p.type}.${p.planName}`,
    type: store.PAID_SUBSCRIPTION, // set product type
   });
 });

Assign listeners (functions that process a certain event) for main store events

Every time a user triggers some kind of a state change to any product, we perform specific actions.

Explore the product lifecycle during its execution below:

In-app-purchases-for-ios-based-mobile-marketplace-apps-1


    store.products.forEach(({ alias }) =>; {
     store.when(alias).approved(function (p) {
      p.verify().expired(function (product) { cancelIAPSubscription(product); });
     });
     store.when(alias).verified(function (p) {
      p.finish();
     });
     store.when(alias).expired(function (p) { cancelIAPSubscription(p);
     });
     store.when(alias).cancelled(function (p) { cancelIAPSubscription(p);
     });
     store.when(alias).refunded(function (p) { cancelIAPSubscription(p);
     });
    });

Register a validator to call it for event verification

Below we define how the store will validate the purchase.

store.validator = function (product, callback) {
    const receipt = product.transaction.appStoreReceipt;
    if (!receipt) {
    callback(false);
    return;
    }
    Meteor.call(

Now let’s validate in-app purchase on the server side

   'packages.validateIAPPurchase', receipt, (err, res) => {
     if (err) {
      if (err.error && err.error.code === 6778003) {
       callback(false, {
       error: {
        code: store.PURCHASE_EXPIRED,
        message: 'Purchase expired',
       },
      });
     } else callback(false, err.error);
    } else {
     callback(true, res); // success!
    }
   }
  );
};

We can call actions, like show products to the user, when the store is ready:

store.ready(() => {});

This action is called for store to come to life:

  store.refresh();
}

  function orderSubscription(product) {

Initiate the product’s purchase

  store.order(product);
}

When validation fails or subscription is no longer active we should cancel the in-app subscription purchase

function cancelIAPSubscription({ alias }) {
  Meteor.call('packages.cancelIAP', alias);
}
What’s happening on the server side?

const methods = {

Product receipt validation on the server after the purchase or during the synchronization

    'packages.validateIAPPurchase'(IAPReceipt) {
      check(IAPReceipt, String);
      const user = Meteor.user();
      if (!user) throw new Meteor.Error('401', 'User not found');
      this.unblock();

      const validationResponse = Async.runSync(done => {
       processIAPReceipt(IAPReceipt, done);
      });
      const error = validationResponse.error;
      if (error) throw new Meteor.Error(error);

We need to sync Apple’s data with our own data each time we validate a purchase

  return syncIAPPurchase(user, validationResponse.result);
},

Cancel subscription is called when the validation fails

    'packages.cancelIAP'(planType) {
      check(planType, String);
      const user = Meteor.user();
      if (!user) throw new Meteor.Error('401', 'User not found');
      const hasActiveApplePlan = user.hasActiveApplePlan();
      if (hasActiveApplePlan) {
       const promoPlan = {
        type: 'free',
       };

Users’ subscription data is stored inside of our system to know what content to give access to

     Meteor.users.update(
      { _id: user._id, 'promoPlan.type': planType, 'promoPlan.isApple': true },
      { $set: { promoPlan } }
     );
    }
   },
  };

Let’s call Apple services for receipt validation using IAP package

function processIAPReceipt(IAPReceipt, done) {
 iap.setup((error) => {
 if (error) {
  done({
   code: 6778002,
   message: 'something went wrong...',
  });
 } else {
  iap.validate(iap.APPLE, IAPReceipt, (err, appleRes) => {
   if (err) {
    done(err);
   } else {
    if (iap.isValidated(appleRes)) {
     const options = {
      ignoreExpired: true,
     };
     const purchaseDataList = iap.getPurchaseData(appleRes, options);
     if (!purchaseDataList || !purchaseDataList.length) {
      done({
       code: 6778003,
       message: 'Purchase expired',
      });
     } else {
       done(null, purchaseDataList[0]);
      }
     } else {
      done({
        code: 6778001,
        message: 'Code cannot be validated',
      });
     }
    }
   });
  }
 });
}
Bottom line

We hope that the enlighted aspects of this iOS in-app purchase short tutorial will help you in your project’s endeavours, since the code is the heart of it all. If you’ve got a plan on how to implement well-thought mobile app monetization methods but you’re looking for team to bring your B2B and B2C marketplace development plans to life, you can surely call us for help.