iapurchase.cpp 11 KB

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