A complete mobile application that demonstrates purchasing in-app products.
Note: The Qt Purchasing module was one of the Removed Modules in Qt 6.0, and the code is now contained within this example as a guide on how you can use Qt to integrate with common marketplaces.
This demo is a mobile game application based on the classic word guessing game Hangman, where vowels can be purchased through the demo's internal store. In the game you will be presented with a row of dashes, representing letters of the word to guess. By guessing a correct letter that occurs in the given word, the letter will be placed on the corresponding dash or dashes in the word. By guessing every letter of the word or just guessing the whole word correctly at any time the game is over and you have won. If the guess is wrong, one element of a hanging stick figure is drawn. Once the figure is complete, you are out of guesses and you lose.
The demo shows how it is possible to offer in-app products inside a Qt application, for the Android and iOS platforms. In order to test the in-app purchase functionality in the the demo, you must first register the application and its products in the external store. For an introduction on how to do this, see the guides for Google Play and App Store respectively.
The in-app products must be registered in the target stores, before they can be queried or purchased in an application. We recommend using the same identifiers for the products in each store, as it simplifies the code to query and purchase the products.
The demo is a QML application that registers QML types to access information about in-app products, as well as to request purchases for those products. These are registered in the external store for the target platform
In-app purchasing are added to application by first adding a Store object. In the demo the Store object is created by the MainView component that is loaded on application startup.
Store { id: iapStore }
The demo defines a component for displaying a store for purchasing in-app products made available. These products must be first registered with the store object we created above in MainView. There are two products available, the first being a consumable type.
Product { id: product100Vowels store: iapStore type: Product.Consumable identifier: "qt.io.demo.hangman.100vowels" onPurchaseSucceeded: { console.log(identifier + " purchase successful"); //Add 100 Vowels applicationData.vowelsAvailable += 100; transaction.finalize(); pageStack.pop(); } onPurchaseFailed: { console.log(identifier + " purchase failed"); console.log("reason: " + transaction.failureReason === Transaction.CanceledByUser ? "Canceled" : transaction.errorString); transaction.finalize(); } }
This consumable product provides 100 additional vowels to be used when guessing words in the game. When it is successfully purchased, we update the state of the application to include 100 additional vowels. Then we call finalize() on the transaction object to confirm to the platform store that the consumable product has been provided.
The second product is a non-consumable type that will unlock vowels permanently in the future. In addition to updating the application state on purchase, we must make sure to provide a way to restore this purchase on other devices used by the end user. In this case we create a signal handler for onPurchaseRestored.
Product { id: productUnlockVowels type: Product.Unlockable store: iapStore identifier: "qt.io.demo.hangman.unlockvowels" onPurchaseSucceeded: { console.log(identifier + " purchase successful"); applicationData.vowelsUnlocked = true; transaction.finalize(); pageStack.pop(); } onPurchaseFailed: { console.log(identifier + " purchase failed"); console.log("reason: " + transaction.failureReason === Transaction.CanceledByUser ? "Canceled" : transaction.errorString); transaction.finalize(); } onPurchaseRestored: { console.log(identifier + " purchase restored"); applicationData.vowelsUnlocked = true; console.log("timestamp: " + transaction.timestamp); transaction.finalize(); pageStack.pop(); } }
In addition to registering the products, the demo also provide an interface to actually purchase the registered product. The demo defines a custom component called StoreItem
to display and handle the purchasing
interaction.
Product { id: productUnlockVowels type: Product.Unlockable store: iapStore identifier: "qt.io.demo.hangman.unlockvowels" onPurchaseSucceeded: { console.log(identifier + " purchase successful"); applicationData.vowelsUnlocked = true; transaction.finalize(); pageStack.pop(); } onPurchaseFailed: { console.log(identifier + " purchase failed"); console.log("reason: " + transaction.failureReason === Transaction.CanceledByUser ? "Canceled" : transaction.errorString); transaction.finalize(); } onPurchaseRestored: { console.log(identifier + " purchase restored"); applicationData.vowelsUnlocked = true; console.log("timestamp: " + transaction.timestamp); transaction.finalize(); pageStack.pop(); } }
The StoreItem component will display the product data that is queried from the platform's store, and will call the purchase() method on the product when it is clicked by the user.
Text { id: titleText text: product.title font.bold: true anchors.right: priceText.left anchors.rightMargin: topLevel.globalMargin anchors.top: parent.top anchors.topMargin: topLevel.globalMargin anchors.left: parent.left anchors.leftMargin: topLevel.globalMargin } Text { id: descriptionText text: product.description anchors.right: priceText.left anchors.rightMargin: topLevel.globalMargin anchors.left: parent.left anchors.leftMargin: topLevel.globalMargin anchors.top: titleText.bottom anchors.topMargin: topLevel.globalMargin / 2 wrapMode: Text.WordWrap } Text { id: priceText text: product.price anchors.right: parent.right anchors.rightMargin: topLevel.globalMargin anchors.verticalCenter: parent.verticalCenter } MouseArea { anchors.fill: parent onClicked: { pendingRect.visible = true; spinBox.visible = true; statusText.text = "Purchasing..."; storeItem.state = "PURCHASING"; product.purchase(); } onPressed: { storeItem.state = "PRESSED"; } onReleased: { storeItem.state = "NORMAL"; } }
Android and iOS use the base classes. From base classes there are derivative classes for android and ios:
In-app purchases are a way to monetize an application. These purchases are made from inside the application and can include anything from unlocking content to virtual items. The demo uses the system APIs for in-app purchases, which means the purchase process is more familiar to the user, and the information already stored by the platform (such as credit card information) can be used to simplify the purchase process.
In regards to deploying the demo on Android see Android GNU C++ Run-time Licensing for more information.
Files: