mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-23 14:40:36 +01:00
* Checkout: Allow NFC/LNURL-W whenever LNURL is available With what we have in master right now, we display NFC only for top-up invoices. With these changes, we display NFC in all cases, where LNURL is available. Note that this hides LNURL from the list of selectable payment methods, it's only available to use the NFC — and explicitely selectable only for the edge case of top-up invoice + non-unified QR (as before). Rationale: Now that we got NFC tightly integrated, it doesn't make sense to support the NFC experience only for top-up invoices. With this we bring back LNURL for regular invoices as well, but don't make it selectable and use it only for the NFC functionality. * Fix LNURL condition * Improve and test NFC/LNURL display condition Restores what was fixed in #4660. * Fix and test Lightning-only case * Add cache busting for locales
150 lines
6 KiB
Text
150 lines
6 KiB
Text
@using BTCPayServer.Abstractions.Extensions
|
|
@using BTCPayServer.Abstractions.TagHelpers
|
|
<template id="lnurl-withdraw-template">
|
|
<template v-if="display">
|
|
<button v-if="isV2" class="btn btn-secondary rounded-pill w-100 mt-4" type="button"
|
|
:disabled="scanning || submitting" v-on:click="startScan" :id="btnId"
|
|
:class="{ 'loading': scanning || submitting, 'text-secondary': !supported }">{{btnText}}</button>
|
|
<bp-loading-button v-else>
|
|
<button class="action-button" style="margin: 0 45px;width:calc(100% - 90px) !important"
|
|
:disabled="scanning || submitting" v-on:click="startScan" :id="btnId"
|
|
:class="{ 'loading': scanning || submitting, 'action-button': supported, 'btn btn-text w-100': !supported }">
|
|
<span class="button-text">{{btnText}}</span>
|
|
<div class="loader-wrapper">
|
|
@await Html.PartialAsync("~/Views/UIInvoice/Checkout-Spinner.cshtml")
|
|
</div>
|
|
</button>
|
|
</bp-loading-button>
|
|
</template>
|
|
</template>
|
|
<script type="text/javascript">
|
|
Vue.component("lnurl-withdraw-checkout", {
|
|
template: "#lnurl-withdraw-template",
|
|
props: {
|
|
model: Object,
|
|
isV2: Boolean
|
|
},
|
|
computed: {
|
|
display: function () {
|
|
const {
|
|
onChainWithLnInvoiceFallback: isUnified,
|
|
paymentMethodId: activePaymentMethodId,
|
|
availableCryptos: availablePaymentMethods,
|
|
invoiceBitcoinUrl: paymentUrl
|
|
} = this.model
|
|
const lnurlwAvailable =
|
|
// Either we have LN or LNURL available directly
|
|
!!availablePaymentMethods.find(pm => ['BTC_LNURLPAY', 'BTC_LightningLike'].includes(pm.paymentMethodId)) ||
|
|
// Or the BIP21 payment URL flags Lightning support
|
|
!!paymentUrl.match(/lightning=ln/i)
|
|
return activePaymentMethodId === 'BTC_LNURLPAY' || (
|
|
// Unified QR/BIP21 case
|
|
(activePaymentMethodId === 'BTC' && isUnified && lnurlwAvailable) ||
|
|
// Lightning with LNURL available
|
|
(activePaymentMethodId === 'BTC_LightningLike' && lnurlwAvailable))
|
|
},
|
|
btnId: function () {
|
|
return this.supported ? 'PayByNFC' : 'PayByLNURL'
|
|
},
|
|
btnText: function () {
|
|
if (this.supported) {
|
|
return this.isV2 ? this.$t('pay_by_nfc') : 'Pay by NFC (LNURL-Withdraw)'
|
|
} else {
|
|
return this.isV2 ? this.$t('pay_by_lnurl') : 'Pay by LNURL-Withdraw'
|
|
}
|
|
}
|
|
},
|
|
data: function () {
|
|
return {
|
|
url: @Safe.Json(Context.Request.GetAbsoluteUri(Url.Action("SubmitLNURLWithdrawForInvoice", "NFC"))),
|
|
supported: ('NDEFReader' in window && window.self === window.top),
|
|
scanning: false,
|
|
submitting: false,
|
|
readerAbortController: null,
|
|
amount: 0
|
|
}
|
|
},
|
|
methods: {
|
|
startScan: async function () {
|
|
try {
|
|
if (this.scanning || this.submitting) {
|
|
return;
|
|
}
|
|
if (this.model.isUnsetTopUp) {
|
|
const amountStr = prompt("How many sats do you want to pay?")
|
|
if (amountStr){
|
|
try {
|
|
this.amount = parseInt(amountStr)
|
|
} catch {
|
|
alert("Please provide a valid number amount in sats");
|
|
}
|
|
}else{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const self = this;
|
|
self.submitting = false;
|
|
self.scanning = true;
|
|
if (!this.supported) {
|
|
const result = prompt("Enter LNURL withdraw");
|
|
if (result) {
|
|
self.sendData.bind(self)(result);
|
|
return;
|
|
}
|
|
self.scanning = false;
|
|
}
|
|
ndef = new NDEFReader()
|
|
self.readerAbortController = new AbortController()
|
|
await ndef.scan({signal: self.readerAbortController.signal})
|
|
|
|
ndef.addEventListener('readingerror', () => {
|
|
self.scanning = false;
|
|
self.readerAbortController.abort()
|
|
})
|
|
|
|
ndef.addEventListener('reading', ({message, serialNumber}) => {
|
|
//Decode NDEF data from tag
|
|
const record = message.records[0]
|
|
const textDecoder = new TextDecoder('utf-8')
|
|
const lnurl = textDecoder.decode(record.data)
|
|
|
|
//User feedback, show loader icon
|
|
self.scanning = false;
|
|
self.sendData.bind(self)(lnurl);
|
|
|
|
})
|
|
} catch(e) {
|
|
self.scanning = false;
|
|
self.submitting = false;
|
|
}
|
|
},
|
|
sendData: function (lnurl) {
|
|
this.submitting = true;
|
|
//Post LNURLW data to server
|
|
var xhr = new XMLHttpRequest()
|
|
xhr.open('POST', this.url, true)
|
|
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
xhr.send(JSON.stringify({lnurl, invoiceId: this.model.invoiceId, amount: this.amount}))
|
|
const self = this;
|
|
//User feedback, reset on failure
|
|
xhr.onload = function () {
|
|
if (xhr.readyState === xhr.DONE) {
|
|
console.log(xhr.response);
|
|
console.log(xhr.responseText);
|
|
self.scanning = false;
|
|
self.submitting = false;
|
|
|
|
if(self.readerAbortController) {
|
|
self.readerAbortController.abort()
|
|
}
|
|
|
|
if(xhr.response){
|
|
alert(xhr.response)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
</script>
|