LiveCode LessonsLiveCode LessonsHow To - LiveCode Mobile TasksIn-App PurchasingHow do I implement in-app purchases in LiveCode - Apple AppStore?

How do I implement in-app purchases in LiveCode - Apple AppStore?

You can download the sample stack from this url: https://tinyurl.com/y8pkp2mh

Note: You need LiveCode 6.7+ to follow this lesson.

1. Creating an in-app purchase

1. Creating an in-app purchase

In order to use the in-app purchase with LiveCode, we must first create it in iTunes Connect. From the front page of iTunes Connect, go to "Manage Your Applications", select the app for which you wish to create an in-app purchase, click "Manage In-App Purchases", and click "Create New". From there, follow the instructions to create the type of in-app purchase you wish to use. In the stack for this lesson, we are using a consumable and a non-consumable purchase.

2. Setting up a stack to access in-app purchases

2. Setting up a stack to access in-app purchases

First create two buttons. One button for consumable, and one button for non-consumable purchase. Then add code that allows us to interface with iTunes Connect and request the in-app purchases for download.

To our consumable purchase button, add the following code:

on mouseUp
   mobileStoreEnablePurchaseUpdates
   mobileStoreSetProductType "com.runrev.sampleapp.consumable", "inapp"
   mobileStoreMakePurchase "com.runrev.sampleapp.consumable", "1", "This belongs to me"
end mouseUp

 

And similarly, add the following to our non-consumable purchase button:

on mouseUp
   mobileStoreEnablePurchaseUpdates
   mobileStoreSetProductType "com.runrev.sampleapp.nonconsumable", "inapp"
   mobileStoreMakePurchase "com.runrev.sampleapp.nonconsumable", "1", "This belongs to me"
end mouseUp

 

Finally, add the following code to the stack script:

on purchaseStateUpdate pPurchaseID, pProductID, pState
   switch pState
      case "paymentReceived" 
         answer "payment received!"
         offerPurchasedProduct pProductID
         mobileStoreConfirmPurchase pProductID
         mobileStoreDisablePurchaseUpdates
         break
      case "error"
         answer "Error occured during purchase handling:" & return & return & mobileStorePurchaseError(pPurchaseID)
         mobileStoreDisablePurchaseUpdates
         break
      case "invalidSKU"
         answer "Invalid SKU."
         mobileStoreDisablePurchaseUpdates
         break
      case "alreadyEntitled"
         answer "Already Owned."
         mobileStoreDisablePurchaseUpdates
         break
      case "restored"
         answer "restored"
         offerPurchasedProduct pProductID
         mobileStoreConfirmPurchase pProductID
         mobileStoreDisablePurchaseUpdates
         break
      case "cancelled"
         answer "Purchase Cancelled:" && pProductID
         mobileStoreDisablePurchaseUpdates
         break     
   end switch
end purchaseStateUpdate

 

on offerPurchasedProduct pProductID
   if pProductID is "com.runrev.sampleapp.consumable" then
      set the cConsumablesCount of this stack to (the cConsumablesCount of this stack + 1)
   else if pProductID is "com.runrev.sampleapp.nonconsumable" then
      set the cNonConsumablePurchased of this stack to true 
   end if
end offerPurchasedProduct	

The mobileStoreEnablePurchaseUpdates command allows you to monitor the status of each in-app purchase request, using the built-in message, purchaseStateUpdate. This is handled to check the status of the in-app purchase.

The mobileStoreSetProductType command is used to set the type of the in-app purchase. The type can either be “inapp” for consumable and non-consumable items, or “subs” for subscriptions.  This distinction is not always necessary, since it is only used by the underlying Google API for in-app purchases, because it uses different methods, depending on the item type. However, it is recommended that you always use this command, even for non-Google stores.  The mobileStoreSetProductType command is used with the identifier and the type of the in-app purchase we are requesting. These values must match the identifier and the type of an in-app purchase that has been set up in iTunes Connect for the app.

The mobileStoreMakePurchase command is then used with three parameters, denoting the in-app purchase identifier we are requesting, the requested quantity of the item and the developer payload. The quantity should always be “1” for non-consumable and subscription items. The developer payload is a string that contains some extra information about the order.  This command sends the request and begins the purchasing process. The purchaseStateUpdate message is then generated as the purchasing process takes place.  This is sent with three parameters denoting the product identifier, the purchase identifier and the state of the specific purchase that the message is regarding. In the handler, the state of the purchase is checked using the parameter pState. Here we take action if it returns paymentReceived, indicating that payment for the purchase was received, or restored, indicating that the restoration request was successful. In all other cases, we print a message to the screen giving details on what has happened.

In case of an error, we can use mobileStorePurchaseError with the purchase ID to find the details of the error and then use the mobileStoreDisablePurchaseUpdates command to indicate that the purchase process is complete. In cases where the item is already owned by the user, or in cases where there is no item with the specified identifier in the store listing, or in cases of the user cancelling the purchase, we use mobileStoreDisablePurchaseUpdates again. In case of payment being received, we take action in our app to finalize the purchase process. As there are multiple possible in-app purchases in this app, we use the pProductID parameter with offerPurchasedProduct method, to find out which in-app purchase we are dealing with. We can then take appropriate action for the in-app purchase that has been purchased, and use the mobileStoreConfirmPurchase command to confirm to Apple that payment has been received, or that the restoration operation has been successful, finalizing the in-app purchase process on Apple’s side.

The bundle identifier that you create the standalone application with (as specified in the standalone application settings\Pmust match the bundle identifier tied to the in-app purchases in iTunes Connect.

3. Handling in-app purchases once they have been purchased

3. Handling in-app purchases once they have been purchased

The above step handles a purchase. In this app, we set a custom property to indicate to the app that the purchase has been made. With the non-consumable and the subscription purchase we set a custom property to true, but with the consumable in-app purchase, the user can purchase it as many times as they like. We increment a custom property to indicate how many times a purchase has been made.

We can now use these custom properties to take action based on the in-app purchases the user makes. Handling the in-app purchase process records the purchases with Apple and transfers money. For this app, we are using the non-consumable purchase to change the background color of the stack and change the text of a field. The consumable purchase is used to make a graphic flash on and off briefly.

For the non-consumable purchase, we can use a setProp to implement this:

setProp cNonConsumablePurchased pValue
   set the cNonConsumablePurchased of this stack to pValue
   if pValue then
      set the backColor of this stack to 0,255,0
      put "PURCHASED" into fld "purchased"
   else
      set the backColor of this stack to 255,255,255
      put "NOT PURCHASED" into fld "purchased"
   end if
end cNonConsumablePurchased

For the consumable purchase, we want the user to be able to activate it whenever they want rather than immediately. For this we use two handlers, a setProp and a button. The setProp indicates to the user how many consumable purchases they have remaining, and the button is used to activate the consumable.

Our setProp will look like this:

setProp cConsumablesCount pValue
   set the cConsumablesCount of this stack to pValue
   put pValue into fld "consumablescount"
end cConsumablesCount

 

And our button should have this code:

on mouseUp
   if the cConsumablesCount of this stack > 0 then
      set the cConsumablesCount of this stack to (the cConsumablesCount of this stack - 1)
      repeat with x = 1 to 10
         wait 100 millisecs with messages
         if the backcolor of grc "consumablegrc" is "255,255,255" then
            set the backcolor of grc "consumablegrc" to "0,0,255"
         else
            set the backcolor of grc "consumablegrc" to "255,255,255"
         end if
      end repeat
      set the backcolor of grc "consumablegrc" to "255,255,255"
   else
      answer "you need to purchase a consumable first!"
   end if
end mouseUp

This will let use 'use up' a consumable each time the user clicks the button, and only let them carry out the action we want if they have purchased a consumable in the first place.

4. Making purchases persistent

4. Making purchases persistent

One problem that you may have noticed, is that it is not possible to save a stack that is running under iOS (due to technical restrictions imposed by Apple). Therefore, our custom properties will revert to whatever values they are at build-time, each time the app is closed. We therefore need to somehow save our custom properties, so that we can re-load them every time we open up the app on our mobile device.

A first thought would be to use a restore button.

Our button should have this code :

on mouseUp
   mobileStoreEnablePurchaseUpdates
   mobileStoreRestorePurchases
end mouseUp

Here, the command mobileStoreRestorePurchases will trigger a callback to the purchaseStateUpdate message. The purchaseStateUpdate message will be called as many times as the number of non-consumable items we have already purchased, each time with a different productID and purchaseID, but always with the same state, restored. In that way, we can offer again (this time for free\P    to the user the products they have already bought. However, this approach is not that good, for the following reasons:

1. Consumable products cannot be restored (for our example, this means that purchaseStateUpdate will be not be called with productID = "com.runrev.sampleapp.consumable"). This is because consumable products are not meant to be available in any device other than the device where they where originally purchased. Thus, we need a mechanism to store locally that the user has purchased a number of consumable products.

2. Non-consumable products can be restored, but is recommended by Apple not to use the restoration operation that often. Restoring purchases prompts for the user’s AppStore credentials, which interrupts the flow of your app. Because of this, do not automatically restore purchases, especially not every time your app is launched. Thus, we could again use a mechanism to store locally the non-consumable purchases of the user, and use the restore button only in cases where the user switches devices.

One simple way of doing this is adding some code to our setProps which will write the value of the custom property to a location outside of the .app bundle. For example, this could be an SQLite Database or a text file. In this case, we will write a text file to the documents folder. We add the following handlers to our stack script and call them at the end of our setProps for each custom property as follows:

on saveConsumablesCount
   put the cConsumablesCount of this stack into url("file:" & specialFolderPath("documents") & "/consumablescount.txt")
end saveConsumablesCount

 

on saveNonConsumablePurchased
   put the cNonConsumablePurchased of this stack into url("file:" & specialFolderPath("documents") & "/nonconsumablepurchased.txt")
end saveNonConsumablePurchased

 

setProp cNonConsumablePurchased pValue
   set the cNonConsumablePurchased of this stack to pValue
   if pValue then
      set the backColor of this stack to "0,255,0"
      put "PURCHASED" into fld "purchased"
   else
      set the backColor of this stack to 255,255,255
      put "NOT PURCHASED" into fld "purchased"
   end if
   saveNonConsumablePurchased
end cNonConsumablePurchased

 

setProp cConsumablesCount pValue
   set the cConsumablesCount of this stack to pValue
   put pValue into fld "consumablescount"
   saveConsumablesCount
end cConsumablesCount

 

And we then simply load these values back in each time we open the app, by adding the following code:

on loadConsumablesCount
   local tPath
   put specialFolderPath("documents") & "/consumablescount.txt" into tPath
         if there is a file tPath then
      set the cConsumablesCount of this stack to url("file:" & tPath)
   end if
end loadConsumablesCount

 

on loadNonConsumablePurchased
   local tPath
   put specialFolderPath("documents") & "/nonconsumablepurchased.txt" into tPath
         if there is a file tPath then
      set the cNonConsumablePurchased of this stack to url("file:" & tPath)
   end if
end loadNonConsumablePurchased

 

on preOpenStack
   loadNonConsumablePurchase
   loadConsumablesCount
end preOpenStack

We now have a system which can control our in-app purchases simply by setting the value of the relevant custom properites, without having to worry about manually saving or loading them, or losing them.

Note: You have to include some mechanism in your app to let the user restore their purchases across different devices, or else your app will be rejected by Apple.

5. Testing purchases

5. Testing purchases

From the front page of iTunes Connect, go to "Manage Users", select "Test User", and click "Add New User". From there, follow the instructions to create a new test user account. You will need a test user for each territory you want to test the app in.\L Then clear any account information stored on your test device. (Settings -> Store -> Sign Out).

Do not sign in using your test account information in the Store settings panel. Doing so may invalidate your test account.\L\L\L Then launch your app and make a purchase. A window pops up and prompts you to sign in. Choose "Sign in using existing Apple ID", and use your test account credentials to sign in. You will not be charged for any purchases you make using your test user account.

If you notice unusual behaviour in the purchase flow, for example a "Cannot connect to iTunes store" error, then try creating and using a new test user account.

6. A note about subscription items in iOS

You may noticed in this lesson that we did not use any subscription item, as we did in the other in-app purchasing lessons for android stores. This is because iOS handles subscription items differently. There are two kinds of subscriptions:

1. Auto-renewable subscriptions, where automated billing occurs every fixed period.

2. Non-renewable subscriptions that are not available after the period expires. These are like short-term items. The user can buy an item again after it has expired.

Apple has placed strict rules around auto-renewable subscriptions, meaning their usage is (usually\P    exclusive to Newsstand apps.

As a result of this restriction, the subscription item that was used in the other 3 in-app purchase lessons for the android stores is not ideally suited for the Apple AppStore. We can use that subscription item for testing purposes, to see how automated billing occurs, but it is most likely going to be rejected by Apple in the app approval process, because it does not fall into the category of products that are suitable for auto-renewable subscriptions. This means that if we want to use the same example for this lesson, we have to implement it as a non-renewable subscription.

Unlike auto-renewable subscriptions, where subscription durations and renewals are handled through the StoreKit framework, this is the underlying framework that is used when interacting with the Apple AppStore, non-renewable subscriptions require the developer to do all the heavy lifting. Which means that the developer is responsible for:

1. Checking the purchase date of the non-renewable subscription item and making sure that the item will only be available during this period. This could be implemented by locally storing the purchase time in the app and checking each time when the app launches if the period has expired.

2. Making the non-renewable subscription item accessible from every device the user owns. This means that locally storing the purchase time is not sufficient. The solution could be to store the purchase time - as well as the user's email address on a server, and checking each time when the app launches if the period has expired.

You can find more information on subscription items for iOS on the official website:

Subscription Items for iOS : https://tinyurl.com/yaqeqqa7

39 Comments

George Vossos

Why doesn't mobileStoreEnablePurchaseUpdates work under Livecode v6.6.2? The documentation instead refers to mobileEnablePurchaseUpdates? Same for all other Apple In-App livcode commands.

Panos

Hi George,
The mobileStoreEnablePurchaseUpdates command, as well as all mobileStore* commands/functions, were first introduced in LiveCode 6.7. So, if you use LC 6.6.2, you have to use the old in-app purchasing related commands, as described in the dictionary.

Warm regards,
Panos
--

Stephen Elkins

Trying to get this code to work ... but it just doesn't. So frustrating. I downloaded the entire LiveCode file and tried it just the way it is and it still doesn't work. Not sure what to do. Is there any way I can send somebody my code to help me get this going? I want to have the app ready to send to Apple by end of week and this is the only thing stopping me right now. Thanks!

Panos Merakos

Hi Stephen,
Thank you for your comment. The attached LC file is not supposed to work just the way it is. You have to replace the product IDs of the example with your own product IDs. These must match the ones you created in iTunes Connect (see step 1). Moreover, you have to use LC 6.7 or above, and make sure you test your in-app purchases on a physical Apple mobile device. Testing on simulator may not work. Furthermore, it is recommended to have a test account set up in iTunes Connect so that you can test your in-app purchases without spending any money.

Warm regards,
Panos
--

Stephen Elkins

Panos! Thank you so much for your quick response. Yeah, I have been trying it with the product IDs with nothing happening. That's why I tried downloading just the original LiveCode file to see if the stack would turn the green background color that was shown in the tutorial but it didn't work. However, that was in 6.6.2 not in 6.7. Downloading that now. Another question ... So, let's say my app is called com.stephenbrucedesigns.marie and my product is called Africa1. Would I put just com.stephenbrucedesigns.marie in product ID or com.stephenbrucedesigns.marie.Africa1? Sorry if these are simple questions. I'm still new to LiveCode. Thank you for your help!

Panos Merakos

Hi Stephen,
You are welcome. The product ID you use in your code must match the product ID of the in-app purchase that has been set up in iTunes Connect for the app. So, if in iTunes Connect you have put "Africa1", you must use "Africa1" in your code. Hope this helps!

Best regards,
Panos
--

Stephen Elkins

Hey Panos. So, on my iPhone, when I click on the "non-consumable" button it asks me to sign in to my account (which I add my test account). When I enter it, I get this error ... "Error occured during purchase handling: Cannot connect to iTunes Store." What does that mean and how can I fix it? Thank you so so SO very much for your help so far. Really appreciate it!

Panos Merakos

Hi Stephen,
No worries. In the standalone application settings, have you replaced the example app ID with your unique app identifier (com.stephenbrucedesigns.marie)? Moreover, it might be the case that your test account has become invalid. I had this problem as well! Try creating a new test account, and use this for your testing :)

Stephen Elkins

Hey Panos. Yep, I changed the app id to mine. I will, however, try adding a new test account. I haven't tried that yet. Thank you! I'll keep you posted in case others have this issue as well.

Panos Merakos

In case you have not done it already, you have to select a valid provisioning profile (in the standalone application settings).

Stephen Elkins

Even with setting up a new test user it still does it. I also tried it with my actual iTunes account and I still get the same error. I double and triple checked my app id. It's good. I tried different provisioning profiles. Nothing works. I'm sure it's something easy and right in front of my eyes:D

Panos Merakos

Mm that's weird. The "Cannot connect to iTunes store" error comes from Apple, not from the LiveCode API that handles in-app purchasing. If you google for this error you will see that is a quite generic and not very helpful one! In my case, the issue was fixed by adding a new test account. Note that first you have to clear any account information stored on your test device. (Settings -> Store -> Sign Out). Then, do not sign in using your test account information in the Store settings panel. Doing so may invalidate your test account.


 Then launch your app and make a purchase. A window pops up and prompts you to sign in. Choose "Sign in using existing Apple ID", and use your test account credentials to sign in.
That worked for me!

Stephen Elkins

Very weird. It should go through with my actual iTunes account, right? Now, do I have to have the actual app file uploaded to iTunes Connect? I read that you didn't have to in order to test but I'm willing to try anything at this point. Not sure what to do.

Panos Merakos

Yes it should, although you would be charged. No, i don't think it is necessary to upload the actual app file at this point. Another thing to note is that the status of your product should be "ready to submit", in order to be ready for testing. You can check this status in iTunes Connect ("Manage your apps" -> select your app -> "Manage In-App Purchases")

Stephen Elkins

You bring up an interesting point. Mine says "prepare for upload" and not "ready to submit." Do you think that could be the cause?

Panos Merakos

Yeap I am almost sure that this is the cause. If you have a look at Apple's official documentation for in-app purchasing, it is stated that "If the product is 'Ready to Submit', the product’s configuration is complete and ready for you to test the product with your app". So you have to complete all the required fields of your product details (including screenshots etc) in order to change the status to "ready to submit". This will hopefully resolve the issue :)

Stephen Elkins

Once again, thanks for you help Panos. I made a couple changes to the back end on Apple's side ...

1) The app is now "Waiting to Upload"
2) I also noticed this message at the top of my in-app purchase ... "This In-App Purchase is not currently available for testing in the sandbox environment because you have chosen to host your content with Apple, but have not delivered your content. Upload your content to test this In-App Purchase in sandbox."

So, it looks like I do need to upload the content. I think we may be on to something here. Thank you so much! I'll keep ya posted.

Stephen Elkins

Hey Panos. Making some progress. Did come across another issue. How do I create a .pkg file instead of .app in LiveCode? The in-app purchase has to be uploaded as .pkg. Thanks!

Panos Merakos

Hi Stephen,
Yeah you have made some progress! Some things to note first. You are getting these messages because you have chosen to host your in-app purchase content with Apple. This option is available from iOS version 6 and above. This is the reason you have to upload your in-app purchases as .pkg files. You cannot create a .pkg file in LiveCode. The easiest way to do that is with Xcode. It is quite straightforward and does not require any Objective-C programming. In short, all you have to do is create a product in Xcode using a In-App Purchase Content template, include your content, and upload it to iTunes Connect using the Archive process You can find detailed information about this in Apple's official documentation for in-app purchasing, or have a look at this tutorial:

http://www.techotopia.com/index.php/Configuring_and_Creating_App_Store_Hosted_Content_for_iOS_6_In-App_Purchases

Things to note:
1. After selecting "In-App Purchase Content" as the template for your new project, you have to enter the Product Name and Company Identifier such that the Bundle Identifier matches the Product ID you entered in iTunes Connect for this in-app purchase product.
2. It can take some time between uploading the archive and the iTunes Connect status being updated.

If you struggle to make things work (or if you accidentally enabled Hosting Content with Apple!), you can always undo this selection by deleting the In-App Purchase product and re-configuring it. Note that you can’t use the same product ID when you re-create the product. However, I don't think you will have any problems if you read carefully the Apple docs or the tutorial.

I will be out of office for two weeks, but keep me posted and I will reply back as soon as I return. Good luck!

Stephen Elkins

Have a great couple weeks off! Yeah, looks like I'll have to do it in XCode. Which really stinks because I did all the code for the In-Apps Purchases in LiveCode. You see ... I'm developing a kids storybook app. The different stories will be downloaded through in-app purchases. Each story has code for swipe and a page scroller. So, really stinks. Thanks for all your help.

George Vossos

Hi Panos,

Despite having followed all the above, I'm still experiencing fatal crashes when implementing Apple In-App behavior under LiveCode v6.6.2.

Looking at the XCode console log, the crash occurs after a successful call to 'mobilePurchaseSendRequest tID' but before the call to the 'purchaseStateUpdate' handler.

Is there anything glaringly wrong with the handler below?

on purchaseStateUpdate pPurchaseID, pState

switch pState

case "initialized"
break

case "sendingRequest"
break

case "error"
mobileDisablePurchaseUpdates
answer "Error occured during purchase handling:" & return & return & mobilePurchaseError(pPurchaseID)
break

case "cancelled"
mobileDisablePurchaseUpdates
answer "Purchase cancelled."
break

case "restored"
break

case "paymentReceived"
switch mobilePurchaseGet(pPurchaseID, "productID")
case "com.netlifestyle.mixtape.cpackage03"
// some business logic
break

case "com.netlifestyle.mixtape.cpackage06"
// some business logic
break

case "com.netlifestyle.mixtape.cpackage09"
// some business logic
break

end switch

mobilePurchaseConfirmDelivery pPurchaseID

mobileDisablePurchaseUpdates

// my Business Logic goes here ....
paymentReceivedProc
break

end switch

end purchaseStateUpdate

on paymentReceivedProc

answer "Thank you - payment received!"

// some other business logic

end paymentReceivedProc

Panos Merakos

Hi George,

Your code seems fine.Can you also post the code that is responsible for sending the purchase request?

Louis Eo

I found that when this lesson was created the auto renewal subscription was not allowed by Apple to all categories except magazines and newspapers. but Now apple allows all apps implementing this subscription model.

"3.1.2 Subscriptions: Apps may offer auto-renewing in-app purchase subscriptions, regardless of category on the App Store. When incorporating auto-renewable subscriptions into your app, be sure to follow the guidelines below.” - From App Review Guideline.

So here is my question. How can I restore a purchased subscription item in the iOS environment? Can I just adopt code from android in-app lesson? hope you could help me regarding this matter..Thanks for your time.

Louis

Louis Eo

For auto-renewable subscription, how can I know whether user cancel it or not?

What if the user cancel the purchased auto-renewable subscription after using few months? I like to know how to detect a cancellation made by user so that I can disable the subscriptions.

Cheers,

Louis

Jun

Is push notification required for in app purchase? I saw push notification is checked when I downloaded the test stack.

Elanor Buchanan

Hi Jun, no push notifications are not required. If this is checked in the sample stack it is an oversight.

Kind regards

Elanor

Randy

There is an error in the Instruction (not the code file itself though).
The line should be:
on saveNonConsumablePurchased
put the cNonConsumablePurchased of this stack into url("file:" &
specialFolderPath("documents") & "/nonconsumablepurchased.txt")

The instructions didn't properly enclose ("documents"). I quickly tried to fix this by adding a ) to the end of the sentence, but that really was incorrect. In short....Use this:
on saveNonConsumablePurchased
put the cNonConsumablePurchased of this stack into url("file:" & specialFolderPath("documents") & "/nonconsumablepurchased.txt")
end saveNonConsumablePurchased

Elanor Buchanan

Hi Randy

Thanks for bringing this to our attention, I have updated the lesson with the corrections.

Elanor

Andrew Bell

While this code works for Android, mobileStoreMakePurchase running in iOS returns “An unknown error occurred” for the mobileStorePurchaseError even after the in-app purchase items have been approved by Apple (published app downloaded from App Store using real account, no sandbox). Has this changed in a recent version of Xcode

Compiled with LiveCode 9.6.2rc3 on macOS 11.0.1 using Xcode 12.1

Panos Merakos

Hello Andrew,

Hmm, I think we need more info on this. Does your real account have 2FA enabled? If yes, could you disable it temporarily and check if you get different results?

Cheers,
Panos
--

Andrew Bell

Problem was user error! The in-app purchase in Google was created using their recommended reverse domain name style (like your example) but the purchase in Apple was just the custom string at the end (they didn't explicitly recommend the reverse domain name style, but I will use in the future for parity) so the ProductID didn't match what was setup in the iOS app store.

This could NOT be tested in the Simulator but was verified using a sandbox account on a physical device as documented in your Testing Purchases section of this tutorial.

Trevix

New to inAppPurchase, I am doing some test before putting it in my already published App.
My free App already has OneTimeBuy inAppPurchase listed in Apple Connect, even if we never fully implemented it.
In order to test then, if i understand, I just need to change in your demostack the Interna App Id with the one used in my App, create a standalone and load it in my Hardware iPhone.
I also created a Test user on Apple connect.
You said "...Then clear any account information stored on your test device. (Settings -> Store -> Sign Out)".
I went to my iPhone (iOS 14.4.2) Settings, but could not find a "Store" menu were to sign out.
I have a "App Store" menu but inside I cannot see any way to sign out.
Can you please clarify?
Thanks

Panos Merakos

Hello Trevix,

In the demo stack, you have to:

- Change the app ID in the iOS settings to match your app id
- Sign with a provisioning profile that supports in-app purchase
- In the code that makes/offers/restores the purchased product(s), replace the in-app purchase ID (e.g. "com.runrev.sampleapp.nonconsumable") with the ID of your own in-app purchase as it appears in AppStore Connect.

In the Settings menu in your device, you have to go to General -> AppStore. If you see no account there, then it means that no account is stored, and the app will ask you to enter your (test) account's credentials the very first time you attempt to make purchase.

Cheers,
Panos

trevix

Hello. Almost there...
I get the following msg when I try a Consumable or nonConsumable buy:
"Error occurred during purchase handling: An unknown error occurred" (not very helpful)
I test it on hardware iPhone iOS 14.4.2
"com.trevix.it.segnapunto" is the internal app ID of standalone setting
I replaced the purchase ID in the scripts of your test stack with "com.trevix.it.segnapunto.SP100" (Nonconsumable) and "com.trevix.it.segnapunto.SP101"(Consumable ). SP100 and SP101 are the product ID that I have setup on Connect.
I installed the test stack on the iPhone
Both in-app purchase, on Connect, are marked as "ready for send"
Am I missing something?
Thanks

Panos Merakos

Hello Trevix,

The purchase IDs in the scripts should match the ones on Connect, so you have to use *just* "SP100", not "com.trevix.it.segnapunto.SP100". Similarly for "SP101".

Kind regards,
Panos
--

Trevix

I haven't see an answer to Louis Eo, about how to handle subscription in iOS, now that apple allows all apps implementing this subscription model.
This is also very interesting for me.Can you please integrate?
Thanks

Panos Merakos

Hello Trevix,

So, the suggested way to deal with auto-renewable subscription is to use a receipt validation mechanism, but this is not supported in LiveCode yet. There is an enhancement request about it (https://quality.livecode.com/show_bug.cgi?id=19507). However, you can use "mobileStoreRestorePurchases", to restore existing purchases, similar to the lesson about in-app purchases for Google Play Store. So, if an auto-renewable subscription is still valid, it will be restored when calling mobileStoreRestorePurchases. If the user had cancelled the subscription in the meanwhile, then "mobileStoreRestorePurchases" will restore the subscription only until the current subscription period ends.

Hope this helps.

Kind regards,
Panos
--

Trevix

This bug report (https://quality.livecode.com/show_bug.cgi?id=19507) has been created in 2018, but not yet resolved.
My problem is how to check in the standalone if the subscription has been canceled, so to remove the "Pro" features.
How bad is it and what can of problems can I have if, at every launch on iOS and android, I do a
mobileStoreEnablePurchaseUpdates and mobileStoreRestorePurchases?
Thanks

Panos Merakos

Hello Trevix,

The short answer is that you can use mobileStoreEnablePurchaseUpdates and mobileStoreRestorePurchases. In fact, you *have to* use these since there is no other accurate way to know when a user has canceled their subscription. It is not too bad - Apple just advices against using mobileStoreRestorePurchases on every launch because it can be expensive and cause slowness in the app while it tries to restore the purchases. But you can test to see how it affects your app - I think you should be fine.

An alternative would be to call mobileStoreRestorePurchases not on *every* launch, but every e.g. X days, depending on the duration of the subscription. However, this has the following disadvantage, say for 7 days:

- User buys a subscription on day 1
- User buys a new device on day 2 and installs the app
- The subscription will not be available in the new device, until mobileStoreRestorePurchases is called, i.e. on day 7

A solution for this would be to add an option to force restoring purchases (e.g. a button "Restore" that will call mobileStoreRestorePurchases on demand), and instruct the users to click that button if they install the app on a new device and have existing valid purchases.

Or you can check on preopenstack if this is a new device - you can do that by creating a file in the device the very first time the app is installed, and check for the existence of this file on subsequent launches. If the file is there, it means this is not a new device, so call mobileStoreRestorePurchases every 7 days. Otherwise, it is a new device, so force calling mobileStoreRestorePurchases.

You get the idea!

Kind regards,
Panos

Add your comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.