Support crypto payment request URIs, add request crypto QR generator

master
Skylar Ittner 2 years ago
parent 771bb9edeb
commit 2167d7c206

@ -5,9 +5,52 @@
*/
var walletPubKeyRegex = /^(bc1|[13]|D)[a-zA-HJ-NP-Z0-9]{25,}$/;
var paymentRequestRegex = /^(bitcoin|dogecoin):(bc1|[13]|D)[a-zA-HJ-NP-Z0-9]{25,}$/;
var paymentRequestRegex = /^(?:bitcoin|dogecoin):([a-zA-Z0-9]{20,40})(?:\?(.*))?$/;
var walletPrivateKeyRegex = /^[0-9A-Za-z]+$/;
/**
* Parse a crypto URI and extract the info in it.
* Copyright (c) 2019 Robin Linus, MIT license, https://github.com/coins/bitcoin-uri-js
* @param {type} uri
* @returns {parsePaymentURI.parsed}
*/
function parsePaymentURI(uri) {
const legalKeys = ['address', 'amount', 'value', 'message', 'send', 'tx'];
const match = paymentRequestRegex.exec(uri);
if (!match) {
return null;
}
const parsed = {uri: uri}
if (match[2]) {
const queries = match[2].split('&');
for (let i = 0; i < queries.length; i++) {
const query = queries[i].split('=');
const key = query[0];
if (query.length === 2 && legalKeys.includes(key)) {
parsed[key] = decodeURIComponent(query[1].replace(/\+/g, '%20'));
}
}
}
parsed.address = match[1];
return parsed;
}
function openWalletPage(walletaddress) {
var navuri = '/crypto/' + walletaddress;
if (typeof router.currentRoute.query.paymenturi != 'undefined') {
var parsed = parsePaymentURI(router.currentRoute.query.paymenturi);
if (parsed != null) {
navuri += '/' + parsed.address;
if (typeof parsed['amount'] != 'undefined') {
navuri += '/' + parsed.amount;
}
}
}
router.navigate(navuri);
}
function scanWalletQrCode(callback) {
scanBarcode(function (result) {
if (walletPubKeyRegex.test(result)) {
@ -302,17 +345,16 @@ function openWalletBalancePage( {to, resolve, reject}) {
fiatvalue: resp.usdvalue,
currencyname: resp.label,
attribution: resp.attribution,
exchangerate: resp.exchangerates.usd,
logo: "./assets/images/crypto/" + resp.currency + ".svg",
walletaddress: to.params.walletaddress
walletaddress: to.params.walletaddress,
sendtoaddress: (typeof to.params.toaddress != "undefined" ? to.params.toaddress : ""),
sendtoamount: (typeof to.params.amount != "undefined" ? to.params.amount : "")
};
resolve({
content: compiledPages.crypto_wallet(context)
});
$("#walletBalanceAmount").text(resp.balance + " " + resp.currency);
$("#walletFiatAmount").text(resp.usdvalue);
$("#walletCurrency").text(resp.label);
$("#walletBalanceAttribution").text(resp.attribution);
$("#walletBalanceLogo").attr("src", "./assets/images/crypto/" + resp.currency + ".svg");
} else {
reject();
app.dialog.alert(resp.msg, "Error");
@ -377,9 +419,45 @@ function setupFiatConversion(walletAddress) {
var exchangerate = parseFloat($("#transactionAmountFiat").data("exchange-rate"));
$("#transactionAmount").val((fiatamount / exchangerate).toFixed(8));
});
if ($("#transactionAmount").val() != "") {
// Update the fiat conversion calculation if there's an amount prefilled
$("#transactionAmount").trigger("input");
}
});
}
function setupReceiveFiatConversion() {
var exchangerate = $("#receiveAmountFiat").data("exchangerate");
var fiatlabel = $("#receiveAmountFiat").data("currencylabel");
if (exchangerate == -1) {
return;
}
$("#cryptoAmountReceiveFiatLI").css("display", "");
$("#cryptoAmountReceiveFiatLabel").text(fiatlabel);
$("#receiveAmount").off("input change paste keyup");
$("#receiveAmountFiat").off("input change paste keyup");
$("#receiveAmount").on("input change paste keyup", function () {
if ($("#cryptoAmountReceiveFiatLI").css("display") == "none") {
return;
}
var amount = parseFloat($("#receiveAmount").val());
var exchangerate = parseFloat($("#receiveAmountFiat").data("exchangerate"));
$("#receiveAmountFiat").val((amount * exchangerate).toFixed(2));
});
$("#receiveAmountFiat").on("input change paste keyup", function () {
var fiatamount = parseFloat($("#receiveAmountFiat").val());
var exchangerate = parseFloat($("#receiveAmountFiat").data("exchangerate"));
$("#receiveAmount").val((fiatamount / exchangerate).toFixed(8));
});
if ($("#receiveAmount").val() != "") {
// Update the fiat conversion calculation if there's an amount prefilled
$("#receiveAmount").trigger("input");
}
}
/**
* Hides the fiat conversion input box.
* @returns {undefined}
@ -393,6 +471,24 @@ function unsetupFiatConversion() {
$("#transactionAmountFiat").off("input change paste keyup");
}
function showPaymentRequestQRCode() {
var paymenturi = "";
switch ($("#receiveAmount").data("currency")) {
case "DOGE":
paymenturi = "dogecoin:";
break;
case "BTC":
paymenturi = "bitcoin:";
break;
}
paymenturi += $('#walletAddress').text();
if ($("#receiveAmount").val() > 0) {
paymenturi += "?amount=" + $("#receiveAmount").val();
}
$("#paymentRequestQRCodeContainer").html("");
new QRCode(document.getElementById("paymentRequestQRCodeContainer"), paymenturi);
}
$("#app").on("click", "#sendCryptoOpenPopupBtn", function () {
if (walletPubKeyRegex.test($("#walletAddress").text())) {
setupFiatConversion($("#walletAddress").text());

@ -25,7 +25,7 @@ function openGenericBarcodeScanner() {
action = "track";
}
} else if (paymentRequestRegex.test(result)) {
code = result;
code = encodeURIComponent(result);
action = "sendcrypto";
} else if (walletPubKeyRegex.test(result)) {
code = result;
@ -46,7 +46,7 @@ function openGenericBarcodeScanner() {
router.navigate("/crypto/" + code);
break;
case "sendcrypto":
router.navigate("/crypto");
router.navigate("/crypto?paymenturi=" + code);
//app.dialog.alert("Not implemented.");
break;
default:

File diff suppressed because one or more lines are too long

@ -44,6 +44,7 @@
<script src="node_modules/template7/dist/template7.min.js"></script>
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/maplibre-gl/dist/maplibre-gl.js"></script>
<script src="assets/js/qrcode.min.js"></script>
<script src="assets/js/bitcore-lib.min.js"></script>
<script src="assets/js/bitcore-lib-doge.min.js"></script>

@ -20,21 +20,47 @@
<div class="block">
<p>This program is licensed under the Mozilla Public License 2.0.
To get the source code, visit https://source.netsyms.com/Netsyms/HelenaExpressApp.
<hr>
When viewing tracking results for a UPS package, the following copyright notice applies to the tracking data shown:
<br>
© 2021 United Parcel Service of America, Inc. All Rights Reserved. Confidential and Proprietary.
The use, disclosure, reproduction, modification, transfer, or transmittal of this work for any purpose in any form
or by any means without the written permission of United Parcel Service is strictly prohibited.
<hr>
This application relies on and is bundled with third-party code.
See below for the their licenses and where to find source code.
<hr>
When viewing tracking results for a UPS package, the following copyright notice applies to the tracking data shown:
<br>
© 2022 United Parcel Service of America, Inc. All Rights Reserved. Confidential and Proprietary.
The use, disclosure, reproduction, modification, transfer, or transmittal of this work for any purpose in any form
or by any means without the written permission of United Parcel Service is strictly prohibited.
<hr>
This application relies on and is bundled with third-party code.
See below for the their licenses and where to find source code.
</div>
<div class="block">
<h2>Code and Libraries</h2>
<pre style="white-space: pre-line; overflow-wrap: break-word;">
{{credits}}
-----
The following software may be included in this product: QRCode.js A copy of the source code may be downloaded from https://github.com/davidshimjs/qrcodejs. This software contains the following license and notice below:
The MIT License (MIT)
Copyright (c) 2012 davidshimjs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
</pre>
</div>

@ -35,9 +35,7 @@
</div>
<div class="col-100 medium-40 large-30">
<div class="block text-align-center">
<div class="button hapticbtn button-fill" onclick="scanWalletQrCode(function (code) {
router.navigate('/crypto/' + code);
});"><i class="fa-solid fa-qrcode"></i> &nbsp; Scan Wallet</div>
<div class="button hapticbtn button-fill" onclick="scanWalletQrCode(openWalletPage);"><i class="fa-solid fa-qrcode"></i> Scan Wallet</div>
</div>
<div class="block text-align-center">
<a href="#" onclick="$('#wallet-address-manual-entry').removeClass('display-none');$('a[name=wallet-address-manual-entry-anchor]').get(0).scrollIntoView();">Can't scan? <span class="taptext">Tap</span><span class="clicktext">Click</span> here.</a>
@ -57,7 +55,7 @@
</div>
</li>
<li class="item-content">
<div class="button button-outline hapticbtn" onclick="router.navigate('/crypto/' + $('#walletPubKeyManualEntry').val())">Open Wallet</div>
<div class="button button-outline hapticbtn" onclick="openWalletPage($('#walletPubKeyManualEntry').val());">Open Wallet</div>
</li>
</ul>
</div>

@ -31,7 +31,10 @@
<div class="row justify-content-center">
<div class="col-100 medium-50 large-30">
<div class="block">
<div class="button hapticbtn button-fill popup-open" data-popup="#sendCryptoPopup" id="sendCryptoOpenPopupBtn"><i class="fa-solid fa-paper-plane"></i> &nbsp; Send Crypto</div>
<div class="button hapticbtn button-fill popup-open" data-popup="#sendCryptoPopup" id="sendCryptoOpenPopupBtn"><i class="fa-solid fa-inbox-out"></i> Send</div>
</div>
<div class="block">
<div class="button hapticbtn button-fill popup-open" data-popup="#receiveCryptoPopup" id="receiveCryptoOpenPopupBtn"><i class="fa-solid fa-inbox-in"></i> Receive</div>
</div>
</div>
</div>
@ -44,101 +47,163 @@
</div>
<div class="popup" id="sendCryptoPopup">
<div class="card">
<div class="card-header">Send Crypto</div>
<div class="card-content card-content-padding">
<div class="list margin-bottom-half">
<ul>
<li class="item-divider">Step 1</li>
<li class="item-content">
<div class="item-inner">
Scan your private key. The private key unlocks your wallet and authorizes the transfer.
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<input type="text" id="walletPrivateKey" placeholder="6JJRxyW..." />
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content">
<div class="button hapticbtn button-fill" onclick="scanPrivateKeyQrCode(function (d) {
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="left">
<a class="link popup-close" href="#">
<i class="icon icon-back"></i>
<span class="if-not-md">Close</span>
</a>
</div>
<div class="title">Send Crypto</div>
</div>
</div>
<div class="list margin-bottom-half">
<ul>
<li class="item-divider">Step 1</li>
<li class="item-content">
<div class="item-inner">
Scan your private key. The private key unlocks your wallet and authorizes the transfer.
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<input type="text" id="walletPrivateKey" placeholder="6JJRxyW..." />
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content">
<div class="button hapticbtn button-fill" onclick="scanPrivateKeyQrCode(function (d) {
$('#walletPrivateKey').val(d);
});"><i class="fa-solid fa-key"></i> &nbsp; Scan Private Key
</div>
</li>
});"><i class="fa-solid fa-key"></i> Scan Private Key
</div>
</li>
<li class="item-divider">Step 2</li>
<li class="item-content">
<div class="item-inner">
Scan or paste the recipient's wallet address.
The money will be sent here. Important: the recipient must be expecting the
same cryptocurrency your wallet uses. Otherwise the money will
be lost forever.
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<input type="text" id="walletToAddress" placeholder="1X68a3n1..." />
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content">
<div class="button hapticbtn button-fill" onclick="scanWalletQrCode(function (d) {
$('#walletToAddress').val(d);
});"><i class="fa-solid fa-inbox-in"></i> &nbsp; Scan Recipient's Wallet
</div>
</li>
<li class="item-divider">Step 2</li>
<li class="item-content">
<div class="item-inner">
Scan or paste the recipient's wallet address.
The money will be sent here. Important: the recipient must be expecting the
same cryptocurrency your wallet uses. Otherwise the money will
be lost forever.
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<input type="text" id="walletToAddress" placeholder="1X68a3n1..." value="{{sendtoaddress}}" />
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content">
<div class="button hapticbtn button-fill" onclick="scanWalletQrCode(function (d) {
var parsed = parsePaymentURI(d);
if (parsed == null) {
$('#walletToAddress').val(d);
} else {
$('#walletToAddress').val(parsed.address);
if (typeof parsed['amount'] != 'undefined') {
$('#transactionAmount').val(parsed.amount);
}
}
});"><i class="fa-solid fa-inbox-in"></i> Scan Recipient's Wallet
</div>
</li>
<li class="item-divider">Step 3</li>
<li class="item-content">
<div class="item-inner">
Enter the amount to send.
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-title item-label" id="cryptoAmountSendCurrencyLabel"></div>
<div class="item-input-wrap">
<input type="number" id="transactionAmount" step="0.00000001" min="0.00000001" max="999999.99999999"/>
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content item-input" id="cryptoFiatInputItem" style="display: none;">
<div class="item-inner">
<div class="item-title item-label" id="cryptoAmountSendFiatLabel"></div>
<div class="item-input-wrap">
<input type="number" id="transactionAmountFiat" step="0.01" min="0.01" max="9999.99"/>
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-divider">Step 3</li>
<li class="item-content">
<div class="item-inner">
Enter the amount to send.
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-title item-label" id="cryptoAmountSendCurrencyLabel"></div>
<div class="item-input-wrap">
<input type="number" id="transactionAmount" step="0.00000001" min="0.00000001" max="999999.99999999" value="{{sendtoamount}}"/>
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content item-input" id="cryptoFiatInputItem" style="display: none;">
<div class="item-inner">
<div class="item-title item-label" id="cryptoAmountSendFiatLabel"></div>
<div class="item-input-wrap">
<input type="number" id="transactionAmountFiat" step="0.01" min="0.01" max="9999.99"/>
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-divider">Step 4</li>
<li class="item-content">
<div class="item-inner">
<div><span class="taptext">Tap</span><span class="clicktext">Click</span> the button to send the transaction.</div>
</div>
</li>
<li class="item-content">
<div class="button hapticbtn button-fill" onclick="walletGUISendCoins()">
<i class="fa-solid fa-paper-plane"></i> &nbsp; Send Transaction
</div>
</li>
<li class="item-content">
<div class="button hapticbtn popup-close" onclick="$('#walletPrivateKey').val('');" >
<i class="fa-solid fa-xmark"></i> &nbsp; Cancel
</div>
</li>
</ul>
<li class="item-divider">Step 4</li>
<li class="item-content">
<div class="item-inner">
<div><span class="taptext">Tap</span><span class="clicktext">Click</span> the button to send the transaction.</div>
</div>
</li>
<li class="item-content">
<div class="button hapticbtn button-fill" onclick="walletGUISendCoins()">
<i class="fa-solid fa-paper-plane"></i> Send Transaction
</div>
</li>
<li class="item-content">
<div class="button hapticbtn popup-close" onclick="$('#walletPrivateKey').val('');" >
<i class="fa-solid fa-xmark"></i> Cancel
</div>
</li>
</ul>
</div>
</div>
<div class="popup" id="receiveCryptoPopup">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="left">
<a class="link popup-close" href="#">
<i class="icon icon-back"></i>
<span class="if-not-md">Close</span>
</a>
</div>
<div class="title">Request Crypto</div>
</div>
</div>
<div class="margin">
Enter the amount to request.
</div>
<div class="list margin-bottom-half margin-top-half">
<ul>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-title item-label" id="cryptoAmountReceiveCurrencyLabel">{{currencyunit}}</div>
<div class="item-input-wrap">
<input type="number" id="receiveAmount" step="0.00000001" min="0.00000001" max="999999.99999999" data-currency="{{currencyunit}}" />
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content item-input" id="cryptoAmountReceiveFiatLI" style="display: none;">
<div class="item-inner">
<div class="item-title item-label" id="cryptoAmountReceiveFiatLabel"></div>
<div class="item-input-wrap">
<input type="number" id="receiveAmountFiat" step="0.01" min="0.01" max="9999.99" data-exchangerate="{{exchangerate}}" data-currencylabel="$"/>
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content">
<div class="button hapticbtn button-fill" onclick="showPaymentRequestQRCode()">
<i class="fa-solid fa-qrcode"></i> Show Payment Request Code
</div>
</li>
</ul>
</div>
<div id="paymentRequestQRCodeContainer" class="block display-flex justify-content-center"></div>
</div>
</div>

@ -54,7 +54,7 @@ var routes = [
slideshow: [
{
image: "assets/images/crypto/slides/intro.svg",
text: "Bitcoin and cryptocurrency can be complicated. We've made it simple."
text: "Bitcoin and cryptocurrency can be complicated. We've made it simple. Swipe left for more."
},
{
image: "assets/images/crypto/slides/vault.svg",
@ -89,10 +89,37 @@ var routes = [
}
}
},
{
path: '/crypto/:walletaddress/:toaddress',
async: openWalletBalancePage,
name: 'crypto_wallet',
on: {
pageAfterIn: function () {
$("#sendCryptoOpenPopupBtn").click();
setupReceiveFiatConversion();
}
}
},
{
path: '/crypto/:walletaddress/:toaddress/:amount',
async: openWalletBalancePage,
name: 'crypto_wallet',
on: {
pageAfterIn: function () {
$("#sendCryptoOpenPopupBtn").click();
setupReceiveFiatConversion();
}
}
},
{
path: '/crypto/:walletaddress',
async: openWalletBalancePage,
name: 'crypto_wallet'
name: 'crypto_wallet',
on: {
pageAfterIn: function () {
setupReceiveFiatConversion();
}
}
},
{
path: '/home',

Loading…
Cancel
Save