Merge lp:~dobey/pay-ui/iap-support into lp:pay-ui

Proposed by dobey
Status: Merged
Approved by: dobey
Approved revision: 134
Merged at revision: 136
Proposed branch: lp:~dobey/pay-ui/iap-support
Merge into: lp:pay-ui
Prerequisite: lp:~dobey/pay-ui/fix-page-returns
Diff against target: 330 lines (+88/-30)
5 files modified
app/payui.qml (+2/-2)
backend/modules/payui/network.cpp (+52/-13)
backend/modules/payui/network.h (+5/-4)
backend/modules/payui/purchase.cpp (+20/-2)
backend/tests/test_network.cpp (+9/-9)
To merge this branch: bzr merge lp:~dobey/pay-ui/iap-support
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Charles Kerr (community) Approve
Review via email: mp+276310@code.launchpad.net

This proposal supersedes a proposal from 2015-09-21.

Commit message

Support for performing in-app purchases.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote : Posted in a previous version of this proposal

No blockers, a couple of suggestions inline

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/payui.qml'
2--- app/payui.qml 2015-10-30 19:32:58 +0000
3+++ app/payui.qml 2015-10-30 19:32:58 +0000
4@@ -89,7 +89,6 @@
5 checkout.itemTitle = title;
6 checkout.itemSubtitle = publisher;
7 checkout.price = formatted_price;
8- checkCredentials();
9 }
10
11 onPaymentTypesObtained: {
12@@ -176,6 +175,7 @@
13 } else if (mainView.state != "checkout" && !mainView.purchasing && mainView.state != "buy-interaction") {
14 purchase.getPaymentTypes(suggestedCurrency);
15 }
16+ purchase.getItemDetails();
17 }
18
19 onItemNotPurchased: {
20@@ -357,7 +357,7 @@
21 Component.onCompleted: {
22 showLoading();
23 mainView.createDB();
24- purchase.getItemDetails();
25+ purchase.checkCredentials();
26 }
27
28 onCurrentPageChanged: {
29
30=== modified file 'backend/modules/payui/network.cpp'
31--- backend/modules/payui/network.cpp 2015-02-06 21:57:58 +0000
32+++ backend/modules/payui/network.cpp 2015-10-30 19:32:58 +0000
33@@ -1,5 +1,5 @@
34 /*
35- * Copyright 2014 Canonical Ltd.
36+ * Copyright 2014-2015 Canonical Ltd.
37 *
38 * This library is free software; you can redistribute it and/or
39 * modify it under the terms of version 3 of the GNU Lesser General Public
40@@ -38,6 +38,8 @@
41 #define PAY_PAYMENTMETHODS_ADD_PATH PAY_PAYMENTMETHODS_PATH + "/add"
42 #define SUGGESTED_CURRENCY_HEADER_NAME "X-Suggested-Currency"
43
44+#define DEVICE_ID_HEADER "X-Device-Id"
45+
46 #define PARTNER_ID_HEADER "X-Partner-ID"
47 #define PARTNER_ID_FILE "/custom/partner-id"
48
49@@ -208,9 +210,18 @@
50 } else if (state->operation.contains(ITEM_INFO) && document.isObject()) {
51 qDebug() << "Reply state: ITEM_INFO";
52 QJsonObject object = document.object();
53+ QString icon;
54+ QString publisher;
55+ if (object.contains("publisher")) {
56+ publisher = object.value("publisher").toString();
57+ }
58+ if (object.contains("icon_url")) {
59+ icon = object.value("icon_url").toString();
60+ } else if (object.contains("icon")) {
61+ icon = object.value("icon").toString();
62+ }
63+
64 QString title = object.value("title").toString();
65- QString publisher = object.value("publisher").toString();
66- QString icon = object.value("icon_url").toString();
67
68 QJsonObject prices = object.value("prices").toObject();
69 QString suggested_currency = DEFAULT_CURRENCY;
70@@ -226,13 +237,25 @@
71 if (isSupportedCurrency(suggested_currency) && prices.contains(suggested_currency)) {
72 currency = suggested_currency;
73 }
74- double price = prices[currency].toDouble();
75+ double price = 0.00;
76+ if (prices[currency].isDouble()) {
77+ price = prices[currency].toDouble();
78+ } else if (prices[currency].isString()) {
79+ price = prices[currency].toString().toDouble();
80+ }
81 QLocale locale;
82 QString formatted_price = locale.toCurrencyString(price, getSymbolForCurrency(currency));
83 qDebug() << "Sending signal: itemDetailsObtained: " << title << " " << formatted_price;
84 Q_EMIT itemDetailsObtained(title, publisher, currency, formatted_price, icon);
85 } else if (state->operation.contains(CHECK_PURCHASED)) {
86- Q_EMIT buyItemSucceeded();
87+ QJsonObject object = document.object();
88+ auto state = object.value("state").toString();
89+ if (state == "Complete" || state == "purchased "||
90+ state == "approved") {
91+ Q_EMIT buyItemSucceeded();
92+ } else {
93+ Q_EMIT itemNotPurchased();
94+ }
95 } else {
96 qDebug() << "Reply received for non valid state.";
97 QString message("Reply received for non valid state.");
98@@ -314,8 +337,10 @@
99 qDebug() << "Payment" << m_selectedAppId << m_selectedBackendId << m_selectedPaymentId;
100 QJsonObject serializer;
101
102- serializer.insert("device_id", getDeviceId());
103 serializer.insert("name", m_selectedAppId);
104+ if (!m_selectedItemId.isEmpty()) {
105+ serializer.insert("item_sku", m_selectedItemId);
106+ }
107 serializer.insert("backend_id", m_selectedBackendId);
108 serializer.insert("method_id", m_selectedPaymentId);
109 serializer.insert("currency", m_currency);
110@@ -325,6 +350,7 @@
111
112 QNetworkRequest request;
113 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
114+ request.setRawHeader(DEVICE_ID_HEADER, getDeviceId().toUtf8().data());
115
116 // Get the partner ID and add it to the request.
117 QByteArray partner_id = getPartnerId();
118@@ -368,17 +394,25 @@
119 return reply.value();
120 }
121
122-void Network::getItemInfo(const QString &packagename)
123+void Network::getItemInfo(const QString& packagename, const QString& sku)
124 {
125- QUrl url(getSearchApiUrl(QString(SEARCH_API_ROOT) + "/package/" + packagename));
126+ QUrl url;
127+
128+ if (sku.isEmpty()) {
129+ url = getSearchApiUrl(QString(SEARCH_API_ROOT) + "/package/" + packagename);
130+ qDebug() << "Request Item Info:" << url;
131+ QUrlQuery query;
132+ query.addQueryItem("fields", "title,description,price,icon_url");
133+ url.setQuery(query);
134+ } else {
135+ url = getPayApiUrl(QString(IAP_API_ROOT) + "/packages/" + packagename + "/items/by-sku/" + sku);
136+ }
137 qDebug() << "Request Item Info:" << url;
138- QUrlQuery query;
139- query.addQueryItem("fields", "title,description,price,icon_url");
140- url.setQuery(query);
141
142 QNetworkRequest request;
143 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
144 request.setUrl(url);
145+ signRequestUrl(request, url.toString(), QStringLiteral("GET"));
146 RequestObject* reqObject = new RequestObject(QString(ITEM_INFO));
147 request.setOriginatingObject(reqObject);
148 m_nam.get(request);
149@@ -427,9 +461,14 @@
150 return m_token.updated();
151 }
152
153-void Network::checkItemPurchased(const QString& appid)
154+void Network::checkItemPurchased(const QString& appid, const QString& sku)
155 {
156- QUrl url(getPayApiUrl(QString(PAY_API_ROOT) + PAY_PURCHASES_PATH + "/" + appid + "/"));
157+ QUrl url;
158+ if (sku.isEmpty()) {
159+ url = getPayApiUrl(QString(PAY_API_ROOT) + PAY_PURCHASES_PATH + "/" + appid + "/");
160+ } else {
161+ url = getPayApiUrl(QString(IAP_API_ROOT) + "/packages/" + appid + "/items/by-sku/" + sku);
162+ }
163
164 qDebug() << "Checking for previous purchase:" << url;
165 QNetworkRequest request;
166
167=== modified file 'backend/modules/payui/network.h'
168--- backend/modules/payui/network.h 2015-02-06 19:16:42 +0000
169+++ backend/modules/payui/network.h 2015-10-30 19:32:58 +0000
170@@ -1,5 +1,5 @@
171 /*
172- * Copyright 2014 Canonical Ltd.
173+ * Copyright 2014-2015 Canonical Ltd.
174 *
175 * This library is free software; you can redistribute it and/or
176 * modify it under the terms of version 3 of the GNU Lesser General Public
177@@ -37,8 +37,9 @@
178 namespace UbuntuPurchase {
179
180 constexpr static const char* PAY_BASE_URL_ENVVAR{"PAY_BASE_URL"};
181-constexpr static const char* PAY_BASE_URL{"https://software-center.ubuntu.com"};
182+constexpr static const char* PAY_BASE_URL{"https://myapps.developer.ubuntu.com"};
183 constexpr static const char* PAY_API_ROOT{"/api/2.0/click"};
184+constexpr static const char* IAP_API_ROOT{"/inventory/api/v1"};
185 constexpr static const char* CURRENCY_ENVVAR {"U1_SEARCH_CURRENCY"};
186 constexpr static const char* SEARCH_BASE_URL_ENVVAR{"U1_SEARCH_BASE_URL"};
187 constexpr static const char* SEARCH_BASE_URL{"https://search.apps.ubuntu.com"};
188@@ -72,14 +73,14 @@
189 const QString& otp,
190 const QString& appid, const QString& itemid, const QString& currency,
191 bool recentLogin);
192- void getItemInfo(const QString &packagename);
193+ void getItemInfo(const QString& packagename, const QString& sku);
194 void checkPassword(const QString& email, const QString& password,
195 const QString& otp, bool purchasing=false);
196 void getCredentials();
197 void setCredentials(Token token);
198 QString getAddPaymentUrl(const QString& currency);
199 QDateTime getTokenUpdated();
200- void checkItemPurchased(const QString& appid);
201+ void checkItemPurchased(const QString& appid, const QString& sku);
202 static QString getSymbolForCurrency(const QString& currency_code);
203 static bool isSupportedCurrency(const QString& currency_code);
204 static QString sanitizeUrl(const QUrl& url);
205
206=== modified file 'backend/modules/payui/purchase.cpp'
207--- backend/modules/payui/purchase.cpp 2014-12-02 04:34:04 +0000
208+++ backend/modules/payui/purchase.cpp 2015-10-30 19:32:58 +0000
209@@ -1,3 +1,21 @@
210+/*
211+ * Copyright 2014-2015 Canonical Ltd.
212+ *
213+ * This library is free software; you can redistribute it and/or
214+ * modify it under the terms of version 3 of the GNU Lesser General Public
215+ * License as published by the Free Software Foundation.
216+ *
217+ * This program is distributed in the hope that it will be useful,
218+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
219+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
220+ * General Public License for more details.
221+ *
222+ * You should have received a copy of the GNU Lesser General Public
223+ * License along with this library; if not, write to the
224+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
225+ * Boston, MA 02110-1301, USA.
226+ */
227+
228 #include "purchase.h"
229 #include <QUrl>
230 #include <QUrlQuery>
231@@ -93,7 +111,7 @@
232 quitCancel();
233 }
234 qDebug() << "Getting Item Details";
235- m_network.getItemInfo(m_appid);
236+ m_network.getItemInfo(m_appid, m_itemid);
237 }
238
239 void Purchase::getPaymentTypes(const QString& currency)
240@@ -123,7 +141,7 @@
241
242 void Purchase::checkItemPurchased()
243 {
244- m_network.checkItemPurchased(m_appid);
245+ m_network.checkItemPurchased(m_appid, m_itemid);
246 }
247
248 }
249
250=== modified file 'backend/tests/test_network.cpp'
251--- backend/tests/test_network.cpp 2015-02-06 21:57:58 +0000
252+++ backend/tests/test_network.cpp 2015-10-30 19:32:58 +0000
253@@ -1,5 +1,5 @@
254 /*
255- * Copyright 2014 Canonical Ltd.
256+ * Copyright 2014-2015 Canonical Ltd.
257 *
258 * This library is free software; you can redistribute it and/or
259 * modify it under the terms of version 3 of the GNU Lesser General Public
260@@ -196,7 +196,7 @@
261 setenv(SEARCH_BASE_URL_ENVVAR, "http://localhost:8000/iteminfo/", 1);
262 unsetenv(CURRENCY_ENVVAR);
263 QSignalSpy spy(&network, SIGNAL(itemDetailsObtained(QString,QString,QString,QString,QString)));
264- network.getItemInfo("packagename");
265+ network.getItemInfo("packagename", "");
266 QTRY_COMPARE(spy.count(), 1);
267 QList<QVariant> arguments = spy.takeFirst();
268 QCOMPARE(arguments.at(2).toString(), QStringLiteral("USD"));
269@@ -208,7 +208,7 @@
270 setenv(SEARCH_BASE_URL_ENVVAR, "http://localhost:8000/iteminfo/eurozone/", 1);
271 unsetenv(CURRENCY_ENVVAR);
272 QSignalSpy spy(&network, SIGNAL(itemDetailsObtained(QString,QString,QString,QString,QString)));
273- network.getItemInfo("packagename");
274+ network.getItemInfo("packagename", "");
275 QTRY_COMPARE(spy.count(), 1);
276 QList<QVariant> arguments = spy.takeFirst();
277 QCOMPARE(arguments.at(2).toString(), QStringLiteral("EUR"));
278@@ -220,7 +220,7 @@
279 setenv(SEARCH_BASE_URL_ENVVAR, "http://localhost:8000/iteminfo/dotar/", 1);
280 unsetenv(CURRENCY_ENVVAR);
281 QSignalSpy spy(&network, SIGNAL(itemDetailsObtained(QString,QString,QString,QString,QString)));
282- network.getItemInfo("packagename");
283+ network.getItemInfo("packagename", "");
284 QTRY_COMPARE(spy.count(), 1);
285 QList<QVariant> arguments = spy.takeFirst();
286 QCOMPARE(arguments.at(2).toString(), QStringLiteral("USD"));
287@@ -232,7 +232,7 @@
288 setenv(SEARCH_BASE_URL_ENVVAR, "http://localhost:8000/iteminfo/", 1);
289 setenv(CURRENCY_ENVVAR, "EUR", true);
290 QSignalSpy spy(&network, SIGNAL(itemDetailsObtained(QString,QString,QString,QString,QString)));
291- network.getItemInfo("packagename");
292+ network.getItemInfo("packagename", "");
293 QTRY_COMPARE(spy.count(), 1);
294 QList<QVariant> arguments = spy.takeFirst();
295 QCOMPARE(arguments.at(2).toString(), QStringLiteral("EUR"));
296@@ -245,7 +245,7 @@
297 setenv(SEARCH_BASE_URL_ENVVAR, "http://localhost:8000/iteminfo/dotar/", 1);
298 setenv(CURRENCY_ENVVAR, "EUR", true);
299 QSignalSpy spy(&network, SIGNAL(itemDetailsObtained(QString,QString,QString,QString,QString)));
300- network.getItemInfo("packagename");
301+ network.getItemInfo("packagename", "");
302 QTRY_COMPARE(spy.count(), 1);
303 QList<QVariant> arguments = spy.takeFirst();
304 QCOMPARE(arguments.at(2).toString(), QStringLiteral("EUR"));
305@@ -258,7 +258,7 @@
306 setenv(SEARCH_BASE_URL_ENVVAR, "http://localhost:8000/fail/iteminfo/", 1);
307 unsetenv(CURRENCY_ENVVAR);
308 QSignalSpy spy(&network, SIGNAL(error(QString)));
309- network.getItemInfo("packagename");
310+ network.getItemInfo("packagename", "");
311 QTRY_COMPARE(spy.count(), 1);
312 }
313
314@@ -274,7 +274,7 @@
315 {
316 setenv(PAY_BASE_URL_ENVVAR, "http://localhost:8000/", 1);
317 QSignalSpy spy(&network, SIGNAL(buyItemSucceeded()));
318- network.checkItemPurchased("com.example.fakeapp");
319+ network.checkItemPurchased("com.example.fakeapp", "");
320 QTRY_COMPARE(spy.count(), 1);
321 }
322
323@@ -282,7 +282,7 @@
324 {
325 setenv(PAY_BASE_URL_ENVVAR, "http://localhost:8000/notpurchased/", 1);
326 QSignalSpy spy(&network, SIGNAL(itemNotPurchased()));
327- network.checkItemPurchased("com.example.fakeapp");
328+ network.checkItemPurchased("com.example.fakeapp", "");
329 QTRY_COMPARE(spy.count(), 1);
330 }
331

Subscribers

People subscribed via source and target branches

to all changes: