iapurchase.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /**
  2. * Copyright (c) 2011 Nokia Corporation.
  3. */
  4. #include "iapurchase.h"
  5. #include <QDebug>
  6. #include <QDesktopServices>
  7. #include <QtCore/QMetaType>
  8. #include <QtNetwork/QNetworkReply>
  9. #include <QTimer>
  10. #include <QUrl>
  11. #include "downloadmanager.h"
  12. #include "product.h"
  13. Q_DECLARE_METATYPE(IAPClient::ProductDataList)
  14. /*!
  15. \class IAPurchase
  16. \brief The main class for managing IAP. Provides the interface for QML code.
  17. */
  18. /*!
  19. Constructor.
  20. */
  21. IAPurchase::IAPurchase(QObject *parent) :
  22. QObject(parent),
  23. m_iapClient(0),
  24. m_downloadMgr(0),
  25. m_buyProductRequestId(0),
  26. m_invalidProductPurchaseRequestId(0),
  27. m_iapReady(false)
  28. {
  29. // Register new metatypes so that these can be queued in the signal
  30. qRegisterMetaType<Request::RequestType>("Request::RequestType");
  31. // Construct the download manager and connect its signals.
  32. m_downloadMgr = new DownloadManager(this);
  33. connect(m_downloadMgr, SIGNAL(downloadCompleted(Request::RequestType, QString, QString, int)),
  34. this, SLOT(downloadCompleted(Request::RequestType, QString, QString, int)));
  35. connect(m_downloadMgr, SIGNAL(productIdsCompleted(QList<QObject*>)),
  36. this, SLOT(productIdsCompleted(QList<QObject*>)));
  37. connect(m_downloadMgr, SIGNAL(downloadProgress(QString,qint64,qint64)),
  38. this, SLOT(onDownloadProgress(QString,qint64,qint64)));
  39. // Delayed creation
  40. QTimer::singleShot(500, this, SLOT(createIAP()));
  41. }
  42. /*!
  43. Property getter. Returns true if IAP is ready, false otherwise.
  44. */
  45. bool IAPurchase::iapready() const
  46. {
  47. return m_iapReady;
  48. }
  49. /*!
  50. Returns product data at \a index as QVariant.
  51. */
  52. QVariant IAPurchase::productDataAt(int index)
  53. {
  54. if (index >= 0 && index < m_productCache.count()) {
  55. return QVariant::fromValue(m_productCache[index]);
  56. }
  57. // Invalid index!
  58. return QVariant();
  59. }
  60. /*!
  61. Returns the product data count in product cache list.
  62. */
  63. int IAPurchase::productDataCount()
  64. {
  65. return m_productCache.length();
  66. }
  67. /*!
  68. Cancels all requests and downloads.
  69. */
  70. void IAPurchase::cancel()
  71. {
  72. m_downloadMgr->cancel();
  73. }
  74. /*!
  75. Retrieves product specific data from the back-end by \a productName.
  76. */
  77. void IAPurchase::getIapDataFromBackend(QString productName)
  78. {
  79. QString request = "http://fn-iap-repo.cloudapp.net/api/" + productName;
  80. m_downloadMgr->requestProductsIds(QUrl(request));
  81. // Response to productIdsCompleted()
  82. }
  83. /*!
  84. Retrieves product specific data from the Nokia Store by \a iapId.
  85. */
  86. void IAPurchase::getIapDataFromStore(QString iapId)
  87. {
  88. Product *product = productFromCacheByProductId(iapId);
  89. if (product) {
  90. int requestId = m_iapClient->getProductData(iapId);
  91. if (requestId > 0) {
  92. product->setReqId(requestId);
  93. m_productCache.append(product);
  94. }
  95. else {
  96. emit productDataReadError();
  97. }
  98. }
  99. else {
  100. emit productDataReadError();
  101. }
  102. }
  103. /*!
  104. Tries to buy a product with \a iapId.
  105. */
  106. void IAPurchase::buyProduct(QString iapId)
  107. {
  108. if (m_buyProductRequestId) {
  109. // Purchase ongoing, reject this request
  110. emit busy();
  111. return;
  112. }
  113. Product *product = productFromCacheByProductId(iapId);
  114. if (!product) {
  115. // Product does not exist!
  116. emit productDoesNotExists(iapId);
  117. return;
  118. }
  119. // Buy the product
  120. int requestId = m_iapClient->purchaseProduct(iapId, IAPClient::ForcedAutomaticRestoration);
  121. product->setReqId(requestId);
  122. if (requestId > 0) {
  123. // Request ok
  124. m_buyProductRequestId = requestId;
  125. }
  126. else {
  127. // Error
  128. m_buyProductRequestId = 0;
  129. emit productPurchaseError();
  130. }
  131. }
  132. /*!
  133. Sets the product cache. This slot is called when the DownloadManager has
  134. retrieved the product IDs.
  135. */
  136. void IAPurchase::productIdsCompleted(QList<QObject*> productList)
  137. {
  138. m_productCache = productList;
  139. emit iapBackendDataReaded();
  140. }
  141. /*!
  142. Returns a product from the cache by \a productId or NULL if not found.
  143. */
  144. Product *IAPurchase::productFromCacheByProductId(QString productId)
  145. {
  146. for (int i = 0; i < m_productCache.count(); i++) {
  147. Product *p = (Product*)m_productCache[i];
  148. if (p->id() == productId) {
  149. return p;
  150. }
  151. }
  152. return 0;
  153. }
  154. /*!
  155. Returns a product from the cache by \a requestId or NULL if not found.
  156. */
  157. Product *IAPurchase::productFromCacheByRequestId(int requestId)
  158. {
  159. for (int i = 0; i < m_productCache.count(); i++) {
  160. Product *p = (Product*)m_productCache[i];
  161. if (p->reqId() == requestId) {
  162. return p;
  163. }
  164. }
  165. return 0;
  166. }
  167. /*!
  168. Constructs the IAP client and connects its signals to slots of this class.
  169. */
  170. void IAPurchase::createIAP()
  171. {
  172. m_iapClient = new IAPClient(this);
  173. // Connect IAP API's signals to slots
  174. connect(m_iapClient, SIGNAL(productDataReceived(int,QString,IAPClient::ProductDataHash)),
  175. this, SLOT(productDataReceived(int,QString,IAPClient::ProductDataHash)));
  176. connect(m_iapClient, SIGNAL(purchaseCompleted(int,QString, QString)),
  177. this, SLOT(purchaseCompleted(int,QString,QString)));
  178. connect(m_iapClient, SIGNAL(purchaseFlowFinished(int)),
  179. this, SLOT(purchaseFlowFinished(int)));
  180. // Notify that IAP is ready.
  181. m_iapReady = true;
  182. emit iapCreatedAndReady(m_iapReady);
  183. }
  184. /*!
  185. Symbian SIS downloading progress
  186. */
  187. void IAPurchase::onDownloadProgress(QString productId, qint64 bytesReceived, qint64 bytesTotal)
  188. {
  189. emit downloadProgress(productId, bytesReceived, bytesTotal);
  190. }
  191. /*!
  192. If download of a .sis file was successful, starts installing the file.
  193. Otherwise a signal indicating an error is emitted.
  194. */
  195. void IAPurchase::downloadCompleted(Request::RequestType type,
  196. QString productId,
  197. QString fileName,
  198. int error)
  199. {
  200. if (error == QNetworkReply::NoError) {
  201. // OK
  202. switch (type) {
  203. case Request::EDownloadSis: {
  204. // Symbian SIS downloaded and it's time to install it
  205. emit downloaded(productId);
  206. emit installingProduct(productId);
  207. // Install Symbian sis file
  208. QDesktopServices::openUrl(QUrl("file:///" + fileName, QUrl::TolerantMode));
  209. break;
  210. }
  211. case Request::EProductIds: {
  212. // No action
  213. break;
  214. }
  215. };
  216. }
  217. else {
  218. // ERROR
  219. switch (type) {
  220. case Request::EDownloadSis: {
  221. emit downloadingError();
  222. break;
  223. }
  224. case Request::EProductIds: {
  225. emit iapBackendDataReadError();
  226. break;
  227. }
  228. };
  229. }
  230. }
  231. /*!
  232. This slot is called when a signal is emitted by the IAP client to indicate
  233. that the product data has been received.
  234. */
  235. void IAPurchase::productDataReceived(int requestId,
  236. QString status,
  237. IAPClient::ProductDataHash productData)
  238. {
  239. if (QString::compare(status, "OK", Qt::CaseInsensitive) == 0) {
  240. // Find product request from our cache
  241. Product *product = productFromCacheByRequestId(requestId);
  242. if (product) {
  243. // Store product data from the Store to the data from the back-end.
  244. product->setProductData(productData);
  245. emit productDataRead(product);
  246. }
  247. else {
  248. emit productDataReadError();
  249. }
  250. }
  251. else {
  252. emit productDataReadError();
  253. }
  254. }
  255. /*!
  256. Starts downloading the product related .sis file from the back-end (if the
  257. purchase was successful).
  258. */
  259. void IAPurchase::purchaseCompleted(int requestId, QString status, QString purchaseTicket)
  260. {
  261. Product *product = 0;
  262. if (QString::compare(status, "OK", Qt::CaseInsensitive) == 0) {
  263. // Find product request from our cache
  264. product = productFromCacheByRequestId(requestId);
  265. if (product) {
  266. // Purchase done
  267. emit productPurchaseDone(product->id());
  268. // Start downloading level plugins AND send purchase ticket to
  269. // the back-end server
  270. emit downloadingProduct(product->id());
  271. QString productIdForDownload = product->id();
  272. QString request =
  273. "http://fn-iap-repo.cloudapp.net/api/quickhit/"
  274. + productIdForDownload;
  275. request = request.trimmed();
  276. QUrl url(request,QUrl::StrictMode);
  277. m_downloadMgr->downloadSis(productIdForDownload, purchaseTicket, url);
  278. }
  279. else {
  280. // Error
  281. m_invalidProductPurchaseRequestId = m_buyProductRequestId;
  282. productPurchaseError();
  283. }
  284. }
  285. else {
  286. // Error
  287. m_invalidProductPurchaseRequestId = m_buyProductRequestId;
  288. productPurchaseError();
  289. }
  290. }
  291. /*!
  292. This slot gets called when the IAP client emits purchaseFlowFinished()
  293. signal.
  294. */
  295. void IAPurchase::purchaseFlowFinished(int requestId)
  296. {
  297. m_buyProductRequestId = 0;
  298. m_invalidProductPurchaseRequestId = 0;
  299. }