QIap - a simple Qt interface for In-App Purchasing
This article explains how to use QIap, a class wrapper which provides a simpler interface to the standard Qt In App Purchasing API.
Article Metadata
Code Example
Tested with
Compatibility
Platform Security
Article
Contents |
Introduction
In-App Purchasing is a technology that enables end users to purchase and pay for additional content (such as a new level in a game) or services (such as a subscription) from within your application. The standard In App Purchasing API provides a lot of functionality but can be complicated to use. QIap introduces a very high level wrapper that makes this technology easier to use.
QIap is hosted on Nokia Projects: http://projects.developer.nokia.com/qiap . You can get the project source as a zip (qiap.zip) and there is a quick start here: InAppPurchase_QuickStartForQt.pdf.
Get started with QIap
Setup
First you have to create your own resource product in OVI Publisher panel. Please refer to this document ( page 7 ).
- Download QIap files and extract inside your project directory.
- Open your .pro file and add the following lines:
-
symbian {
# Enables In-App Purchase API
DEFINES += IN_APP_PURCHASE
#DEFINES += IN_APP_PURCHASE_DEBUG
# Enables test mode for IAP
#DEFINES += IA_PURCHASE_TEST_MODE
contains(DEFINES, IN_APP_PURCHASE) {
include(./qiap/in-app-purchase.pri)
}
}
-
- Create a directory called iap and put inside the following files and directories:
- <drive>:\myproject\data\ — resource files for paid apps
- <drive>:\myproject\data\resourceid_XXXXXX\ — The XXXXXX is the six–digit in-app ID provided by Nokia Publish when you identified the in-app purchase items
- IAP_VARIANTID.TXT — A file containing 000000 (six zeroes), which is required to use the In-App Purchase API. When you submit your app to Nokia Publish, Nokia Publish populates IAP_VARIANTID.TXT with data used to get DRM access.
- TEST_MODE.TXT — A file used to simulate purchases and bypass real payments. Remove this file for real purchases with the live Nokia Store server.
For details about using this file during testing, see here
QML
Inside main.cpp
#ifdef IN_APP_PURCHASE
#include "qiap/qiap.h"
#include <QtDeclarative>
#endif
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
#ifdef IN_APP_PURCHASE
qmlRegisterType<QIap>("IAP", 1, 0, "QIap");
#endif
.....
return app.exec();
}
QML file
import IAP 1.0
import Qt 4.7
import QtQuick 1.0
Rectangle {
id:main
anchors.fill: parent;
Button {
id: photo
text: "Buy Photo"
MouseArea {
anchors.fill: parent
onClicked: {
iap_manager.purchaseProductByName("SBK Photo")
}
}
}
Text {
id:text_purchased
}
}
QIap {
id:iap_manager
onPurchaseCompleted: {
console.log("onPurchaseCompleted")
console.log(">"+status)
console.log(">"+productID)
if( status==="OK") {
if( productID === "813279" ) {
text_purchased.text = iap_manager.getDRMFileContent(productID,"video.csv");
} else
if( productID === "821073" ) {
text_purchased.text = iap_manager.getDRMFileContent(productID,"photo.txt");
}
}
}
}
}
QT
Inside mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui/QMainWindow>
#ifdef IN_APP_PURCHASE
#include "qiap.h"
#endif
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
private slots:
#ifdef IN_APP_PURCHASE
private:
QIap* iap_manager;
private slots:
void getProductsCompleted();
void restoreProductsCompleted();
void purchaseFlowFinished(int);
void purchaseCompleted(QString status, QString productID);
void itemRestored(QString);
#endif
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
#ifdef IN_APP_PURCHASE
iap_manager = new QIap(this);
connect(iap_manager, SIGNAL(getProductsCompleted()), this, SLOT(getProductsCompleted()));
connect(iap_manager, SIGNAL(restoreProductsCompleted()), this, SLOT(restoreProductsCompleted()));
connect(iap_manager, SIGNAL(itemRestored(QString)), this, SLOT(itemRestored(QString)));
connect(iap_manager,SIGNAL(purchaseCompleted(QString, QString)),this,SLOT(purchaseCompleted(QString, QString)));
connect(iap_manager,SIGNAL(purchaseFlowFinished(int)),this,SLOT(purchaseFlowFinished(int)));
#endif
}
void MainWindow::purchaseProductByID(QString productId)
{
iap_manager->purchaseProductByID(productId, IAPClient::ForcedAutomaticRestoration);
}
void MainWindow::purchaseProductByName(QString productName)
{
if( iap_manager->purchaseProductByName(productName, IAPClient::ForcedAutomaticRestoration) == -1 )
QMessageBox::critical(this,"IAP Error","In App Purchase Error.\nMay be product name isn't correct.");
}
purchaseProductByID is the method to purchase a product by ID.
purchaseProductByName is the method to purchase a product by Name you gave into ovi publisher panel.
It's better to use IAPClient::ForcedAutomaticRestoration parameter.
void MainWindow::purchaseCompleted(QString status, QString productID){
qDebug() << "purchaseCompleted: status :" << status;
qDebug() << "purchaseCompleted: item ID:" << productID;
if( status == "OK"){
QString fileName = "/resourceid_"+productID+"/";
switch( productID.toInt() ){
case 813279 : fileName.append("video.csv"); break;
case 821073 : fileName.append("photo.txt"); break;
default : break;
}
// DRMFile file;
QString filepath = QApplication::applicationDirPath();
filepath.append("/drm/data");
filepath.append(fileName);
uchar* buffer = NULL;
int len = iap_manager->getDRMFileContent(filepath, buffer);
if(len>0) {
QString content = QString::fromAscii((char*)buffer,len);
qDebug() << content;
QMessageBox::information(this,"IAP Example", content);
} else {
qDebug() << "Error reading DRM file";
QMessageBox::critical(this,"IAP Example", "Error reading DRM file");
}
delete buffer;
} else
QMessageBox::critical(this,"IAP Error", status);
}
purchaseCompleted occurs when purchase process is completed retrieving information about transaction status and managed ID product. Status is Cancel if user aborts purchasing.
void MainWindow::purchaseFlowFinished(int value){
qDebug() << "purchaseFlowFinished:"<<QString::number(value);
qDebug() << "purchaseFlowFinished item ID:"<<iap_manager->currentPruductID();
}
purchaseFlowFinished occurs when user closes IAP window and control returns to main GUI.
Restoration Process
Restoration is the process to recover products already purchased in the past
You can use QIap::restoreProducts() to restore all purchased products.
Signal itemRestored(QString) is emitted for each restored item.
Signal restoreProductsCompleted is emitted on restore process terminated.
Qt C++
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
connect(iap_manager, SIGNAL(itemRestored(QString)), this, SLOT(itemRestored(QString)));
connect(iap_manager, SIGNAL(restoreProductsCompleted()), this, SLOT(restoreProductsCompleted()));
}
void MainWindow::itemRestored(QString productID) {
qDebug() << "Restoring item :" << productID;
}
void MainWindow::restoreProductsCompleted(){
qDebug() << "Restore: process terminated";
}
QML
MenuItem {
text: qsTr("Restore purchased products");
onClicked: {
iap_manager.restoreProducts();
}
}
onRestoreProductsCompleted: {
console.log("onRestoredCompleted")
if( status==="OK") {
if( productID === "854595" ) {
fullVersion = true
}
}
}
Source code
Available the full basic example code here
Links
Featured Samples
Superbike
- Superbike. App dedicated to World Superbike Championship. QIap is used to easily perform the sale of contents.
Blacklist Manager
- Blacklist Manager. App to blacklist unwanted phone calls. QIap is used to switch to full version.








Contents
Khurram.saeed - How to Test this api in Test Mode
In order to test QIAP do I have to make the following changes only in the .pro file?
Will this change be enough to see whether it works in test mode or not provided I have added the TEST_MODE.TXT file also.khurram.saeed 10:01, 23 May 2012 (EEST)
Galazzo - Test Mode
Hi Khurram
no, this is the correct configuration for test mode:
IN_APP_PURCHASE enables the component, useful if you want to build an app also for Symbian^1 devices that don't provide In App Purchase.
IA_PURCHASE_TEST_MODE add the TEST_MODE.txt file that enables the test modeIN_APP_PURCHASE_DEBUG enables qDebug() messages from component.
galazzo 12:29, 13 June 2012 (EEST)
Khurram.saeed -
Hi Gallzao,
I am getting the following exceptions : [Qt Message] exception on 3 [will close the socket handle - hack] [Qt Message] exception on 7 [will close the socket handle - hack] [Qt Message] exception on 6 [will close the socket handle - hack]
Please could you tell what do they mean?
Thanks,
Moizkhurram.saeed 14:36, 15 June 2012 (EEST)
Galazzo -
Hi
Sockets let me think to connection problems, anyway send me the code you are using and more details on device, os and configuration.
Sebastianogalazzo 09:12, 18 June 2012 (EEST)
Khurram.saeed -
I am using Nokia n8-00 for testing purposes. My code is very similar to yours. I am redirecting the user to a new page when the user clicks buy button. In the Component.OnCompleted function I am calling the following function:
I shoud tell you that the problem does not appear the first time I run this line. But it comes after I cancel it the whole process and then run it again. It happens usually on the 3rd or 4th run.
Thanks,
Moizkhurram.saeed 10:34, 18 June 2012 (EEST)
Khurram.saeed -
One more thing. Which is rather more important. I sent my latest build (with the error still unfixed) to the Nokia Publishing for QA. Their reply was: "We noticed you are declaring an old IAP dependency in your .pro file. Please update to the latest Qt IAP SDK found on the IAP landing page http://www.developer.nokia.com/Distribute/In-app_purchasing/ to automatically get the latest dependency declarations. That is, it is now defined in the inapppurchase.prf file bundled with the SDK, and for example, is included by the following entry in the .pro file:
CONFIG += mobility \
The current minimum version is 3.23.7
Please note that in the future we will no longer be accepting apps that are declaring dependency on an older version of the IAP library."
I am using Qt Sdk 1.1 and have done everything mentioned in the tutorial above. What could they possibly mean?
Thankskhurram.saeed 10:36, 18 June 2012 (EEST)
Galazzo -
Hi
It's a common problem and not well documented at all.
You have to update your IAP libraries.
Unfortunately there is no backward compatibility!!!
You need to update the repository using the URL provided by instructions link and after update the library.
I will update the wiki with detailed instructions as soon as possible.
Sebastianogalazzo 11:01, 18 June 2012 (EEST)
Khurram.saeed -
Hi
I have updated the library for the QtSdk InApp Purchase. But if I want to implement IAP through your wrapper, does that mean I have to replace "drmfile.h", "drmfile.cpp","drmfile_p.h" and "drmfile_p.cpp"in the qiap folder that you made in the project? I am really stuck at this. Any help would be highly appreciated.
What other changes might occur in the pro file? Also I believe the inapp-purchase.pri file's content has to be copied in the pro file... Is that true?
Thanks, Moiz
Galazzo -
Hi
you have to change nothing, just update the QtSDK.
Why do you think to change drmfile* files ? I can confirm there are published applications using the wrapper unchanged ad QtSDK updated.
As I wrote to you I should look at your code to try to help you and find the problem, so to better help you send me a portion of code that reproduce your problem.
Sebastianogalazzo 12:09, 20 June 2012 (EEST)
Sohz -
Hi
I wanted to know how does the function getProducts() works exactly? From where does it fetch all the products? Basically I want to somehow fetch all the products available from Nokia Store for a particular application. These products can be added at a later stage too, so I cannot keep content Ids in the data folder for the products that are yet to come. How can i recieve products list from store?
Thanks,
Moizsohz 17:32, 28 June 2012 (EEST)
Galazzo - getProducts()
Hi Sohz
getProducts() reads the /drm/data content in which you should have filled with the instrunctions above. Something like that:
.. /data/resourceid_<productID>/
Where productID is the id the code provided by Nokia during item registration on Ovi Publisher panel. These items are matched into the Ovi Store and related informations retrieved. Basically this is because you can have more than one item for an app and more than one app for an account, so you ask informations just for items you need.
You can add new items on your account, but due the fact the items to read are inside the application ( the data directory ) you have to update your application to manage new contents.
Let me know if I answered good to your questions.
Sebastianogalazzo 18:25, 28 June 2012 (EEST)
Sohz -
Hi
Thanks for the detailed reply Sebastiano. I have found a solution which takes me away from this wrapper class. Actually what I wanted was to fetch productsId from my own backend server and then in order to show the description to the user, I had to make one more call to Nokia store to fetch the meta deta of the Product Id. The quickhit example has implemented this and I was able to get it from there. It would be better if you can integrate the backend server implementation :)
Thanks alot for your replies.
Thanks,
Moizsohz 08:37, 3 July 2012 (EEST)