send/create/scan buttons for clear LNURL support.
This commit is contained in:
parent
fa8713de17
commit
7a5159f293
|
@ -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.use(VueQrcodeReader)
|
||||
|
@ -132,8 +132,9 @@ new Vue({
|
|||
send: {
|
||||
show: false,
|
||||
invoice: null,
|
||||
lnurl: {},
|
||||
data: {
|
||||
bolt11: ''
|
||||
request: ''
|
||||
}
|
||||
},
|
||||
theCamera: {
|
||||
|
@ -206,12 +207,6 @@ new Vue({
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
// closeCamera: function () {
|
||||
// this.sendCamera.show = false
|
||||
// },
|
||||
// showCamera: function () {
|
||||
// this.sendCamera.show = true
|
||||
// },
|
||||
closeCamera: function () {
|
||||
this.theCamera.show = false
|
||||
},
|
||||
|
@ -240,8 +235,9 @@ new Vue({
|
|||
this.send = {
|
||||
show: true,
|
||||
invoice: null,
|
||||
lnurl: {},
|
||||
data: {
|
||||
bolt11: ''
|
||||
request: ''
|
||||
},
|
||||
paymentChecker: null
|
||||
}
|
||||
|
@ -253,7 +249,6 @@ new Vue({
|
|||
}, 10000)
|
||||
},
|
||||
closeSendDialog: function () {
|
||||
// this.sendCamera.show = false
|
||||
var checker = this.send.paymentChecker
|
||||
setTimeout(function () {
|
||||
clearInterval(checker)
|
||||
|
@ -290,29 +285,32 @@ new Vue({
|
|||
})
|
||||
},
|
||||
decodeQR: function (res) {
|
||||
if (res.substring(0, 4) == 'lnurl') {
|
||||
console.log(res)
|
||||
var self = this
|
||||
this.send.data.request = res
|
||||
this.decodeRequest()
|
||||
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
|
||||
.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) {
|
||||
console.log(response.data)
|
||||
this.send.lnurl[response.kind] = Object.freeze(response)
|
||||
})
|
||||
.catch(function (error) {
|
||||
clearInterval(self.checker)
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
} else {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
let invoice
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
color="deep-purple"
|
||||
class="full-width"
|
||||
@click="showSendDialog"
|
||||
>Send</q-btn
|
||||
>Paste Request</q-btn
|
||||
>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
@ -26,7 +26,7 @@
|
|||
color="deep-purple"
|
||||
class="full-width"
|
||||
@click="showReceiveDialog"
|
||||
>Receive</q-btn
|
||||
>Create Invoice</q-btn
|
||||
>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
@ -141,7 +141,9 @@
|
|||
<div v-if="props.row.isIn && props.row.pending">
|
||||
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
||||
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">
|
||||
<a :href="'lightning:' + props.row.bolt11">
|
||||
<q-responsive :ratio="1" class="q-mx-xl">
|
||||
|
@ -172,7 +174,9 @@
|
|||
:color="'green'"
|
||||
></q-icon>
|
||||
Payment Received
|
||||
<lnbits-payment-details :payment="props.row"></lnbits-payment-details>
|
||||
<lnbits-payment-details
|
||||
:payment="props.row"
|
||||
></lnbits-payment-details>
|
||||
</div>
|
||||
<div v-else-if="props.row.isPaid && props.row.isOut">
|
||||
<q-icon
|
||||
|
@ -181,12 +185,16 @@
|
|||
:color="'pink'"
|
||||
></q-icon>
|
||||
Payment Sent
|
||||
<lnbits-payment-details :payment="props.row"></lnbits-payment-details>
|
||||
<lnbits-payment-details
|
||||
:payment="props.row"
|
||||
></lnbits-payment-details>
|
||||
</div>
|
||||
<div v-else-if="props.row.isOut && props.row.pending">
|
||||
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
||||
Outgoing payment pending
|
||||
<lnbits-payment-details :payment="props.row"></lnbits-payment-details>
|
||||
<lnbits-payment-details
|
||||
:payment="props.row"
|
||||
></lnbits-payment-details>
|
||||
</div>
|
||||
</div>
|
||||
</q-card>
|
||||
|
@ -240,7 +248,6 @@
|
|||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
|
||||
<q-card
|
||||
|
@ -304,25 +311,25 @@
|
|||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||
<div v-if="!send.invoice">
|
||||
<q-form
|
||||
v-if="!sendCamera.show"
|
||||
v-if="!theCamera.show"
|
||||
@submit="decodeInvoice"
|
||||
class="q-gutter-md"
|
||||
>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="send.data.bolt11"
|
||||
v-model.trim="send.data.request"
|
||||
type="textarea"
|
||||
label="Paste an invoice *"
|
||||
label="Paste an invoice, payment request or lnurl code *"
|
||||
>
|
||||
</q-input>
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="deep-purple"
|
||||
:disable="send.data.bolt11 == ''"
|
||||
:disable="send.data.request == ''"
|
||||
type="submit"
|
||||
>Read invoice</q-btn
|
||||
>Read</q-btn
|
||||
>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel</q-btn
|
||||
|
@ -343,6 +350,29 @@
|
|||
</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>
|
||||
{% raw %}
|
||||
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
||||
|
@ -383,7 +413,7 @@
|
|||
</q-dialog>
|
||||
|
||||
<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>
|
||||
<canvas ref="canvas" width="600" height="400"></canvas>
|
||||
</q-card-section>
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
<<<<<<< HEAD
|
||||
import trio # type: ignore
|
||||
import json
|
||||
import traceback
|
||||
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 binascii import unhexlify
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from lnbits import bolt11
|
||||
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
|
||||
|
||||
|
||||
<<<<<<< HEAD
|
||||
@core_app.route("/api/v1/payments/sse", methods=["GET"])
|
||||
@api_check_wallet_key("invoice")
|
||||
async def api_payments_sse():
|
||||
|
@ -183,3 +190,36 @@ async def api_payments_sse():
|
|||
)
|
||||
response.timeout = None
|
||||
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-section>
|
||||
<h6 class="text-subtitle1 q-mb-sm q-mt-none">LNbits LNURL-pay link</h6>
|
||||
<p class="q-my-none">
|
||||
Use an LNURL compatible bitcoin wallet to pay.
|
||||
</p>
|
||||
<p class="q-my-none">Use an LNURL compatible bitcoin wallet to pay.</p>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-separator></q-separator>
|
||||
|
|
Loading…
Reference in New Issue
Block a user