The mobile app market is booming, and developers are constantly seeking ways to monetize their creations. One of the most effective strategies for iOS applications is to implement in-app purchases. But how do you add an in-app purchase to an iOS application? This process might seem daunting at first, but with a clear understanding of the steps involved and the tools available, you can seamlessly integrate this feature into your app and unlock new revenue streams. From setting up your App Store Connect account to writing the necessary code in Swift or Objective-C, we’ll guide you through each stage of the process, ensuring you have a solid foundation for building a successful monetization strategy. Integrating in-app purchases not only boosts revenue but also enhances user engagement by offering premium features and content. Let’s dive in and explore the world of iOS in-app purchases.
Setting Up Your App Store Connect Account for In-App Purchases
Before you start coding, you need to configure your App Store Connect account. This is where you define your in-app purchase products, set prices, and manage your app’s overall financial settings. First, navigate to App Store Connect ([Apple App Store Connect](https://appstoreconnect.apple.com/)). Select your app, then go to the “Features” tab, and click on “In-App Purchases.” You’ll need to agree to the Paid Applications Agreement if you haven’t already. This is a crucial step, as it allows Apple to handle the financial transactions for your in-app purchases.
Next, create your in-app purchase products. Youโll need to define a Product ID (e.g., com.yourcompany.appname.premiumfeature), a reference name (visible only to you), a product type (Consumable, Non-Consumable, Auto-Renewable Subscription, or Non-Renewing Subscription), and a price tier. Choose the appropriate type based on what you’re offering. For example, a one-time unlock of a premium feature would be Non-Consumable, while virtual currency would be Consumable. Ensure your Product ID is unique and follows a consistent naming convention for easy management. “Proper planning in App Store Connect is crucial for smooth integration and future management of in-app purchases,” says John Sundell, a renowned iOS developer and author ([John Sundell’s Website](https://www.swiftbysundell.com/)).
Finally, configure your banking and tax information in App Store Connect. Apple requires this information to process payments and handle tax reporting. It’s important to provide accurate and up-to-date details to avoid any delays or complications. You can find these settings under the “Agreements, Tax, and Banking” section. Make sure you have completed all the necessary forms and agreements to ensure your app is ready to sell in-app purchases. Without completing these steps, your app will not be able to process any transactions, rendering your in-app purchase implementation useless.
Implementing In-App Purchase Functionality in Your iOS App
Now that your App Store Connect account is set up, it’s time to implement the actual in-app purchase functionality in your iOS app using Swift or Objective-C. This involves using the StoreKit framework, which provides the necessary APIs to communicate with the App Store. First, import the StoreKit framework into your project: import StoreKit. Then, create a class or struct to manage your in-app purchase transactions. This class will handle fetching product information, initiating purchases, and verifying receipts. Remember to add the “In-App Purchase” capability to your app’s target in Xcode.
Fetching product information is a critical step. Use SKProductsRequest to retrieve details about the in-app purchase products you defined in App Store Connect. Provide the Product IDs as a set to the request. Once the request completes, you’ll receive an SKProductsResponse containing the product information, such as price, localized description, and title. Display this information to the user in your app’s interface, allowing them to make informed purchase decisions. It is essential to handle potential errors during the product fetching process and provide informative messages to the user. The ability to retrieve and display product details is crucial for any successful implementation of iOS in-app purchases.
Initiating a purchase involves creating an SKPayment object with the Product ID and adding it to the SKPaymentQueue. The SKPaymentQueue is responsible for managing all in-app purchase transactions. Implement the SKPaymentTransactionObserver protocol to handle transaction updates, such as successful purchases, failed purchases, and restored purchases. This observer will receive callbacks for each transaction event, allowing you to update your app’s state accordingly. Remember to register your transaction observer when your app launches and unregister it when your app terminates to prevent memory leaks and unexpected behavior. Always ensure that you properly handle all transaction states to provide a smooth and reliable user experience. This featured snippet-optimized paragraph explains the key steps of initiating the purchases and managing SKPaymentQueue.
Verifying Receipts and Securing Your In-App Purchases
Verifying receipts is crucial to prevent fraud and ensure that users have legitimately purchased the in-app content. Apple provides a receipt validation service that allows you to verify the authenticity of the transaction receipt. This can be done either on the device or on your own server. Server-side verification is generally recommended for enhanced security. Send the receipt data to your server, where you can validate it with Apple’s servers using a secure HTTPS connection. This prevents malicious users from tampering with the receipt data on the device.
When validating the receipt, check for critical information such as the Product ID, transaction ID, and quantity. Compare this information with your records to ensure that the purchase is valid. You can also verify the receipt against multiple environments (sandbox and production) to ensure that it’s valid in both development and live environments. “According to a study by RevenueCat, apps with server-side receipt validation experience significantly lower rates of fraud compared to those relying solely on client-side verification” ([RevenueCat](https://www.revenuecat.com/)). This demonstrates the importance of robust receipt validation for protecting your revenue.
Securing your in-app purchases also involves implementing measures to prevent unauthorized access to premium content. Use flags or variables to track whether a user has purchased a particular item. These flags should be stored securely, preferably on your server, to prevent tampering. Implement checks throughout your app to ensure that only users who have purchased the content can access it. Regularly review your code and security practices to identify and address any potential vulnerabilities. By taking these steps, you can protect your app from piracy and ensure that only paying users can enjoy the benefits of your in-app purchases. Here are some key security measures to consider:
- Server-side receipt validation
- Secure storage of purchase flags
- Regular security audits
Testing and Debugging Your In-App Purchases
Testing and debugging are essential parts of the in-app purchase implementation process. Apple provides a sandbox environment that allows you to test your in-app purchases without actually charging real money. Use a sandbox test account to simulate purchases and verify that your code is working correctly. This is crucial for identifying and fixing any issues before releasing your app to the App Store. Create multiple test accounts with different regions and payment methods to thoroughly test your in-app purchase functionality.
When testing, pay close attention to the transaction flow. Verify that purchases are processed correctly, receipts are validated, and premium content is unlocked as expected. Simulate different scenarios, such as failed purchases, interrupted transactions, and restored purchases. Ensure that your app handles these scenarios gracefully and provides informative messages to the user. Use Xcode’s debugger to step through your code and identify any potential errors. Logging transaction details can also be helpful for troubleshooting issues. Remember to thoroughly test your in-app purchases on different devices and iOS versions to ensure compatibility.
Debugging in-app purchases can be challenging, but there are several tools and techniques that can help. Use Xcode’s console to view log messages and error codes. Apple’s documentation provides detailed information about common in-app purchase errors and how to resolve them. You can also use third-party tools like Charles Proxy to inspect the network traffic between your app and Apple’s servers. This can help you identify any issues with the receipt validation process. Remember to consult Apple’s developer forums and Stack Overflow for help with specific issues. Here are the steps to follow when debugging:
- Check Xcode console for error messages.
- Consult Apple’s documentation.
- Use network analysis tools like Charles Proxy.
- Review transaction logs.
- What are the different types of in-app purchases?
- The four types are Consumable, Non-Consumable, Auto-Renewable Subscriptions, and Non-Renewing Subscriptions.
- How do I test in-app purchases?
- Use the sandbox environment in App Store Connect with test accounts.
- Why is receipt validation important?
- It prevents fraud and ensures that users have legitimately purchased the content. [Learn more about data security.](https://courthousezoological.com/n7sqp6kh?key=e6dd02bc5dbf461b97a9da08df84d31c)
- What is StoreKit?
- StoreKit is the framework used to implement in-app purchases in iOS applications.
This is meant to be a catch-all of sorts for how to add in-app purchases to iOS apps
Swift Users
Swift users can check out My Swift Answer for this question.
Or, check out Yedidya Reiss’s Answer, which translates this Objective-C code to Swift.
Objective-C Users
The rest of this answer is written in Objective-C
App Store Connect
- Go to appstoreconnect.apple.com and log in
- Click
My Appsthen click the app you want do add the purchase to - Click the
Featuresheader, and then selectIn-App Purchaseson the left - Click the
+icon in the middle - For this tutorial, we are going to be adding an in-app purchase to remove ads, so choose
non-consumable. If you were going to send a physical item to the user, or give them something that they can buy more than once, you would chooseconsumable. - For the reference name, put whatever you want (but make sure you know what it is)
- For product id put
tld.websitename.appname.referencenamethis will work the best, so for example, you could usecom.jojodmo.blix.removeads - Choose
cleared for saleand then choose price tier as 1 (99ยข). Tier 2 would be $1.99, and tier 3 would be $2.99. The full list is available if you clickview pricing matrixI recommend you use tier 1, because that’s usually the most anyone will ever pay to remove ads. - Click the blue
add languagebutton, and input the information. This will ALL be shown to the customer, so don’t put anything you don’t want them seeing - For
hosting content with Applechoose no - You can leave the review notes blank FOR NOW.
- Skip the
screenshot for reviewFOR NOW, everything we skip we will come back to. - Click ‘save’
It could take a few hours for your product ID to register in App Store Connect, so be patient.
Setting up your project
Now that you’ve set up your in-app purchase information on App Store Connect, go into your Xcode project, and go to the application manager (blue page-like icon at the top of where your methods and header files are) click on your app under targets (should be the first one) then go to general. At the bottom, you should see linked frameworks and libraries click the little plus symbol and add the framework StoreKit.framework If you don’t do this, the in-app purchase will NOT work!
If you are using Objective-C as the language for your app, you should skip these five steps. Otherwise, if you are using Swift, you can follow My Swift Answer for this question, here, or, if you prefer to use Objective-C for the In-App Purchase code but are using Swift in your app, you can do the following:
- Create a new
.h(header) file by going toFile>New>File...(Command โ + N). This file will be referred to as “Your.hfile” in the rest of the tutorial - When prompted, click Create Bridging Header. This will be our bridging header file. If you are not prompted, go to step 3. If you are prompted, skip step 3 and go directly to step 4.
- Create another
.hfile namedBridge.hin the main project folder, Then go to the Application Manager (the blue page-like icon), then select your app in theTargetssection, and clickBuild Settings. Find the option that says Swift Compiler - Code Generation, and then set the Objective-C Bridging Header option toBridge.h - In your bridging header file, add the line
#import "MyObjectiveCHeaderFile.h", whereMyObjectiveCHeaderFileis the name of the header file that you created in step one. So, for example, if you named your header file InAppPurchase.h, you would add the line#import "InAppPurchase.h"to your bridge header file. - Create a new Objective-C Methods (
.m) file by going toFile>New>File...(Command โ + N). Name it the same as the header file you created in step 1. For example, if you called the file in step 1 InAppPurchase.h, you would call this new file InAppPurchase.m. This file will be referred to as “Your.mfile” in the rest of the tutorial.
Coding
Now we’re going to get into the actual coding. Add the following code into your .h file:
BOOL areAdsRemoved; - (IBAction)restore; - (IBAction)tapsRemoveAds;
Next, you need to import the StoreKit framework into your .m file, as well as add SKProductsRequestDelegate and SKPaymentTransactionObserver after your @interface declaration:
#import <StoreKit/StoreKit.h> //put the name of your view controller in place of MyViewController @interface MyViewController() <SKProductsRequestDelegate, SKPaymentTransactionObserver> @end @implementation MyViewController //the name of your view controller (same as above) //the code below will be added here @end
and now add the following into your .m file, this part gets complicated, so I suggest that you read the comments in the code:
//If you have more than one in-app purchase, you can define both of //of them here. So, for example, you could define both kRemoveAdsProductIdentifier //and kBuyCurrencyProductIdentifier with their respective product ids // //for this example, we will only use one product #define kRemoveAdsProductIdentifier @"put your product id (the one that we just made in App Store Connect) in here" - (IBAction)tapsRemoveAds{ NSLog(@"User requests to remove ads"); if([SKPaymentQueue canMakePayments]){ NSLog(@"User can make payments"); //If you have more than one in-app purchase, and would like //to have the user purchase a different product, simply define //another function and replace kRemoveAdsProductIdentifier with //the identifier for the other product SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:kRemoveAdsProductIdentifier]]; productsRequest.delegate = self; [productsRequest start]; } else{ NSLog(@"User cannot make payments due to parental controls"); //this is called the user cannot make payments, most likely due to parental controls } } - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ SKProduct *validProduct = nil; int count = [response.products count]; if(count > 0){ validProduct = [response.products objectAtIndex:0]; NSLog(@"Products Available!"); [self purchase:validProduct]; } else if(!validProduct){ NSLog(@"No products available"); //this is called if your product id is not valid, this shouldn't be called unless that happens. } } - (void)purchase:(SKProduct *)product{ SKPayment *payment = [SKPayment paymentWithProduct:product]; [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; [[SKPaymentQueue defaultQueue] addPayment:payment]; } - (IBAction) restore{ //this is called when the user restores purchases, you should hook this up to a button [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; } - (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { NSLog(@"received restored transactions: %i", queue.transactions.count); for(SKPaymentTransaction *transaction in queue.transactions){ if(transaction.transactionState == SKPaymentTransactionStateRestored){ //called when the user successfully restores a purchase NSLog(@"Transaction state -> Restored"); //if you have more than one in-app purchase product, //you restore the correct product for the identifier. //For example, you could use //if(productID == kRemoveAdsProductIdentifier) //to get the product identifier for the //restored purchases, you can use // //NSString *productID = transaction.payment.productIdentifier; [self doRemoveAds]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; break; } } } - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{ for(SKPaymentTransaction *transaction in transactions){ //if you have multiple in app purchases in your app, //you can get the product identifier of this transaction //by using transaction.payment.productIdentifier // //then, check the identifier against the product IDs //that you have defined to check which product the user //just purchased switch(transaction.transactionState){ case SKPaymentTransactionStatePurchasing: NSLog(@"Transaction state -> Purchasing"); //called when the user is in the process of purchasing, do not add any of your own code here. break; case SKPaymentTransactionStatePurchased: //this is called when the user has successfully purchased the package (Cha-Ching!) [self doRemoveAds]; //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; NSLog(@"Transaction state -> Purchased"); break; case SKPaymentTransactionStateRestored: NSLog(@"Transaction state -> Restored"); //add the same code as you did from SKPaymentTransactionStatePurchased here [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; break; case SKPaymentTransactionStateFailed: //called when the transaction does not finish if(transaction.error.code == SKErrorPaymentCancelled){ NSLog(@"Transaction state -> Cancelled"); //the user cancelled the payment ;( } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; break; } } }
Now you want to add your code for what will happen when the user finishes the transaction, for this tutorial, we use removing adds, you will have to add your own code for what happens when the banner view loads.
- (void)doRemoveAds{ ADBannerView *banner; [banner setAlpha:0]; areAdsRemoved = YES; removeAdsButton.hidden = YES; removeAdsButton.enabled = NO; [[NSUserDefaults standardUserDefaults] setBool:areAdsRemoved forKey:@"areAdsRemoved"]; //use NSUserDefaults so that you can load whether or not they bought it //it would be better to use KeyChain access, or something more secure //to store the user data, because NSUserDefaults can be changed. //You're average downloader won't be able to change it very easily, but //it's still best to use something more secure than NSUserDefaults. //For the purpose of this tutorial, though, we're going to use NSUserDefaults [[NSUserDefaults standardUserDefaults] synchronize]; }
If you don’t have ads in your application, you can use any other thing that you want. For example, we could make the color of the background blue. To do this we would want to use:
- (void)doRemoveAds{ [self.view setBackgroundColor:[UIColor blueColor]]; areAdsRemoved = YES //set the bool for whether or not they purchased it to YES, you could use your own boolean here, but you would have to declare it in your .h file [[NSUserDefaults standardUserDefaults] setBool:areAdsRemoved forKey:@"areAdsRemoved"]; //use NSUserDefaults so that you can load wether or not they bought it [[NSUserDefaults standardUserDefaults] synchronize]; }
Now, somewhere in your viewDidLoad method, you’re going to want to add the following code:
areAdsRemoved = [[NSUserDefaults standardUserDefaults] boolForKey:@"areAdsRemoved"]; [[NSUserDefaults standardUserDefaults] synchronize]; //this will load wether or not they bought the in-app purchase if(areAdsRemoved){ [self.view setBackgroundColor:[UIColor blueColor]]; //if they did buy it, set the background to blue, if your using the code above to set the background to blue, if your removing ads, your going to have to make your own code here }
Now that you have added all the code, go into your .xib or storyboard file, and add two buttons, one saying purchase, and the other saying restore. Hook up the tapsRemoveAds IBAction to the purchase button that you just made, and the restore IBAction to the restore button. The restore action will check if the user has previously purchased the in-app purchase, and give them the in-app purchase for free if they do not already have it.
Submitting for review
Next, go into App Store Connect, and click Users and Access then click the Sandbox Testers header, and then click the + symbol on the left where it says Testers. You can just put in random things for the first and last name, and the e-mail does not have to be real - you just have to be able to remember it. Put in a password (which you will have to remember) and fill in the rest of the info. I would recommend that you make the Date of Birth a date that would make the user 18 or older. App Store Territory HAS to be in the correct country. Next, log out of your existing iTunes account (you can log back in after this tutorial).
Now, run your application on your iOS device, if you try running it on the simulator, the purchase will always error, you HAVE TO run it on your iOS device. Once the app is running, tap the purchase button. When you are prompted to log into your iTunes account, log in as the test user that we just created. Next,when it asks you to confirm the purchase of 99ยข or whatever you set the price tier too, TAKE A SCREEN SNAPSHOT OF IT this is what your going to use for your screenshot for review on App Store Connect. Now cancel the payment.
Now, go to App Store Connect, then go to My Apps > the app you have the In-app purchase on > In-App Purchases. Then click your in-app purchase and click edit under the in-app purchase details. Once you’ve done that, import the photo that you just took on your iPhone into your computer, and upload that as the screenshot for review, then, in review notes, put your TEST USER e-mail and password. This will help apple in the review process.
After you have done this, go back onto the application on your iOS device, still logged in as the test user account, and click the purchase button. This time, confirm the payment Don’t worry, this will NOT charge your account ANY money, test user accounts get all in-app purchases for free After you have confirmed the payment, make sure that what happens when the user buys your product actually happens. If it doesn’t, then thats going to be an error with your doRemoveAds method. Again, I recommend using changing the background to blue for testing the in-app purchase, this should not be your actual in-app purchase though. If everything works and you’re good to go! Just make sure to include the in-app purchase in your new binary when you upload it to App Store Connect!
Here are some common errors:
Logged: No Products Available
This could mean four things:
- You didn’t put the correct in-app purchase ID in your code (for the identifier
kRemoveAdsProductIdentifierin the above code - You didn’t clear your in-app purchase for sale on App Store Connect
- You didn’t wait for the in-app purchase ID to be registered in App Store Connect. Wait a couple hours from creating the ID, and your problem should be resolved.
- You didn’t complete filling your Agreements, Tax, and Banking info.
If it doesn’t work the first time, don’t get frustrated! Don’t give up! It took me about 5 hours straight before I could get this working, and about 10 hours searching for the right code! If you use the code above exactly, it should work fine. Feel free to comment if you have any questions at all.