mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2026-02-23 15:14:49 +01:00
Merge pull request #4819 from dennisreimann/nfc
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user