mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 14:22:40 +01:00
Checkout v2: Clicking QR code copies full payment URI (#5627)
* Checkout v2: Clicking QR code copies full payment URI Before it copied only the destination value (Bitcoin address or Lightning BOLT11). This didn't include the BOLT11 in case of the unified QR code. Now it will copy the full payment URI, which is the same as the QR represents: - Unified: `bitcoin:ADDRESS?amount=AMOUNT&lightning=BOLT11` - Bitcoin: `bitcoin:ADDRESS?amount=AMOUNT` - Lightning: `lightning:BOLT11` Fixes #5625. * Test fix
This commit is contained in:
parent
5e25ee2996
commit
89d294524a
4 changed files with 29 additions and 26 deletions
|
@ -60,13 +60,13 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Contains("Bitcoin", s.Driver.FindElement(By.CssSelector(".payment-method.active")).Text);
|
Assert.Contains("Bitcoin", s.Driver.FindElement(By.CssSelector(".payment-method.active")).Text);
|
||||||
Assert.Contains("LNURL", s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Text);
|
Assert.Contains("LNURL", s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Text);
|
||||||
var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
var clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
var payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
var payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
var copyAddress = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
var address = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||||
Assert.Equal($"bitcoin:{address}", payUrl);
|
|
||||||
Assert.StartsWith("bcrt", s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text);
|
Assert.StartsWith("bcrt", s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text);
|
||||||
Assert.DoesNotContain("lightning=", payUrl);
|
Assert.DoesNotContain("lightning=", payUrl);
|
||||||
Assert.Equal(address, copyAddress);
|
Assert.Equal($"bitcoin:{address}", payUrl);
|
||||||
|
Assert.Equal($"bitcoin:{address}", clipboard);
|
||||||
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
||||||
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
|
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
|
||||||
|
|
||||||
|
@ -97,11 +97,11 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Contains("Lightning", s.Driver.WaitForElement(By.CssSelector(".payment-method.active")).Text);
|
Assert.Contains("Lightning", s.Driver.WaitForElement(By.CssSelector(".payment-method.active")).Text);
|
||||||
Assert.Contains("Bitcoin", s.Driver.WaitForElement(By.CssSelector(".payment-method")).Text);
|
Assert.Contains("Bitcoin", s.Driver.WaitForElement(By.CssSelector(".payment-method")).Text);
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
copyAddress = s.Driver.FindElement(By.CssSelector("#Lightning_BTC_LightningLike .truncate-center-start")).Text;
|
address = s.Driver.FindElement(By.CssSelector("#Lightning_BTC_LightningLike .truncate-center-start")).Text;
|
||||||
Assert.Equal($"lightning:{address}", payUrl);
|
Assert.Equal($"lightning:{address}", payUrl);
|
||||||
Assert.Equal(address, copyAddress);
|
Assert.Equal($"lightning:{address}", clipboard);
|
||||||
Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue);
|
Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue);
|
||||||
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ namespace BTCPayServer.Tests
|
||||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||||
|
|
||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
address = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||||
var amountFraction = "0.00001";
|
var amountFraction = "0.00001";
|
||||||
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest),
|
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest),
|
||||||
Money.Parse(amountFraction));
|
Money.Parse(amountFraction));
|
||||||
|
@ -202,15 +202,14 @@ namespace BTCPayServer.Tests
|
||||||
|
|
||||||
// Pay partial amount
|
// Pay partial amount
|
||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
s.Driver.FindElement(By.Id("test-payment-amount")).Clear();
|
||||||
amountFraction = "0.00001";
|
s.Driver.FindElement(By.Id("test-payment-amount")).SendKeys("0.00001");
|
||||||
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest),
|
|
||||||
Money.Parse(amountFraction));
|
|
||||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
|
||||||
|
|
||||||
// Fake Pay
|
// Fake Pay
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
|
s.Driver.FindElement(By.Id("FakePayment")).Click();
|
||||||
|
s.Driver.FindElement(By.Id("mine-block")).Click();
|
||||||
paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo"));
|
paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo"));
|
||||||
Assert.Contains("The invoice hasn't been paid in full", paymentInfo.Text);
|
Assert.Contains("The invoice hasn't been paid in full", paymentInfo.Text);
|
||||||
Assert.Contains("Please send", paymentInfo.Text);
|
Assert.Contains("Please send", paymentInfo.Text);
|
||||||
|
@ -265,18 +264,19 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
||||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
var copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
var copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||||
var copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text;
|
var copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text;
|
||||||
Assert.StartsWith($"bitcoin:{address}?amount=", payUrl);
|
Assert.StartsWith($"bitcoin:{copyAddressOnchain}?amount=", payUrl);
|
||||||
Assert.Contains("?amount=", payUrl);
|
Assert.Contains("?amount=", payUrl);
|
||||||
Assert.Contains("&lightning=", payUrl);
|
Assert.Contains("&lightning=", payUrl);
|
||||||
Assert.StartsWith("bcrt", copyAddressOnchain);
|
Assert.StartsWith("bcrt", copyAddressOnchain);
|
||||||
Assert.Equal(address, copyAddressOnchain);
|
|
||||||
Assert.StartsWith("lnbcrt", copyAddressLightning);
|
Assert.StartsWith("lnbcrt", copyAddressLightning);
|
||||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?amount=", qrValue);
|
Assert.StartsWith($"bitcoin:{copyAddressOnchain.ToUpperInvariant()}?amount=", qrValue);
|
||||||
Assert.Contains("&lightning=LNBCRT", qrValue);
|
Assert.Contains("&lightning=LNBCRT", qrValue);
|
||||||
|
Assert.Contains("&lightning=lnbcrt", clipboard);
|
||||||
|
Assert.Equal(clipboard, payUrl);
|
||||||
|
|
||||||
// Check details
|
// Check details
|
||||||
s.Driver.ToggleCollapse("PaymentDetails");
|
s.Driver.ToggleCollapse("PaymentDetails");
|
||||||
|
@ -333,17 +333,18 @@ namespace BTCPayServer.Tests
|
||||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||||
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||||
copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text;
|
copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text;
|
||||||
Assert.StartsWith($"bitcoin:{address}", payUrl);
|
Assert.StartsWith($"bitcoin:{copyAddressOnchain}", payUrl);
|
||||||
Assert.Contains("?lightning=lnurl", payUrl);
|
Assert.Contains("?lightning=lnurl", payUrl);
|
||||||
Assert.DoesNotContain("amount=", payUrl);
|
Assert.DoesNotContain("amount=", payUrl);
|
||||||
Assert.StartsWith("bcrt", copyAddressOnchain);
|
Assert.StartsWith("bcrt", copyAddressOnchain);
|
||||||
Assert.Equal(address, copyAddressOnchain);
|
|
||||||
Assert.StartsWith("lnurl", copyAddressLightning);
|
Assert.StartsWith("lnurl", copyAddressLightning);
|
||||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
Assert.StartsWith($"bitcoin:{copyAddressOnchain.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
||||||
|
Assert.Contains($"bitcoin:{copyAddressOnchain}?lightning=lnurl", clipboard);
|
||||||
|
Assert.Equal(clipboard, payUrl);
|
||||||
|
|
||||||
// Check details
|
// Check details
|
||||||
s.Driver.ToggleCollapse("PaymentDetails");
|
s.Driver.ToggleCollapse("PaymentDetails");
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<template id="bitcoin-method-checkout-template">
|
<template id="bitcoin-method-checkout-template">
|
||||||
@await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-v2-bitcoin-pre-content", model = Model})
|
@await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-v2-bitcoin-pre-content", model = Model})
|
||||||
<div class="payment-box">
|
<div class="payment-box">
|
||||||
<div v-if="model.invoiceBitcoinUrlQR" class="qr-container" :data-qr-value="model.invoiceBitcoinUrlQR" :data-clipboard="model.btcAddress">
|
<div v-if="model.invoiceBitcoinUrlQR" class="qr-container" :data-qr-value="model.invoiceBitcoinUrlQR" :data-clipboard="model.invoiceBitcoinUrl" data-clipboard-confirm-element="#Address_@Model.PaymentMethodId [data-clipboard]">
|
||||||
<div>
|
<div>
|
||||||
<qrcode :value="model.invoiceBitcoinUrlQR" tag="div" :options="qrOptions" />
|
<qrcode :value="model.invoiceBitcoinUrlQR" tag="div" :options="qrOptions" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<template id="lightning-method-checkout-template">
|
<template id="lightning-method-checkout-template">
|
||||||
<div class="payment-box">
|
<div class="payment-box">
|
||||||
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-v2-lightning-pre-content", model = Model})
|
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-v2-lightning-pre-content", model = Model})
|
||||||
<div v-if="model.invoiceBitcoinUrlQR" class="qr-container" :data-qr-value="model.invoiceBitcoinUrlQR" :data-clipboard="model.btcAddress">
|
<div v-if="model.invoiceBitcoinUrlQR" class="qr-container" :data-qr-value="model.invoiceBitcoinUrlQR" :data-clipboard="model.invoiceBitcoinUrl" data-clipboard-confirm-element="#Lightning_@Model.PaymentMethodId [data-clipboard]">
|
||||||
<div>
|
<div>
|
||||||
<qrcode :value="model.invoiceBitcoinUrlQR" tag="div" :options="qrOptions" />
|
<qrcode :value="model.invoiceBitcoinUrlQR" tag="div" :options="qrOptions" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,8 +4,10 @@ function confirmCopy(el, message) {
|
||||||
if (hasIcon) {
|
if (hasIcon) {
|
||||||
el.innerHTML = el.innerHTML.replace('#copy', '#checkmark');
|
el.innerHTML = el.innerHTML.replace('#copy', '#checkmark');
|
||||||
} else {
|
} else {
|
||||||
|
const { width, height } = el.getBoundingClientRect();
|
||||||
el.dataset.clipboardInitial = el.innerHTML;
|
el.dataset.clipboardInitial = el.innerHTML;
|
||||||
el.style.minWidth = el.getBoundingClientRect().width + 'px';
|
el.style.minWidth = width + 'px';
|
||||||
|
el.style.minHeight = height + 'px';
|
||||||
el.innerHTML = confirmHTML;
|
el.innerHTML = confirmHTML;
|
||||||
}
|
}
|
||||||
el.dataset.clipboardConfirming = true;
|
el.dataset.clipboardConfirming = true;
|
||||||
|
@ -28,7 +30,7 @@ window.copyToClipboard = async function (e, data) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const item = e.target.closest('[data-clipboard]') || e.target.closest('[data-clipboard-target]') || e.target;
|
const item = e.target.closest('[data-clipboard]') || e.target.closest('[data-clipboard-target]') || e.target;
|
||||||
const confirm = item.dataset.clipboardConfirmElement
|
const confirm = item.dataset.clipboardConfirmElement
|
||||||
? document.getElementById(item.dataset.clipboardConfirmElement) || item
|
? document.querySelector(item.dataset.clipboardConfirmElement) || item
|
||||||
: item.querySelector('[data-clipboard-confirm]') || item;
|
: item.querySelector('[data-clipboard-confirm]') || item;
|
||||||
const message = confirm.getAttribute('data-clipboard-confirm') || 'Copied';
|
const message = confirm.getAttribute('data-clipboard-confirm') || 'Copied';
|
||||||
// Check compatibility and permissions:
|
// Check compatibility and permissions:
|
||||||
|
|
Loading…
Add table
Reference in a new issue