mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-22 22:25:47 +01:00
send/create/scan buttons for clear LNURL support.
This commit is contained in:
parent
fa8713de17
commit
7a5159f293
4 changed files with 147 additions and 81 deletions
|
@ -1,4 +1,4 @@
|
||||||
/* globals moment, decode, Vue, VueQrcodeReader, VueQrcode, Quasar, LNbits, _, EventHub, Chart */
|
/* globals windowMixin, decode, Vue, VueQrcodeReader, VueQrcode, Quasar, LNbits, _, EventHub, Chart */
|
||||||
|
|
||||||
Vue.component(VueQrcode.name, VueQrcode)
|
Vue.component(VueQrcode.name, VueQrcode)
|
||||||
Vue.use(VueQrcodeReader)
|
Vue.use(VueQrcodeReader)
|
||||||
|
@ -132,8 +132,9 @@ new Vue({
|
||||||
send: {
|
send: {
|
||||||
show: false,
|
show: false,
|
||||||
invoice: null,
|
invoice: null,
|
||||||
|
lnurl: {},
|
||||||
data: {
|
data: {
|
||||||
bolt11: ''
|
request: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
theCamera: {
|
theCamera: {
|
||||||
|
@ -206,12 +207,6 @@ new Vue({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// closeCamera: function () {
|
|
||||||
// this.sendCamera.show = false
|
|
||||||
// },
|
|
||||||
// showCamera: function () {
|
|
||||||
// this.sendCamera.show = true
|
|
||||||
// },
|
|
||||||
closeCamera: function () {
|
closeCamera: function () {
|
||||||
this.theCamera.show = false
|
this.theCamera.show = false
|
||||||
},
|
},
|
||||||
|
@ -240,8 +235,9 @@ new Vue({
|
||||||
this.send = {
|
this.send = {
|
||||||
show: true,
|
show: true,
|
||||||
invoice: null,
|
invoice: null,
|
||||||
|
lnurl: {},
|
||||||
data: {
|
data: {
|
||||||
bolt11: ''
|
request: ''
|
||||||
},
|
},
|
||||||
paymentChecker: null
|
paymentChecker: null
|
||||||
}
|
}
|
||||||
|
@ -253,7 +249,6 @@ new Vue({
|
||||||
}, 10000)
|
}, 10000)
|
||||||
},
|
},
|
||||||
closeSendDialog: function () {
|
closeSendDialog: function () {
|
||||||
// this.sendCamera.show = false
|
|
||||||
var checker = this.send.paymentChecker
|
var checker = this.send.paymentChecker
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
clearInterval(checker)
|
clearInterval(checker)
|
||||||
|
@ -290,29 +285,32 @@ new Vue({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
decodeQR: function (res) {
|
decodeQR: function (res) {
|
||||||
if (res.substring(0, 4) == 'lnurl') {
|
this.send.data.request = res
|
||||||
console.log(res)
|
this.decodeRequest()
|
||||||
var self = this
|
this.sendCamera.show = false
|
||||||
|
},
|
||||||
|
decodeRequest: function () {
|
||||||
|
if (this.send.data.request.startsWith('lightning:')) {
|
||||||
|
this.send.data.request = this.send.data.request.slice(10)
|
||||||
|
}
|
||||||
|
if (this.send.data.request.startsWith('lnurl:')) {
|
||||||
|
this.send.data.request = this.send.data.request.slice(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.send.data.request.toLowerCase().startsWith('lnurl1')) {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request('GET', '/lnurlscan/' + res, this.g.user.wallets[0].adminkey)
|
.request(
|
||||||
|
'GET',
|
||||||
|
'/api/v1/lnurlscan/' + this.send.data.request,
|
||||||
|
this.g.user.wallets[0].adminkey
|
||||||
|
)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
console.log(response.data)
|
this.send.lnurl[response.kind] = Object.freeze(response)
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
clearInterval(self.checker)
|
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
})
|
})
|
||||||
} else {
|
return
|
||||||
this.send.data.bolt11 = res
|
|
||||||
this.decodeInvoice()
|
|
||||||
this.theCamera.show = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
decodeInvoice: function () {
|
|
||||||
if (this.send.data.bolt11.startsWith('lightning:')) {
|
|
||||||
this.send.data.bolt11 = this.send.data.bolt11.slice(10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let invoice
|
let invoice
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
color="deep-purple"
|
color="deep-purple"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
@click="showSendDialog"
|
@click="showSendDialog"
|
||||||
>Send</q-btn
|
>Paste Request</q-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
color="deep-purple"
|
color="deep-purple"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
@click="showReceiveDialog"
|
@click="showReceiveDialog"
|
||||||
>Receive</q-btn
|
>Create Invoice</q-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
@ -141,7 +141,9 @@
|
||||||
<div v-if="props.row.isIn && props.row.pending">
|
<div v-if="props.row.isIn && props.row.pending">
|
||||||
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
||||||
Invoice waiting to be paid
|
Invoice waiting to be paid
|
||||||
<lnbits-payment-details :payment="props.row"></lnbits-payment-details>
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
<div v-if="props.row.bolt11" class="text-center q-mb-lg">
|
<div v-if="props.row.bolt11" class="text-center q-mb-lg">
|
||||||
<a :href="'lightning:' + props.row.bolt11">
|
<a :href="'lightning:' + props.row.bolt11">
|
||||||
<q-responsive :ratio="1" class="q-mx-xl">
|
<q-responsive :ratio="1" class="q-mx-xl">
|
||||||
|
@ -172,7 +174,9 @@
|
||||||
:color="'green'"
|
:color="'green'"
|
||||||
></q-icon>
|
></q-icon>
|
||||||
Payment Received
|
Payment Received
|
||||||
<lnbits-payment-details :payment="props.row"></lnbits-payment-details>
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="props.row.isPaid && props.row.isOut">
|
<div v-else-if="props.row.isPaid && props.row.isOut">
|
||||||
<q-icon
|
<q-icon
|
||||||
|
@ -181,12 +185,16 @@
|
||||||
:color="'pink'"
|
:color="'pink'"
|
||||||
></q-icon>
|
></q-icon>
|
||||||
Payment Sent
|
Payment Sent
|
||||||
<lnbits-payment-details :payment="props.row"></lnbits-payment-details>
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="props.row.isOut && props.row.pending">
|
<div v-else-if="props.row.isOut && props.row.pending">
|
||||||
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
||||||
Outgoing payment pending
|
Outgoing payment pending
|
||||||
<lnbits-payment-details :payment="props.row"></lnbits-payment-details>
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
@ -199,47 +207,46 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-md-5 q-gutter-y-md">
|
<div class="col-12 col-md-5 q-gutter-y-md">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<q-btn flat color="grey" @click="exportCSV" class="float-right"
|
<q-btn flat color="grey" @click="exportCSV" class="float-right"
|
||||||
>Renew keys</q-btn
|
>Renew keys</q-btn
|
||||||
>
|
>
|
||||||
<h6 class="text-subtitle1 q-mt-none q-mb-sm">LNbits wallet</h6>
|
<h6 class="text-subtitle1 q-mt-none q-mb-sm">LNbits wallet</h6>
|
||||||
<strong>Wallet name: </strong><em>{{ wallet.name }}</em><br />
|
<strong>Wallet name: </strong><em>{{ wallet.name }}</em><br />
|
||||||
<strong>Wallet ID: </strong><em>{{ wallet.id }}</em><br />
|
<strong>Wallet ID: </strong><em>{{ wallet.id }}</em><br />
|
||||||
<strong>Admin key: </strong><em>{{ wallet.adminkey }}</em><br />
|
<strong>Admin key: </strong><em>{{ wallet.adminkey }}</em><br />
|
||||||
<strong>Invoice/read key: </strong><em>{{ wallet.inkey }}</em>
|
<strong>Invoice/read key: </strong><em>{{ wallet.inkey }}</em>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="q-pa-none">
|
<q-card-section class="q-pa-none">
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<q-list>
|
||||||
|
{% include "core/_api_docs.html" %}
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
<q-list>
|
<q-expansion-item
|
||||||
{% include "core/_api_docs.html" %}
|
group="extras"
|
||||||
<q-separator></q-separator>
|
icon="remove_circle"
|
||||||
<q-expansion-item
|
label="Delete wallet"
|
||||||
group="extras"
|
>
|
||||||
icon="remove_circle"
|
<q-card>
|
||||||
label="Delete wallet"
|
<q-card-section>
|
||||||
>
|
<p>
|
||||||
<q-card>
|
This whole wallet will be deleted, the funds will be
|
||||||
<q-card-section>
|
<strong>UNRECOVERABLE</strong>.
|
||||||
<p>
|
</p>
|
||||||
This whole wallet will be deleted, the funds will be
|
<q-btn
|
||||||
<strong>UNRECOVERABLE</strong>.
|
unelevated
|
||||||
</p>
|
color="red-10"
|
||||||
<q-btn
|
@click="deleteWallet('{{ wallet.id }}', '{{ user.id }}')"
|
||||||
unelevated
|
>Delete wallet</q-btn
|
||||||
color="red-10"
|
>
|
||||||
@click="deleteWallet('{{ wallet.id }}', '{{ user.id }}')"
|
</q-card-section>
|
||||||
>Delete wallet</q-btn
|
</q-card>
|
||||||
>
|
</q-expansion-item>
|
||||||
</q-card-section>
|
</q-list>
|
||||||
</q-card>
|
</q-card-section>
|
||||||
</q-expansion-item>
|
</q-card>
|
||||||
</q-list>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
|
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
|
||||||
|
@ -304,25 +311,25 @@
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<div v-if="!send.invoice">
|
<div v-if="!send.invoice">
|
||||||
<q-form
|
<q-form
|
||||||
v-if="!sendCamera.show"
|
v-if="!theCamera.show"
|
||||||
@submit="decodeInvoice"
|
@submit="decodeInvoice"
|
||||||
class="q-gutter-md"
|
class="q-gutter-md"
|
||||||
>
|
>
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.trim="send.data.bolt11"
|
v-model.trim="send.data.request"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
label="Paste an invoice *"
|
label="Paste an invoice, payment request or lnurl code *"
|
||||||
>
|
>
|
||||||
</q-input>
|
</q-input>
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
color="deep-purple"
|
color="deep-purple"
|
||||||
:disable="send.data.bolt11 == ''"
|
:disable="send.data.request == ''"
|
||||||
type="submit"
|
type="submit"
|
||||||
>Read invoice</q-btn
|
>Read</q-btn
|
||||||
>
|
>
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||||
>Cancel</q-btn
|
>Cancel</q-btn
|
||||||
|
@ -343,6 +350,29 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="send.lnurl.withdraw">
|
||||||
|
{% raw %}
|
||||||
|
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
||||||
|
<q-separator class="q-my-sm"></q-separator>
|
||||||
|
<p style="word-break: break-all">
|
||||||
|
<strong>Description:</strong> {{ send.invoice.description }}<br />
|
||||||
|
<strong>Payment hash:</strong> {{ send.invoice.hash }}<br />
|
||||||
|
<strong>Expire date:</strong> {{ send.invoice.expireDate }}
|
||||||
|
</p>
|
||||||
|
{% endraw %}
|
||||||
|
<div v-if="canPay" class="row q-mt-lg">
|
||||||
|
<q-btn unelevated color="deep-purple" @click="payInvoice"
|
||||||
|
>Send satoshis</q-btn
|
||||||
|
>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||||
|
</div>
|
||||||
|
<div v-else class="row q-mt-lg">
|
||||||
|
<q-btn unelevated disabled color="yellow" text-color="black"
|
||||||
|
>Not enough funds!</q-btn
|
||||||
|
>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{% raw %}
|
{% raw %}
|
||||||
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
||||||
|
@ -383,7 +413,7 @@
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
<q-dialog v-model="paymentsChart.show" position="top">
|
<q-dialog v-model="paymentsChart.show" position="top">
|
||||||
<q-card class="q-pa-sm" style="width: 800px; max-width: unset;">
|
<q-card class="q-pa-sm" style="width: 800px; max-width: unset">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<canvas ref="canvas" width="600" height="400"></canvas>
|
<canvas ref="canvas" width="600" height="400"></canvas>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
|
<<<<<<< HEAD
|
||||||
import trio # type: ignore
|
import trio # type: ignore
|
||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
from quart import g, jsonify, request, make_response
|
from quart import g, jsonify, request, make_response
|
||||||
|
=======
|
||||||
|
import lnurl
|
||||||
|
from quart import g, jsonify, request
|
||||||
|
>>>>>>> da8fd9a... send/create buttons wip.
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from lnbits import bolt11
|
from lnbits import bolt11
|
||||||
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
||||||
|
@ -131,6 +137,7 @@ async def api_payment(payment_hash):
|
||||||
return jsonify({"paid": not payment.pending}), HTTPStatus.OK
|
return jsonify({"paid": not payment.pending}), HTTPStatus.OK
|
||||||
|
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
@core_app.route("/api/v1/payments/sse", methods=["GET"])
|
@core_app.route("/api/v1/payments/sse", methods=["GET"])
|
||||||
@api_check_wallet_key("invoice")
|
@api_check_wallet_key("invoice")
|
||||||
async def api_payments_sse():
|
async def api_payments_sse():
|
||||||
|
@ -183,3 +190,36 @@ async def api_payments_sse():
|
||||||
)
|
)
|
||||||
response.timeout = None
|
response.timeout = None
|
||||||
return response
|
return response
|
||||||
|
=======
|
||||||
|
return jsonify({"paid": False}), HTTPStatus.OK
|
||||||
|
|
||||||
|
|
||||||
|
@core_app.route("/api/v1/lnurlscan/<code>", methods=["GET"])
|
||||||
|
@api_check_wallet_key("invoice")
|
||||||
|
async def api_lnurlscan(code: str):
|
||||||
|
try:
|
||||||
|
url = lnurl.Lnurl(code)
|
||||||
|
except ValueError:
|
||||||
|
return jsonify({"error": "invalid lnurl"}), HTTPStatus.BAD_REQUEST
|
||||||
|
|
||||||
|
domain = urlparse(url.url).netloc
|
||||||
|
if url.is_login:
|
||||||
|
return jsonify({"domain": domain, "kind": "auth", "error": "unsupported"})
|
||||||
|
|
||||||
|
data: lnurl.LnurlResponseModel = lnurl.get(url.url)
|
||||||
|
if not data.ok:
|
||||||
|
return jsonify({"domain": domain, "error": "failed to get parameters"})
|
||||||
|
|
||||||
|
if type(data) is lnurl.LnurlChannelResponse:
|
||||||
|
return jsonify({"domain": domain, "kind": "channel", "error": "unsupported"})
|
||||||
|
|
||||||
|
params = data.dict()
|
||||||
|
if type(data) is lnurl.LnurlWithdrawResponse:
|
||||||
|
params.update(kind="withdraw", fixed=data.min_withdrawable == data.max_withdrawable)
|
||||||
|
|
||||||
|
if type(data) is lnurl.LnurlPayResponse:
|
||||||
|
params.update(kind="pay", fixed=data.min_sendable == data.max_sendable)
|
||||||
|
|
||||||
|
params.update(domain=domain)
|
||||||
|
return jsonify(params)
|
||||||
|
>>>>>>> da8fd9a... send/create buttons wip.
|
||||||
|
|
|
@ -26,9 +26,7 @@
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<h6 class="text-subtitle1 q-mb-sm q-mt-none">LNbits LNURL-pay link</h6>
|
<h6 class="text-subtitle1 q-mb-sm q-mt-none">LNbits LNURL-pay link</h6>
|
||||||
<p class="q-my-none">
|
<p class="q-my-none">Use an LNURL compatible bitcoin wallet to pay.</p>
|
||||||
Use an LNURL compatible bitcoin wallet to pay.
|
|
||||||
</p>
|
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="q-pa-none">
|
<q-card-section class="q-pa-none">
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
|
|
Loading…
Add table
Reference in a new issue