diff options
author | Liang Qi <liang.qi@qt.io> | 2016-10-28 06:59:28 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2016-10-28 06:59:36 +0200 |
commit | 68ca35302901a5b9e52b3dd068aa8d34ccea4f42 (patch) | |
tree | b11be19fee497b24f5ed774baee133e187aec243 | |
parent | 2adefcb818a33a51a1639f37b907ee67dfbe6077 (diff) | |
parent | cdbf96e2442285562d7e55e4e7072f44a72200c0 (diff) |
Merge remote-tracking branch 'origin/5.8' into dev
Change-Id: I240ebf03b391889dea32f8ed97acf00ed753c020
6 files changed, 139 insertions, 29 deletions
diff --git a/src/imports/purchasing/inapppurchase.cpp b/src/imports/purchasing/inapppurchase.cpp index f20fff9..b0b3e3a 100644 --- a/src/imports/purchasing/inapppurchase.cpp +++ b/src/imports/purchasing/inapppurchase.cpp @@ -39,7 +39,7 @@ QT_BEGIN_NAMESPACE class QInAppPurchaseModule : public QQmlExtensionPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0") + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) public: void registerTypes(const char *uri) { diff --git a/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend.cpp b/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend.cpp index 396123d..37ed8a9 100644 --- a/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend.cpp +++ b/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend.cpp @@ -61,6 +61,13 @@ Q_LOGGING_CATEGORY(lcPurchasingBackend, "qt.purchasing.backend") const QString qt_win_app_identifier = QLatin1String("app"); +inline QString hStringToQString(const HString &h) +{ + unsigned int length; + const wchar_t* raw = h.GetRawBuffer(&length); + return QString::fromWCharArray(raw, length); +} + class QWinRTAppBridge { public: HRESULT activate(); @@ -253,15 +260,13 @@ inline bool compareProductTypes(QInAppProduct::ProductType qtType, ProductType n return false; } -QWinRTInAppTransaction* createTransaction(AsyncStatus status, - QWinRTInAppProduct *product, - QWinRTInAppPurchaseBackend *backend) +void QWinRTInAppPurchaseBackend::createTransactionDelayed(qt_WinRTTransactionData data) { - QInAppTransaction::TransactionStatus qStatus = (status == AsyncStatus::Completed) ? + QInAppTransaction::TransactionStatus qStatus = (data.status == AsyncStatus::Completed) ? QInAppTransaction::PurchaseApproved : QInAppTransaction::PurchaseFailed; QInAppTransaction::FailureReason reason; - switch (status) { + switch (data.status) { case AsyncStatus::Completed: reason = QInAppTransaction::NoFailure; break; @@ -274,8 +279,11 @@ QWinRTInAppTransaction* createTransaction(AsyncStatus status, break; } - auto transaction = new QWinRTInAppTransaction(qStatus, product, reason, backend); - return transaction; + auto transaction = new QWinRTInAppTransaction(qStatus, data.product, reason, data.receipt, this); + transaction->m_purchaseResults = data.purchaseResults; + emit transactionReady(transaction); + + return; } class QWinRTInAppPurchaseBackendPrivate @@ -302,6 +310,9 @@ QWinRTInAppPurchaseBackend::QWinRTInAppPurchaseBackend(QObject *parent) : QInAppPurchaseBackend(parent) { d_ptr.reset(new QWinRTInAppPurchaseBackendPrivate(this)); + + qRegisterMetaType<qt_WinRTTransactionData>("TransactionData"); + qCDebug(lcPurchasingBackend) << __FUNCTION__; } @@ -341,6 +352,16 @@ bool QWinRTInAppPurchaseBackend::isReady() const return !d->m_waitingForList && !d->nativeProducts.isEmpty(); } +inline QString createStringForSubReceipt(const QXmlStreamReader &reader) +{ + QString result; + QXmlStreamWriter writer(&result); + writer.writeStartDocument(); + writer.writeCurrentToken(reader); + writer.writeEndDocument(); + return result; +} + void QWinRTInAppPurchaseBackend::restorePurchases() { qCDebug(lcPurchasingBackend) << __FUNCTION__; @@ -363,8 +384,7 @@ void QWinRTInAppPurchaseBackend::restorePurchases() return; } - quint32 length; - QString parse = QString::fromWCharArray(receipt.GetRawBuffer(&length)); + const QString parse = hStringToQString(receipt); QXmlStreamReader reader(parse); while (reader.readNextStartElement()) { @@ -382,6 +402,8 @@ void QWinRTInAppPurchaseBackend::restorePurchases() return; } + const QString appReceipt = createStringForSubReceipt(reader); + while (!reader.atEnd()) { reader.readNext(); if (reader.attributes().hasAttribute(QLatin1String("ProductId"))) { @@ -395,9 +417,13 @@ void QWinRTInAppPurchaseBackend::restorePurchases() continue; } + + const QString receipt = createStringForSubReceipt(reader); + auto transaction = new QWinRTInAppTransaction(QInAppTransaction::PurchaseRestored, product, QInAppTransaction::NoFailure, + receipt, this); emit transactionReady(transaction); @@ -418,10 +444,16 @@ void QWinRTInAppPurchaseBackend::restorePurchases() qCDebug(lcPurchasingBackend) << "Restoring app product"; QInAppProduct *product = store()->registeredProduct(qt_win_app_identifier); + // App is special and needs explicit registration + if (!product) { + queryProduct(QInAppProduct::Unlockable, qt_win_app_identifier); + product = store()->registeredProduct(qt_win_app_identifier); + } auto transaction = new QWinRTInAppTransaction(QInAppTransaction::PurchaseRestored, product, QInAppTransaction::NoFailure, + appReceipt, this); emit transactionReady(transaction); } @@ -455,7 +487,7 @@ void QWinRTInAppPurchaseBackend::restorePurchases() } quint32 length; - QString receipt = QString::fromWCharArray(receiptString.GetRawBuffer(&length)); + const QString receipt = hStringToQString(receiptString); qDebug() << "Received receipt:" << receipt; // Create new transaction with status == Restored and emit @@ -496,10 +528,9 @@ void QWinRTInAppPurchaseBackend::queryProduct(QInAppProduct::ProductType product return; } - quint32 length; NativeProductInfo *cachedInfo = d->nativeProducts.value(identifier); - QString price = QString::fromWCharArray(cachedInfo->formatPrice.GetRawBuffer(&length)); - QString name = QString::fromWCharArray(cachedInfo->productName.GetRawBuffer(&length)); + const QString price = hStringToQString(cachedInfo->formatPrice); + const QString name = hStringToQString(cachedInfo->productName); QWinRTInAppProduct *appProduct = new QWinRTInAppProduct(this, price, name, @@ -527,12 +558,23 @@ void QWinRTInAppPurchaseBackend::purchaseProduct(QWinRTInAppProduct *product) hr = QEventDispatcherWinRT::runOnXamlThread([d, product, this]() { HRESULT hr; ComPtr<IAsyncOperation<HSTRING>> appOp; - hr = d->m_bridge.RequestAppPurchaseAsync(false, appOp); + hr = d->m_bridge.RequestAppPurchaseAsync(true, appOp); Q_ASSERT_SUCCEEDED(hr); - auto purchaseCallback = Callback<IAsyncOperationCompletedHandler<HSTRING>>([d, product, this](IAsyncOperation<HSTRING> *, AsyncStatus status) + auto purchaseCallback = Callback<IAsyncOperationCompletedHandler<HSTRING>>([d, product, this](IAsyncOperation<HSTRING> *op, AsyncStatus status) { - auto transaction = createTransaction(status, product, this); - emit transactionReady(transaction); + HString receiptH; + QString receiptQ; + HRESULT hr; + hr = op->GetResults(receiptH.GetAddressOf()); + if (SUCCEEDED(hr)) + receiptQ = hStringToQString(receiptH); + else + qWarning("Could not receive transaction receipt."); + + qt_WinRTTransactionData tData(status, product, receiptQ); + QMetaObject::invokeMethod(this, "createTransactionDelayed", Qt::QueuedConnection, + Q_ARG(qt_WinRTTransactionData, tData)); + return S_OK; }); hr = appOp->put_Completed(purchaseCallback.Get()); @@ -543,12 +585,23 @@ void QWinRTInAppPurchaseBackend::purchaseProduct(QWinRTInAppProduct *product) hr = QEventDispatcherWinRT::runOnXamlThread([d, product, &productId, this]() { ComPtr<IAsyncOperation<HSTRING>> purchaseOp; HRESULT hr; - hr = d->m_bridge.RequestProductPurchaseAsync(productId.Get(), false, purchaseOp); + hr = d->m_bridge.RequestProductPurchaseAsync(productId.Get(), true, purchaseOp); Q_ASSERT_SUCCEEDED(hr); - auto purchaseCallback = Callback<IAsyncOperationCompletedHandler<HSTRING>>([d, product, this](IAsyncOperation<HSTRING> *, AsyncStatus status) + auto purchaseCallback = Callback<IAsyncOperationCompletedHandler<HSTRING>>([d, product, this](IAsyncOperation<HSTRING> *op, AsyncStatus status) { - auto transaction = createTransaction(status, product, this); - emit transactionReady(transaction); + HString receiptH; + QString receiptQ; + HRESULT hr; + hr = op->GetResults(receiptH.GetAddressOf()); + if (SUCCEEDED(hr)) + receiptQ = hStringToQString(receiptH); + else + qWarning("Could not receive transaction receipt."); + + qt_WinRTTransactionData tData(status, product, receiptQ); + QMetaObject::invokeMethod(this, "createTransactionDelayed", Qt::QueuedConnection, + Q_ARG(qt_WinRTTransactionData, tData)); + return S_OK; }); hr = purchaseOp->put_Completed(purchaseCallback.Get()); @@ -565,14 +618,23 @@ void QWinRTInAppPurchaseBackend::purchaseProduct(QWinRTInAppProduct *product) auto purchaseCallback = Callback<IAsyncOperationCompletedHandler<PurchaseResults*>>([d, product, this](IAsyncOperation<PurchaseResults*> *op, AsyncStatus status) { - auto transaction = createTransaction(status, product, this); + ComPtr<IPurchaseResults> purchaseResults; + QString receiptQ; if (status == AsyncStatus::Completed) { HRESULT hr; - hr = op->GetResults(&transaction->m_purchaseResults); + hr = op->GetResults(&purchaseResults); + Q_ASSERT_SUCCEEDED(hr); + HString receiptH; + hr = purchaseResults->get_ReceiptXml(receiptH.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); + receiptQ = hStringToQString(receiptH); } - emit transactionReady(transaction); + + qt_WinRTTransactionData tData(status, product, receiptQ, purchaseResults); + QMetaObject::invokeMethod(this, "createTransactionDelayed", Qt::QueuedConnection, + Q_ARG(qt_WinRTTransactionData, tData)); + return S_OK; }); @@ -661,7 +723,7 @@ HRESULT QWinRTInAppPurchaseBackendPrivate::onListingInformation(IAsyncOperation< ComPtr<IProductListing> value; hr = currentItem->get_Key(nativeKey.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); - productKey = QString::fromWCharArray(nativeKey.GetRawBuffer(nullptr)); + productKey = hStringToQString(nativeKey); qCDebug(lcPurchasingBackend) << "ProductKey:" << productKey; hr = currentItem->get_Value(&value); Q_ASSERT_SUCCEEDED(hr); diff --git a/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend_p.h b/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend_p.h index ce83b79..af48908 100644 --- a/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend_p.h +++ b/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend_p.h @@ -44,11 +44,41 @@ #include "qinappproduct.h" #include "qinapptransaction.h" +#include <Windows.ApplicationModel.store.h> +#include <wrl.h> + +namespace ABI { + namespace Windows { + namespace Foundation { + enum class AsyncStatus; + } + } +} + QT_BEGIN_NAMESPACE class QWinRTInAppProduct; class QWinRTInAppPurchaseBackendPrivate; class QWinRTInAppTransaction; +class QWinRTInAppPurchaseBackend; + +struct qt_WinRTTransactionData +{ + qt_WinRTTransactionData() { } + explicit qt_WinRTTransactionData(ABI::Windows::Foundation::AsyncStatus s, + QWinRTInAppProduct *p, + const QString &r, + Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::Store::IPurchaseResults> pRes = Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::Store::IPurchaseResults>()) + : status(s) + , product(p) + , receipt(r) + , purchaseResults(pRes) + { } + ABI::Windows::Foundation::AsyncStatus status; + QWinRTInAppProduct *product; + QString receipt; + Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::Store::IPurchaseResults> purchaseResults; +}; class QWinRTInAppPurchaseBackend : public QInAppPurchaseBackend { @@ -68,6 +98,8 @@ public: void purchaseProduct(QWinRTInAppProduct *product); void fulfillConsumable(QWinRTInAppTransaction *transaction); +public slots: + void createTransactionDelayed(qt_WinRTTransactionData data); private: QScopedPointer<QWinRTInAppPurchaseBackendPrivate> d_ptr; Q_DECLARE_PRIVATE(QWinRTInAppPurchaseBackend) diff --git a/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction.cpp b/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction.cpp index 5e83530..212837a 100644 --- a/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction.cpp +++ b/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction.cpp @@ -38,8 +38,9 @@ Q_LOGGING_CATEGORY(lcPurchasingTransaction, "qt.purchasing.transaction") QWinRTInAppTransaction::QWinRTInAppTransaction(TransactionStatus status, QInAppProduct *product, FailureReason reason, - QObject *parent) + const QString &receipt, QObject *parent) : QInAppTransaction(status, product, parent) + , m_receipt(receipt) , m_failureReason(reason) { qCDebug(lcPurchasingTransaction) << __FUNCTION__; @@ -56,4 +57,13 @@ void QWinRTInAppTransaction::finalize() deleteLater(); } +QString QWinRTInAppTransaction::platformProperty(const QString &propertyName) const +{ + qCDebug(lcPurchasingTransaction) << __FUNCTION__ << propertyName; + + if (propertyName == QLatin1String("receipt")) + return m_receipt; + return QString(); +} + QT_END_NAMESPACE diff --git a/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction_p.h b/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction_p.h index 4a1a752..34cc3b9 100644 --- a/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction_p.h +++ b/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction_p.h @@ -58,13 +58,16 @@ public: explicit QWinRTInAppTransaction(TransactionStatus status, QInAppProduct *product, FailureReason reason, + const QString &receipt, QObject *parent = Q_NULLPTR); FailureReason failureReason() const override { return m_failureReason; } void finalize() override; + QString platformProperty(const QString &propertyName) const override; Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::Store::IPurchaseResults> m_purchaseResults; + QString m_receipt; private: QWinRTInAppPurchaseBackend *m_backend; FailureReason m_failureReason; diff --git a/tests/auto/purchasing/qinappstore/tst_qinappstore.cpp b/tests/auto/purchasing/qinappstore/tst_qinappstore.cpp index d2efd0d..6411fed 100644 --- a/tests/auto/purchasing/qinappstore/tst_qinappstore.cpp +++ b/tests/auto/purchasing/qinappstore/tst_qinappstore.cpp @@ -73,11 +73,14 @@ void tst_QInAppStore::registerUnknownProduct() store.registerProduct(QInAppProduct::Consumable, QStringLiteral("unknownConsumable")); store.registerProduct(QInAppProduct::Unlockable, QStringLiteral("unknownUnlockable")); -#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) +//The backend is implemented on iOS, macOS, WinRT and Android, for others we expect failure. +#if !(defined(Q_OS_DARWIN) && !defined(Q_OS_WATCHOS)) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) QEXPECT_FAIL("", "Qt Purchasing not implemented on this platform.", Abort); #endif - QTRY_COMPARE(receiver.unknownProducts.size(), 2); + //Due to network overload or connectivity issues QTRY_COMPARE sometimes fails with timeout, + //that's why we need to increase the value, since it's better to wait than to fail. + QTRY_COMPARE_WITH_TIMEOUT(receiver.unknownProducts.size(), 2, 10000); QCOMPARE(receiver.registeredProducts.size(), 0); QCOMPARE(receiver.readyTransactions.size(), 0); |