Checkout v2: Display and copy addresses (#4489)

* Checkout v2: Display and copy addresses

Closes #4442.

* Refinements
This commit is contained in:
d11n 2023-01-12 02:41:33 +01:00 committed by GitHub
parent 42c5f732a2
commit 2301769419
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 28 deletions

View File

@ -99,7 +99,7 @@ namespace BTCPayServer.Tests
// Pay partial amount
await Task.Delay(200);
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-destination");
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
var amountFraction = "0.00001";
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest),
Money.Parse(amountFraction));

View File

@ -3,18 +3,28 @@
<template id="bitcoin-method-checkout-template">
<div class="payment-box">
<div class="qr-container" data-clipboard-confirm-element="QR_Text_@Model.PaymentMethodId" :data-clipboard="model.invoiceBitcoinUrl" :data-clipboard-confirm="$t('copy_confirm')" :data-destination="model.btcAddress">
<qrcode v-if="model.invoiceBitcoinUrlQR" :value="model.invoiceBitcoinUrlQR" tag="div" :options="qrOptions" />
</div>
<div class="mt-2 mb-4">
<div v-if="model.invoiceBitcoinUrlQR" class="qr-container" :data-clipboard="model.btcAddress" data-clipboard-confirm-element="QR_Text_@Model.PaymentMethodId">
<div>
<qrcode :value="model.invoiceBitcoinUrlQR" tag="div" :options="qrOptions" />
</div>
<img class="qr-icon" :src="model.cryptoImage" :alt="model.paymentMethodName"/>
<small class="qr-text" id="QR_Text_@Model.PaymentMethodId" v-t="'qr_text'"></small>
@*
<input type="text" class="form-control form-control-sm" :value="model.btcAddress"
:data-clipboard="model.btcAddress" :data-clipboard-confirm="`$t('copy_confirm')"
data-clipboard-confirm-element="QR_Text_@Model.PaymentMethodId" readonly>
*@
</div>
<a v-if="model.invoiceBitcoinUrl" class="btn btn-primary rounded-pill w-100" target="_top"
<div v-if="model.btcAddress" class="input-group mt-3">
<div class="form-floating">
<input id="Address_@Model.PaymentMethodId" class="form-control-plaintext" readonly="readonly" :value="model.btcAddress">
<label for="Address_@Model.PaymentMethodId" v-t="{ path: 'address', args: { paymentMethod: model.paymentMethodName }}"></label>
</div>
<button type="button" class="btn btn-link" data-clipboard-target="#Address_@Model.PaymentMethodId" :data-clipboard-confirm="$t('copy_confirm')" v-t="'copy'"></button>
</div>
<div v-if="BOLT11" class="input-group mt-3">
<div class="form-floating">
<input id="BOLT11_@Model.PaymentMethodId" class="form-control-plaintext" readonly="readonly" :value="BOLT11" />
<label for="BOLT11_@Model.PaymentMethodId" v-t="'lightning'"></label>
</div>
<button type="button" class="btn btn-link" data-clipboard-target="#BOLT11_@Model.PaymentMethodId" :data-clipboard-confirm="$t('copy_confirm')" v-t="'copy'"></button>
</div>
<a v-if="model.invoiceBitcoinUrl" class="btn btn-primary rounded-pill w-100 mt-4" target="_top"
:href="model.invoiceBitcoinUrl" :title="$t(hasPayjoin ? 'BIP21 payment link with PayJoin support' : 'BIP21 payment link')" v-t="'pay_in_wallet'"></a>
</div>
</template>
@ -27,12 +37,17 @@
qrcode: VueQrcode
},
data () {
console.log(this.model.cryptoImage)
// currentTab is needed for backwards-compatibility with old plugin versions
return { currentTab: undefined };
},
computed: {
hasPayjoin () {
return this.model.invoiceBitcoinUrl.indexOf('@PayjoinClient.BIP21EndpointKey=') !== -1;
},
BOLT11 () {
const match = this.model.invoiceBitcoinUrl.match(/&LIGHTNING=(.*)&?/i);
return match ? match[1].toLowerCase() : null;
}
}
});

View File

@ -2,18 +2,21 @@
<template id="lightning-method-checkout-template">
<div class="payment-box">
<div class="qr-container" data-clipboard-confirm-element="QR_Text_@Model.PaymentMethodId" :data-clipboard="model.invoiceBitcoinUrl" :data-destination="model.btcAddress">
<qrcode v-if="model.invoiceBitcoinUrlQR" :value="model.invoiceBitcoinUrlQR" tag="div" :options="qrOptions" />
</div>
<div class="mt-2 mb-4">
<div v-if="model.invoiceBitcoinUrlQR" class="qr-container" :data-clipboard="model.btcAddress" data-clipboard-confirm-element="QR_Text_@Model.PaymentMethodId">
<div>
<qrcode :value="model.invoiceBitcoinUrlQR" tag="div" :options="qrOptions" />
</div>
<img class="qr-icon" :src="model.cryptoImage" :alt="model.paymentMethodName"/>
<small class="qr-text" id="QR_Text_@Model.PaymentMethodId" v-t="'qr_text'"></small>
@*
<input type="text" class="form-control form-control-sm" :value="model.btcAddress"
:data-clipboard="model.invoiceBitcoinUrl" :data-clipboard-confirm="$t('copy_confirm')"
data-clipboard-confirm-element="QR_Text_@Model.PaymentMethodId" readonly>
*@
</div>
<a v-if="model.invoiceBitcoinUrl" class="btn btn-primary rounded-pill w-100" target="_top"
<div v-if="model.btcAddress" class="input-group mt-3">
<div class="form-floating">
<input id="Address_@Model.PaymentMethodId" class="form-control-plaintext" readonly="readonly" :value="model.btcAddress">
<label for="Address_@Model.PaymentMethodId" v-t="'lightning'"></label>
</div>
<button type="button" class="btn btn-link" data-clipboard-target="#Address_@Model.PaymentMethodId" :data-clipboard-confirm="$t('copy_confirm')" v-t="'copy'"></button>
</div>
<a v-if="model.invoiceBitcoinUrl" class="btn btn-primary rounded-pill w-100 mt-4" target="_top"
:href="model.invoiceBitcoinUrl" v-t="'pay_in_wallet'"></a>
</div>
</template>

View File

@ -241,7 +241,7 @@
const statusWsUrl = @Safe.Json(Url.Action("GetStatusWebSocket", new { invoiceId = Model.InvoiceId }));
const availableLanguages = ['en']; // @Safe.Json(LangService.GetLanguages().Select(language => language.Code));
const initialSrvModel = @Safe.Json(Model);
const qrOptions = { margin: 1, type: 'svg', color: { dark: '#000', light: '#fff' } };
const qrOptions = { margin: 0, type: 'svg', color: { dark: '#000', light: '#fff' } };
</script>
<script src="~/vendor/vuejs/vue.min.js" asp-append-version="true"></script>
<script src="~/vendor/vue-qrcode/vue-qrcode.min.js" asp-append-version="true"></script>

View File

@ -1,6 +1,9 @@
:root {
--navbutton-size: .8rem;
--qr-size: 256px;
--icon-size: 64px;
--icon-border-size: var(--btcpay-space-s);
--icon-border-color: var(--btcpay-body-text);
--section-padding: 1.5rem;
--border-radius: var(--btcpay-border-radius-l);
--wrap-max-width: 400px;
@ -123,19 +126,48 @@ section dl > div dd {
margin: 0 auto;
text-align: center;
}
.payment-box .qr-text {
display: block;
color: var(--btcpay-light-text);
}
.payment-box .qr-container {
display: flex;
align-items: center;
justify-content: center;
position: relative;
min-height: var(--qr-size);
}
.payment-box .qr-container svg {
border-radius: var(--btcpay-border-radius);
}
.payment-box svg {
padding: var(--btcpay-space-s);
background: var(--btcpay-white);
width: 100%;
}
.payment-box .qr-container img {
box-sizing: content-box;
position: absolute;
width: var(--icon-size);
border-radius: 50%;
padding: var(--icon-border-size);
background: var(--icon-border-color);
}
.payment-box .qr-container small {
display: none;
}
.payment-box .input-group {
align-items: flex-end;
}
.payment-box .input-group .form-control-plaintext {
padding-left: 3px;
padding-bottom: 0;
font-weight: var(--btcpay-font-weight-semibold);
}
.payment-box .input-group label {
padding-left: 0;
text-transform: uppercase;
letter-spacing: .1rem;
font-weight: var(--btcpay-font-weight-semibold);
}
.payment-box .input-group button {
padding: var(--btcpay-space-xs) 0;
font-weight: var(--btcpay-font-weight-semibold);
}
.payment-details dl {
margin: 0;
}

View File

@ -18,11 +18,15 @@
"network_cost": "Network Cost",
"tx_count": "{{count}} transactions",
"qr_text": "Scan the QR code, or tap to copy the address.",
"address": "{{paymentMethod}} Address",
"lightning": "Lightning",
"payment_link": "Payment Link",
"invoice_paid": "Invoice Paid",
"invoice_expired": "Invoice Expired",
"invoice_expired_body": "An invoice is only valid for {{minutes}} minutes.\n\nReturn to {{storeName}} if you like to resubmit a payment.",
"view_receipt": "View Receipt",
"return_to_store": "Return to {{storeName}}",
"copy": "Copy",
"copy_confirm": "Copied",
"powered_by": "Powered by",
"conversion_body": "You can pay {{btcDue}} {{cryptoCode}} using altcoins other than the ones merchant directly supports.\n\nThis service is provided by 3rd party. Please keep in mind that we have no control over how providers will forward your funds. Invoice will only be marked paid once funds are received on {{cryptoCode}} Blockchain."