Mobile Development 15 min read

In‑App Purchase (IAP) Overview and Implementation Guide for iOS

The guide explains Apple’s four in‑app purchase types, how to create and configure products in App Store Connect, implements the StoreKit purchase flow (including product request, payment, transaction observation, and receipt verification on server), addresses common pitfalls, and introduces NetEase’s NEStoreKit wrapper for easier integration.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
In‑App Purchase (IAP) Overview and Implementation Guide for iOS

IAP Product Types

When integrating Apple In‑App Purchase (IAP), developers must choose the appropriate product type according to the functionality offered. Apple defines four categories:

Consumable products – e.g., virtual coins. The same Apple ID can purchase repeatedly; each purchase is used up.

Non‑consumable products – e.g., unlocking a feature. Purchase is allowed only once per Apple ID and remains permanently available.

Auto‑renewable subscriptions – e.g., monthly music membership. The system checks renewal status; if the user cancels, a new purchase is possible.

Non‑renewable subscriptions – e.g., quarterly or yearly membership. Purchasable multiple times, but each purchase is limited by its own time period.

Creating and Managing IAP Products

After selecting a product type, create the item in App Store Connect. Required fields include:

Product identifier – unique within the app.

Price – must use Apple’s price tiers.

Localized description.

Screenshot & operation path (required for review).

Refer to Apple’s “Create In‑App Purchases” guide for detailed steps.

Implementing IAP Purchase on iOS

Developers use the StoreKit framework. Although StoreKit 2 (iOS 15+) exists, many projects still rely on the legacy StoreKit for broader compatibility.

Core Flow

Request product information via SKProductsRequest . SKProductsRequest *productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:self.productIdentifier]]; productRequest.delegate = self; [productRequest start];

Initiate payment. SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:self.product]; payment.quantity = MAX(_quantity,1); payment.applicationUsername = self.userIdentifier; [[SKPaymentQueue defaultQueue] addPayment:payment];

Observe transaction updates. [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: /* purchase completed */ break; case SKPaymentTransactionStateFailed: /* failed */ break; case SKPaymentTransactionStateRestored: /* restored */ break; case SKPaymentTransactionStatePurchasing: /* in progress */ break; default: break; } } }

Retrieve the receipt and send it to the server for verification. NSData *receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]; // send receiptData to backend [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

Server‑Side Receipt Verification

The backend posts the receipt to Apple’s verification endpoint:

Sandbox: https://sandbox.itunes.apple.com/verifyReceipt

Production: https://buy.itunes.apple.com/verifyReceipt

Request body (JSON):

{
  "receipt-data": "
",
  "password": "
" // optional for auto‑renewable subscriptions
}

Apple returns a JSON response containing fields such as environment , status , receipt , latest_receipt_info , and pending_renewal_info . The server must parse these fields, verify bundle_id , match product_id and timestamps with internal orders, and handle refunds or trial periods accordingly.

Common Issues and Mitigations

Failure to fetch product information – usually network‑related; cache product data after a successful fetch.

Receipt verification timeout – use overseas proxy or retry logic.

Matching Apple transaction with internal order – store productIdentifier and a local order ID; if missing, back‑track using receipt data.

Charge recorded by Apple but entitlement not granted – persist receipt locally and retry verification; provide a manual “Refresh receipt” UI; improve logging.

Auto‑renewal handling – receive Server‑to‑Server notifications or client‑side callbacks; optionally poll old receipts before renewal.

NEStoreKit Library

To address the above pain points, NetEase Cloud Music built NEStoreKit , a wrapper that abstracts the IAP flow, queues tasks, and retries verification automatically.

Typical usage:

// configure
NEStoreConfig *storeConfig = [NEStoreConfig new];
storeConfig.verifyRequestUrl = @
iOSSwiftObjective-CIn-App PurchaseReceipt VerificationStoreKit
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.