From bc39dbba8b38b80f6023f5e47dd75d4248c6cce6 Mon Sep 17 00:00:00 2001
From: Zach Fox <fox@highfidelity.io>
Date: Thu, 29 Nov 2018 12:44:30 -0800
Subject: [PATCH] Finish out the feature

---
 .../commerce/common/sendAsset/SendAsset.qml   | 297 +++++++++++++++---
 .../common/sendAsset/images/clipboard.svg     |   1 +
 interface/src/commerce/Ledger.cpp             |  16 +
 interface/src/commerce/Ledger.h               |   4 +
 interface/src/commerce/QmlCommerce.cpp        |  16 +
 interface/src/commerce/QmlCommerce.h          |   2 +
 6 files changed, 291 insertions(+), 45 deletions(-)
 create mode 100644 interface/resources/qml/hifi/commerce/common/sendAsset/images/clipboard.svg

diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
index 2a2369795e..36113077f5 100644
--- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
+++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml
@@ -36,6 +36,8 @@ Item {
     property bool isCurrentlySendingAsset: false;
     property string assetName: "";
     property string assetCertID: "";
+    property string secret: "";
+    property string authorizationID: "";
     property string sendingPubliclyEffectImage;
     property var http;
     property var listModelName;
@@ -108,6 +110,27 @@ Item {
             }
         }
 
+        onAuthorizeAssetTransferResult: {
+            if (!root.visible) {
+                return;
+            }
+
+            root.isCurrentlySendingAsset = false;
+
+            if (result.status === 'success') {
+                root.authorizationID = result.data.authorization_id;
+                authorizationIDText.text = root.authorizationID;
+                root.secret = result.data.secret;
+                secretText.text = root.secret
+                if (scriptSecretTextField.text !== root.secret) {
+                    console.log("SendAsset: Returned secret doesn't match client-generated secret!");
+                }
+                root.nextActiveView = 'paymentSuccess';
+            } else {
+                root.nextActiveView = 'paymentFailure';
+            }
+        }
+
         onCertificateInfoResult: {
             if (result.status !== 'success') {
                 console.log("Failed to get certificate info", result.data.message);
@@ -408,6 +431,7 @@ Item {
                     onClicked: {
                         sendAssetStep.referrer = "authorizedScript";
                         sendAssetStep.selectedRecipientNodeID = "";
+                        scriptSecretTextField.text = generateRandomSecret();
 
                         root.nextActiveView = "sendAssetStep";
                     }
@@ -1022,7 +1046,6 @@ Item {
 
                 HifiControlsUit.TextField {
                     id: scriptSecretTextField;
-                    text: generateRandomSecret();
                     colorScheme: root.assetCertID === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
                     // Anchors
                     anchors.verticalCenter: parent.verticalCenter;
@@ -1034,8 +1057,6 @@ Item {
                     activeFocusOnPress: true;
                     activeFocusOnTab: true;
 
-                    validator: RegExpValidator { regExp: /^[a-zA-Z0-9]+$/ }
-
                     onAccepted: {
                         optionalMessage.focus = true;
                     }
@@ -1377,7 +1398,10 @@ Item {
                                 parseInt(amountTextField.text),
                                 optionalMessage.text);
                         } else if (sendAssetStep.referrer === "authorizedScript") {
-                            console.log("ZRF HERE: SENDING TO AUTHORIZED SCRIPT");
+                            Commerce.authorizeAssetTransfer(scriptSecretTextField.text || "",
+                                root.assetCertID,
+                                parseInt(amountTextField.text) || 1,
+                                optionalMessage.text)
                         }
                     }
                 }
@@ -1449,18 +1473,24 @@ Item {
 
         Rectangle {
             anchors.top: parent.top;
-            anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125;
+            anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
+                sendAssetStep.referrer === "authorizedScript" ? 15 : 125;
             anchors.left: parent.left;
-            anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
+            anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
+                sendAssetStep.referrer === "authorizedScript" ? 15 : 50;
             anchors.right: parent.right;
-            anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
+            anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
+                sendAssetStep.referrer === "authorizedScript" ? 15 : 50;
             anchors.bottom: parent.bottom;
-            anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125;
+            anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
+                sendAssetStep.referrer === "authorizedScript" ? 15 : 125;
             color: "#FFFFFF";
 
             RalewaySemiBold {
                 id: paymentSentText;
-                text: root.assetCertID === "" ? "Payment Sent" : (sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent");
+                text: root.assetCertID === "" ? (sendAssetStep.referrer === "authorizedScript" ? "Payment Authorized" : "Payment Sent") :
+                    (sendAssetStep.referrer === "authorizedScript" ? "Item Transfer Authorized" :
+                    (sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent"));
                 // Anchors
                 anchors.top: parent.top;
                 anchors.topMargin: 26;
@@ -1498,6 +1528,8 @@ Item {
                     onClicked: {
                         if (sendAssetStep.referrer === "payIn") {
                             sendToScript({method: "closeSendAsset"});
+                        } else if (sendAssetStep.referrer === "authorizedScript") {
+                            showDidYouCopyLightbox();
                         } else {
                             root.nextActiveView = "sendAssetHome";
                             resetSendAssetData();
@@ -1517,38 +1549,176 @@ Item {
                 anchors.leftMargin: 20;
                 anchors.right: parent.right;
                 anchors.rightMargin: 20;
-                height: 80;
+                height: childrenRect.height;
 
-                RalewaySemiBold {
-                    id: sendToText_paymentSuccess;
-                    text: "Sent To:";
-                    // Anchors
+                Item {
+                    id: sendToScriptContainer_paymentSuccess;
+                    visible: sendAssetStep.referrer === "authorizedScript";
                     anchors.top: parent.top;
                     anchors.left: parent.left;
-                    anchors.bottom: parent.bottom;
-                    width: 90;
-                    // Text size
-                    size: 18;
-                    // Style
-                    color: hifi.colors.baseGray;
-                    verticalAlignment: Text.AlignVCenter;
-                }
-
-                RecipientDisplay {
-                    anchors.top: parent.top;
-                    anchors.left: sendToText_paymentSuccess.right;
                     anchors.right: parent.right;
-                    height: parent.height;
-                    textColor: hifi.colors.blueAccent;
+                    height: childrenRect.height;
 
-                    displayName: sendAssetStep.selectedRecipientDisplayName;
-                    userName: sendAssetStep.selectedRecipientUserName;
-                    profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
-                        sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
-                    multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
+                    RalewaySemiBold {
+                        id: authorizationIDLabel;
+                        text: "Authorization ID:";
+                        // Anchors
+                        anchors.left: parent.left;
+                        anchors.top: authorizationIDText.top;
+                        width: paintedWidth;
+                        // Text size
+                        size: 18;
+                        // Style
+                        color: hifi.colors.baseGray;
+                        verticalAlignment: Text.AlignVCenter;
+                    }
+
+                    RalewayRegular {
+                        id: authorizationIDText;
+                        text: root.authorizationID;
+                        anchors.top: parent.top;
+                        anchors.left: authorizationIDLabel.right;
+                        anchors.leftMargin: 16;
+                        anchors.right: authorizationIDClipboardButton.left;
+                        anchors.rightMargin: 16;
+                        // Text size
+                        size: 18;
+                        // Style
+                        color: hifi.colors.baseGray;
+                        horizontalAlignment: Text.AlignHCenter;
+                        verticalAlignment: Text.AlignVCenter;
+                        wrapMode: Text.WrapAnywhere;
+                    }
+
+                    Image {
+                        id: authorizationIDClipboardButton;
+                        source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project
+                        fillMode: Image.PreserveAspectFit;
+                        // Anchors
+                        anchors.right: parent.right;
+                        anchors.top: authorizationIDText.top;
+                        height: 40;
+                        width: height;
+
+                        MouseArea {
+                            anchors.fill: parent;
+                            onClicked: {
+                                Window.copyToClipboard(root.authorizationID);
+                                authorizationIDText.text = "Copied to Clipboard!\n";
+                                authorizationIDClipboardTimer.start();
+                            }
+                        }
+                    }
+
+                    Timer {
+                        id: authorizationIDClipboardTimer;
+                        interval: 2000;
+                        repeat: false;
+                        onTriggered: {
+                            authorizationIDText.text = root.authorizationID;
+                        }
+                    }
+
+                    RalewaySemiBold {
+                        id: secretLabel;
+                        text: "Secret:";
+                        // Anchors
+                        anchors.left: parent.left;
+                        anchors.top: secretText.top;
+                        width: authorizationIDLabel.width;
+                        // Text size
+                        size: 18;
+                        // Style
+                        color: hifi.colors.baseGray;
+                        verticalAlignment: Text.AlignVCenter;
+                    }
+
+                    RalewayRegular {
+                        id: secretText;
+                        text: root.secret;
+                        anchors.top: authorizationIDText.bottom;
+                        anchors.topMargin: 16;
+                        anchors.left: secretLabel.right;
+                        anchors.leftMargin: 16;
+                        anchors.right: secretClipboardButton.left;
+                        anchors.rightMargin: 16;
+                        // Text size
+                        size: 18;
+                        // Style
+                        color: hifi.colors.baseGray;
+                        horizontalAlignment: Text.AlignHCenter;
+                        verticalAlignment: Text.AlignVCenter;
+                        wrapMode: Text.WrapAnywhere;
+                    }                    
+
+                    Image {
+                        id: secretClipboardButton;
+                        source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project
+                        fillMode: Image.PreserveAspectFit;
+                        // Anchors
+                        anchors.right: parent.right;
+                        anchors.top: secretText.top;
+                        height: 40;
+                        width: height;
+
+                        MouseArea {
+                            anchors.fill: parent;
+                            onClicked: {
+                                Window.copyToClipboard(root.secret);
+                                secretText.text = "Copied to Clipboard!\n";
+                                secretClipboardTimer.start();
+                            }
+                        }
+                    }
+
+                    Timer {
+                        id: secretClipboardTimer;
+                        interval: 2000;
+                        repeat: false;
+                        onTriggered: {
+                            secretText.text = root.secret;
+                        }
+                    }
                 }
-            }
-            
+
+                Item {
+                    id: sendToRecipientContainer_paymentSuccess;
+                    visible: !sendToScriptContainer_paymentSuccess.visible;
+                    anchors.top: parent.top;
+                    anchors.left: parent.left;
+                    anchors.right: parent.right;
+                    height: 80;
+
+                    RalewaySemiBold {
+                        id: sendToText_paymentSuccess;
+                        text: "Sent To:";
+                        // Anchors
+                        anchors.top: parent.top;
+                        anchors.left: parent.left;
+                        anchors.bottom: parent.bottom;
+                        width: 90;
+                        // Text size
+                        size: 18;
+                        // Style
+                        color: hifi.colors.baseGray;
+                        verticalAlignment: Text.AlignVCenter;
+                    }
+
+                    RecipientDisplay {
+                        anchors.top: parent.top;
+                        anchors.left: sendToText_paymentSuccess.right;
+                        anchors.right: parent.right;
+                        height: parent.height;
+                        textColor: hifi.colors.blueAccent;
+
+                        displayName: sendAssetStep.selectedRecipientDisplayName;
+                        userName: sendAssetStep.selectedRecipientUserName;
+                        profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
+                            sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
+                        multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
+                    }
+                }
+            }            
 
             Item {
                 id: giftContainer_paymentSuccess;
@@ -1563,7 +1733,8 @@ Item {
 
                 RalewaySemiBold {
                     id: gift_paymentSuccess;
-                    text: sendAssetStep.referrer === "payIn" ? "Item:" : "Gift:";
+                    text: sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "authorizedScript" ?
+                        "Item:" : "Gift:";
                     // Anchors
                     anchors.top: parent.top;
                     anchors.left: parent.left;
@@ -1681,6 +1852,8 @@ Item {
                 onClicked: {
                     if (sendAssetStep.referrer === "payIn") {
                         sendToScript({method: "closeSendAsset"});
+                    } else if (sendAssetStep.referrer === "authorizedScript") {
+                        showDidYouCopyLightbox();
                     } else {
                         root.nextActiveView = "sendAssetHome";
                         resetSendAssetData();
@@ -1714,13 +1887,17 @@ Item {
 
         Rectangle {
             anchors.top: parent.top;
-            anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 150;
+            anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
+                sendAssetStep.referrer === "authorizedScript" ? 15 : 150;
             anchors.left: parent.left;
-            anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
+            anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
+                sendAssetStep.referrer === "authorizedScript" ? 15 : 50;
             anchors.right: parent.right;
-            anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
+            anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
+                sendAssetStep.referrer === "authorizedScript" ? 15 : 50;
             anchors.bottom: parent.bottom;
-            anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 300;
+            anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ||
+                sendAssetStep.referrer === "authorizedScript" ? 15 : 300;
             color: "#FFFFFF";
 
             RalewaySemiBold {
@@ -1772,8 +1949,9 @@ Item {
 
             RalewaySemiBold {
                 id: paymentFailureDetailText;
-                text: "The recipient you specified was unable to receive your " +
-                    (root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift."));
+                text: sendAssetStep.referrer === "authorizedScript" ? "The server was unable to handle your request. Please try again later." :
+                ("The recipient you specified was unable to receive your " +
+                    (root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift.")));
                 anchors.top: paymentFailureText.bottom;
                 anchors.topMargin: 20;
                 anchors.left: parent.left;
@@ -1791,7 +1969,8 @@ Item {
 
             Item {
                 id: sendToContainer_paymentFailure;
-                visible: root.assetCertID === "" || sendAssetStep.referrer === "payIn";
+                visible: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") &&
+                    sendAssetStep.referrer !== "authorizedScript";
                 anchors.top: paymentFailureDetailText.bottom;
                 anchors.topMargin: 8;
                 anchors.left: parent.left;
@@ -1833,7 +2012,8 @@ Item {
             Item {
                 id: amountContainer_paymentFailure;
                 visible: root.assetCertID === "";
-                anchors.top: sendToContainer_paymentFailure.bottom;
+                anchors.top: sendToContainer_paymentFailure.visible ?
+                    sendToContainer_paymentFailure.bottom : paymentFailureDetailText.bottom;
                 anchors.topMargin: 16;
                 anchors.left: parent.left;
                 anchors.leftMargin: 20;
@@ -1954,6 +2134,11 @@ Item {
                             root.assetCertID,
                             parseInt(amountTextField.text),
                             optionalMessage.text);
+                    } else if (sendAssetStep.referrer === "authorizedScript") {
+                        Commerce.authorizeAssetTransfer(scriptSecretTextField.text || "",
+                            root.assetCertID,
+                            parseInt(amountTextField.text) || 1,
+                            optionalMessage.text)
                     }
                 }
             }
@@ -1983,16 +2168,38 @@ Item {
     }
 
     function generateRandomSecret() {
+        var RANDOM_SECRET_LENGTH = 25;
         var randomSecret = "";
         var possibleCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
-        for (var i = 0; i < 10; i++) {
+        for (var i = 0; i < RANDOM_SECRET_LENGTH; i++) {
             randomSecret += possibleCharacters.charAt(Math.floor(Math.random() * possibleCharacters.length));
         }
 
         return randomSecret;
     }
 
+    function showDidYouCopyLightbox() {
+        lightboxPopup.titleText = "Close Confirmation";
+        lightboxPopup.bodyText = "Did you copy your Authorization ID and your Script Secret?\n\n" +
+            "You won't be able to see your Authorization ID or your Script Secret once " +
+            "you close this window.";
+        lightboxPopup.button1text = "GO BACK";
+        lightboxPopup.button1method = function() {
+            lightboxPopup.visible = false;
+        }
+        lightboxPopup.button2text = "I'M ALL SET";
+        lightboxPopup.button2method = function() {
+            lightboxPopup.visible = false;
+            root.nextActiveView = "sendAssetHome";
+            resetSendAssetData();
+            if (root.assetName !== "") {
+                sendSignalToParent({method: "closeSendAsset"});
+            }
+        }
+        lightboxPopup.visible = true;
+    }
+
     //
     // Function Name: fromScript()
     //
diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/images/clipboard.svg b/interface/resources/qml/hifi/commerce/common/sendAsset/images/clipboard.svg
new file mode 100644
index 0000000000..798fdaaab1
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/common/sendAsset/images/clipboard.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 44 55" enable-background="new 0 0 44 44" xml:space="preserve"><path d="M30.201,10.811h-1.599v-0.076c0-0.754-0.612-1.367-1.366-1.367h-2.063c0.005-0.074,0.023-0.146,0.023-0.225  c0-1.766-1.432-3.197-3.197-3.197s-3.197,1.432-3.197,3.197c0,0.078,0.018,0.15,0.023,0.225h-2.063  c-0.754,0-1.366,0.613-1.366,1.367v0.076h-1.599c-1.38,0-2.502,1.123-2.502,2.502v22.24c0,1.379,1.122,2.502,2.502,2.502h16.402  c1.38,0,2.502-1.123,2.502-2.502v-22.24C32.703,11.934,31.581,10.811,30.201,10.811z M22,7.893c0.691,0,1.251,0.559,1.251,1.25  s-0.56,1.252-1.251,1.252s-1.251-0.561-1.251-1.252S21.309,7.893,22,7.893z M31.035,35.553c0,0.459-0.374,0.834-0.834,0.834H13.799  c-0.46,0-0.834-0.375-0.834-0.834v-22.24c0-0.459,0.374-0.834,0.834-0.834h1.599v1.443h13.205v-1.443h1.599  c0.46,0,0.834,0.375,0.834,0.834V35.553z"/><rect x="15.397" y="16.648" width="13.205" height="0.975"/><rect x="15.397" y="20.402" width="13.205" height="0.973"/><rect x="15.397" y="24.154" width="13.205" height="0.975"/><rect x="15.397" y="27.908" width="13.205" height="0.973"/><rect x="15.397" y="31.66" width="13.205" height="0.975"/><text x="0" y="59" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Bieutuong Bon</text><text x="0" y="64" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
\ No newline at end of file
diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp
index b10c9647a0..fb177ddc82 100644
--- a/interface/src/commerce/Ledger.cpp
+++ b/interface/src/commerce/Ledger.cpp
@@ -63,6 +63,7 @@ Handler(balance)
 Handler(inventory)
 Handler(transferAssetToNode)
 Handler(transferAssetToUsername)
+Handler(authorizeAssetTransfer)
 Handler(alreadyOwned)
 Handler(availableUpdates)
 Handler(updateItem)
@@ -428,6 +429,7 @@ void Ledger::transferAssetToUsername(const QString& hfc_key, const QString& user
     transaction["username"] = username;
     transaction["quantity"] = amount;
     transaction["message"] = optionalMessage;
+    transaction["place_name"] = DependencyManager::get<AddressManager>()->getPlaceName();
     if (!certificateID.isEmpty()) {
         transaction["certificate_id"] = certificateID;
     }
@@ -440,6 +442,20 @@ void Ledger::transferAssetToUsername(const QString& hfc_key, const QString& user
     }
 }
 
+void Ledger::authorizeAssetTransfer(const QString& hfc_key, const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage) {
+    QJsonObject transaction;
+    transaction["public_key"] = hfc_key;
+    transaction["secret"] = secret;
+    transaction["quantity"] = amount;
+    transaction["message"] = optionalMessage;
+    if (!certificateID.isEmpty()) {
+        transaction["certificate_id"] = certificateID;
+    }
+    QJsonDocument transactionDoc{ transaction };
+    auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
+    signedSend("transaction", transactionString, hfc_key, "authorize", "authorizeAssetTransferSuccess", "authorizeAssetTransferFailure");
+}
+
 void Ledger::alreadyOwned(const QString& marketplaceId) {
     auto wallet = DependencyManager::get<Wallet>();
     QString endpoint = "already_owned";
diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h
index 715d6337ad..700cbe2c4b 100644
--- a/interface/src/commerce/Ledger.h
+++ b/interface/src/commerce/Ledger.h
@@ -36,6 +36,7 @@ public:
     void certificateInfo(const QString& certificateId);
     void transferAssetToNode(const QString& hfc_key, const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage);
     void transferAssetToUsername(const QString& hfc_key, const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage);
+    void authorizeAssetTransfer(const QString& hfc_key, const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage);
     void alreadyOwned(const QString& marketplaceId);
     void getAvailableUpdates(const QString& itemId = "", const int& pageNumber = 1, const int& itemsPerPage = 10);
     void updateItem(const QString& hfc_key, const QString& certificate_id);
@@ -59,6 +60,7 @@ signals:
     void certificateInfoResult(QJsonObject result);
     void transferAssetToNodeResult(QJsonObject result);
     void transferAssetToUsernameResult(QJsonObject result);
+    void authorizeAssetTransferResult(QJsonObject result);
     void alreadyOwnedResult(QJsonObject result);
     void availableUpdatesResult(QJsonObject result);
     void updateItemResult(QJsonObject result);
@@ -86,6 +88,8 @@ public slots:
     void transferAssetToNodeFailure(QNetworkReply* reply);
     void transferAssetToUsernameSuccess(QNetworkReply* reply);
     void transferAssetToUsernameFailure(QNetworkReply* reply);
+    void authorizeAssetTransferSuccess(QNetworkReply* reply);
+    void authorizeAssetTransferFailure(QNetworkReply* reply);
     void alreadyOwnedSuccess(QNetworkReply* reply);
     void alreadyOwnedFailure(QNetworkReply* reply);
     void availableUpdatesSuccess(QNetworkReply* reply);
diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp
index 566f7ba324..aab053484b 100644
--- a/interface/src/commerce/QmlCommerce.cpp
+++ b/interface/src/commerce/QmlCommerce.cpp
@@ -38,6 +38,7 @@ QmlCommerce::QmlCommerce() {
     connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus);
     connect(ledger.data(), &Ledger::transferAssetToNodeResult, this, &QmlCommerce::transferAssetToNodeResult);
     connect(ledger.data(), &Ledger::transferAssetToUsernameResult, this, &QmlCommerce::transferAssetToUsernameResult);
+    connect(ledger.data(), &Ledger::authorizeAssetTransferResult, this, &QmlCommerce::authorizeAssetTransferResult);
     connect(ledger.data(), &Ledger::availableUpdatesResult, this, &QmlCommerce::availableUpdatesResult);
     connect(ledger.data(), &Ledger::updateItemResult, this, &QmlCommerce::updateItemResult);
 
@@ -246,6 +247,21 @@ void QmlCommerce::transferAssetToUsername(const QString& username,
     ledger->transferAssetToUsername(key, username, certificateID, amount, optionalMessage);
 }
 
+void QmlCommerce::authorizeAssetTransfer(const QString& secret,
+    const QString& certificateID,
+    const int& amount,
+    const QString& optionalMessage) {
+    auto ledger = DependencyManager::get<Ledger>();
+    auto wallet = DependencyManager::get<Wallet>();
+    QStringList keys = wallet->listPublicKeys();
+    if (keys.count() == 0) {
+        QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
+        return emit authorizeAssetTransferResult(result);
+    }
+    QString key = keys[0];
+    ledger->authorizeAssetTransfer(key, secret, certificateID, amount, optionalMessage);
+}
+
 void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) {
     if (!certificateID.isEmpty()) {
         auto ledger = DependencyManager::get<Ledger>();
diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h
index c5fbdaf4a4..e22b540624 100644
--- a/interface/src/commerce/QmlCommerce.h
+++ b/interface/src/commerce/QmlCommerce.h
@@ -51,6 +51,7 @@ signals:
 
     void transferAssetToNodeResult(QJsonObject result);
     void transferAssetToUsernameResult(QJsonObject result);
+    void authorizeAssetTransferResult(QJsonObject result);
 
     void contentSetChanged(const QString& contentSetHref);
 
@@ -84,6 +85,7 @@ protected:
 
     Q_INVOKABLE void transferAssetToNode(const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage);
     Q_INVOKABLE void transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage);
+    Q_INVOKABLE void authorizeAssetTransfer(const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage);
 
     Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);