In-App Purchases in iOS. Part 1: Creating purchases and adding them to the project

Sergey Zhuravel
14 min readNov 22, 2022

--

Hi everyone! My name is Sergey. I’ve been working as iOS Team Lead at Futurra Group for more than six years.

I work with In-App Purchases (or IAP) every day, and over the years, I have gained experience in managing IAP correctly and effectively. In this guide, which consists of several articles, I will showcase how to add purchases to the app without unnecessary problems, starting with the purchase configuration in the App Store Connect and ending with processing purchases in the app with source code examples. Unfortunately, Apple does not give us step-by-step guides. Therefore, many have difficulties with IAP implementation. I want to fix this situation.

Links to all my articles about IAP:

  1. In-App Purchases in iOS. Part 1: Creating purchases and adding them to the project.
  2. In-App Purchases in iOS. Part 2: Initialization and processing of purchases.
  3. In-App Purchases in iOS. Part 3: Testing purchases in TestFlight, Sandbox and locally in Xcode.
  4. In-App Purchases in iOS. Part 4: Receipt validation.

An IAP allows developers to charge users for specific functionality or content while using an app. Implementing IAPs is particularly compelling for several reasons:

  • It’s an extra way to earn money, in addition to simply selling the app for a fee upfront. Some users want to spend a lot more on additional content or features.
  • An app can be offered for free, which makes it a no-brainer download for most people. Free apps will typically get many more downloads than paid apps. If users enjoy your app, they can purchase more content or functionality later.
  • Following the initial release of an app, you can add new paid content to the same app instead of developing a brand-new app to earn more money.
  • You can display advertisements to the user in a free app with an option to remove them by purchasing an IAP.

Implementing IAP requires the application to utilize the StoreKit API on the device. StoreKit manages all communication with Apple’s iTunes servers to get product information and perform transactions. The provisioning profile must be configured for IAP and product information must be entered in App Store Connect.

In-App Purchase Rules

You cannot accept other forms of payment for digital products or services inside your app, nor mention them or refer your users to them from within an app. It means you cannot accept credit cards or PayPal when IAP is the most appropriate payment mechanism. There is a special case for purchasing digital products outside the app but for use in the app, such as purchasing books on a website associated with a specific “login” and using that “login” in the app lets the user access the purchased books. Applications that operate this way are not allowed to mention or link to the external purchasing feature — developers must communicate this capability to their users in other ways (perhaps via email marketing or some other direct channel).

However, since you can’t use IAP for physical goods, in that case, you are allowed to use an alternate payment mechanism (e.g. credit card, PayPal) from within the app.

Apple must approve every product before it goes on sale — you must provide the name, description, and screenshot of the ‘product’ for review. Product review times are the same as application reviews.

You cannot choose any price for your product — you may only select a ‘price tier’ with a specific value in each country/currency that Apple supports. You cannot have a different price tier in different markets.

Checking Your Agreements

Before creating IAP and offering them in your app, you must do two things:

If you have not done this, usually App Store Connect gives you a warning like the following:

If you see something like the above, follow the steps to accept the appropriate agreements.

It’s also good to double-check the Agreements, Tax, and Banking section in App Store Connect:

If you see a section entitled Request Contracts containing a row for Paid Applications, then click the Request button. Fill out all the necessary information and submit it. It may take some time for your request to be approved.

Otherwise, if you see Paid Applications listed under Contracts In Effect, it looks like you’ve already done this step! Good job!

Note: Apple can take a few days to approve these IAP-related agreements after you submit them. During this time, you won’t be able to display IAP products in your apps even if you implement everything correctly in the code. It is a common source of frustration for folks implementing IAP for the first time.

Creating purchases in App Store Connect

In this part, we’ll create the IAP that your app will offer. Before making a product, let’s find out what types of IAP are available.

There are four types of IAP, and you can offer multiple types within your app.

  • Consumables are depleted after one use. Customers can purchase them multiple times.
  • Non-consumables are a type that customers purchase once. They don’t expire.
  • Auto-renewable subscriptions to services or content are a type that customers purchase once and that renew automatically regularly until customers decide to cancel.
  • Non-renewing subscriptions to services or content provide access over a limited duration and don’t renew automatically. Customers can purchase them again.

Create a subscription product:

  1. From My Apps, select your app.
  2. In the sidebar under Features, click Subscriptions.
  3. Before creating a product, you must first create a Subscription Group. Click the Create button in the Auto-Renewable Subscriptions section.
Subscription Group creation interface
Set name for Subscription Group

A Subscription Group is a set of Subscriptions that you can create to provide users with a range of content offerings, service levels, or durations.

Subscriptions within a Subscription Group are mutually exclusive, meaning that users are only able to subscribe to one option within a group at a time. If you want users to be able to purchase more than one subscription at a time, you can put these in-app purchases in different subscription groups. Besides, all the introductory offers such as trial subscriptions are applied directly to the entire group. Groups serve to separate business logic in the application.

4. Go to the Subscription Group you created and click on the Create button in the Subscriptions section to create your first product.

Subscription product creation interface

Add your product reference name and product ID, then click Create.

  • Reference Name: It’s the name of the Subscription product on the App Store Connect, but it’s for internal use only. It won’t be shown to the users, so don’t worry too much about the value you will provide here. However, give a name that makes clear what this in-app purchase regards.
  • Product ID: This must be a unique string (alphanumeric, as Apple says) that will be used for reporting, but here’s a recommendation: Use the app’s bundle identifier as a prefix to the ID value you will specify here. That way, you ensure that it’ll always be unique. In our case, “com.testapp.year” (without quotes) is a unique product ID.
Set name and product id for a Subscription product

Add the second product as described above.

Go to the Features tabSubscriptions Your Subscription Group should look like this:

a list of your Subscription products in the App Store Connect

Product Configuration

At this point, we have added two products, but they are not ready for use yet. The status of the products is Missing Metadata because we have not added information about the price and duration of the subscription. Let’s fix this.

Click on the product to configure it.

Setting the subscription duration and price

Here we need to select Subscription Duration. In our case, choose 1 Year. To select a price, click Add Subscription Price in the Subscription Prices section.

You can set the price depending on the country, but we will limit ourselves to automatic prices in USD. App Store Connect automatically converts prices to other currencies based on the exchange rates. You can manually change the price for a specific country, adjusting to your target market.

Setting prices for the product

We have set the duration of the subscription and the price, but the product status has not changed. Now we need to set the name of the product that the user will see and indicate the review information.

Add App Store Localization for Subscription product
Set name and description
  • Display Name: The name of the product that will be visible on the App Store.
  • Description: Depending on your product, this description may also be visible to your customers.
  • Review Information: According to Apple guidelines, you must upload a screenshot of the product page. But I usually upload the application icon in the resolution of 1024×768. In my case, it always worked :)
Set Review Information

Press the Save button, and the product status remains Missing Metadata. To change it, we need to make the last step. Go to the Features tabSubscriptions Your Subscription Group. We need to set the group name that customers will see.

Set App Store Localization for Subscription Group
Set name for Subscription Group

Click on the Create button, and if you have done everything correctly, the status of your products should change to Ready to Submit.

Free trial period

You can set up introductory offers for your apps that contain auto-renewable subscriptions in App Store Connect. An introductory offer is a limited-time discounted price or free trial for the initial period of a subscription.

The most popular way to increase subscriptions is a free trial:

A customer activates it and uses an application for free. If he/she doesn’t cancel the subscription, after the trial expires, the customer is being charged automatically.
The developers love free trials because such subscriptions have a good conversion. Let’s add to one of our subscriptions.

Click (+) in the Subscription Prices section and choose to Create Introductory Offer.

Select the countries where the trial period will be available. I always select All.

Choosing countries for the trial period.

In the next step, we need to pick the dates on which the trial period will be available. We select No End Date so that the trial period is available for customers without date limitations.

We select the dates on which the trial period will be available.

In the last step, we need to select Offer Type. There are three types of introductory pricing that you can offer per subscription, per territory:

  • Pay as you go (SKProductDiscount.PaymentMode.payAsYouGo): New subscribers will pay a discounted price each billing period for a specific duration. This can attract new users who are interested in your app but need that extra push to pay the normal, higher price for each subscription period.
    For example
    , you can offer a discounted price of $1.99 per month for three months with a standard subscription price of $3.99 per month starting in the fourth month.
  • Pay upfront (SKProductDiscount.PaymentMode.payUpFront): New subscribers will pay a one-time introductory price for a specific duration. This is useful when you believe the user may need longer to get hooked on the content your app provides.
    For example
    , if you want to offer a monthly subscription, but you think users need about six months to get used to the experience and become more likely to keep their subscription, you can offer a six-month introductory price of $9.99, followed by a standard price of $3.99 per month starting in the seventh month.
    Note that you don’t have to use the same length of time for the introductory price and the regular subscription. So, for example, you can offer an introductory price for a six-month period followed by a yearly subscription.
  • Free trial (SKProductDiscount.PaymentMode.freeTrial): New subscribers will get access to your app’s content for free for a specific duration. The subscription begins immediately, and the first billing occurs once the free trial period is over.
    This gives your users the option to cancel before the first billing takes place. If you feel confident about the content your app provides but have difficulty convincing potential subscribers it’s worth their money, this is a low-risk option that lets them decide.

No matter which type you choose to use, once the introductory period is over, the subscription will renew at the regular price.

In our case, we choose Free and set the duration of one week.

Set duration and type of trial period

Click Confirm to finish. That’s it! You’ve just set up an introductory price for your auto-renewable subscription.

Determining User Eligibility

Before displaying the discounted price to your users, you have to make sure that a specific, current user is eligible for the discounted price.

How will you know if the user is eligible? Great question!

Review the user’s past transactions to see if they’ve already used a discount from the same subscription group in the past. If so, the user isn’t eligible for a discounted price.

The fields you need in the JSON response from the App Store server are: latest_receipt_info, whose value is an array containing all in-app purchase transactions, and the is_trial_period and is_in_intro_offer_period fields in the receipt field for each relevant transaction.

To determine a user’s eligibility, check the values of the Subscription Trial Period and the Subscription Introductory Price Period for all IAP transactions. If the value for either of these fields is true for a given subscription, the user isn’t eligible for an introductory price for any of the products within the same subscription group.

New users are always eligible for the offered introductory price while lapsed subscribers — those who had a subscription in the past — are only eligible for the introductory price if they haven’t already used such an offer on this subscription or other products from the same subscription group.

Introductory Pricing Best Practices

To add Introductory Pricing to an app that already has auto-renewable subscriptions, you don’t have to make any changes to the code. You can just make the relevant changes in App Store Connect and let Apple take care of the rest. However, if you really want to make the most of this great new way of enticing users into subscriptions, you might want to make some UI/UX changes to make sure potential buyers are aware of the introductory price your app offers.

Apple Human Interface Guidelines about In-App Purchase point this out specifically: “When offering an introductory price, indicate the introductory price, the duration of the offer, and the standard price the user pays after the offer ends.”

Consider these points when adding introductory prices to apps already live in the App Store:

  • Make sure your users are aware of the offering. Display the discounted price in a clear and appealing way so users know exactly what they’re purchasing. You can get all of the details you need in the new property added to SKProduct called introductoryPrice (of type SKProductDiscount). This data includes price, priceLocale, paymentMode, subscriptionPeriod, and numberOfPeriods.
  • Don’t promise your users an introductory price that they find out they’re ineligible for after they’ve already started the purchase process. Determine eligibility as early as possible in order to match the display to the user’s specific case.
  • Introductory prices for promoted IAPs also appear on the App Store. When deciding how to use this feature, remember that potential users can see the discount before they install your app. When used properly, introductory prices can attract new users to install your app and convert existing users into new subscribers.
  • Users can now easily view and manage their subscriptions via this link apps.apple.com/account/subscriptions. There, they can see their subscriptions and switch between different options in a subscription group. It’s important to check how your IAP offerings look there and to direct your users there when relevant.
  • Finally, Apple added a new subscription retention dashboard in App Store Connect ▸ App Analytics. Once your app is live, keep an eye on the retention dashboard for trends. Notice how introductory prices improve user acquisition and use this data to make decisions about your app’s future offerings and business model.

Start Implementing The In-App Purchases. Download SKProduct’s list.

Let’s focus on implementing the reusable class now that will manage IAPs in our app. It’s a good rule to create a class singleton for working with StoreKit. Such a class has only one instance in the entire app.

Our first task is to update IAPManager to retrieve a list of IAPs — from Apple’s servers. Let’s declare the new class with the same name as the file: IAPManager. Leave a couple of empty lines and add this:

class IAPManager: NSObject {
static let shared = IAPManager()
}

Next, we need to add a few variables. The constants of our products ID and productsRequest who will be responsible for product downloads.

let PREMIUM_MONTH_PRODUCT_ID = “com.testapp.month”
let PREMIUM_YEAR_PRODUCT_ID = “com.testapp.year”
fileprivate var productsRequest = SKProductsRequest()

Let’s add a method in which we will create a SKProductsRequest for receiving SKProduct’s.

func fetchAvailableProducts() {
productsRequest.cancel()
// Put here your IAP Products ID’s
let productIdentifiers = NSSet(objects: PREMIUM_MONTH_PRODUCT_ID, PREMIUM_YEAR_PRODUCT_ID)
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
}

Now, it’s mandatory to adopt the SKProductsRequestDelegate protocol and implement at least one required method. Go after the closing curly bracket of the IAPManager class, and add the extension where we’ll implement the following method, which gets called when the App Store sends back a response to the original request:

extension IAPManager: SKProductsRequestDelegate {
// MARK: — REQUEST IAP PRODUCTS
func productsRequest (_ request:SKProductsRequest, didReceive
response:SKProductsResponse) {
if response.products.count > 0 {
iapProducts = response.products
for product in iapProducts {
print("productIdentifier - ",
product.productIdentifier)
}
}
}
func request(_ request: SKRequest, didFailWithError error:
Error) {
print(“Error load products”, error)
}
}

Note: The response object provides a property called invalidProductIdentifiers. It’s a collection of identifiers regarding products that are not valid to be purchased. Although we do not use it here, keep it in mind in case you ever need it.

If you did everything correctly, a list of products ID will be displayed in the log.

Below is the entire source code of the IAPManager class.

In the next article, we will make a simple paywall, and I will talk about the initialization and processing of purchases.

Contact me: My Twitter
Full source code: GitHub

--

--

Sergey Zhuravel

iOS Engineer @ Futurra Group | Reverse-Engineering iOS Apps