lnurl-pay and lnurl-withdraw UI.

This commit is contained in:
fiatjaf 2020-10-11 22:19:27 -03:00
parent 7a5159f293
commit 3cd15c40fc
3 changed files with 260 additions and 174 deletions

View File

@ -14,12 +14,8 @@ function generateChart(canvas, payments) {
} }
_.each( _.each(
payments payments.filter(p => !p.pending).sort((a, b) => a.time - b.time),
.filter(p => !p.pending) tx => {
.sort(function (a, b) {
return a.time - b.time
}),
function (tx) {
txs.push({ txs.push({
hour: Quasar.utils.date.formatDate(tx.date, 'YYYY-MM-DDTHH:00'), hour: Quasar.utils.date.formatDate(tx.date, 'YYYY-MM-DDTHH:00'),
sat: tx.sat sat: tx.sat
@ -27,19 +23,15 @@ function generateChart(canvas, payments) {
} }
) )
_.each(_.groupBy(txs, 'hour'), function (value, day) { _.each(_.groupBy(txs, 'hour'), (value, day) => {
var income = _.reduce( var income = _.reduce(
value, value,
function (memo, tx) { (memo, tx) => (tx.sat >= 0 ? memo + tx.sat : memo),
return tx.sat >= 0 ? memo + tx.sat : memo
},
0 0
) )
var outcome = _.reduce( var outcome = _.reduce(
value, value,
function (memo, tx) { (memo, tx) => (tx.sat < 0 ? memo + Math.abs(tx.sat) : memo),
return tx.sat < 0 ? memo + Math.abs(tx.sat) : memo
},
0 0
) )
n = n + income - outcome n = n + income - outcome
@ -124,23 +116,27 @@ new Vue({
show: false, show: false,
status: 'pending', status: 'pending',
paymentReq: null, paymentReq: null,
minMax: [0, 2100000000000000],
lnurl: null,
data: { data: {
amount: null, amount: null,
memo: '' memo: ''
} }
}, },
send: { parse: {
show: false, show: false,
invoice: null, invoice: null,
lnurl: {}, lnurlpay: null,
data: { data: {
request: '' request: '',
amount: 0
},
paymentChecker: null,
camera: {
show: false,
camera: 'auto'
} }
}, },
theCamera: {
show: false,
camera: 'auto'
},
payments: [], payments: [],
paymentsTable: { paymentsTable: {
columns: [ columns: [
@ -197,8 +193,8 @@ new Vue({
return LNbits.utils.search(this.payments, q) return LNbits.utils.search(this.payments, q)
}, },
canPay: function () { canPay: function () {
if (!this.send.invoice) return false if (!this.parse.invoice) return false
return this.send.invoice.sat <= this.balance return this.parse.invoice.sat <= this.balance
}, },
pendingPaymentsExist: function () { pendingPaymentsExist: function () {
return this.payments return this.payments
@ -206,56 +202,55 @@ new Vue({
: false : false
} }
}, },
filters: {
msatoshiFormat: function (value) {
return LNbits.utils.formatSat(value / 1000)
}
},
methods: { methods: {
closeCamera: function () { closeCamera: function () {
this.theCamera.show = false this.parse.camera.show = false
}, },
showCamera: function () { showCamera: function () {
this.theCamera.show = true this.parse.camera.show = true
}, },
showChart: function () { showChart: function () {
this.paymentsChart.show = true this.paymentsChart.show = true
this.$nextTick(function () { this.$nextTick(() => {
generateChart(this.$refs.canvas, this.payments) generateChart(this.$refs.canvas, this.payments)
}) })
}, },
showReceiveDialog: function () { showReceiveDialog: function () {
this.receive = { this.receive.show = true
show: true, this.receive.status = 'pending'
status: 'pending', this.receive.paymentReq = null
paymentReq: null, this.receive.data.amount = null
data: { this.receive.data.memo = null
amount: null, this.receive.paymentChecker = null
memo: '' this.receive.minMax = [0, 2100000000000000]
}, this.receive.lnurl = null
paymentChecker: null
}
}, },
showSendDialog: function () { showParseDialog: function () {
this.send = { this.parse.show = true
show: true, this.parse.invoice = null
invoice: null, this.parse.lnurlpay = null
lnurl: {}, this.parse.data.request = ''
data: { this.parse.data.paymentChecker = null
request: '' this.parse.camera.show = false
},
paymentChecker: null
}
}, },
closeReceiveDialog: function () { closeReceiveDialog: function () {
var checker = this.receive.paymentChecker var checker = this.receive.paymentChecker
setTimeout(function () { setTimeout(() => {
clearInterval(checker) clearInterval(checker)
}, 10000) }, 10000)
}, },
closeSendDialog: function () { closeParseDialog: function () {
var checker = this.send.paymentChecker var checker = this.parse.paymentChecker
setTimeout(function () { setTimeout(() => {
clearInterval(checker) clearInterval(checker)
}, 1000) }, 1000)
}, },
createInvoice: function () { createInvoice: function () {
var self = this
this.receive.status = 'loading' this.receive.status = 'loading'
LNbits.api LNbits.api
.createInvoice( .createInvoice(
@ -263,59 +258,96 @@ new Vue({
this.receive.data.amount, this.receive.data.amount,
this.receive.data.memo this.receive.data.memo
) )
.then(function (response) { .then(response => {
self.receive.status = 'success' this.receive.status = 'success'
self.receive.paymentReq = response.data.payment_request this.receive.paymentReq = response.data.payment_request
self.receive.paymentChecker = setInterval(function () { if (this.receive.lnurl) {
// send invoice to lnurl callback
console.log('sending', this.receive.lnurl)
LNbits.api.sendInvoiceToLnurlWithdraw(this.receive.paymentReq)
}
this.receive.paymentChecker = setInterval(() => {
LNbits.api LNbits.api
.getPayment(self.g.wallet, response.data.payment_hash) .getPayment(this.g.wallet, response.data.payment_hash)
.then(function (response) { .then(response => {
if (response.data.paid) { if (response.data.paid) {
self.fetchPayments() this.fetchPayments()
self.receive.show = false this.receive.show = false
clearInterval(self.receive.paymentChecker) clearInterval(this.receive.paymentChecker)
} }
}) })
}, 2000) }, 2000)
}) })
.catch(function (error) { .catch(err => {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(err)
self.receive.status = 'pending' this.receive.status = 'pending'
}) })
}, },
decodeQR: function (res) { decodeQR: function (res) {
this.send.data.request = res this.parse.data.request = res
this.decodeRequest() this.decodeRequest()
this.sendCamera.show = false this.parse.camera.show = false
}, },
decodeRequest: function () { decodeRequest: function () {
if (this.send.data.request.startsWith('lightning:')) { this.parse.show = true
this.send.data.request = this.send.data.request.slice(10)
if (this.parse.data.request.startsWith('lightning:')) {
this.parse.data.request = this.parse.data.request.slice(10)
} }
if (this.send.data.request.startsWith('lnurl:')) { if (this.parse.data.request.startsWith('lnurl:')) {
this.send.data.request = this.send.data.request.slice(6) this.parse.data.request = this.parse.data.request.slice(6)
} }
if (this.send.data.request.toLowerCase().startsWith('lnurl1')) { if (this.parse.data.request.toLowerCase().startsWith('lnurl1')) {
LNbits.api LNbits.api
.request( .request(
'GET', 'GET',
'/api/v1/lnurlscan/' + this.send.data.request, '/api/v1/lnurlscan/' + this.parse.data.request,
this.g.user.wallets[0].adminkey this.g.user.wallets[0].adminkey
) )
.then(function (response) { .catch(err => {
this.send.lnurl[response.kind] = Object.freeze(response) LNbits.utils.notifyApiError(err)
}) })
.catch(function (error) { .then(response => {
LNbits.utils.notifyApiError(error) let data = response.data
if (data.status === 'ERROR') {
Quasar.plugins.Notify.create({
timeout: 5000,
type: 'warning',
message: data.reason,
caption: `${data.domain} returned an error to the lnurl call.`,
icon: null
})
return
}
if (data.kind === 'pay') {
this.parse.lnurlpay = Object.freeze(data)
this.parse.data.amount = data.minSendable / 1000
} else if (data.kind === 'withdraw') {
this.parse.show = false
this.receive.show = true
this.receive.status = 'pending'
this.receive.data.amount = data.maxWithdrawable
this.receive.data.memo = data.defaultDescription
this.receive.minMax = [data.minWithdrawable, data.maxWithdrawable]
this.receive.lnurl = {
domain: data.domain,
callback: data.callback,
k1: data.k1,
fixed: data.fixed
}
}
}) })
return return
} }
let invoice let invoice
try { try {
invoice = decode(this.send.data.bolt11) invoice = decode(this.parse.data.bolt11)
} catch (error) { } catch (error) {
this.$q.notify({ this.$q.notify({
timeout: 3000, timeout: 3000,
@ -324,6 +356,7 @@ new Vue({
caption: '400 BAD REQUEST', caption: '400 BAD REQUEST',
icon: null icon: null
}) })
this.parse.show = false
return return
} }
@ -333,7 +366,7 @@ new Vue({
fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000) fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000)
} }
_.each(invoice.data.tags, function (tag) { _.each(invoice.data.tags, tag => {
if (_.isObject(tag) && _.has(tag, 'description')) { if (_.isObject(tag) && _.has(tag, 'description')) {
if (tag.description === 'payment_hash') { if (tag.description === 'payment_hash') {
cleanInvoice.hash = tag.value cleanInvoice.hash = tag.value
@ -352,11 +385,9 @@ new Vue({
} }
}) })
this.send.invoice = Object.freeze(cleanInvoice) this.parse.invoice = Object.freeze(cleanInvoice)
}, },
payInvoice: function () { payInvoice: function () {
var self = this
let dismissPaymentMsg = this.$q.notify({ let dismissPaymentMsg = this.$q.notify({
timeout: 0, timeout: 0,
message: 'Processing payment...', message: 'Processing payment...',
@ -364,55 +395,80 @@ new Vue({
}) })
LNbits.api LNbits.api
.payInvoice(this.g.wallet, this.send.data.bolt11) .payInvoice(this.g.wallet, this.parse.data.bolt11)
.then(function (response) { .then(response => {
self.send.paymentChecker = setInterval(function () { this.parse.paymentChecker = setInterval(() => {
LNbits.api LNbits.api
.getPayment(self.g.wallet, response.data.payment_hash) .getPayment(this.g.wallet, response.data.payment_hash)
.then(function (res) { .then(res => {
if (res.data.paid) { if (res.data.paid) {
self.send.show = false this.parse.show = false
clearInterval(self.send.paymentChecker) clearInterval(this.parse.paymentChecker)
dismissPaymentMsg() dismissPaymentMsg()
self.fetchPayments() this.fetchPayments()
} }
}) })
}, 2000) }, 2000)
}) })
.catch(function (error) { .catch(err => {
dismissPaymentMsg() dismissPaymentMsg()
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(err)
})
},
payLnurl: function () {
let dismissPaymentMsg = this.$q.notify({
timeout: 0,
message: 'Processing payment...',
icon: null
})
LNbits.api
.payInvoice(this.g.wallet, this.parse.data.bolt11)
.then(response => {
this.parse.paymentChecker = setInterval(() => {
LNbits.api
.getPayment(this.g.wallet, response.data.payment_hash)
.then(res => {
if (res.data.paid) {
this.parse.show = false
clearInterval(this.parse.paymentChecker)
dismissPaymentMsg()
this.fetchPayments()
}
})
}, 2000)
})
.catch(err => {
dismissPaymentMsg()
LNbits.utils.notifyApiError(err)
}) })
}, },
deleteWallet: function (walletId, user) { deleteWallet: function (walletId, user) {
LNbits.utils LNbits.utils
.confirmDialog('Are you sure you want to delete this wallet?') .confirmDialog('Are you sure you want to delete this wallet?')
.onOk(function () { .onOk(() => {
LNbits.href.deleteWallet(walletId, user) LNbits.href.deleteWallet(walletId, user)
}) })
}, },
fetchPayments: function (checkPending) { fetchPayments: function (checkPending) {
var self = this
return LNbits.api return LNbits.api
.getPayments(this.g.wallet, checkPending) .getPayments(this.g.wallet, checkPending)
.then(function (response) { .then(response => {
self.payments = response.data this.payments = response.data
.map(function (obj) { .map(obj => {
return LNbits.map.payment(obj) return LNbits.map.payment(obj)
}) })
.sort(function (a, b) { .sort((a, b) => {
return b.time - a.time return b.time - a.time
}) })
}) })
}, },
fetchBalance: function () { fetchBalance: function () {
var self = this LNbits.api.getWallet(this.g.wallet).then(response => {
LNbits.api.getWallet(self.g.wallet).then(function (response) { this.balance = Math.round(response.data.balance / 1000)
self.balance = Math.round(response.data.balance / 1000)
EventHub.$emit('update-wallet-balance', [ EventHub.$emit('update-wallet-balance', [
self.g.wallet.id, this.g.wallet.id,
self.balance this.balance
]) ])
}) })
}, },
@ -423,7 +479,7 @@ new Vue({
icon: null icon: null
}) })
this.fetchPayments(true).then(function () { this.fetchPayments(true).then(() => {
dismissMsg() dismissMsg()
}) })
}, },

View File

@ -16,7 +16,7 @@
unelevated unelevated
color="deep-purple" color="deep-purple"
class="full-width" class="full-width"
@click="showSendDialog" @click="showParseDialog"
>Paste Request</q-btn >Paste Request</q-btn
> >
</div> </div>
@ -249,18 +249,26 @@
</q-card> </q-card>
</div> </div>
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog"> <q-dialog v-model="receive.show" @hide="closeReceiveDialog">
{% raw %}
<q-card <q-card
v-if="!receive.paymentReq" v-if="!receive.paymentReq"
class="q-pa-lg q-pt-xl lnbits__dialog-card" class="q-pa-lg q-pt-xl lnbits__dialog-card"
> >
<q-form @submit="createInvoice" class="q-gutter-md"> <q-form @submit="createInvoice" class="q-gutter-md">
<p v-if="receive.lnurl" class="text-h6 text-center q-my-none">
<b>{{receive.lnurl.domain}}</b> is requesting an invoice:
</p>
<q-input <q-input
filled filled
dense dense
v-model.number="receive.data.amount" v-model.number="receive.data.amount"
type="number" type="number"
label="Amount (sat) *" label="Amount (sat) *"
min="receive.minMax[0]"
max="receive.minMax[1]"
:readonly="receive.lnurl && receive.lnurl.fixed"
></q-input> ></q-input>
<q-input <q-input
filled filled
@ -275,8 +283,12 @@
color="deep-purple" color="deep-purple"
:disable="receive.data.amount == null || receive.data.amount <= 0" :disable="receive.data.amount == null || receive.data.amount <= 0"
type="submit" type="submit"
>Create invoice</q-btn
> >
<span v-if="receive.lnurl">
Withdraw from {{receive.lnurl.domain}}
</span>
<span v-else> Create invoice </span>
</q-btn>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn> <q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
</div> </div>
<q-spinner <q-spinner
@ -305,20 +317,79 @@
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn> <q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div> </div>
</q-card> </q-card>
{% endraw %}
</q-dialog> </q-dialog>
<q-dialog v-model="send.show" position="top" @hide="closeSendDialog"> <q-dialog v-model="parse.show" @hide="closeParseDialog">
<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="parse.invoice">
{% raw %}
<h6 class="q-my-none">{{ parse.invoice.fsat }} sat</h6>
<q-separator class="q-my-sm"></q-separator>
<p style="word-break: break-all">
<strong>Description:</strong> {{ parse.invoice.description }}<br />
<strong>Expire date:</strong> {{ parse.invoice.expireDate }}<br />
<strong>Hash:</strong> {{ parse.invoice.hash }}
</p>
{% endraw %}
<div v-if="canPay" class="row q-mt-lg">
<q-btn unelevated color="deep-purple" @click="payInvoice">Pay</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-if="parse.lnurlpay">
{% raw %}
<q-form @submit="payLnurl" class="q-gutter-md">
<p v-if="parse.lnurlpay.fixed" class="q-my-none text-h6">
{{ parse.lnurlpay.maxSendable | msatoshiFormat }}
</p>
<p v-else class="q-my-none text-h6 text-center">
<b>{{ parse.lnurlpay.domain }}</b> is requesting <br />
between <b>{{ parse.lnurlpay.minSendable | msatoshiFormat }}</b> and
<b>{{ parse.lnurlpay.maxSendable | msatoshiFormat }}</b> sat
</p>
<q-separator class="q-my-sm"></q-separator>
<p class="text-justify text-italic">{{ parse.lnurlpay.description }}</p>
<p v-if="parse.lnurlpay.image">
<q-img :src="parse.lnurlpay.image" width="50%" />
</p>
<q-input
filled
dense
v-model.number="parse.data.amount"
type="number"
label="Amount (sat) *"
min="parse.lnurlpay.minSendable / 1000"
max="parse.lnurlpay.maxSendable / 1000"
:readonly="parse.lnurlpay.fixed"
></q-input>
<div class="row q-mt-lg">
<q-btn unelevated color="deep-purple" type="submit"
>Send satoshis</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>
</div>
</q-form>
{% endraw %}
</div>
<div v-else>
<q-form <q-form
v-if="!theCamera.show" v-if="!parse.camera.show"
@submit="decodeInvoice" @submit="decodeRequest"
class="q-gutter-md" class="q-gutter-md"
> >
<q-input <q-input
filled filled
dense dense
v-model.trim="send.data.request" v-model.trim="parse.data.request"
type="textarea" type="textarea"
label="Paste an invoice, payment request or lnurl code *" label="Paste an invoice, payment request or lnurl code *"
> >
@ -327,7 +398,7 @@
<q-btn <q-btn
unelevated unelevated
color="deep-purple" color="deep-purple"
:disable="send.data.request == ''" :disable="parse.data.request == ''"
type="submit" type="submit"
>Read</q-btn >Read</q-btn
> >
@ -344,62 +415,16 @@
></qrcode-stream> ></qrcode-stream>
</q-responsive> </q-responsive>
<div class="row q-mt-lg"> <div class="row q-mt-lg">
<q-btn @click="closeCamera" flat color="grey" class="q-ml-auto" <q-btn @click="closeCamera" flat color="grey" class="q-ml-auto">
>Cancel</q-btn Cancel
> </q-btn>
</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>
{% 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>Expire date:</strong> {{ send.invoice.expireDate }}<br />
<strong>Hash:</strong> {{ send.invoice.hash }}
</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>
</q-card> </q-card>
</q-dialog> </q-dialog>
<q-dialog v-model="theCamera.show" position="top"> <q-dialog v-model="parse.camera.show">
<q-card class="q-pa-lg q-pt-xl"> <q-card class="q-pa-lg q-pt-xl">
<div class="text-center q-mb-lg"> <div class="text-center q-mb-lg">
<qrcode-stream @decode="decodeQR" class="rounded-borders"></qrcode-stream> <qrcode-stream @decode="decodeQR" class="rounded-borders"></qrcode-stream>
@ -412,7 +437,7 @@
</q-card> </q-card>
</q-dialog> </q-dialog>
<q-dialog v-model="paymentsChart.show" position="top"> <q-dialog v-model="paymentsChart.show">
<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>

View File

@ -1,15 +1,13 @@
<<<<<<< HEAD
import trio # type: ignore import trio # type: ignore
import json import json
import lnurl
import httpx
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 urllib.parse import urlparse
from typing import Dict
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
@ -137,7 +135,6 @@ 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():
@ -190,8 +187,6 @@ 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"]) @core_app.route("/api/v1/lnurlscan/<code>", methods=["GET"])
@ -206,20 +201,30 @@ async def api_lnurlscan(code: str):
if url.is_login: if url.is_login:
return jsonify({"domain": domain, "kind": "auth", "error": "unsupported"}) return jsonify({"domain": domain, "kind": "auth", "error": "unsupported"})
data: lnurl.LnurlResponseModel = lnurl.get(url.url) r = httpx.get(url.url)
if not data.ok: if r.is_error:
return jsonify({"domain": domain, "error": "failed to get parameters"}) return jsonify({"domain": domain, "error": "failed to get parameters"})
try:
jdata = json.loads(r.text)
data: lnurl.LnurlResponseModel = lnurl.LnurlResponse.from_dict(jdata)
except (json.decoder.JSONDecodeError, lnurl.exceptions.LnurlResponseException):
return jsonify({"domain": domain, "error": f"got invalid response '{r.text[:200]}'"})
if type(data) is lnurl.LnurlChannelResponse: if type(data) is lnurl.LnurlChannelResponse:
return jsonify({"domain": domain, "kind": "channel", "error": "unsupported"}) return jsonify({"domain": domain, "kind": "channel", "error": "unsupported"})
params = data.dict() params: Dict = data.dict()
if type(data) is lnurl.LnurlWithdrawResponse: if type(data) is lnurl.LnurlWithdrawResponse:
params.update(kind="withdraw", fixed=data.min_withdrawable == data.max_withdrawable) params.update(kind="withdraw", fixed=data.min_withdrawable == data.max_withdrawable)
if type(data) is lnurl.LnurlPayResponse: if type(data) is lnurl.LnurlPayResponse:
params.update(kind="pay", fixed=data.min_sendable == data.max_sendable) params.update(kind="pay", fixed=data.min_sendable == data.max_sendable)
params.update(description=data.metadata.text)
if data.metadata.images:
image = min(data.metadata.images, key=lambda image: len(image[1]))
data_uri = "data:" + image[0] + "," + image[1]
params.update(image=data_uri)
params.update(domain=domain) params.update(domain=domain)
return jsonify(params) return jsonify(params)
>>>>>>> da8fd9a... send/create buttons wip.