Hello there!

So you’ve spent substantial amount of time over something which seems so unproductive & quite  illogical, right? Been there, done that & it sucks! I know this because I’ve been to dark side before!

Anyway, I assume you’d have little bit idea about Apple’s in-app purchases, right? So let’s take follow up of certain things:

Languages & Tools

We’ll be using following things to get started:

  1. Linux machine (I’m using Debian 9.8)
  2. An IDE preferably VS Code
  3. Ruby distribution (version >= 2.5.1)
  4. PostgreSQL
  5. App Store & Heroku account for configuring in-app purchases & deploying our app
  6. Heroku CLI (Optional)

I’m giving preference to VS Code because it’s easy to operate and offers a lot in terms of plugins & code management.

Also, you can use any language other than Ruby as overall procedure to implement in-app purchases is very much similar.

And lastly, we need App Store account which will be used for setting up iOS client app & configuring in-app products’ details. As this blog is targeted to server-side implementation of in-app purchases, it’s better not to explain how to set up Xcode or App Store for the iOS app.


We’ll separate this into 3 sections i.e. App Store Product configuration, App Store Sandbox configuration and Server configuration.

App Store Product Configuration

First task in our hands is to configure App Store account for the Apple in-app purchases. I assume you’ve already completed the app related configurations. So let’s get started!

On signing in into your iTunes Connect account, click on your app. This will take you to the details screen. Click on second tab ‘Features’ on left-top corner of your screen. This is where we’ll manage our in-app products.

In order to create our first in-app product, follow the steps:

  1. Click on (+) button.
  2. Select Consumable or Automatically Renewable Subscription from dialog shown.
  3. Fill in Reference Name, Product ID, Price, Localization, etc.
  4. When Renewable Subscription is selected, remember to choose a Subscription Duration.
  5. If asked, enter anything in Group Reference Name that you find easy to remember like App Name.
  6. Finally, check the box saying Cleared For Sale.

Some points to remember here:

With completing above steps, we’ll now have our in-app product(s) ready to be purchased!

As we are implementing both consumable & renewing subscription, ensure that different product IDs are created and are ready to be used.

For sake of our peace of mind, let’s just set product ID of our consumable product to 1_coin (1 coin) and that of renewing subscription product to 1_month_subscription (1 month subscription).

Now, go back to the screen where you can see all the products you created. Here, look for App-Specific Shared Secret. Click on it and generate one secret if not already. This secret is required to verify our purchases and we’ll set this as an environment variable.

App Store Sandbox Configuration

We’ll be using Sandbox environment to test in-app purchases without using actual money! Sounds great right? Sorry to break it to you, it doesn’t! Until now the procedure was simple but this is where it all gets ugly. Anyway, for using Sandbox, you’ll have to create a Sandbox user & use it whenever purchase is being made. Follow these steps to get started:

  1. Click on User and Access from My Apps drop-down found on left-top corner of your screen.
  2. Look for Testers under Sandbox & click it.
  3. Click on (+) button.
  4. Fill in all the details. Remember that email entered here must be verified in later step.

If for ‘some’ reason *cough*so called innovative leader in ground-disrupting technologies*cough*, you are stuck at an error ‘There are one or more validation errors below.’, then check if you fall under these scenarios:

If you’re not the forth one, then avoid doing it! Seriously Apple, get it together! One warning or instruction wouldn’t be so much trouble!

Once you’re done creating new Sandbox users ( Yes! You need at least five of Sandbox users for testing! ), verify your newly created Apple ID by clicking an link in the email you just received! Now you can use those to sign in on any Apple devices. Also, consider signing out of your actual accounts before signing in as a Sandbox user.

Quick Tip: You can use email aliases to create multiple Sandbox user accounts. For instance, if your primary email ID is test.user@gmail.com, then you can use test.user+1@gmail.com or test.user+yoda@gmail.com.

Server Configuration

Now let’s take some relief & find out how to configure Server-Side (or Dark Side? :D). We’ll be using Ruby as coding language and Padrino as web-framework. But first, let’s set the environment variable that we’ve talked about.

Execute following command in terminal or bash:

$ echo 'export APP_STORE_SECRET='8456ceb1aa3f48fc881...' >> ~/.profile

To check if above command succeeded or not, execute another command:

$ env | grep APP_STORE_SECRET

You’ll find the environment variable with its value. You can check out this repo created by me for basic skeleton of Padrino app. Repo has the master branch which contains full source code. Please note that you would have to create PostgreSQL database too, which we can only do via shell commands. For creating database you can follow this very useful guide and make sure your connection to the database is successful by executing below command:

$ bundle exec padrino console

If everything’s good, you’ll see the environment loaded Padrino console. The console is an interactive Ruby interface which is very useful when you want to try out code before running it on the server. 

The connection URL should look like this:


Anyway, checkout the basic-skeleton branch and execute following command to fetch any dependencies we’ve set.

$ bundle install

Now let’s get started with creating required files like controllers, migrations etc. They are required for building APIs which will be used to verify our in-app purchases. Open terminal and execute following command:

$ bundle exec padrino g controller users

Above command execution will create 2 files i.e users.rb & users_helper.rb. The first file is a controller whereas later one is just a helper class which is accessible from anywhere. If you aren’t familiar with MVC pattern, this explanation should be helpful for you. Here, Controller means it controls data flow between views & models & itself. View means anything that’s visible to users. Models are entities that actually constitute business logic. Now, execute following command:

$ bundle exec padrino g model user

With this command, you will have model User automatically created and derived from class Sequel::Model. Interesting thing about Padrino is that it knows what you need. You may find a new file 001_create_users.rb in db/migrate directory which is our first migration from nothing. Migrations are useful when you have to alter database structure in a certain manner. If something goes wrong, you can always go back to required migration number without losing all data. Note the naming conventions that I’ve used in above commands. Controller names should always be a plural entity while model name should be a singular entity. The following code tells rake to create user table in our database ‘sample_db’. Edit file 001_create_users.rb & put below code in migration block:

Sequel.migration do
  up do
    create_table :users do
      primary_key :id
      String      :username, unique: true, null: false
      String      :password, null: false

      TrueClass   :is_premium, default: false
      DateTime    :premium_start_time
      DateTime    :premium_end_time
      Text        :receipt_data
      String      :environment
      String      :transaction_id
      String      :original_transaction_id

      DateTime    :created_at
      DateTime    :updated_at
      DateTime    :deleted_at

  down do
    drop_table :users

Um, excuse me? What is Rake? And What is that code doing?

So…Rake is nicely explained here and here. About the code part, it’s blueprint of table that we’re asking it to create. There are 2 blocks here – up & down. When migration is run in upward direction, ‘up’ block gets executed & if migration is run in downward direction, ‘down’ block gets executed. Notice that we’ve used different data types for certain column types e.g. DateTime.

It’s very easy to create tables & add API endpoints, or is it? Again, Don’t worry! I’ll help you. Let’s start by creating users table first. If you remember, you’d know that we’ve used postgres as adapter while creating the project.

The following command creates user table in our database ‘sample_db’. Note down prefixed number of filename ‘001_create_users.rb’ & execute below command:

$ bundle exec rake sq:migrate{:to[001] OR :up}

Things in curly brackets are optional. By using number we’ve noted earlier or just by mentioning keyword ‘up’, we can tell Rake to migrate in upward direction. After successful execution you’d notice that our ‘sample_db’ has new table users. With this step, you can now run your server without any errors.

$ bundle exec padrino start (OR s for short)

You’ll see something like this in the terminal:

=> Padrino/0.14.4 has taken the stage development at

Go on click on the link & you’ll find your server up & running with following message! Sinatra doesn’t know this ditty.

Endpoint & API Setup

I know, this is getting a lot more complicated than you thought, right? But let me assure you my young padawan, we’re on the right path. In this section, we’re going to find out how to create APIs & setup their endpoints. You’d have known by now that we have a controller called users which will form URL like this http://localhost:3000/users/. Any functions we define in this controller can be used as an API endpoint. Let’s create a simple function for fetching user’s details:

AppleInappSample::App.controllers :users do

  get '/profile', with: :user_id do
    ret = {}
      user = User[params[:user_id]]
      ret = { success: true, message: 'User found!', data: user.to_h }
    rescue StandardError => e
      status 400
      ret = { success: false, message: e.message }


I’ll explain little bit about above code block. We have first line i.e get ‘/profile’, with: :user_id do … – yeah you’re right Karen! It’s a GET request! It’s also a function with one query parameter user_id. The curl command for above API might look like this:

$ curl

The function validate_fetch requires params as a parameter and it can found in users_helper.rb class. The line containing User[params[:user_id]] does what it seems to do i.e find an user having id of :user_id. Note that the User class is a Sequel.Model that we created earlier. Before proceeding to next step, add this line to your Gemfile.rb and then execute bundle install command.

gem 'venice'

Venice is a great gem for verifying Apple’s in-app purchases & it’s very simple to use too! We are going to use Venice in the class AppleManager found in lib directory of master branch.

Now we can add one more function like follows. This function will actually verify the purchases we would make from iOS client.

AppleInappSample::App.controllers :users do

  post '/verify_subscription/', csrf_protection: false do
    ret = {}
      manager = AppleManager.new(params)
      ret = { success: true, message: 'Subscription verified!' }
    rescue StandardError => ae
      status 400
      ret = { success: false, message: ae.message }

    return ret.to_json

The curl command for above API might look like this:

'user_id=1' -d 'receipt_data=ewoJInNpZ25hdHVyZSIgPSAiQXhze...' POST

The required parameters for this API are user_id & receipt_data. Receipt data is base64 encoded receipt of the purchase and it should always be generated on iOS client. Before using these parameters to verification, we’re validating them against null values by passing them to the function validate_subscription found in users_helper.rb class. You may also want to checkout AppleManager.rb class to know how Venice works with verification. With this step we have completed endpoint & API setup and yeah, I think next steps should be quite easy…

Getting our hands dirty!

Now that we’ve setup everything & our APIs are ready, we can test our first purchase. I assume you’re ready with 3 things – an iOS client app, the code that initiates in-app purchase flows and the code that hits our APIs. Also, make sure that you’ve signed out of your App Store account from test device and you’re signed in as Sandbox user by clicking Settings -> iTunes & App Store -> Sandbox Account -> Sign In.

Once you sign in, go to the app screen where you can start in-app purchase flow. When you are asked to enter account credentials, just enter the ones you’ve used earlier to sign in into Sandbox account. App Store kit will finally ask to confirm if you want to purchase the in-app product or not? Go on clicking the Continue button and after successful completion, you should check for the purchase receipt from App Store kit.

You can also check out the official documentation about how to get encoded receipt_data from bundle. Also note that field password mentioned need not to be send to server every time as we’ve already configured server’s environment and set that password as variable APP_STORE_SECRET.

The process is ‘simple’ –

  1. You click to buy a product say 1_month_subscription
  2. App Store kit completes purchase & return receipt
  3. Encoded receipt data is built
  4. App sends this receipt data to API /users/verify_subscription/
  5. Server validates it & updates user row as per details in latest receipt
  6. App updates the UI to notify user.
  7. After automatic renewal of the subscription, update to the user is triggered. (Explained later)
  8. When a subscription is expired, update to the user is triggered.
  9. App updates the UI to notify user.

For processing auto-renewals of a subscription, using a Rake task sounds good which will be triggered at a certain time. ‘Common’ practice is that this task should be triggered only at certain times such that it falls between x days before and x days after subscription’s expiry time. You don’t want to waste server resources every day, do you? Then again, Apple doesn’t really care about anything (except money!) I guess. When it comes to check for renewals, they recommend to rely on renewal receipts sent from iOS client devices. I think status polling is efficient for basic use-cases like ours.

In the task I mentioned above, similar procedure is to be followed for checking renewals. If Apple returned latest version of receipt and it contains a transaction of same original_transaction_id that we’ve in database but with different transaction_id, then we can update the database with this transaction as a ‘Renewed‘ subscription. If in that transaction, expires_at is in past, then it can be considered as ‘Renewed but expired’ subscription.

require 'venice'

## This task is triggered each day to check if a subscription is renewed or not?
# Expired subscriptions are also updated in database.
# This is just basic implementation & could vary based on different cases.
class ExpireUpdateSubscriptions
  @queue = :high

  def self.perform
    users = User.where { end_time < DateTime.now }
                .exclude(transaction_id: nil)
                .exclude(receipt_data: nil)
    users.each { |u| verify(u) }

  def self.verify(user)
    params = {}
    params[:user_id] = user.id
    params[:receipt_data] = user.receipt_data
    manager = AppleManager.new(params)

Now, how to trigger this task is totally up to you. There are some options like resque-scheduler which can be really handful sometimes.


I hope this is how Apple’s in-app purchases are implemented but this might not be the best way in your case. I must say that Apple would not care to make their in-app purchases easy to implement in near future, so I would recommend to research & try until you find the implementation best suited to your case. I would totally love to write about the consumable part of the in-app in another blog – may be… it could be easier than this ‘Phantom Menace’! Just kidding! It is lot easier than the subscriptions, I assure you.

If you have an idea of app which requires Apple subscriptions, feel free to contact us. We’ll help you translate them into reality.

Do you have any product idea or business need?



Offline mobile app development is critical for users to sync their data properly, when offline. Here, we help you learn the process from implementation to data synchroniz

Omnivore POS integration - Ensuring Agility To Your Restaurant Businesses

Omnivore POS integration - Ensuring Agility To Your Restaurant Businesses

Omnivore software offers a point-of-sales integration API making the restaurant system agile, more customer engaging and adaptive to fast changing business environments.

Unit Testing using Mockk.io

Unit Testing using mockK.io in Kotlin

Learn about unit testing using mockk.io in Kotlin.