diff options
author | Maurice Kalinowski <maurice.kalinowski@qt.io> | 2017-04-05 12:54:10 +0200 |
---|---|---|
committer | Maurice Kalinowski <maurice.kalinowski@qt.io> | 2017-04-12 07:36:16 +0000 |
commit | 8bdb45e8f3471d59d988f73e8c80bf136395f2c9 (patch) | |
tree | f9fe51bfb421ca701cd8c4887847acdae313f83c /src | |
parent | 9a492e0f47660b4a428fc1fc6dc0fd07b2b3b1b7 (diff) |
winrt: Fix Windows Store backend
A couple of incompatible upstream changes requires to handle a couple of
things differently
- The operation during a purchase is not cancelled anymore, when the
user cancels the purchase. This caused all purchases to succeed from a
Qt perspective.
- Querying for Product Licenses does not work anymore. Hence, we have to
use the information gathered via the listing information and rely on
this.
- Add some more debug output to verify connection with the Windows
Store.
- Fulfilling a consumable restores item requires an ID to be successful
Task-number: QTBUG-59766
Change-Id: I3ae64ff565a7d51870bce5b0809a579f78c5193e
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Diffstat (limited to 'src')
3 files changed, 76 insertions, 17 deletions
diff --git a/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend.cpp b/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend.cpp index 213aece..b57df20 100644 --- a/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend.cpp +++ b/src/purchasing/inapppurchase/winrt/qwinrtinapppurchasebackend.cpp @@ -73,6 +73,7 @@ public: HRESULT activate(); HRESULT LoadListingInformationAsync(ComPtr<IAsyncOperation<ListingInformation *>> &op); HRESULT GetAppReceiptAsync(ComPtr<IAsyncOperation<HSTRING>> &op); + HRESULT GetProductReceiptAsync(HSTRING product, ComPtr<IAsyncOperation<HSTRING>> &op); HRESULT RequestAppPurchaseAsync(bool receipt, ComPtr<IAsyncOperation<HSTRING>> &op); HRESULT RequestProductPurchaseAsync(HSTRING productId, bool receipt, ComPtr<IAsyncOperation<HSTRING>> &op); HRESULT RequestProductPurchaseWithResultsAsync(HSTRING productId, ComPtr<IAsyncOperation<PurchaseResults *>> &purchaseOp); @@ -145,6 +146,16 @@ HRESULT QWinRTAppBridge::GetAppReceiptAsync(ComPtr<IAsyncOperation<HSTRING> > &o return hr; } +HRESULT QWinRTAppBridge::GetProductReceiptAsync(HSTRING product, ComPtr<IAsyncOperation<HSTRING> > &op) +{ + HRESULT hr; + if (m_simulate) + hr = m_simulator->GetProductReceiptAsync(product, &op); + else + hr = m_app->GetProductReceiptAsync(product, &op); + return hr; +} + HRESULT QWinRTAppBridge::RequestAppPurchaseAsync(bool receipt, ComPtr<IAsyncOperation<HSTRING> > &op) { HRESULT hr; @@ -236,7 +247,7 @@ HRESULT QWinRTAppBridge::qt_winrt_load_simulator_config(const QString &fileName, hr = simulator->ReloadSimulatorAsync(storeFile.Get(), &reloadAction); Q_ASSERT_SUCCEEDED(hr); - hr = QWinRTFunctions::await(reloadAction); + hr = QWinRTFunctions::await(reloadAction, QWinRTFunctions::YieldThread); RETURN_HR_IF_FAILED("Failed to load purchasing description."); return hr; @@ -267,9 +278,32 @@ void QWinRTInAppPurchaseBackend::createTransactionDelayed(qt_WinRTTransactionDat QInAppTransaction::FailureReason reason; switch (data.status) { - case AsyncStatus::Completed: + case AsyncStatus::Completed: { reason = QInAppTransaction::NoFailure; + ProductPurchaseStatus purchaseStatus; + HRESULT hr = data.purchaseResults->get_Status(&purchaseStatus); + if (FAILED(hr)) { + qWarning("Could not query purchase status for transaction."); + break; + } + switch (purchaseStatus) { + case ProductPurchaseStatus_Succeeded: + reason = QInAppTransaction::NoFailure; + break; + case ProductPurchaseStatus_NotFulfilled: + case ProductPurchaseStatus_NotPurchased: + reason = QInAppTransaction::CanceledByUser; + qStatus = QInAppTransaction::PurchaseFailed; + break; + case ProductPurchaseStatus_AlreadyPurchased: + default: + reason = QInAppTransaction::ErrorOccurred; + qStatus = QInAppTransaction::PurchaseFailed; + break; + } + break; + } case AsyncStatus::Canceled: reason = QInAppTransaction::CanceledByUser; break; @@ -281,6 +315,8 @@ void QWinRTInAppPurchaseBackend::createTransactionDelayed(qt_WinRTTransactionDat auto transaction = new QWinRTInAppTransaction(qStatus, data.product, reason, data.receipt, this); transaction->m_purchaseResults = data.purchaseResults; + qCDebug(lcPurchasingBackend) << "Emitting Transaction:" << qStatus << "/" + << reason << " Receipt:" << data.receipt; emit transactionReady(transaction); return; @@ -329,7 +365,7 @@ void QWinRTInAppPurchaseBackend::initialize() // ### Keep for later usage. // ComPtr<ILicenseInformation> licenseInfo; - // hr = d->m_app->get_LicenseInformation(&licenseInfo); + // hr = d->m_bridge.get_LicenseInformation(licenseInfo); // RETURN_HR_IF_FAILED("Could not acquire license information."); ComPtr<IAsyncOperation<ListingInformation*>> op; @@ -380,12 +416,14 @@ void QWinRTInAppPurchaseBackend::restorePurchases() hr = QWinRTFunctions::await(op, receipt.GetAddressOf()); if (FAILED(hr)) { - qCDebug(lcPurchasingBackend) << "Could not wait for receipt"; + qCDebug(lcPurchasingBackend) << "Could not wait for app receipt query"; return; } const QString parse = hStringToQString(receipt); + qCDebug(lcPurchasingBackend) << "Receipt:" << parse; + QXmlStreamReader reader(parse); while (reader.readNextStartElement()) { if (reader.name() == QLatin1String("Receipt")) @@ -408,9 +446,16 @@ void QWinRTInAppPurchaseBackend::restorePurchases() reader.readNext(); if (reader.attributes().hasAttribute(QLatin1String("ProductId"))) { const QString id = reader.attributes().value(QLatin1String("ProductId")).toString(); + qCDebug(lcPurchasingBackend) << " Found Product " << id << "to restore"; if (d->nativeProducts.contains(id)) { qCDebug(lcPurchasingBackend) << "Restoring:" << id; + QUuid uuid(reader.attributes().value(QLatin1String("Id")).toString()); + if (uuid.isNull()) { + qCDebug(lcPurchasingBackend) << "Product " << id << " restoration failed due to " + << "no transaction id"; + continue; + } QInAppProduct *product = store()->registeredProduct(id); if (!product) { qCDebug(lcPurchasingBackend) << "Product " << id << "has been bought, but is unknown"; @@ -425,7 +470,7 @@ void QWinRTInAppPurchaseBackend::restorePurchases() QInAppTransaction::NoFailure, receipt, this); - + transaction->m_uuid = uuid; emit transactionReady(transaction); } } @@ -463,6 +508,7 @@ void QWinRTInAppPurchaseBackend::restorePurchases() // However, this returns E_NOTIMPL when using the ICurrentAppSimulator, // so it could not be used during development phase. #if 0 + qCDebug(lcPurchasingBackend) << "Experimenting with Product Receipts"; const QStringList keys = d->nativeProducts.keys(); for (auto item : keys) { HRESULT hr; @@ -471,7 +517,7 @@ void QWinRTInAppPurchaseBackend::restorePurchases() hr = productId.Set(reinterpret_cast<LPCWSTR>(item.utf16()), item.size()); ComPtr<IAsyncOperation<HSTRING>> op; - hr = d->m_app->GetProductReceiptAsync(productId.Get(), &op); + hr = d->m_bridge.GetProductReceiptAsync(productId.Get(), op); if (FAILED(hr)) { qCDebug(lcPurchasingBackend) << "No receipt available for:" << item; @@ -486,9 +532,8 @@ void QWinRTInAppPurchaseBackend::restorePurchases() continue; } - quint32 length; const QString receipt = hStringToQString(receiptString); - qDebug() << "Received receipt:" << receipt; + qDebug() << "Received receipt for " << item << ":" << receipt; // Create new transaction with status == Restored and emit //emit transactionReady(); @@ -521,12 +566,14 @@ void QWinRTInAppPurchaseBackend::queryProduct(QInAppProduct::ProductType product return; } - ComPtr<IProductLicense> productLicense = d->findProductLicense(identifier); - if (!productLicense && identifier != qt_win_app_identifier) { - qCDebug(lcPurchasingBackend) << "Could not find product license even though available in listing:" << identifier; - emit productQueryFailed(productType, identifier); - return; - } + // With the latest Windows Store Updates, product licenses seem to be not working + // anymore. Hence, we have to rely on the listing being correct. + // ComPtr<IProductLicense> productLicense = d->findProductLicense(identifier); + // if (!productLicense && identifier != qt_win_app_identifier) { + // qCDebug(lcPurchasingBackend) << "Could not find product license even though available in listing:" << identifier; + // emit productQueryFailed(productType, identifier); + // return; + // } NativeProductInfo *cachedInfo = d->nativeProducts.value(identifier); const QString price = hStringToQString(cachedInfo->formatPrice); @@ -652,8 +699,11 @@ void QWinRTInAppPurchaseBackend::fulfillConsumable(QWinRTInAppTransaction *trans HRESULT hr; GUID transactionId; - hr = transaction->m_purchaseResults->get_TransactionId(&transactionId); - Q_ASSERT_SUCCEEDED(hr); + if (transaction->m_uuid.isNull()) { + hr = transaction->m_purchaseResults->get_TransactionId(&transactionId); + Q_ASSERT_SUCCEEDED(hr); + } else + transactionId = transaction->m_uuid; HString productId; const QString identifier = transaction->product()->identifier(); @@ -691,6 +741,10 @@ HRESULT QWinRTInAppPurchaseBackendPrivate::onListingInformation(IAsyncOperation< return S_OK; } + unsigned int amount = 0; + productListings->get_Size(&amount); + qCDebug(lcPurchasingBackend) << " Found " << amount << " products registered in the store."; + typedef Collections::IKeyValuePair<HSTRING, ProductListing *> ValueItem; typedef Collections::IIterable<ValueItem *> ValueIterable; typedef Collections::IIterator<ValueItem *> ValueIterator; @@ -768,6 +822,8 @@ HRESULT QWinRTInAppPurchaseBackendPrivate::onListingInformation(IAsyncOperation< boolean trial; hr = appLicense->get_IsTrial(&trial); Q_ASSERT_SUCCEEDED(hr); + + qCDebug(lcPurchasingBackend) << "App registration: Is Active: " << active << " Is Trial: " << trial; if (active) { auto appInfo = new NativeProductInfo; hr = appInfo->productID.Set(reinterpret_cast<LPCWSTR>(qt_win_app_identifier.utf16()), qt_win_app_identifier.size()); diff --git a/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction.cpp b/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction.cpp index 212837a..f0003fe 100644 --- a/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction.cpp +++ b/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction.cpp @@ -51,7 +51,8 @@ void QWinRTInAppTransaction::finalize() { qCDebug(lcPurchasingTransaction) << __FUNCTION__; if (product()->productType() == QInAppProduct::Consumable && - status() == QInAppTransaction::PurchaseApproved) { + (status() == QInAppTransaction::PurchaseApproved || + status() == QInAppTransaction::PurchaseRestored)) { m_backend->fulfillConsumable(this); } deleteLater(); diff --git a/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction_p.h b/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction_p.h index 34cc3b9..30936be 100644 --- a/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction_p.h +++ b/src/purchasing/inapppurchase/winrt/qwinrtinapptransaction_p.h @@ -45,6 +45,7 @@ #include <QtCore/qobject.h> #include <QtCore/qdatetime.h> +#include <QtCore/QUuid> #include <Windows.ApplicationModel.store.h> #include <wrl.h> @@ -68,6 +69,7 @@ public: Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::Store::IPurchaseResults> m_purchaseResults; QString m_receipt; + QUuid m_uuid; private: QWinRTInAppPurchaseBackend *m_backend; FailureReason m_failureReason; |