How do I implement in-app purchases in LiveCode - Samsung Apps Store?
You can download the sample stack from this url: https://tinyurl.com/yaonx9yk
Note: You need LiveCode 6.7+ to follow this lesson.
1. Creating an in-app purchase
Creating your in-app items in the Samsung Apps Seller Office is a bit more complicated compared to the other stores.
1. Create an "Item Group".
2. Create the in-app items within this Item Group.
3. Add your .apk file, and link it with the Item Group you just created. In that way, you have access to the in-app items you created, from within your app.
There are detailed instructions on their website. From the main page of the Samsung Apps Seller Office, choose "Applications", then "Item", and "How to register an In App Purchase item". Follow the instructions to complete all 3 above steps.
In the Samsung Apps Seller Office, the user cannot create the product identifier ("Item ID"\P of the in-app items. This is contrary to other stores. This ID is auto-generated by Samsung. The same applies to "Item Group ID". You need these values when developing and building your app. Item ID is needed for initiating and handling purchase requests, and Item Group ID is needed in the Standalone Builder Settings.
2. Setting up a stack to access in-app purchases
First create three buttons. One button for consumable, one button for non-consumable and one button for subscription. Then add code that allows us to interface with the Samsung Apps store and request the in-app purchases for download.
To our consumables purchase button, add the following code:
on mouseUp mobileStoreEnablePurchaseUpdates mobileStoreSetProductType "your_consumable_product_id", "inapp" mobileStoreMakePurchase "your_consumable_product_id", "1", "This is my payload" end mouseUp
Add the following to our non-consumable and subscription purchase buttons:
on mouseUp mobileStoreEnablePurchaseUpdates mobileStoreSetProductType "your_nonconsumable_product_id", "inapp" mobileStoreMakePurchase "your_nonconsumable_product_id", "1", "This is my payload" end mouseUp
on mouseUp mobileStoreEnablePurchaseUpdates mobileStoreSetProductType "your_subscription_product_id", "subs" mobileStoreMakePurchase "your_subscription_product_id", "1", "This is my payload" end mouseUp
Finally, add the following code to the stack script:
on purchaseStateUpdate pPurchaseID, pProductID, pState switch pState case "paymentReceived" answer "Payment received!" offerPurchasedProducts pProductID mobileStoreConfirmPurchase pProductID mobileStoreDisablePurchaseUpdates break case "error" answer "Error occured during purchase handling: " && mobileStorePurchaseError(pPurchaseID) mobileStoreDisablePurchaseUpdates break case "invalidSKU" answer "Invalid SKU" mobileStoreDisablePurchaseUpdates break case "restored" answer "Restored!" offerPurchasedProducts pProductID mobileStoreConfirmPurchase pProductID mobileStoreDisablePurchaseUpdates break case "alreadyEntitled" answer "Already Owned" mobileStoreDisablePurchaseUpdates break case "cancelled" answer "Purchase Cancelled" mobileStoreDisablePurchaseUpdates break end switch end purchaseStateUpdate
on offerPurchasedProduct pProductID if pProductID is "your_consumable_product_id" then set the cConsumablesCount of this stack to (the cConsumablesCount of this stack + 1) else if pProductID is "your_nonconsumable_product_id" then set the cNonConsumablePurchased of this stack to true else if pProductID is "your_subscription_product_id" then set the cSubPurchased 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 subscription items. 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 the Samsung Apps Seller Office 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-iOS apps. 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 Amazon that payment has been received, finalizing the in-app purchase process on the Samsung Apps store’s side.
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 Samsung 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. The subscription purchase is used to change the color of a button.
For the non-consumable and the subscription 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" end if end cNonConsumablePurchased
setProp cSubPurchased pValue set the cSubPurchased of this stack to pValue if pValue then set the backgroundColor of button "sub" to "0,0,255" end if end cSubPurchased
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 consume and 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 mobileStoreConsumePurchase "your_consumable_product_id" 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 'uses up' a consumable each time the user clicks the button, and only lets them carry out the action we want if they have purchased a consumable in the first place. The mobileStoreConsumePurchase command is called with the consumable product identifier. This command removes the consumable item from the user's inventory, so that it will not be contained in the list returned by mobileStorePurchasedProducts function.
4. Making purchases persistent
Our custom properties revert to whatever values they are at build-time, each time the app is closed. One could therefore assume that custom properties have to be saved explicitly, allowing us to re-load them every time the app is opened. This is not the case for non-consumable and subscription products. The app is no longer responsible to retaining what the user has bought. All the purchased items of this type (non-consumables and subscriptions\P can be restored, not only in the particular device where the app was installed, but in every device the user owns. This can be done using a “restore” button.
Note : As a general rule, consumable products cannot be restored. Therefore, the app should have a mechanism to "remember" the consumable purchases of the user. One simple way of doing this is simply to add some code to our setProps which writes 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 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
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 preOpenStack loadConsumablesCount end preOpenStack
We now have a system which can control our consumable in-app purchases simply by setting the value of the relevant custom property, without having to worry about manually saving or loading them, or losing them.
For restoring non-consumable/ subscription items, we could use a restore button. It should have this code:
on mouseUp mobileStoreEnablePurchaseUpdates mobileStoreRestorePurchases end mouseUp
Here, mobileStoreRestorePurchases command queries the user’s inventory of purchased items, and returns them in the purchaseStateUpdate pPurchaseID, pProductID, pState message. The purchaseStateUpdate message is called as many times as the number of items the user owns, each time with a different pPurchaseID and pProductID, but always with the same pState : "restored”. In that case, the flow should be exactly the same as when the user successfully purchases the product for the very first time, with the difference that this time no actual payment occurs!
In other words, what mobileStoreRestorePurchases does, is simulating a successful purchase flow without charging the user’s credit card for all the items that the user has already purchased. The underlying Samsung in-app purchasing API remembers what the user has bought, and does not allow them to buy an item that they already own, even if they do not call mobileStoreRestorePurchases. To make this clearer, imagine the following scenario for our example app:
1. User launches the app for the very first time.
2. They click on the non-consumable button, and send a purchase request to the Samsung Apps store for the non-consumable item.
3. The Samsung Apps store responds and asks for the user’s password. The user types their password.
4. The Samsung Apps store responds that the purchase is successful.
5. purchaseStateUpdate message is called with parameters (“1”, ”nonconsumable_product_name”, “paymentReceived”).
6. The app provides the user with the purchased item, i.e. the background becomes green.
Now, if the user exits and re-launches the app, the background is not green. But if they select to purchase the non-consumable item again:
1. A window pops up asking for the user's log in details.
2. After the user fills in their details, a new window pops up with the purchase information (product name and price\P . The user can either click "Buy" or "Cancel".
3. If the user clicks the "Buy" button, the purchase flow is paused, and the restore operation occurs "behind the scenes", which means that the purchaseStateUpdate message is called with parameters (“1”, ”nonconsumable_product_name”, “restored”). Then the app delivers the content of the restored purchase (i.e. changes the background color to green).
4. Then the purchase flow resumes, but now the app is informed by the underlying Samsung in-app purchasing API that the user already owns this item (the non-consumable item, for which the purchase what initiated). Thus, the user gets an “Already Owned” message, which means that the purchaseStateUpdate message is called with parameters (“1”, ”nonconsumable_product_name”, “alreadyEntitled”).
5. Testing purchases
Testing of the payment service is not easy due to fee charging. For Samsung, there is not the option to register for a test account, but actually you do not need to, since there is an alternative way of testing. The underlying Samsung API for in-app purchasing supports a developer mode which allows testing in various situations. There are 3 modes supported, and you can set the mode in the Standalone Application Settings:
1. Production Mode : Mode for Production
2. Test Mode Success : Test Mode always returning successful results
3. Test Mode Failure : Test Mode always returning failed results
1. When releasing your app, you must use Production Mode. Payment only occurs in production mode. In cases where you accidentally release your app in developer mode, a pop-up window notifies you for this. If you see this message when making a purchase, please check your mode.
2. In Test Mode (either Success or Failure), you can use any valid Samsung account.
3. In Production Mode, your app can only interact with item groups with sales status. This information exists in the Samsung Seller Office. However, item groups are only given sales status after the app has been certified. In other words, you can test your app in Production Mode only after it has been certified by Samsung.
4. If you notice unusual behaviour in restoring previous purchased items, try using a different account.
5. In app purchasing for apps distributed through the Samsung Apps Store only works on Samsung devices.
6. You must have a Samsung device, as well as the Samsung Apps app on the device.
7. You must have the Samsung In-App Purchase app on your device. If you don't have it installed when you open the app, a message pops up informing you that Samsung IAP is not installed. Click the OK button, and you are redirected to the Samsung website where you can download the Samsung IAP app for free. After downloading it, install it to the device.
8. In the Standalone Builder Settings, you have to enable the "Internet" application permission.