From 1cf81495ab3486f613887719f6c91b4c65244d86 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 9 Aug 2017 11:50:52 -0700 Subject: [PATCH] basic commerce infrastructure --- .../resources/controllers/keyboardMouse.json | 2 +- .../resources/qml/hifi/commerce/Checkout.qml | 6 +++- interface/src/Application.cpp | 2 ++ interface/src/commerce/CommerceLogging.cpp | 14 ++++++++ interface/src/commerce/CommerceLogging.h | 19 ++++++++++ interface/src/commerce/Ledger.cpp | 36 +++++++++++++++++++ interface/src/commerce/Ledger.h | 3 ++ interface/src/commerce/QmlCommerce.cpp | 14 ++++++++ interface/src/commerce/QmlCommerce.h | 2 ++ interface/src/commerce/Wallet.cpp | 29 +++++++++++++++ interface/src/commerce/Wallet.h | 8 +++++ scripts/system/marketplaces/marketplaces.js | 1 + 12 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 interface/src/commerce/CommerceLogging.cpp create mode 100644 interface/src/commerce/CommerceLogging.h diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index f377e02e5f..54d2467f78 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -131,6 +131,6 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } + { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } ] } diff --git a/interface/resources/qml/hifi/commerce/Checkout.qml b/interface/resources/qml/hifi/commerce/Checkout.qml index 6cbd8ace3c..0a068ae9d8 100644 --- a/interface/resources/qml/hifi/commerce/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/Checkout.qml @@ -27,6 +27,9 @@ Rectangle { property string itemId; // Style color: hifi.colors.baseGray; + Hifi.QmlCommerce { + id: commerce; + } // // TITLE BAR START @@ -259,7 +262,7 @@ Rectangle { width: parent.width/2 - anchors.rightMargin*2; text: "Buy" onClicked: { - sendToScript({method: 'checkout_buyClicked', params: itemId}); + sendToScript({method: 'checkout_buyClicked', params: {success: commerce.buy(itemId, parseInt(itemPriceText.text))}}); } } } @@ -290,6 +293,7 @@ Rectangle { itemNameText.text = message.params.itemName; itemAuthorText.text = message.params.itemAuthor; itemPriceText.text = message.params.itemPrice; + itemId = message.params.itemId; break; default: console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1330df1fad..cc9bace9aa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -195,6 +195,7 @@ #include #include "commerce/Ledger.h" #include "commerce/Wallet.h" +#include "commerce/QmlCommerce.h" // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -2061,6 +2062,7 @@ void Application::initializeUi() { LoginDialog::registerType(); Tooltip::registerType(); UpdateDialog::registerType(); + QmlCommerce::registerType(); qmlRegisterType("Hifi", 1, 0, "ResourceImageItem"); qmlRegisterType("Hifi", 1, 0, "Preference"); diff --git a/interface/src/commerce/CommerceLogging.cpp b/interface/src/commerce/CommerceLogging.cpp new file mode 100644 index 0000000000..d89c970f41 --- /dev/null +++ b/interface/src/commerce/CommerceLogging.cpp @@ -0,0 +1,14 @@ +// +// CommerceLogging.cpp +// interface/interface/src/commerce +// +// Created by Howard Stearns on 8/9/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "CommerceLogging.h" + +Q_LOGGING_CATEGORY(commerce, "hifi.commerce") diff --git a/interface/src/commerce/CommerceLogging.h b/interface/src/commerce/CommerceLogging.h new file mode 100644 index 0000000000..cd1cf59df2 --- /dev/null +++ b/interface/src/commerce/CommerceLogging.h @@ -0,0 +1,19 @@ +// +// CommerceLogging.h +// interface/src +// +// Created by Howard Stearns on 8/9/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_CommerceLogging_h +#define hifi_CommerceLogging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(commerce) + +#endif // hifi_CommerceLogging_h diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 71e5ac3fee..dae5c48208 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -9,4 +9,40 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include +#include "AccountManager.h" +#include "Wallet.h" #include "Ledger.h" +#include "CommerceLogging.h" + +bool Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername) { + QJsonObject transaction; + transaction["hfc_key"] = hfc_key; + transaction["hfc"] = cost; + transaction["asset_id"] = asset_id; + transaction["inventory_key"] = inventory_key; + transaction["inventory_buyer_username"] = buyerUsername; + QJsonDocument transctionDoc{ transaction }; + QString transactionString = transctionDoc.toJson(QJsonDocument::Compact); + + auto wallet = DependencyManager::get(); + QString signature = wallet->signWithKey(transactionString, hfc_key); + QJsonObject request; + request["transaction"] = transactionString; + request["signature"] = signature; + + qCInfo(commerce) << "Transaction:" << QJsonDocument(request).toJson(QJsonDocument::Compact); + return true; // FIXME send to server. +} + +bool receiveAt(const QString& hfc_key) { + auto accountManager = DependencyManager::get(); + if (!accountManager->isLoggedIn()) { + qCWarning(commerce) << "Cannot set receiveAt when not logged in."; + return false; + } + auto username = accountManager->getAccountInfo().getUsername(); + qCInfo(commerce) << "Setting default receiving key for" << username; + return true; // FIXME send to server. +} \ No newline at end of file diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index e04bad6198..0b7e8465f0 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -20,6 +20,9 @@ class Ledger : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY +public: + bool buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername = ""); + bool receiveAt(const QString& hfc_key); }; #endif // hifi_Ledger_h diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index d8521330fd..db0dc57b37 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -12,5 +12,19 @@ #include "QmlCommerce.h" #include "Application.h" #include "DependencyManager.h" +#include "Ledger.h" +#include "Wallet.h" HIFI_QML_DEF(QmlCommerce) + +bool QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) { + auto ledger = DependencyManager::get(); + auto wallet = DependencyManager::get(); + QStringList keys = wallet->listPublicKeys(); + if (keys.count == 0) { + return false; + } + QString key = keys[0]; + // For now, we receive at the same key that pays for it. + return ledger->buy(key, cost, assetId, key, buyerUsername); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index ecd366cece..3bb72a935c 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -21,6 +21,8 @@ class QmlCommerce : public OffscreenQmlDialog { Q_OBJECT HIFI_QML_DECL +protected: + Q_INVOKABLE bool buy(const QString& assetId, int cost, const QString& buyerUsername = ""); }; #endif // hifi_QmlCommerce_h diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index a33510a904..d2a6ae4809 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -9,4 +9,33 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include "CommerceLogging.h" +#include "Ledger.h" #include "Wallet.h" + +bool Wallet::createIfNeeded() { + // FIXME: persist in file between sessions. + if (_publicKeys.count() > 0) return false; + qCInfo(commerce) << "Creating wallet."; + return generateKeyPair(); +} + +bool Wallet::generateKeyPair() { + // FIXME: need private key, too, and persist in file. + qCInfo(commerce) << "Generating keypair."; + QString key = QUuid::createUuid().toString(); + _publicKeys.push_back(key); + auto ledger = DependencyManager::get(); + return ledger->receiveAt(key); +} +QStringList Wallet::listPublicKeys() { + qCInfo(commerce) << "Enumerating public keys."; + createIfNeeded(); + return _publicKeys; +} + +QString Wallet::signWithKey(const QString& text, const QString& key) { + qCInfo(commerce) << "Signing text."; + return "fixme signed"; +} \ No newline at end of file diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 5bc9cbc3a9..7cfb14c30d 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -20,6 +20,14 @@ class Wallet : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY +public: + bool createIfNeeded(); + bool generateKeyPair(); + QStringList listPublicKeys(); + QString signWithKey(const QString& text, const QString& key); + +private: + QStringList _publicKeys{}; }; #endif // hifi_Wallet_h diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 74812bb013..24f6f771cc 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -197,6 +197,7 @@ //tablet.popFromStack(); break; case 'checkout_buyClicked': + print("fromQml: " + JSON.stringify(message)); //tablet.popFromStack(); break; default: