Merge pull request #4819 from dennisreimann/nfc

This commit is contained in:
Andrew Camilleri
2023-03-28 09:19:58 +02:00
committed by GitHub
3 changed files with 89 additions and 59 deletions

View File

@@ -175,18 +175,18 @@ namespace BTCPayServer.Plugins.NFC
}
}
if (bolt11 is null)
if (string.IsNullOrEmpty(bolt11))
{
return BadRequest("Could not fetch BOLT11 invoice to pay to.");
}
var result = await info.SendRequest(bolt11, httpClient);
if (result.Status.Equals("ok", StringComparison.InvariantCultureIgnoreCase))
if (!string.IsNullOrEmpty(result.Status) && result.Status.Equals("ok", StringComparison.InvariantCultureIgnoreCase))
{
return Ok(result.Reason);
}
return BadRequest(result.Reason);
return BadRequest(result.Reason ?? "Unknown error");
}
}
}

View File

@@ -5,9 +5,11 @@
<div class="mt-4">
<p id="CheatSuccessMessage" class="alert alert-success text-break" v-if="successMessage" v-text="successMessage"></p>
<p id="CheatErrorMessage" class="alert alert-danger text-break" v-if="errorMessage" v-text="errorMessage"></p>
<button v-if="isV2" class="btn btn-secondary rounded-pill w-100" type="button"
:disabled="scanning || submitting" v-on:click="startScan" :id="btnId"
:class="{ 'loading': scanning || submitting, 'text-secondary': !supported }">{{btnText}}</button>
<template v-if="isV2">
<button class="btn btn-secondary rounded-pill w-100" type="button"
:disabled="scanning || submitting" v-on:click="handleClick" :id="btnId"
:class="{ 'text-secondary': !supported }">{{btnText}}</button>
</template>
<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"
@@ -22,6 +24,7 @@
</template>
</template>
<script type="text/javascript">
// https://developer.chrome.com/articles/nfc/
Vue.component("lnurl-withdraw-checkout", {
template: "#lnurl-withdraw-template",
props: {
@@ -29,7 +32,7 @@ Vue.component("lnurl-withdraw-checkout", {
isV2: Boolean
},
computed: {
display: function () {
display () {
const {
onChainWithLnInvoiceFallback: isUnified,
paymentMethodId: activePaymentMethodId,
@@ -47,84 +50,113 @@ Vue.component("lnurl-withdraw-checkout", {
// Lightning with LNURL available
(activePaymentMethodId === 'BTC_LightningLike' && lnurlwAvailable))
},
btnId: function () {
btnId () {
return this.supported ? 'PayByNFC' : 'PayByLNURL'
},
btnText: function () {
btnText () {
if (this.supported) {
return this.isV2 ? this.$t('pay_by_nfc') : 'Pay by NFC'
if (this.submitting) {
return this.isV2 ? this.$t('submitting_nfc') : 'Submitting NFC …'
} else if (this.scanning) {
return this.isV2 ? this.$t('scanning_nfc') : 'Scanning NFC …'
} else {
return this.isV2 ? this.$t('pay_by_nfc') : 'Pay by NFC'
}
} else {
return this.isV2 ? this.$t('pay_by_lnurl') : 'Pay by LNURL-Withdraw'
}
}
},
data: function () {
data () {
return {
url: @Safe.Json(Context.Request.GetAbsoluteUri(Url.Action("SubmitLNURLWithdrawForInvoice", "NFC"))),
supported: ('NDEFReader' in window && window.self === window.top),
supported: 'NDEFReader' in window && window.self === window.top,
scanning: false,
submitting: false,
permissionGranted: false,
readerAbortController: null,
amount: 0,
successMessage: null,
errorMessage: null
}
},
async mounted () {
try {
this.permissionGranted = navigator.permissions &&
(await navigator.permissions.query({ name: 'nfc' })).state === 'granted'
} catch (e) {}
if (this.permissionGranted) {
this.startScan()
}
},
methods: {
startScan: async function () {
try {
if (this.scanning || this.submitting) {
return;
}
async handleClick () {
if (this.supported) {
this.startScan()
} else {
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) {
await self.sendData.bind(self)(result);
this.handleUnsetTopUp()
if (!this.amount) {
return;
}
self.scanning = false;
}
ndef = new NDEFReader()
self.readerAbortController = new AbortController()
await ndef.scan({signal: self.readerAbortController.signal})
const lnurl = prompt("Enter LNURL-Withdraw")
if (lnurl) {
await this.sendData(lnurl)
}
}
},
handleUnsetTopUp () {
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");
}
}
return false
},
async startScan () {
if (this.scanning || this.submitting) {
return;
}
if (this.model.isUnsetTopUp) {
this.handleUnsetTopUp()
if (!this.amount) {
return;
}
}
this.submitting = false;
this.scanning = true;
try {
const ndef = new NDEFReader()
this.readerAbortController = new AbortController()
this.readerAbortController.signal.onabort = () => {
this.scanning = false;
};
await ndef.scan({ signal: this.readerAbortController.signal })
ndef.addEventListener('readingerror', () => {
self.scanning = false;
self.readerAbortController.abort()
})
ndef.onreadingerror = () => {
this.errorMessage = "Could not read NFC tag";
this.readerAbortController.abort()
}
ndef.addEventListener('reading', async ({message, serialNumber}) => {
//Decode NDEF data from tag
ndef.onreading = async ({ message, serialNumber }) => {
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;
await self.sendData.bind(self)(lnurl);
})
} catch (e) {
self.scanning = false;
self.submitting = false;
await this.sendData(lnurl)
}
// we came here, so the user must have allowed NFC access
this.permissionGranted = true;
} catch (error) {
this.errorMessage = `NFC scan failed: ${error}`;
}
},
sendData: async function (lnurl) {
async sendData (lnurl) {
this.submitting = true;
this.successMessage = null;
this.errorMessage = null;
@@ -145,11 +177,7 @@ Vue.component("lnurl-withdraw-checkout", {
} catch (error) {
this.errorMessage = error;
}
this.scanning = false;
this.submitting = false;
if (this.readerAbortController) {
this.readerAbortController.abort()
}
}
}
});

View File

@@ -11,6 +11,8 @@
"pay_in_wallet": "Pay in wallet",
"pay_by_nfc": "Pay by NFC",
"pay_by_lnurl": "Pay by LNURL-Withdraw",
"scanning_nfc": "Scanning NFC …",
"submitting_nfc": "Submitting NFC …",
"invoice_id": "Invoice ID",
"order_id": "Order ID",
"total_price": "Total Price",