 In the upcoming half hour, you will learn a lot about in-app purchases. These are payment products that are mandatory if you are trying to sell virtual products of services on the App Store. If you are looking to sell your own products on the App Store, or maybe your employer is looking for a trusted business model, then this will be most relevant to you. I'll provide an overview of the types of in-app purchases, how the API workflow can be, how an app can manage its in-app purchases inventory, some case studies on in-app purchases, and of course the obligatory code demo near the end of the talk. A short intro about me. With ACP from 2013 to 2016, I worked on their smart cities efforts and their indoor defecation projects. With NCS, I delivered SingTel's short-lived price call app, if you heard of it, that was made up with a good team from China. I also have been developing my own products for Mac, iOS, and TBRS as a product side job. For more of my profile, you can connect to me on LinkedIn or send a QR code to get my full profile. Yes, shortly, what in-app purchases are? These are non-contruable, non-renewing, outdoor renewing, and subscription. Non-contruable, just like book. Probably the most common in-app purchase that we have right now. It boggles on, meaning that it goes from disabled to enabled, and that's it. Similar to like buying a book. One interesting use of in-app purchases is that you can use it for trial periods. We'll talk more about this later on. You can also provide it for upgrade discount, meaning that if an older version of your app is tall, you provide this in-app purchase for upgrade. Or if a competitor's app is tall, you can also provide a discount upgrade using in-app purchase. Non-contruable data are present in the receipt. The receipt file is a file that is scripted definitely signed by Apple, so you can verify its validity. It contains all transactions including non-contruable books. So, non-renewing subscription, just like a tourist pass. You can buy multiple times, and it expires after a certain period of time. It is meant for purchases that are maintained manually, meaning that users keep on buying and buying and buying themselves. The duration is typically hard-to-get in the app, so the app leaves them though. This purchase is for one week. This purchase is for three months, that's why it's over. The feature can foggle on or foggle off, meaning that once we reach sufficient last, you need to foggle off. And you also can get a history of purchases in the receipt file. Take note that Apple review will probably need you to have this sentence in your UI when you are offering in-app purchase for subscription. Okay, auto-renewing subscription. This is the holy grail of SaaS software as a service. Apple would keep on rebuilding the user until the user cancels. And guess what? The user needs to cancel from iTunes and not from your app. So you have an excuse to not provide the UI on your app. The receipt will contain the in-app purchase as well, the receipt file. And take note of these legalities that you need to put in your app when you do a subscription purchase. Auto-renewing subscription supports subscription groups, meaning that a group that can only one be active at the time. And these are typically different durations of the same product. You can configure the group in... okay, consumer world. Consumer world in-app purchases are often found in games. The purchase can be consumed by the user. Take note that the receipt data is not present. The receipt will not have consumer purchases. But your app will need to have pre-stop purchase as well. If the user installs on another device, users will expect to have pre-stop purchase as well. And therefore, you need to have a back-end to keep track of your purchases. Here's a summary of in-app purchase types. Non-consumer world can be bought only once. Consumer world would need to reside on your back-end. These in-app purchases have their own validity period. Non-consumer world should always be valid, but consumer world will degrade as the app is being used. Now, this section will discuss a sequence of calls that you need to do when you are implementing in-app purchases. This will be listing products, making a purchase, restoring purchases, and validating purchases. In a typical in-app purchase store, you probably need to have a list of products that the user can buy. So, how do you list? First, the app is an SKProductRequest object and gives it the collection of product IDs to receive. These product IDs are typically hard-coded in the app. Since you need to maintain a relationship between the product ID and the features that product ID enables. Then, set the delegate. After that, call start. This will invoke a confirmation dialog, system-confined dialog, and along with some network calls to Apple. After a while, you get a callback, product request is received. You will get the localized title, localized description, and localized price. Take note, do not keep this data inside your app. Why? Exchange phase. Exchange phase, change every time. The price needs to be displayed in the user's currency, not in your app store developer ID currency. That's why, along with the price, the title, and the description as well, these are maintained inside the app store. Now, making a purchase. This is the workflow that your app does when your user selects when they are purchased. They click buy button. To ask the app store for the user's money, and to enable the features that the user has bought. First, you call add transaction observer, as usual, to the object, giving it the callback object that will receive the transaction program. Then, you create an escape payment object. Just give it the product ID that the user has purchased. After that, how? They work. You wait. You wait. But after you add payment, you wait. And as the user buys the product, maybe enter their password, and confirm it with conversion. This is the time where to send this various subscription dialog. Afterwards, you get a callback. Payment queue, updated transactions. And you respond by enabling your features. By enabling your features and updating in your inventory. Meaning that users are in so have bought this product and have, I have to enable these features. Now, if the user installs the app, or install it on another device, here she would expect the pure purchases to be enabled as well, right? This is the restore purchase flow. Apple's support for restoring purchases works for almost all in a purchase except consumable. You need to provide these services for consumers from your own back end. How do you do it? Now, this flow is for the other in a purchase. The non-consumable one. First, as before, you add a transaction observer to the escape payment queue. Then call restore completed transactions. Which will directly make a network request to the app store. Then, you will get a callback payment queue after the transaction. You should respond by updating the in-memory state of the app. Enabling or disabling features as required. Remember that your callback may be called multiple times in a loop. So just be aware of it. At the end of the loop, you will get payment queue restored for completed transactions. This is a good time to stop the spinners and enable the UI. Finally, you shouldn't call restore protected data unless the user asks for it. Because that will invoke a login. That will get a complete list of Apple IDs. If done without a user consent, that is suspicious as best and inconvenient for the user. Therefore, at startup, you will need to verify your receipt yourself. How do you do this? First, call NS bundle. Where's the receipt? App store receipt URL. You will get a receipt to a local file containing the cryptographically signed receipt file containing a history of purchases. Everything but consumable. Then what do you do? You read it, you validate it, and you update your own inventory. Take note that there's no API for this. Each developer will need to come up with their own unique implementation of reading receipts, validating receipts, and updating inventory. Why? This is to prevent widespread tracking. Meaning, as a cracker, you need to crack all app store apps. By having developers having their own unique implementation, it reduces the possibility of having one app to crack everything else. See. Now, data model classes. The main goal of these classes is to be the single point of contact to answer things like, have the user purchase X. How much balance for product type Y? And each feature X enable if you have more fit when you want fit for product. This is not the type of in-app purchases, but the inventory of the purchases that user has for an app. This is the crack class hierarchy. I came up with this class hierarchy when I was developing my own product. It became complete because I had to switch through a number of in-app purchase types. I started at subscription, the auto renewing subscription, and that was rejected. Then I moved to non-renewing subscription. That was rejected as well. Then I go to be non-consumable, which finally got accepted. It was a long journey and fortunately at now I have this class hierarchy which you can reuse in your app as well. Let's start with the product inventory item. This holds the common attribute for all product inventories. Remember, this is the inventory of purchases that the user has made. The most important one will be product identifier, which contains the product ID that matches with the one in the app store. And the last transaction date may be just for display purposes usually. Next, we have a toggle product inventory item. This is the base class for 75% of all in-app purchase types. It goes from disable to enable or disable. Again, back and forth. That's why it's called toggle. The non-consumable product inventory item is the most common toggle product inventory item. It goes from disable to enable and that's it. It doesn't add much method beyond the one that's provided by the toggle product inventory item, but you need to have these things perfect and light. Also, if you implement a $0 in-app purchase trial, you probably need to subclass this class as well for in-app purchases that expires after a few weeks being purchased. More on that later on. Subcussion product inventory item can go from disable to enable and back again. That's why there's a base class for it. The most important thing is the expiry date. And it has a group which groups the product that a product inventory item may have. This usually if you offer multiple tiers of the same product, you put it in a group. Next, the non-renewing product inventory item. It has a subscription duration. Why? Non-renewing product subscriptions need to have the duration inside the app, hard coded inside the app, because the app store does not maintain it for you. However, an outdoor renewing subscription has the product duration maintained in the app store. Different things, remember? Non-renewing, you maintain the duration yourself. Outdoor renewing, you maintain the duration in the app store. And finally, consumable product inventory items. This is like gasoline or coins or credits that you put. Remember that you need to keep the number of purchase items inside your server and then communicate regularly to reduce the deduct that amount that the user have bought. Now, some business cases that are maybe common, maybe not common in nepoticious. We'll discuss about pre-trial for the sale and watch OS add-on. So, according to section 312a of the app review guide, you can provide a trial period now. This was a many people's struggle and negotiation effort. So, you really need to be thankful to them. It's implemented by a pair of non-curchable in-app purchases, whereas one is the $0 trial in-app purchase. There's a non-curchable for $0. And the other one is the one with pricing on it. The happy flow will go like this. User will download the app. Then user will buy the $0 in-app purchase. This will unlock features of the app. And when the user likes it, the user will buy the feature which buys the other in-app purchase and unlocks the feature indefinitely. But if the user doesn't buy it, then the $0 in-app purchase will expire by itself. How do you do this? You look at the purchase date plus some weeks, and that will be the trial period. Remember that all in-app purchase site has a purchase date. But the app needs to be useful without a purchase. Say as a viewer for its old file types, which is a document app. One example that does this is Omnigrafo, which is a technical search business drawing application. You can use it as a viewer for its own file types without purchasing. Or you can buy the $14 in-app purchase. They also implement an upgrade mechanism this way. If you have an older feature of the app, you can upgrade it as a newer feature for a cheaper price. Now, there is a question on Apple's developer forum asking, how can you have an in-app purchase that's available sometime in the future, only for a limited time. But if the user bought it during that time, it will be available forever. And you can take a guess. If an in-app purchase can be made available in the future, if the user bought it in the future, then it will be available for the rest of the lifetime. But it's only available at that time. So, can you take a guess how to do this? Take one. 25. Holiday special. Holiday special? Yeah, this is a holiday special in-app purchase. How do you do this? Is that a holiday special toggle? Okay. How do you do that? Let's say today is September. You want it available for holiday special in December. The answer is to have a non-futurable product that's only visible during that time only. And if the user bought it, that is there. If it passed that period and it's gone. But you also need to make it available during app review as well. Remember that if the app reviewer doesn't see your in-app purchase, they won't like to prove it. So, you control the visibility by having the in-app purchase available during app review and during the holiday period. You can control that using timing or using a switch on your back end. Now, watch OS app on. How do you sell a watch OS app? Of course, watch OS 6, you can sell it without an iOS app. But what if the watch OS app is an add-on to the iOS app and really needs the iOS app? You need to make sure that the user, when the user buys the in-app purchase, the user only have a watch OS attached to the iPhone itself. Remember that not all watches can be attached to iOS, such as the iPad or the Apple Touch. They cannot be attached to our watch OS. How do you make sure? You need to make sure that the app is installed before you display the in-app purchase. This is how you do it. First, the iOS install to watch OS app. And then the watch OS app notify the iOS app that it is installed. Right? When it is installed, you only when you get a signal from the watch OS app, you say, yes, show this in-app purchase. And after that, when the in-app purchase is purchased, you signal back to the watch OS app, enable yourself. Okay, now for the code demo. We're going to explore the contents of a receipt file. This receipt file contains a history of in-app purchases and is technically signed by Apple, so that you can verify that it's genuine. For this demo, we are going to look at the copy of a receipt and genuine one obtained from one of my apps during development. I'll be using a TP in-app receipt library that you can get from CocoaPod for really receipt files. Now, let's go to Playground. Okay, this is the receipt file that I already have installed. That's why I will read the receipt. When this is genuine, you will get the receipt URL from NSBando, not from the in-app receipt URL bundle. And you see the file. When you read the file, just run the code. Okay, now we can get the data. You can get the data file. So this is the in-app app that was used to generate the receipt. And these are the purchases. I was testing the subscription in-app purchase here. So you can see this is product ID. Product ID. And you have the transaction ID here. The original transaction ID here. Remember that for subscription, every transaction has an ID. But the first one will have the original transaction ID. You have the purchase date. You have the original purchase date, which is the starting one. And the expiration date of the subscription. Only in line of ID. Okay. Now, summary of in-app purchase type. Remember, non-consumerable can be bought only once. But it's available for eternity. For consumable, you don't have it on receipt. You need to keep it on your own back end. Just take note of these purchase types. And that's it what I have for now. This is my contact info if you'd like to connect with me. Any questions? Yes, non-consumerable. How to test for lose? How to test for lose? Because they don't inspire. So what I did that time was to buy it from the test line. It saved that while I was. And I found it there with that account. So I need to account every time I test. Yeah, that's one way. You could create a new product ID. Another way. Yeah, new product ID as well. But it would be a good practice if you keep your receipt file. And use that as unit test. That was very useful for testing for my point of view. But keep in mind that you need to remember that a receipt file will have a device ID in there. You'll have an ID of your device in there. So you need to disable the validation of the device ID when you run it in unit test. Otherwise it works on your laptop and someone else's laptop, it breaks. Or it moves to your CI and it breaks. So when you do that, when you do your validation, right, disable the device ID validation. Okay, good luck with your business. And thank you. So when customer buys, the money goes to Apple first and then to you? Money always goes to Apple first. Yeah, yeah, yeah. Then it goes to Apple first. And then usually until something around 2010, they buffer it until it reaches $200, then they give it to you. But now they tend to flush everything every month. So you get it at the end of the next month. Any updates? They still the same. Still the same, no updates. And still no family sharing for in-app purchase. There is a loophole for in-app purchase, right? What's the loophole? It's called viewer apps. It's called viewer app in the app for in-app purchase. Meaning that if the user sign up to your website, right, and bought the stuff there, you don't need to use in-app purchase. So your solution will be start on the web first. Start the business there first. Then you have apps for iOS and Android. Therefore you can go over, not provide the in-app purchase. In-app purchase? Yes. How is Apple is reviewing this kind of app when you sell international? How is reviewing? The hard part, I don't know, there's a guy there. They got no idea. Yeah, hard part, I don't know. All I see is that you need to provide the product ID to press the description and price. And you need to provide the screenshot. How the screenshot is being used to review in-app purchase just beyond me. But we take note that the Apple reviewers would need to see the in-app purchase and they have their own opinion of in-app purchases. Another thing is that if you want to do a subscription, you need to have a cloud service attached to your app. Even though if it's a SaaS, you can say if there's a service, if you don't have cloud service, it will be very likely that your app will get rejected for subscription. Would you also provide the Sandbox and One? What? Sandbox and One. Yes, you can. You can provide, they will provide, during testing, during testing, and I believe test flight, you can use a Sandbox account, which you go to iTunes, right? You get to iTunes Connect, and then you go to a screen to create this thing called Sandbox Accounts. Again, you can use it for purchasing. What's nice with their Sandbox accounts is that the subscription duration is much shorter. I think one day becomes five minutes, one day becomes one second, and then one week and five minutes also. So you can test it really, really quickly. Any experience you want to share with regard to the, when it comes to writing in-app test cases for this? Yeah, one experience. When you do the first time without unit test, keep your receipt file as a backup. That will be very useful for unit test later on. And second, you need to separate your inventory logic, which means the logic that says, yeah, user has product A, B, and C, and A is labeled, B is disabled, C is labeled, which is the one that varies when there's a receipt. The one that varies the receipt, you will likely want to have a thingy that auto-generates the code for every build or every version so that it doesn't get tracked easily. But those things cannot be unit tested easily. So you can use it like the TP in-app receipt where you can disable some of these validation, like the hardware ID validation and all the stuff to run it in a CI environment. Good, no good. How do you handle RIPan? How do you handle RIPan? Have a special page in your website, in your app site. If you have an app, it's a good idea to have an app-specific site, like an adult app to me. And then just say that, refund request, how do you do RIPan? If you want to know, this is how I show RIPan. So it's quite simple, have a pair of page and say, how do you do RIPan? And then support, refund request KC. Just have this somewhere in your website and then point it to this page. When the user bought something from iTunes, they will always get a receipt. And you can always, they can always click a receipt and then ask for refund. That's why you should just do this for virtual products only, not realize products that's hard to define. Does it affect you? Yeah, it did several times. You will see a negative one in your iTunes Connect report. But you don't know who is this customer. Apple don't tell you. Apple don't tell you. Yeah, that's one of the hard things when you sell things in the app store. It's not, you don't know the identity of the customers. But, hey. So when you refund, when you do the superman, they send you the receipt. Ah, when you do a restore purchase or whatnot, right? You should see a cancellation date. Although I don't know how to do a refund on a test account to be frank. So just be, just be aware, there's a field called cancellation date in the receipt. That is usually empty. But if that's there, and that date is in the past, you should disable the functionality. Yeah. Okay. Thank you so much. Thank you. New product. Oh, okay.