debian/0000775000000000000000000000000012645517615007204 5ustar debian/compat0000664000000000000000000000000212645517615010402 0ustar 9 debian/rules0000775000000000000000000000201312645517615010260 0ustar #!/usr/bin/make -f # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 export CFLAGS := $(shell dpkg-buildflags --get CFLAGS) $(shell dpkg-buildflags --get CPPFLAGS) export CXXFLAGS := $(shell dpkg-buildflags --get CXXFLAGS) $(shell dpkg-buildflags --get CPPFLAGS) export LDFLAGS := $(shell dpkg-buildflags --get LDFLAGS) -Wl,--as-needed export QT_SELECT := qt5 %: dh $@ --fail-missing --parallel --with pkgkde_symbolshelper --dbg-package=qtpurchasing5-dbg override_dh_auto_configure: # Now call qmake mkdir .git qmake CONFIG+=ubuntu DEFINES+=Q_OS_UBUNTU override_dh_auto_build-indep: dh_auto_build -Smakefile -- docs QT_QPA_PLATFORM=minimal make -f tests/Makefile check override_dh_auto_install-arch: dh_auto_install # Remove libtool-like files rm -fv debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/*.la # Remove private stuff rm -rfv $(CURDIR)/debian/tmp/usr/include/$(DEB_HOST_MULTIARCH)/qt5/QtPurchasing/*/QtPurchasing/private rm -fv $(CURDIR)/debian/tmp/usr/lib/*/qt5/mkspecs/modules/qt_lib_*_private.pri debian/changelog0000664000000000000000000000201112645517615011050 0ustar qtpurchasing-opensource-src (5.6.0~git20151023.2f454143-0ubuntu4) xenial; urgency=medium * debian/patches/ubuntu_libpay_backend.diff: - Handle error condition from pay-service as purchase failed. -- Rodney Dawes Wed, 13 Jan 2016 13:58:08 -0500 qtpurchasing-opensource-src (5.6.0~git20151023.2f454143-0ubuntu3) xenial; urgency=medium * debian/control: fix copy-paste error -- Timo Jyrinki Fri, 08 Jan 2016 10:10:46 +0000 qtpurchasing-opensource-src (5.6.0~git20151023.2f454143-0ubuntu2) xenial; urgency=medium * debian/copyright: fix to be more precise * debian/libqt5purchasing5.install: specify files explicitly -- Timo Jyrinki Tue, 05 Jan 2016 11:28:31 +0000 qtpurchasing-opensource-src (5.6.0~git20151023.2f454143-0ubuntu1) xenial; urgency=medium * Initial release. * debian/patches/ubuntu_libpay_backend.diff: - Ubuntu libpay backend. -- Timo Jyrinki Fri, 23 Oct 2015 09:16:51 +0000 debian/patches/0000775000000000000000000000000012645517615010633 5ustar debian/patches/series0000664000000000000000000000003312645517615012044 0ustar ubuntu_libpay_backend.diff debian/patches/ubuntu_libpay_backend.diff0000664000000000000000000014575012645517615016032 0ustar diff --git a/src/purchasing/inapppurchase/inapppurchase.pri b/src/purchasing/inapppurchase/inapppurchase.pri index 85bab80..b6f3abc 100644 --- a/src/purchasing/inapppurchase/inapppurchase.pri +++ b/src/purchasing/inapppurchase/inapppurchase.pri @@ -26,3 +26,8 @@ android { mac { include ($$PWD/mac/mac.pri) } + +ubuntu { + message(UBUNTU!) + include ($$PWD/ubuntu/ubuntu.pri) +} \ No newline at end of file diff --git a/src/purchasing/inapppurchase/qinapppurchasebackendfactory.cpp b/src/purchasing/inapppurchase/qinapppurchasebackendfactory.cpp index 757b78a..8ee5979 100644 --- a/src/purchasing/inapppurchase/qinapppurchasebackendfactory.cpp +++ b/src/purchasing/inapppurchase/qinapppurchasebackendfactory.cpp @@ -32,6 +32,8 @@ # include "qandroidinapppurchasebackend_p.h" #elif defined(Q_OS_MAC) # include "qmacinapppurchasebackend_p.h" +#elif defined(Q_OS_UBUNTU) +# include "qubuntuinapppurchasebackend_p.h" #else # include "qinapppurchasebackend_p.h" #endif @@ -44,6 +46,8 @@ QInAppPurchaseBackend *QInAppPurchaseBackendFactory::create() return new QAndroidInAppPurchaseBackend; #elif defined (Q_OS_MAC) return new QMacInAppPurchaseBackend; +#elif defined (Q_OS_UBUNTU) + return new QUbuntuInAppPurchaseBackend; #else return new QInAppPurchaseBackend; #endif diff --git a/src/purchasing/inapppurchase/qinappstore.cpp b/src/purchasing/inapppurchase/qinappstore.cpp index 36ae021..6ff5795 100644 --- a/src/purchasing/inapppurchase/qinappstore.cpp +++ b/src/purchasing/inapppurchase/qinappstore.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "qinappstore.h" +#include "qinapptransaction.h" #include "qinappstore_p.h" #include "qinapppurchasebackend_p.h" #include "qinapppurchasebackendfactory_p.h" @@ -39,6 +40,7 @@ public: IAPRegisterMetaTypes() { qRegisterMetaType("QInAppProduct::ProductType"); + qRegisterMetaType("QInAppTransaction*"); } } _registerIAPMetaTypes; } diff --git a/src/purchasing/inapppurchase/ubuntu/qubuntuinappproduct.cpp b/src/purchasing/inapppurchase/ubuntu/qubuntuinappproduct.cpp new file mode 100644 index 0000000..eea86f8 --- /dev/null +++ b/src/purchasing/inapppurchase/ubuntu/qubuntuinappproduct.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qubuntuinappproduct_p.h" +#include "qubuntuinapppurchasebackend_p.h" + +QT_BEGIN_NAMESPACE + +QUbuntuInAppProduct::QUbuntuInAppProduct(QUbuntuInAppPurchaseBackend *backend, + const QString &price, + const QString &title, + const QString &description, + ProductType productType, + const QString &identifier, + QObject *parent) + : QInAppProduct(price, title, description, productType, identifier, parent) + , m_backend(backend) +{ + Q_ASSERT(backend != nullptr); +} + +void QUbuntuInAppProduct::purchase() +{ + m_backend->purchase(this); +} + +QT_END_NAMESPACE + diff --git a/src/purchasing/inapppurchase/ubuntu/qubuntuinappproduct_p.h b/src/purchasing/inapppurchase/ubuntu/qubuntuinappproduct_p.h new file mode 100644 index 0000000..6cf7b35 --- /dev/null +++ b/src/purchasing/inapppurchase/ubuntu/qubuntuinappproduct_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUBUNTUINAPPPRODUCT_P_H +#define QUBUNTUINAPPPRODUCT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qinappproduct.h" + +QT_BEGIN_NAMESPACE + +class QUbuntuInAppPurchaseBackend; +class QUbuntuInAppProduct : public QInAppProduct +{ + Q_OBJECT + Q_DISABLE_COPY(QUbuntuInAppProduct) + +public: + explicit QUbuntuInAppProduct(QUbuntuInAppPurchaseBackend *backend, + const QString &price, + const QString &title, + const QString &description, + ProductType productType, + const QString &identifier, + QObject *parent = 0); + + + void purchase(); + +private: + QUbuntuInAppPurchaseBackend * const m_backend; +}; + +Q_DECLARE_METATYPE(QUbuntuInAppProduct*) + +QT_END_NAMESPACE + +#endif // QUBUNTUINAPPPRODUCT_P_H diff --git a/src/purchasing/inapppurchase/ubuntu/qubuntuinapppurchasebackend.cpp b/src/purchasing/inapppurchase/ubuntu/qubuntuinapppurchasebackend.cpp new file mode 100644 index 0000000..a879e77 --- /dev/null +++ b/src/purchasing/inapppurchase/ubuntu/qubuntuinapppurchasebackend.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qubuntuinappproduct_p.h" +#include "qubuntuinapppurchasebackend_p.h" +#include "qubuntuinapptransaction_p.h" + +#include +#include + +#include +#include + +namespace +{ +class IAPRegisterUbuntuMetaTypes +{ +public: + IAPRegisterUbuntuMetaTypes() + { + qRegisterMetaType( + "PayPackageItemStatus"); + } +} _registerIAPUbuntuMetaTypes; +} + +namespace { + +QString getPackageName() +{ + QString pkgname; + + const auto appid = QString::fromUtf8(qgetenv("APP_ID")); + if (appid.isEmpty()) { + qWarning("$APP_ID environment variable not set."); + return pkgname; + } + + // $APP_ID has the form of __ + const auto tokens = appid.split(QStringLiteral("_")); + if (tokens.size() != 3) { + qWarning() << QString(QStringLiteral("Invalid $APP_ID (%1).")).arg(appid); + return pkgname; + } + + pkgname = tokens.at(0); + return pkgname; +} + +bool types_equal(QInAppProduct::ProductType qtype, PayItemType utype) +{ + return (qtype == QInAppProduct::Consumable) + ? (utype == PAY_ITEM_TYPE_CONSUMABLE) + : (utype == PAY_ITEM_TYPE_UNLOCKABLE); +} + +void pay_item_deleter(PayItem *pay_item) +{ + if (pay_item != nullptr) + pay_item_unref(pay_item); +} + +} // anonymous namespace + +QT_BEGIN_NAMESPACE + + +QUbuntuInAppPurchaseBackend::QUbuntuInAppPurchaseBackend(QObject *parent) + : QInAppPurchaseBackend(parent) +{ + connect(this, SIGNAL(itemObserverSignal(QString, PayPackageItemStatus)), + this, SLOT (itemObserver (QString, PayPackageItemStatus))); +} + +/*** +**** QInAppPurchaseBackend API +***/ + +bool QUbuntuInAppPurchaseBackend::isReady() const +{ + return !m_package.isNull(); +} + +void QUbuntuInAppPurchaseBackend::initialize() +{ + const auto pkgname = getPackageName(); + if (pkgname.isEmpty()) { + qWarning("Unable to initialize QUbuntuInAppPurchaseBackend -- appname cannot be found"); + return; + } + + auto package = pay_package_new(qUtf8Printable(pkgname)); + pay_package_item_observer_install(package, itemObserverStatic, this); + + auto package_deleter = [this](PayPackage *p) { + pay_package_item_observer_uninstall(p, itemObserverStatic, this); + pay_package_delete(p); + }; + + m_package.reset(package, package_deleter); + emit ready(); +} + +void QUbuntuInAppPurchaseBackend::queryProducts(const QList &products) +{ + for (auto &product : products) + queryProduct(product.productType, product.identifier); +} + +void QUbuntuInAppPurchaseBackend::queryProduct(QInAppProduct::ProductType productType, + const QString &identifier) +{ + QSharedPointer item( + pay_package_get_item(m_package.data(), qUtf8Printable(identifier)), + pay_item_deleter + ); + + if (!item) + { + emit productQueryFailed(productType, identifier); + } + else if (!types_equal(productType, pay_item_get_type(item.data()))) + { + emit productQueryFailed(productType, identifier); + } + else + { + emit productQueryDone(createUbuntuProduct(item.data())); + + if (pay_item_get_status(item.data()) == PAY_PACKAGE_ITEM_STATUS_APPROVED) + emitTransactionReadyForItem(item.data(), QInAppTransaction::PurchaseApproved); + } +} + +void QUbuntuInAppPurchaseBackend::restorePurchases() +{ + auto items = pay_package_get_purchased_items(m_package.data()); + + for (int i=0; items && items[i]; ++i) { + auto &item = items[i]; + if ((pay_item_get_type(item) == PAY_ITEM_TYPE_UNLOCKABLE) && (pay_item_get_status(item) == PAY_PACKAGE_ITEM_STATUS_PURCHASED)) + emitTransactionReadyForItem(item, QInAppTransaction::PurchaseRestored); + pay_item_unref(item); + } + + free(items); +} + +void QUbuntuInAppPurchaseBackend::setPlatformProperty(const QString &propertyName, + const QString &value) +{ + Q_UNUSED(propertyName) + Q_UNUSED(value) +} + +void QUbuntuInAppPurchaseBackend::purchase(QUbuntuInAppProduct *product) +{ + Q_ASSERT(product != nullptr); + + pay_package_item_start_purchase(m_package.data(), + qUtf8Printable(product->identifier())); +} + + +/*** +**** +***/ + +void QUbuntuInAppPurchaseBackend::itemObserverStatic(PayPackage * /*package*/, + const char *sku_utf8, + PayPackageItemStatus status, + void *vself) +{ + // defer this work back to the main thread + const auto sku = QString::fromUtf8(sku_utf8); + emit static_cast(vself)->itemObserverSignal (sku, status); +} + +void QUbuntuInAppPurchaseBackend::itemObserver(const QString &sku, + PayPackageItemStatus status) +{ + const auto prev = m_statuses.value(sku, PAY_PACKAGE_ITEM_STATUS_UNKNOWN); + + if (prev == PAY_PACKAGE_ITEM_STATUS_PURCHASING) + { + auto item = pay_package_get_item(m_package.data(), qUtf8Printable(sku)); + + switch (status) { + case PAY_PACKAGE_ITEM_STATUS_APPROVED: + case PAY_PACKAGE_ITEM_STATUS_PURCHASED: + emitTransactionReadyForItem(item, QInAppTransaction::PurchaseApproved); + break; + case PAY_PACKAGE_ITEM_STATUS_NOT_PURCHASED: + case PAY_PACKAGE_ITEM_STATUS_UNKNOWN: + emitTransactionReadyForItem(item, QInAppTransaction::PurchaseFailed); + break; + default: + qCritical() << "Unexpected status transition to:" << status; + break; + } + pay_item_unref(item); + } + + m_statuses[sku] = status; +} + +/*** +**** +***/ + +void QUbuntuInAppPurchaseBackend::emitTransactionReadyForItem(const PayItem *item, + QInAppTransaction::TransactionStatus status) +{ + qDebug("Emitting transactionReady for %s with status %d.", pay_item_get_sku(item), status); + + emit transactionReady(new QUbuntuInAppTransaction( + QString::fromUtf8("%1").arg(pay_item_get_purchase_id(item)), + status, + createUbuntuProduct(item), + QDateTime::fromTime_t(pay_item_get_completed_timestamp(item)), + QInAppTransaction::NoFailure, + QString(), + this + )); +} + +QUbuntuInAppProduct * QUbuntuInAppPurchaseBackend::createUbuntuProduct(const PayItem *item) +{ + const auto type = pay_item_get_type(item) == PAY_ITEM_TYPE_CONSUMABLE + ? QInAppProduct::Consumable + : QInAppProduct::Unlockable; + + return new QUbuntuInAppProduct( + this, // backend + QString::fromUtf8(pay_item_get_price(item), -1), + QString::fromUtf8(pay_item_get_title(item), -1), + QString::fromUtf8(pay_item_get_description(item), -1), + type, + QString::fromUtf8(pay_item_get_sku(item), -1), + this // parent + ); +} + +void QUbuntuInAppPurchaseBackend::finalizeTransaction(const QUbuntuInAppTransaction &transaction) +{ + pay_package_item_start_acknowledge (m_package.data(), + qUtf8Printable(transaction.product()->identifier())); +} + +QT_END_NAMESPACE diff --git a/src/purchasing/inapppurchase/ubuntu/qubuntuinapppurchasebackend_p.h b/src/purchasing/inapppurchase/ubuntu/qubuntuinapppurchasebackend_p.h new file mode 100644 index 0000000..2e220a7 --- /dev/null +++ b/src/purchasing/inapppurchase/ubuntu/qubuntuinapppurchasebackend_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUBUNTUINAPPPURCHASEBACKEND_P_H +#define QUBUNTUINAPPPURCHASEBACKEND_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qinapppurchasebackend_p.h" +#include "qubuntuinappproduct_p.h" +#include "qubuntuinapptransaction_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QInAppProduct; +class QInAppTransaction; +class QUbuntuInAppPurchaseBackend : public QInAppPurchaseBackend +{ + Q_OBJECT +public: + + explicit QUbuntuInAppPurchaseBackend(QObject *parent = 0); + + void initialize() override; + bool isReady() const override; + + void queryProducts(const QList &products) override; + void queryProduct(QInAppProduct::ProductType productType, const QString &identifier) override; + void restorePurchases() override; + + void setPlatformProperty(const QString &propertyName, const QString &value) override; + + void finalizeTransaction(const QUbuntuInAppTransaction &transaction); + + void purchase(QUbuntuInAppProduct* product); + +Q_SIGNALS: + void itemObserverSignal(const QString &sku, PayPackageItemStatus); + +private Q_SLOTS: + void itemObserver(const QString &sku, PayPackageItemStatus); + +private: + static void itemObserverStatic(PayPackage *, const char *sku, PayPackageItemStatus, void *); + + void emitTransactionReadyForItem(const PayItem *item, + QInAppTransaction::TransactionStatus status); + + QUbuntuInAppProduct* createUbuntuProduct(const PayItem *item); + + + QSharedPointer m_package; + QMap m_statuses; + + Q_DISABLE_COPY(QUbuntuInAppPurchaseBackend) +}; + +QT_END_NAMESPACE + +#endif // QUBUNTUINAPPPURCHASEBACKEND_P_H diff --git a/src/purchasing/inapppurchase/ubuntu/qubuntuinapptransaction.cpp b/src/purchasing/inapppurchase/ubuntu/qubuntuinapptransaction.cpp new file mode 100644 index 0000000..6fa3289 --- /dev/null +++ b/src/purchasing/inapppurchase/ubuntu/qubuntuinapptransaction.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qubuntuinapptransaction_p.h" +#include "qubuntuinapppurchasebackend_p.h" +#include "qinappproduct.h" + +QT_BEGIN_NAMESPACE + +QUbuntuInAppTransaction::QUbuntuInAppTransaction(const QString &orderId, + TransactionStatus status, + QInAppProduct *product, + const QDateTime ×tamp, + FailureReason failureReason, + const QString &errorString, + QUbuntuInAppPurchaseBackend *parent) + : QInAppTransaction(status, product, parent) + , m_orderId(orderId) + , m_timestamp(timestamp) + , m_errorString(errorString) + , m_failureReason(failureReason) +{ + Q_ASSERT(parent != nullptr); +} + +QString QUbuntuInAppTransaction::orderId() const +{ + return m_orderId; +} + +QDateTime QUbuntuInAppTransaction::timestamp() const +{ + return m_timestamp; +} + +QString QUbuntuInAppTransaction::errorString() const +{ + return m_errorString; +} + +QInAppTransaction::FailureReason QUbuntuInAppTransaction::failureReason() const +{ + return m_failureReason; +} + +QString QUbuntuInAppTransaction::platformProperty(const QString &propertyName) const +{ + Q_UNUSED(propertyName) + + return QString(); +} + +void QUbuntuInAppTransaction::finalize() +{ + qobject_cast(parent())->finalizeTransaction(*this); + + // FIXME: android's impl calls deleteLater() here, but the docs don't specify the + // life cycle of QInAppTransaction, so it's unclear what The Right Thing to do is... +} + +QT_END_NAMESPACE diff --git a/src/purchasing/inapppurchase/ubuntu/qubuntuinapptransaction_p.h b/src/purchasing/inapppurchase/ubuntu/qubuntuinapptransaction_p.h new file mode 100644 index 0000000..b1c998d --- /dev/null +++ b/src/purchasing/inapppurchase/ubuntu/qubuntuinapptransaction_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUBUNTUINAPPTRANSACTION_P_H +#define QUBUNTUINAPPTRANSACTION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include "qinapptransaction.h" + +QT_BEGIN_NAMESPACE + +class QUbuntuInAppPurchaseBackend; + +class QUbuntuInAppTransaction : public QInAppTransaction +{ + Q_OBJECT + Q_DISABLE_COPY(QUbuntuInAppTransaction) +public: + explicit QUbuntuInAppTransaction(const QString &orderId, + TransactionStatus status, + QInAppProduct *product, + const QDateTime ×tamp, + FailureReason failureReason, + const QString &errorString, + QUbuntuInAppPurchaseBackend *parent); + + QString orderId() const override; + QString errorString() const override; + FailureReason failureReason() const override; + QDateTime timestamp() const override; + + void finalize() override; + QString platformProperty(const QString &propertyName) const override; + +private: + const QString m_orderId; + const QString m_purchaseToken; + const QDateTime m_timestamp; + const QString m_errorString; + const FailureReason m_failureReason; +}; + +Q_DECLARE_METATYPE(QUbuntuInAppTransaction*) + +QT_END_NAMESPACE + +#endif // QUBUNTUINAPPTRANSACTION_P_H diff --git a/src/purchasing/inapppurchase/ubuntu/ubuntu.pri b/src/purchasing/inapppurchase/ubuntu/ubuntu.pri new file mode 100644 index 0000000..77dc321 --- /dev/null +++ b/src/purchasing/inapppurchase/ubuntu/ubuntu.pri @@ -0,0 +1,14 @@ +CONFIG += link_pkgconfig +PKGCONFIG += pay-2 + +INCLUDEPATH += $$PWD +HEADERS += \ + $$PWD/qubuntuinapppurchasebackend_p.h \ + $$PWD/qubuntuinappproduct_p.h \ + $$PWD/qubuntuinapptransaction_p.h + +SOURCES += \ + $$PWD/qubuntuinapppurchasebackend.cpp \ + $$PWD/qubuntuinappproduct.cpp \ + $$PWD/qubuntuinapptransaction.cpp + diff --git a/tests/auto/purchasing/purchasing.pro b/tests/auto/purchasing/purchasing.pro index 91d6199..1174fbd 100644 --- a/tests/auto/purchasing/purchasing.pro +++ b/tests/auto/purchasing/purchasing.pro @@ -1,3 +1,8 @@ TEMPLATE = subdirs SUBDIRS += \ qinappstore + +ubuntu { + SUBDIRS += \ + ubuntu +} diff --git a/tests/auto/purchasing/qinappstore/tst_qinappstore.cpp b/tests/auto/purchasing/qinappstore/tst_qinappstore.cpp index d2efd0d..a8f5111 100644 --- a/tests/auto/purchasing/qinappstore/tst_qinappstore.cpp +++ b/tests/auto/purchasing/qinappstore/tst_qinappstore.cpp @@ -63,6 +63,10 @@ public slots: void tst_QInAppStore::registerUnknownProduct() { +#ifdef Q_OS_UBUNTU + qputenv("APP_ID", "test_test.application_1234567890"); +#endif + QInAppStore store(this); SignalReceiver receiver; @@ -73,7 +77,7 @@ 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) +#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) && !defined(Q_OS_UBUNTU) QEXPECT_FAIL("", "Qt Purchasing not implemented on this platform.", Abort); #endif diff --git a/tests/auto/purchasing/ubuntu/store_mock.cpp b/tests/auto/purchasing/ubuntu/store_mock.cpp new file mode 100644 index 0000000..af16b4d --- /dev/null +++ b/tests/auto/purchasing/ubuntu/store_mock.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include + +#include "store_mock.h" + +namespace +{ + +constexpr char const * BUS_NAME {"com.canonical.payments"}; +constexpr char const * TEMPLATE_NAME {"com_canonical_pay_store"}; +constexpr char const * STORE_ROOT {"/com/canonical/pay/store"}; +constexpr char const * STORE_IFACE {"com.canonical.pay.store"}; +constexpr char const * STORE_MOCK_IFACE{"com.canonical.pay.storemock"}; + +// copied from pay-service/common/BusUtils.cpp +// Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_" and must not begin with a digit. +QString encodePathElement(const QString& input) +{ + QString output; + bool first = true; + + for (auto qch : input) + { + const int c = qch.toLatin1(); + + if ( (('a'<=c) && (c<='z')) + || (('A'<=c) && (c<='Z')) + || (('0'<=c) && (c<='9') && !first)) { + output += qch; + } else { + output += QString("_%1").arg(c, 2, 16, QChar('0')).toLower(); + } + + first = false; + } + + return output; +} + +} // anonymous namespace + +/*** +**** +***/ + +QT_BEGIN_NAMESPACE + +struct StoreMockPrivate +{ + StoreMockPrivate(const QString &storeName): + m_dbusMock(m_dbusTestRunner), + m_storeName(storeName) + { + m_dbusMock.registerTemplate(BUS_NAME, TEMPLATE_NAME, QDBusConnection::SessionBus); + m_dbusTestRunner.startServices(); + qDebug() << "starting dbusmock pay store on" << m_dbusTestRunner.sessionBus(); + qputenv("DBUS_SESSION_BUS_ADDRESS", m_dbusTestRunner.sessionBus().toUtf8()); + } + + QtDBusTest::DBusTestRunner m_dbusTestRunner; + QtDBusMock::DBusMock m_dbusMock; + const QString m_storeName; +}; + +StoreMock::StoreMock(const QString &storeName, + const QList &items_, + QObject *parent) + : QObject(parent), + d {QSharedPointer(new StoreMockPrivate(storeName))} +{ + // add the store + auto message = QDBusMessage::createMethodCall(BUS_NAME, STORE_ROOT, STORE_MOCK_IFACE, "AddStore"); + // wrap the QVariantMaps in QVariants to fit in the QVariantList + QVariantList items; + for (const auto& item : items_) + items += item; + message.setArguments(QVariantList{storeName, items}); + d->m_dbusTestRunner.sessionConnection().call(message); + + auto objectPath = QString::fromUtf8(STORE_ROOT) + QStringLiteral("/") + encodePathElement(storeName); + auto &mockInterface = d->m_dbusMock.mockInterface(BUS_NAME, objectPath, STORE_IFACE, QDBusConnection::SessionBus); + connect(&mockInterface, SIGNAL(MethodCalled(const QString &, const QVariantList &)), + this, SLOT(methodCalled(const QString &, const QVariantList &))); +} + +StoreMock::~StoreMock() +{ +} + +void +StoreMock::addItem(const QVariantMap &itemProperties) +{ + auto message = QDBusMessage::createMethodCall(BUS_NAME, STORE_ROOT, STORE_MOCK_IFACE, "AddItem"); + + message.setArguments(QVariantList{d->m_storeName, itemProperties}); + + d->m_dbusTestRunner.sessionConnection().call(message); +} + +void +StoreMock::setItem(const QVariantMap &itemProperties) +{ + auto message = QDBusMessage::createMethodCall(BUS_NAME, STORE_ROOT, STORE_MOCK_IFACE, "SetItem"); + + message.setArguments(QVariantList{d->m_storeName, itemProperties}); + + d->m_dbusTestRunner.sessionConnection().call(message); +} + +void StoreMock::methodCalled(const QString &name, const QVariantList &args) +{ + if (name == "AcknowledgeItem") + emit itemFinalized(args.at(0).toString()); +} diff --git a/tests/auto/purchasing/ubuntu/store_mock.h b/tests/auto/purchasing/ubuntu/store_mock.h new file mode 100644 index 0000000..87e5fb2 --- /dev/null +++ b/tests/auto/purchasing/ubuntu/store_mock.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef STORE_MOCK_H +#define STORE_MOCK_H + +#include +#include + +QT_BEGIN_NAMESPACE + +struct StoreMockPrivate; +class StoreMock: public QObject +{ + Q_OBJECT + +public: + StoreMock(const QString &storeName, + const QList &item = { }, + QObject *parent = nullptr); + ~StoreMock(); + + void addItem(const QVariantMap &itemProperties); + void setItem(const QVariantMap &itemProperties); + +Q_SIGNALS: + void itemFinalized(const QString &sku); + +private Q_SLOTS: + void methodCalled(const QString &method, const QVariantList &args); + +private: + friend class QInAppStore; + Q_DISABLE_COPY(StoreMock) + + QSharedPointer d; +}; + +QT_END_NAMESPACE + +#endif // STORE_MOCK_H diff --git a/tests/auto/purchasing/ubuntu/tst_qinappstore_ubuntu.cpp b/tests/auto/purchasing/ubuntu/tst_qinappstore_ubuntu.cpp new file mode 100644 index 0000000..adf6cee --- /dev/null +++ b/tests/auto/purchasing/ubuntu/tst_qinappstore_ubuntu.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Purchasing module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3-COMM$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q_OS_UBUNTU +#error Compiling Ubuntu specific code without Q_OS_UBUNTU +#endif + +#include "store_mock.h" + +#include +#include + +#include +#include + + +/// @todo Needed because QInAppProduct::ProductType is +/// declared with Q_ENUMS instead of Q_ENUM +Q_DECLARE_METATYPE(QInAppProduct::ProductType) + +// https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId +#define PACKAGE_S "test" +#define APP_S "test.application" +#define VERSION_S "1234567890" +#define APP_ID_S PACKAGE_S "_" APP_S "_" VERSION_S + +class tst_QInAppStore: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase() + { + // will be called before the first test function is executed. + qputenv("APP_ID", APP_ID_S); + } + + void cleanupTestCase() + { + // will be called after the last test function was executed. + qunsetenv("APP_ID"); + } + + void registerUnknownProduct(); + void registerKnownProducts(); + void registeredProducts(); + void restorePurchases(); + void successfulPurchase(); + void purchaseCancelled(); + void purchaseAccessDenied(); + +private: + + struct IAP { + qulonglong completedTimestamp; + qulonglong acknowledgedTimestamp; + qulonglong purchaseId; + QString state; + + QString price; + QInAppProduct::ProductType type; + + QString sku; + QString title; + QString description; + }; + + QMap m_iaps { + { "sword", { 0, 0, 0, "available", "$1", QInAppProduct::Consumable, "sword", "Sword", "A Sword." } }, + { "shield", { 1000, 0, 100, "approved", "$1", QInAppProduct::Unlockable, "shield", "Shield", "A Shield." } }, + { "amulet", { 1000, 1010, 101, "purchased", "$1", QInAppProduct::Unlockable, "amulet", "Amulet", "An Amulet." } }, + { "cancel", { 0, 0, 0, "available", "$0", QInAppProduct::Unlockable, "cancel", "Cancel", "For testing cancelling of purchase." } }, + { "denied", { 0, 0, 0, "available", "$0", QInAppProduct::Unlockable, "denied", "Denied", "For testing access denied by trust store." } } + }; + + void verifyProduct(const QInAppProduct *product, const IAP item) { + QCOMPARE(product->description(), item.description); + QCOMPARE(product->identifier(), item.sku); + QCOMPARE(product->price(), item.price); + QCOMPARE(product->productType(), item.type); + QCOMPARE(product->title(), item.title); + } + + void verifyTransaction(const QInAppTransaction *transaction, bool restored=false) { + + const auto &product = transaction->product(); + const auto identifier = product->identifier(); + + QVERIFY(m_iaps.contains(identifier)); + const auto &iap = m_iaps[identifier]; + + verifyProduct(product, iap); + + QCOMPARE(transaction->timestamp(), QDateTime::fromTime_t(iap.completedTimestamp)); + + if (restored) { + QCOMPARE(transaction->status(), QInAppTransaction::PurchaseRestored); + } else if ((iap.state == "approved") || (iap.state == "purchased")) { + QCOMPARE(transaction->status(), QInAppTransaction::PurchaseApproved); + } else { + QFAIL("unhandled case"); + } + } + + QList storeItems() { + QList vitems; + for (const auto &item : m_iaps) { + vitems += QVariantMap({ + { "completed_timestamp", item.completedTimestamp }, + { "acknowledged_timestamp", item.acknowledgedTimestamp }, + { "description", item.description }, + { "price", item.price }, + { "purchase_id", item.purchaseId }, + { "sku", item.sku }, + { "state", item.state }, + { "title", item.title }, + { "type", item.type==QInAppProduct::Unlockable ? "unlockable" : "consumable" } + }); + } + return vitems; + } +}; + +void tst_QInAppStore::registerUnknownProduct() +{ + // SETUP + + StoreMock mock (PACKAGE_S); + + QInAppStore store(this); + + const QMap queries ({ + { "unknownConsumable", QInAppProduct::Consumable }, + { "unknownUnlockable", QInAppProduct::Unlockable } + }); + + // set our expectations + const int expectedUnknownCount = queries.size(); + const int expectedRegisteredCount = 0; + const int expectedReadyTransactions = 0; + + // TEST + + QSignalSpy registered (&store, SIGNAL(productRegistered(QInAppProduct *))); + QSignalSpy unknowns (&store, SIGNAL(productUnknown(QInAppProduct::ProductType,QString))); + QSignalSpy readyTransactions (&store, SIGNAL(transactionReady(QInAppTransaction *))); + + for (auto it=queries.begin(), end=queries.end(); it!=end; ++it) + store.registerProduct(it.value(), it.key()); + + // EVALUATE + + QTRY_COMPARE (unknowns.length() , expectedUnknownCount); + QCOMPARE (registered.length() , expectedRegisteredCount); + QCOMPARE (readyTransactions.length() , expectedReadyTransactions); + + for (const auto &unknown : unknowns) { + const auto sku = unknown.at(1).toString(); + const auto it = queries.find(sku); + QVERIFY(it != queries.end()); + QCOMPARE(unknown.at(0).value(), *it); + } +} + +void tst_QInAppStore::registerKnownProducts() +{ + // SETUP + + StoreMock mock (PACKAGE_S, storeItems()); + + QInAppStore store(this); + + QSignalSpy registered (&store, SIGNAL(productRegistered(QInAppProduct *))); + QSignalSpy unknown (&store, SIGNAL(productUnknown(QInAppProduct::ProductType,QString))); + QSignalSpy readyTransactions (&store, SIGNAL(transactionReady(QInAppTransaction *))); + + // Set our expectations + const int expectedUnknownCount = 0; + const int expectedRegisteredCount = m_iaps.size(); + int expectedReadyTransactions = 0; + for (const auto &item : m_iaps) + if ((item.completedTimestamp != 0) && (item.acknowledgedTimestamp == 0)) + ++expectedReadyTransactions; + + // TEST + + for (const auto &item : m_iaps) + store.registerProduct(item.type, item.sku); + + // EVALUATE + + QTRY_COMPARE (registered.length() , expectedRegisteredCount); + QCOMPARE (unknown.length() , expectedUnknownCount); + QCOMPARE (readyTransactions.length(), expectedReadyTransactions); + + for (const auto &args : registered) { + const auto &product = args.at(0).value(); + const auto sku = product->identifier(); + QVERIFY(m_iaps.contains(sku)); + verifyProduct(product, m_iaps.value(sku)); + } +} + +void tst_QInAppStore::registeredProducts() +{ + // SETUP + + StoreMock mock (PACKAGE_S, storeItems()); + + // instantiate a store with the registered products + QInAppStore store(this); + QSignalSpy registered (&store, SIGNAL(productRegistered(QInAppProduct *))); + for (const auto &item : m_iaps) + store.registerProduct(item.type, item.sku); + QTRY_COMPARE (registered.length(), m_iaps.size()); + + // TEST + + for (const auto &item : m_iaps) { + auto product = store.registeredProduct(item.sku); + QVERIFY(product != nullptr); + verifyProduct(product, item); + } +} + +void tst_QInAppStore::restorePurchases() +{ + // SETUP + + StoreMock mock (PACKAGE_S, storeItems()); + + // instantiate a store with the registered products + QInAppStore store(this); + for (const auto &item : m_iaps) + store.registerProduct(item.type, item.sku); + + // Set our expectations. + // (1) a PurchaseRestored TransactionReady for each unlockable that's finalized + QMap expectedRestored; + for (const auto &iap : m_iaps) + if ((iap.state == "purchased") && (iap.type == QInAppProduct::Unlockable)) + expectedRestored[iap.sku] = iap; + + // TEST + + QSignalSpy registered (&store, SIGNAL(productRegistered(QInAppProduct *))); + QSignalSpy unknown (&store, SIGNAL(productUnknown(QInAppProduct::ProductType,QString))); + QSignalSpy readyTransactions (&store, SIGNAL(transactionReady(QInAppTransaction *))); + + store.restorePurchases(); + + // EVALUATE + + QTRY_COMPARE (registered.length() , 0); + QCOMPARE (unknown.length() , 0); + QCOMPARE (readyTransactions.length(), expectedRestored.size()); + for (const auto &args : readyTransactions) + verifyTransaction(args.at(0).value(), true); +} + +void tst_QInAppStore::successfulPurchase() +{ + // SETUP + + StoreMock mock (PACKAGE_S, storeItems()); + + // instantiate a store with the registered products + QInAppStore store(this); + for (const auto &item : m_iaps) + store.registerProduct(item.type, item.sku); + + // Set our expectations. + + const int expectedRegistered = 0; + const int expectedUnknown = 0; + const int expectedReady = 1; + const auto sku = QString::fromUtf8("sword"); + const auto expectedTimeStamp = QDateTime::currentDateTime(); + + // TEST: PURCHASE + + QSignalSpy registered (&store, SIGNAL(productRegistered(QInAppProduct *))); + QSignalSpy unknown (&store, SIGNAL(productUnknown(QInAppProduct::ProductType,QString))); + QSignalSpy ready (&store, SIGNAL(transactionReady(QInAppTransaction *))); + + auto product = store.registeredProduct(sku); + QVERIFY(product != nullptr); + product->purchase(); + + QTRY_COMPARE (ready.length(), expectedReady); + QCOMPARE (registered.length(), expectedRegistered); + QCOMPARE (unknown.length(), expectedUnknown); + auto transaction = ready.at(0).at(0).value(); + QVERIFY (transaction->errorString().isEmpty()); + QCOMPARE (transaction->failureReason(), QInAppTransaction::NoFailure); + QVERIFY (transaction->orderId() != 0); + verifyProduct(transaction->product(), m_iaps[sku]); + QCOMPARE (transaction->status(), QInAppTransaction::PurchaseApproved); + // We have no guarantee that the store mock will run immediately, + // so do a fuzzy "about now" test here + const int aboutNowSecs = 5; + QVERIFY (qAbs(transaction->timestamp().secsTo(expectedTimeStamp)) < aboutNowSecs); + + // TEST: FINALIZE + + QSignalSpy finalized (&mock, SIGNAL(itemFinalized(QString))); + transaction->finalize(); + QTRY_COMPARE (finalized.length(), expectedReady); + QVERIFY(finalized.length() == 1); + QVERIFY(finalized.at(0).length() == 1); + const auto finalizedSku = finalized.at(0).at(0).value(); + QCOMPARE(finalizedSku, sku); +} + +void tst_QInAppStore::purchaseCancelled() +{ + // SETUP + + StoreMock mock (PACKAGE_S, storeItems()); + + // instantiate a store with the registered products + QInAppStore store(this); + for (const auto &item : m_iaps) + store.registerProduct(item.type, item.sku); + + // Set our expectations. + + const int expectedRegistered = 0; + const int expectedUnknown = 0; + const int expectedReady = 1; + const auto sku = QString::fromUtf8("cancel"); + const auto expectedTimeStamp = QDateTime::currentDateTime(); + + // TEST: PURCHASE + + QSignalSpy registered (&store, SIGNAL(productRegistered(QInAppProduct *))); + QSignalSpy unknown (&store, SIGNAL(productUnknown(QInAppProduct::ProductType,QString))); + QSignalSpy ready (&store, SIGNAL(transactionReady(QInAppTransaction *))); + + auto product = store.registeredProduct(sku); + QVERIFY(product != nullptr); + product->purchase(); + + QTRY_COMPARE (ready.length(), expectedReady); + QCOMPARE (registered.length(), expectedRegistered); + QCOMPARE (unknown.length(), expectedUnknown); + auto transaction = ready.at(0).at(0).value(); + QVERIFY (transaction->errorString().isEmpty()); + QCOMPARE (transaction->failureReason(), QInAppTransaction::NoFailure); + QVERIFY (transaction->orderId() != 0); + verifyProduct(transaction->product(), m_iaps[sku]); + QCOMPARE (transaction->status(), QInAppTransaction::PurchaseFailed); +} + +void tst_QInAppStore::purchaseAccessDenied() +{ + // SETUP + + StoreMock mock (PACKAGE_S, storeItems()); + + // instantiate a store with the registered products + QInAppStore store(this); + for (const auto &item : m_iaps) + store.registerProduct(item.type, item.sku); + + // Set our expectations. + + const int expectedRegistered = 0; + const int expectedUnknown = 0; + const int expectedReady = 1; + const auto sku = QString::fromUtf8("denied"); + const auto expectedTimeStamp = QDateTime::currentDateTime(); + + // TEST: PURCHASE + + QSignalSpy registered (&store, SIGNAL(productRegistered(QInAppProduct *))); + QSignalSpy unknown (&store, SIGNAL(productUnknown(QInAppProduct::ProductType,QString))); + QSignalSpy ready (&store, SIGNAL(transactionReady(QInAppTransaction *))); + + auto product = store.registeredProduct(sku); + QVERIFY(product != nullptr); + product->purchase(); + + QTRY_COMPARE (ready.length(), expectedReady); + QCOMPARE (registered.length(), expectedRegistered); + QCOMPARE (unknown.length(), expectedUnknown); + auto transaction = ready.at(0).at(0).value(); + QVERIFY (transaction->errorString().isEmpty()); + QCOMPARE (transaction->failureReason(), QInAppTransaction::NoFailure); + QVERIFY (transaction->orderId() != 0); + verifyProduct(transaction->product(), m_iaps[sku]); + QCOMPARE (transaction->status(), QInAppTransaction::PurchaseFailed); +} + + +QTEST_GUILESS_MAIN(tst_QInAppStore) + +#include "tst_qinappstore_ubuntu.moc" diff --git a/tests/auto/purchasing/ubuntu/ubuntu.pro b/tests/auto/purchasing/ubuntu/ubuntu.pro new file mode 100644 index 0000000..bd44f7a --- /dev/null +++ b/tests/auto/purchasing/ubuntu/ubuntu.pro @@ -0,0 +1,16 @@ +CONFIG += link_pkgconfig +PKGCONFIG += libqtdbusmock-1 +PKGCONFIG += libqtdbustest-1 + +# qtdbusmock requires c++11 +CONFIG += c++11 + +QT += dbus + +CONFIG += testcase +TARGET = tst_qinappstore_ubuntu +QT += testlib purchasing +HEADERS += store_mock.h +SOURCES += store_mock.cpp tst_qinappstore_ubuntu.cpp + + debian/libqt5purchasing5.install0000664000000000000000000000014612645517615014146 0ustar usr/lib/*/libQt5Purchasing.so.5 usr/lib/*/libQt5Purchasing.so.5.6 usr/lib/*/libQt5Purchasing.so.5.6.0 debian/qml-module-qtpurchasing.install0000664000000000000000000000022212645517615015352 0ustar usr/lib/*/qt5/qml/QtPurchasing/libdeclarative_purchasing.so usr/lib/*/qt5/qml/QtPurchasing/plugins.qmltypes usr/lib/*/qt5/qml/QtPurchasing/qmldir debian/source/0000775000000000000000000000000012645517615010504 5ustar debian/source/format0000664000000000000000000000001412645517615011712 0ustar 3.0 (quilt) debian/copyright0000664000000000000000000000667212645517615011152 0ustar Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: qtpurchasing-opensource-src Source: http://code.qt.io/cgit/qt/qtpurchasing.git/ Files: * Copyright: 2015 The Qt Company Ltd. License: LGPL-3 Files: *.qdoc Copyright: 2015 The Qt Company Ltd. License: GFDL-1.3+NIV Files: debian/* Copyright: 2007-2012 Fathi Boudra 2007-2012 Sune Vuorela 2008-2012 Modestas Vainius 2007-2009 Ana Beatriz Guerrero Lopez 2005-2007 Brian Nelson 2012-2015 Timo Jyrinki 2014 Lisandro Damián Nicanor Pérez Meyer License: LGPL-2.1 Files: src/android/src/com/android/vending/billing/* Copyright: 2012 The Android Open Source Project License: Apache Files: src/android/src/org/qtproject/qt5/android/purchasing/* Copyright: 2002 Google, Inc. 2015 The Qt Company Ltd. License: Apache Files: src/android/src/org/qtproject/qt5/android/purchasing/QtInAppPurchase.java Copyright: 2015 The Qt Company Ltd. License: LGPL-3 License: LGPL-3 GNU Lesser General Public License Usage . Alternatively, this file may be used under the terms of the GNU Lesser General Public License version 3 as published by the Free Software Foundation and appearing in the file LICENSE.LGPLv3 included in the packaging of this file. Please review the following information to ensure the GNU Lesser General Public License version 3 requirements will be met: https://www.gnu.org/licenses/lgpl.html. . On Debian systems, the complete text of the LGPL-2.1 license can be found in `/usr/share/common-licenses/LGPL-3`, License: LGPL-2.1 This file may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation and appearing in the file LICENSE.LGPL included in the packaging of this file. Please review the following information to ensure the GNU Lesser General Public License version 2.1 requirements will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. . On Debian systems, the complete text of the LGPL-2.1 license can be found in `/usr/share/common-licenses/LGPL-2.1`, License: Apache Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at . http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. . On Debian systems, the complete text of the Apache License can be found in `/usr/share/common-licenses/Apache-2.0'. License: GFDL-1.3+NIV GNU Free Documentation License Usage Alternatively, this file may be used under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation and appearing in the file included in the packaging of this file. Please review the following information to ensure the GNU Free Documentation License version 1.3 requirements will be met: http://www.gnu.org/copyleft/fdl.html. . On Debian systems, the complete text of the GFDL-1.3 license can be found in `/usr/share/common-licenses/GFDL-1.3`, debian/libqt5purchasing5.symbols0000664000000000000000000001017212645517615014170 0ustar # SymbolsHelper-Confirmed: 5.6.0~git20151002.2d04db2e amd64 libQt5Purchasing.so.5 libqt5purchasing5 #MINVER# _ZN11QInAppStore11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore11qt_metacastEPKc@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore12setupBackendEv@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore14productUnknownEN13QInAppProduct11ProductTypeERK7QString@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore15registerProductEN13QInAppProduct11ProductTypeERK7QString@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore15registerProductEP13QInAppProduct@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore16restorePurchasesEv@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore16staticMetaObjectE@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore16transactionReadyEP17QInAppTransaction@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore17productRegisteredEP13QInAppProduct@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore19setPlatformPropertyERK7QStringS2_@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStore23registerPendingProductsEv@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStoreC1EP7QObject@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStoreC2EP7QObject@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStoreD0Ev@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStoreD1Ev@Base 5.6.0~git20151002.2d04db2e _ZN11QInAppStoreD2Ev@Base 5.6.0~git20151002.2d04db2e _ZN13QInAppProduct11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.6.0~git20151002.2d04db2e _ZN13QInAppProduct11qt_metacastEPKc@Base 5.6.0~git20151002.2d04db2e _ZN13QInAppProduct16staticMetaObjectE@Base 5.6.0~git20151002.2d04db2e _ZN13QInAppProductC1ERK7QStringS2_S2_NS_11ProductTypeES2_P7QObject@Base 5.6.0~git20151002.2d04db2e _ZN13QInAppProductC2ERK7QStringS2_S2_NS_11ProductTypeES2_P7QObject@Base 5.6.0~git20151002.2d04db2e _ZN13QInAppProductD0Ev@Base 5.6.0~git20151002.2d04db2e _ZN13QInAppProductD1Ev@Base 5.6.0~git20151002.2d04db2e _ZN13QInAppProductD2Ev@Base 5.6.0~git20151002.2d04db2e _ZN17QInAppTransaction11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.6.0~git20151002.2d04db2e _ZN17QInAppTransaction11qt_metacastEPKc@Base 5.6.0~git20151002.2d04db2e _ZN17QInAppTransaction16staticMetaObjectE@Base 5.6.0~git20151002.2d04db2e _ZN17QInAppTransactionC1ENS_17TransactionStatusEP13QInAppProductP7QObject@Base 5.6.0~git20151002.2d04db2e _ZN17QInAppTransactionC2ENS_17TransactionStatusEP13QInAppProductP7QObject@Base 5.6.0~git20151002.2d04db2e _ZN17QInAppTransactionD0Ev@Base 5.6.0~git20151002.2d04db2e _ZN17QInAppTransactionD1Ev@Base 5.6.0~git20151002.2d04db2e _ZN17QInAppTransactionD2Ev@Base 5.6.0~git20151002.2d04db2e _ZNK11QInAppStore10metaObjectEv@Base 5.6.0~git20151002.2d04db2e _ZNK11QInAppStore17registeredProductERK7QString@Base 5.6.0~git20151002.2d04db2e _ZNK13QInAppProduct10identifierEv@Base 5.6.0~git20151002.2d04db2e _ZNK13QInAppProduct10metaObjectEv@Base 5.6.0~git20151002.2d04db2e _ZNK13QInAppProduct11descriptionEv@Base 5.6.0~git20151002.2d04db2e _ZNK13QInAppProduct11productTypeEv@Base 5.6.0~git20151002.2d04db2e _ZNK13QInAppProduct5priceEv@Base 5.6.0~git20151002.2d04db2e _ZNK13QInAppProduct5titleEv@Base 5.6.0~git20151002.2d04db2e _ZNK17QInAppTransaction10metaObjectEv@Base 5.6.0~git20151002.2d04db2e _ZNK17QInAppTransaction11errorStringEv@Base 5.6.0~git20151002.2d04db2e _ZNK17QInAppTransaction13failureReasonEv@Base 5.6.0~git20151002.2d04db2e _ZNK17QInAppTransaction16platformPropertyERK7QString@Base 5.6.0~git20151002.2d04db2e _ZNK17QInAppTransaction6statusEv@Base 5.6.0~git20151002.2d04db2e _ZNK17QInAppTransaction7orderIdEv@Base 5.6.0~git20151002.2d04db2e _ZNK17QInAppTransaction7productEv@Base 5.6.0~git20151002.2d04db2e _ZNK17QInAppTransaction9timestampEv@Base 5.6.0~git20151002.2d04db2e _ZTI11QInAppStore@Base 5.6.0~git20151002.2d04db2e _ZTI13QInAppProduct@Base 5.6.0~git20151002.2d04db2e _ZTI17QInAppTransaction@Base 5.6.0~git20151002.2d04db2e _ZTS11QInAppStore@Base 5.6.0~git20151002.2d04db2e _ZTS13QInAppProduct@Base 5.6.0~git20151002.2d04db2e _ZTS17QInAppTransaction@Base 5.6.0~git20151002.2d04db2e _ZTV11QInAppStore@Base 5.6.0~git20151002.2d04db2e _ZTV13QInAppProduct@Base 5.6.0~git20151002.2d04db2e _ZTV17QInAppTransaction@Base 5.6.0~git20151002.2d04db2e debian/control0000664000000000000000000000466212645517615010617 0ustar Source: qtpurchasing-opensource-src Section: libs Priority: optional Maintainer: Ubuntu Developers Uploaders: Timo Jyrinki , Build-Depends: debhelper (>= 9.0.0), libpay2-dev, libqtdbusmock1-dev, libqtdbustest1-dev, pkg-config, pkg-kde-tools, qtbase5-dev (>= 5.4.1+dfsg~), qtdeclarative5-dev (>= 5.4.1~), Standards-Version: 3.9.6 Homepage: http://qt-project.org/ Vcs-Bzr: https://code.launchpad.net/~kubuntu-packagers/kubuntu-packaging/qtpurchasing-opensource-src Vcs-Browser: https://code.launchpad.net/~kubuntu-packagers/kubuntu-packaging/qtpurchasing-opensource-src Package: libqt5purchasing5 Architecture: linux-any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends}, Depends: ${misc:Depends}, ${shlibs:Depends}, Description: Qt Purchasing module Qt is a cross-platform C++ application framework. Qt's primary feature is its rich set of widgets that provide standard GUI functionality. . This package contains the Qt Purchasing module. Package: qml-module-qtpurchasing Architecture: linux-any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends}, Depends: ${misc:Depends}, ${shlibs:Depends}, Description: Qt Purchasing QML module Qt is a cross-platform C++ application framework. Qt's primary feature is its rich set of widgets that provide standard GUI functionality. . This package contains the Qt Purchasing QML module. Package: qtpurchasing5-dev Section: libdevel Architecture: linux-any Multi-Arch: same Depends: libqt5purchasing5 (= ${binary:Version}), libpay2-dev, qtbase5-dev, ${misc:Depends}, Description: Qt Purchasing development files Qt is a cross-platform C++ application framework. Qt's primary feature is its rich set of widgets that provide standard GUI functionality. . This package contains the header development files used for building Qt 5 applications using Qt Purchasing libraries. Package: qtpurchasing5-dbg Priority: extra Architecture: linux-any Multi-Arch: same Section: debug Depends: libqt5purchasing5 (= ${binary:Version}), ${misc:Depends}, Description: Qt Purchasing library debugging symbols Qt is a cross-platform C++ application framework. Qt's primary feature is its rich set of widgets that provide standard GUI functionality. . This package contains the debugging symbols for the Qt 5 Purchasing libraries. debian/qtpurchasing5-dev.install0000664000000000000000000000121612645517615014145 0ustar usr/include/*/qt5/QtPurchasing/QInAppProduct usr/include/*/qt5/QtPurchasing/QInAppStore usr/include/*/qt5/QtPurchasing/QInAppTransaction usr/include/*/qt5/QtPurchasing/QtPurchasing usr/include/*/qt5/QtPurchasing/QtPurchasingDepends usr/include/*/qt5/QtPurchasing/QtPurchasingVersion usr/include/*/qt5/QtPurchasing/qinappproduct.h usr/include/*/qt5/QtPurchasing/qinappstore.h usr/include/*/qt5/QtPurchasing/qinapptransaction.h usr/include/*/qt5/QtPurchasing/qtpurchasingglobal.h usr/include/*/qt5/QtPurchasing/qtpurchasingversion.h usr/lib/*/*.prl usr/lib/*/*.so usr/lib/*/cmake/* usr/lib/*/pkgconfig/* usr/lib/*/qt5/mkspecs/modules/qt_lib_purchasing.pri