Updated Withdraw extension to handle unique facets

This commit is contained in:
benarc 2020-07-20 21:03:23 +01:00
parent 6c286cd265
commit 294beb2fca
9 changed files with 322 additions and 230 deletions

View File

@ -1,6 +1,6 @@
from datetime import datetime
from typing import List, Optional, Union
import shortuuid # type: ignore
from lnbits.db import open_ext_db
from lnbits.helpers import urlsafe_short_hash
@ -17,6 +17,7 @@ def create_withdraw_link(
wait_time: int,
is_unique: bool,
) -> WithdrawLink:
with open_ext_db("withdraw") as db:
link_id = urlsafe_short_hash()
db.execute(
@ -32,9 +33,10 @@ def create_withdraw_link(
is_unique,
unique_hash,
k1,
open_time
open_time,
usescsv
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
link_id,
@ -48,24 +50,36 @@ def create_withdraw_link(
urlsafe_short_hash(),
urlsafe_short_hash(),
int(datetime.now().timestamp()) + wait_time,
usescsv,
),
)
return get_withdraw_link(link_id)
return get_withdraw_link(link_id, uses)
def get_withdraw_link(link_id: str) -> Optional[WithdrawLink]:
def get_withdraw_link(link_id: str, num=None) -> Optional[WithdrawLink]:
with open_ext_db("withdraw") as db:
row = db.fetchone("SELECT * FROM withdraw_links WHERE id = ?", (link_id,))
return WithdrawLink.from_row(row) if row else None
link = []
for item in row:
link.append(item)
tohash = row["id"] + row["unique_hash"] + str(num)
link.append(shortuuid.uuid(name=tohash))
return WithdrawLink._make(link)
def get_withdraw_link_by_hash(unique_hash: str) -> Optional[WithdrawLink]:
def get_withdraw_link_by_hash(unique_hash: str, num=None) -> Optional[WithdrawLink]:
with open_ext_db("withdraw") as db:
row = db.fetchone("SELECT * FROM withdraw_links WHERE unique_hash = ?", (unique_hash,))
return WithdrawLink.from_row(row) if row else None
link = []
for item in row:
link.append(item)
if not num:
link.append("")
return WithdrawLink._make(link)
tohash = row["id"] + row["unique_hash"] + str(num)
link.append(shortuuid.uuid(name=tohash))
return WithdrawLink._make(link)
def get_withdraw_links(wallet_ids: Union[str, List[str]]) -> List[WithdrawLink]:
@ -75,13 +89,15 @@ def get_withdraw_links(wallet_ids: Union[str, List[str]]) -> List[WithdrawLink]:
with open_ext_db("withdraw") as db:
q = ",".join(["?"] * len(wallet_ids))
rows = db.fetchall(f"SELECT * FROM withdraw_links WHERE wallet IN ({q})", (*wallet_ids,))
return [WithdrawLink.from_row(row) for row in rows]
links = []
for x in rows:
links.append(WithdrawLink.from_row(row) for row in rows)
return links
def update_withdraw_link(link_id: str, **kwargs) -> Optional[WithdrawLink]:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
with open_ext_db("withdraw") as db:
db.execute(f"UPDATE withdraw_links SET {q} WHERE id = ?", (*kwargs.values(), link_id))
row = db.fetchone("SELECT * FROM withdraw_links WHERE id = ?", (link_id,))
@ -92,3 +108,7 @@ def update_withdraw_link(link_id: str, **kwargs) -> Optional[WithdrawLink]:
def delete_withdraw_link(link_id: str) -> None:
with open_ext_db("withdraw") as db:
db.execute("DELETE FROM withdraw_links WHERE id = ?", (link_id,))
def chunks(lst, n):
for i in range(0, len(lst), n):
yield lst[i:i + n]

View File

@ -5,34 +5,6 @@ from lnbits.helpers import urlsafe_short_hash
def m001_initial(db):
"""
Initial withdraw table.
"""
db.execute(
"""
CREATE TABLE IF NOT EXISTS withdraws (
key INTEGER PRIMARY KEY AUTOINCREMENT,
usr TEXT,
wal TEXT,
walnme TEXT,
adm INTEGER,
uni TEXT,
tit TEXT,
maxamt INTEGER,
minamt INTEGER,
spent INTEGER,
inc INTEGER,
tme INTEGER,
uniq INTEGER DEFAULT 0,
withdrawals TEXT,
tmestmp INTEGER,
rand TEXT
);
"""
)
def m002_change_withdraw_table(db):
"""
Creates an improved withdraw table and migrates the existing data.
"""
@ -50,52 +22,13 @@ def m002_change_withdraw_table(db):
unique_hash TEXT UNIQUE,
k1 TEXT,
open_time INTEGER,
used INTEGER DEFAULT 0
used INTEGER DEFAULT 0,
usescsv TEXT
);
"""
)
db.execute("CREATE INDEX IF NOT EXISTS wallet_idx ON withdraw_links (wallet)")
db.execute("CREATE UNIQUE INDEX IF NOT EXISTS unique_hash_idx ON withdraw_links (unique_hash)")
for row in [list(row) for row in db.fetchall("SELECT * FROM withdraws")]:
db.execute(
"""
INSERT INTO withdraw_links (
id,
wallet,
title,
min_withdrawable,
max_withdrawable,
uses,
wait_time,
is_unique,
unique_hash,
k1,
open_time,
used
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
row[5], # uni
row[2], # wal
row[6], # tit
row[8], # minamt
row[7], # maxamt
row[10], # inc
row[11], # tme
row[12], # uniq
urlsafe_short_hash(),
urlsafe_short_hash(),
int(datetime.now().timestamp()) + row[11],
row[9], # spent
),
)
db.execute("DROP TABLE withdraws")
""")
def migrate():
with open_ext_db("withdraw") as db:
m001_initial(db)
m002_change_withdraw_table(db)

View File

@ -19,11 +19,14 @@ class WithdrawLink(NamedTuple):
k1: str
open_time: int
used: int
usescsv: str
multihash: str
@classmethod
def from_row(cls, row: Row) -> "WithdrawLink":
data = dict(row)
data["is_unique"] = bool(data["is_unique"])
data["multihash"] = ""
return cls(**data)
@property
@ -33,14 +36,20 @@ class WithdrawLink(NamedTuple):
@property
def lnurl(self) -> Lnurl:
scheme = "https" if FORCE_HTTPS else None
url = url_for("withdraw.api_lnurl_response", unique_hash=self.unique_hash, _external=True, _scheme=scheme)
print(self.is_unique)
if self.is_unique:
url = url_for("withdraw.api_lnurl_multi_response", unique_hash=self.unique_hash, id_unique_hash=self.multihash, _external=True, _scheme=scheme)
else:
url = url_for("withdraw.api_lnurl_response", unique_hash=self.unique_hash, _external=True, _scheme=scheme)
return lnurl_encode(url)
@property
def lnurl_response(self) -> LnurlWithdrawResponse:
scheme = "https" if FORCE_HTTPS else None
url = url_for("withdraw.api_lnurl_callback", unique_hash=self.unique_hash, _external=True, _scheme=scheme)
url = url_for("withdraw.api_lnurl_callback", unique_hash=self.unique_hash, _external=True, _scheme=scheme)
print(url)
return LnurlWithdrawResponse(
callback=url,
k1=self.k1,

View File

@ -1,16 +1,24 @@
Vue.component(VueQrcode.name, VueQrcode);
Vue.component(VueQrcode.name, VueQrcode)
var locationPath = [window.location.protocol, '//', window.location.host, window.location.pathname].join('');
var locationPath = [
window.location.protocol,
'//',
window.location.host,
window.location.pathname
].join('')
var mapWithdrawLink = function (obj) {
obj._data = _.clone(obj);
obj.date = Quasar.utils.date.formatDate(new Date(obj.time * 1000), 'YYYY-MM-DD HH:mm');
obj.min_fsat = new Intl.NumberFormat(LOCALE).format(obj.min_withdrawable);
obj.max_fsat = new Intl.NumberFormat(LOCALE).format(obj.max_withdrawable);
obj.uses_left = obj.uses - obj.used;
obj.print_url = [locationPath, 'print/', obj.id].join('');
obj.withdraw_url = [locationPath, obj.id].join('');
return obj;
obj._data = _.clone(obj)
obj.date = Quasar.utils.date.formatDate(
new Date(obj.time * 1000),
'YYYY-MM-DD HH:mm'
)
obj.min_fsat = new Intl.NumberFormat(LOCALE).format(obj.min_withdrawable)
obj.max_fsat = new Intl.NumberFormat(LOCALE).format(obj.max_withdrawable)
obj.uses_left = obj.uses - obj.used
obj.print_url = [locationPath, 'print/', obj.id].join('')
obj.withdraw_url = [locationPath, obj.id].join('')
return obj
}
new Vue({
@ -24,8 +32,18 @@ new Vue({
columns: [
{name: 'id', align: 'left', label: 'ID', field: 'id'},
{name: 'title', align: 'left', label: 'Title', field: 'title'},
{name: 'wait_time', align: 'right', label: 'Wait', field: 'wait_time'},
{name: 'uses_left', align: 'right', label: 'Uses left', field: 'uses_left'},
{
name: 'wait_time',
align: 'right',
label: 'Wait',
field: 'wait_time'
},
{
name: 'uses_left',
align: 'right',
label: 'Uses left',
field: 'uses_left'
},
{name: 'min', align: 'right', label: 'Min (sat)', field: 'min_fsat'},
{name: 'max', align: 'right', label: 'Max (sat)', field: 'max_fsat'}
],
@ -45,118 +63,146 @@ new Vue({
show: false,
data: null
}
};
}
},
computed: {
sortedWithdrawLinks: function () {
return this.withdrawLinks.sort(function (a, b) {
return b.uses_left - a.uses_left;
});
return b.uses_left - a.uses_left
})
}
},
methods: {
getWithdrawLinks: function () {
var self = this;
var self = this
LNbits.api.request(
'GET',
'/withdraw/api/v1/links?all_wallets',
this.g.user.wallets[0].inkey
).then(function (response) {
self.withdrawLinks = response.data.map(function (obj) {
return mapWithdrawLink(obj);
});
}).catch(function (error) {
clearInterval(self.checker);
LNbits.utils.notifyApiError(error);
});
LNbits.api
.request(
'GET',
'/withdraw/api/v1/links?all_wallets',
this.g.user.wallets[0].inkey
)
.then(function (response) {
self.withdrawLinks = response.data.map(function (obj) {
return mapWithdrawLink(obj)
})
})
.catch(function (error) {
clearInterval(self.checker)
LNbits.utils.notifyApiError(error)
})
},
closeFormDialog: function () {
this.formDialog.data = {
is_unique: false
};
}
},
openQrCodeDialog: function (linkId) {
var link = _.findWhere(this.withdrawLinks, {id: linkId});
this.qrCodeDialog.data = _.clone(link);
this.qrCodeDialog.show = true;
var link = _.findWhere(this.withdrawLinks, {id: linkId})
this.qrCodeDialog.data = _.clone(link)
this.qrCodeDialog.show = true
},
openUpdateDialog: function (linkId) {
var link = _.findWhere(this.withdrawLinks, {id: linkId});
this.formDialog.data = _.clone(link._data);
this.formDialog.show = true;
var link = _.findWhere(this.withdrawLinks, {id: linkId})
this.formDialog.data = _.clone(link._data)
this.formDialog.show = true
},
sendFormData: function () {
var wallet = _.findWhere(this.g.user.wallets, {id: this.formDialog.data.wallet});
var data = _.omit(this.formDialog.data, 'wallet');
var wallet = _.findWhere(this.g.user.wallets, {
id: this.formDialog.data.wallet
})
var data = _.omit(this.formDialog.data, 'wallet')
data.wait_time = data.wait_time * {
'seconds': 1,
'minutes': 60,
'hours': 3600
}[this.formDialog.secondMultiplier];
data.wait_time =
data.wait_time *
{
seconds: 1,
minutes: 60,
hours: 3600
}[this.formDialog.secondMultiplier]
if (data.id) { this.updateWithdrawLink(wallet, data); }
else { this.createWithdrawLink(wallet, data); }
if (data.id) {
this.updateWithdrawLink(wallet, data)
} else {
this.createWithdrawLink(wallet, data)
}
},
updateWithdrawLink: function (wallet, data) {
var self = this;
var self = this
LNbits.api.request(
'PUT',
'/withdraw/api/v1/links/' + data.id,
wallet.adminkey,
_.pick(data, 'title', 'min_withdrawable', 'max_withdrawable', 'uses', 'wait_time', 'is_unique')
).then(function (response) {
self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) { return obj.id == data.id; });
self.withdrawLinks.push(mapWithdrawLink(response.data));
self.formDialog.show = false;
}).catch(function (error) {
LNbits.utils.notifyApiError(error);
});
LNbits.api
.request(
'PUT',
'/withdraw/api/v1/links/' + data.id,
wallet.adminkey,
_.pick(
data,
'title',
'min_withdrawable',
'max_withdrawable',
'uses',
'wait_time',
'is_unique'
)
)
.then(function (response) {
self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) {
return obj.id == data.id
})
self.withdrawLinks.push(mapWithdrawLink(response.data))
self.formDialog.show = false
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
createWithdrawLink: function (wallet, data) {
var self = this;
var self = this
LNbits.api.request(
'POST',
'/withdraw/api/v1/links',
wallet.adminkey,
data
).then(function (response) {
self.withdrawLinks.push(mapWithdrawLink(response.data));
self.formDialog.show = false;
}).catch(function (error) {
LNbits.utils.notifyApiError(error);
});
LNbits.api
.request('POST', '/withdraw/api/v1/links', wallet.adminkey, data)
.then(function (response) {
self.withdrawLinks.push(mapWithdrawLink(response.data))
self.formDialog.show = false
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
deleteWithdrawLink: function (linkId) {
var self = this;
var link = _.findWhere(this.withdrawLinks, {id: linkId});
var self = this
var link = _.findWhere(this.withdrawLinks, {id: linkId})
LNbits.utils.confirmDialog(
'Are you sure you want to delete this withdraw link?'
).onOk(function () {
LNbits.api.request(
'DELETE',
'/withdraw/api/v1/links/' + linkId,
_.findWhere(self.g.user.wallets, {id: link.wallet}).adminkey
).then(function (response) {
self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) { return obj.id == linkId; });
}).catch(function (error) {
LNbits.utils.notifyApiError(error);
});
});
LNbits.utils
.confirmDialog('Are you sure you want to delete this withdraw link?')
.onOk(function () {
LNbits.api
.request(
'DELETE',
'/withdraw/api/v1/links/' + linkId,
_.findWhere(self.g.user.wallets, {id: link.wallet}).adminkey
)
.then(function (response) {
self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) {
return obj.id == linkId
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
})
},
exportCSV: function () {
LNbits.utils.exportCSV(this.paywallsTable.columns, this.paywalls);
LNbits.utils.exportCSV(this.paywallsTable.columns, this.paywalls)
}
},
created: function () {
if (this.g.user.wallets.length) {
var getWithdrawLinks = this.getWithdrawLinks;
getWithdrawLinks();
this.checker = setInterval(function () { getWithdrawLinks(); }, 20000);
var getWithdrawLinks = this.getWithdrawLinks
getWithdrawLinks()
this.checker = setInterval(function () {
getWithdrawLinks()
}, 20000)
}
}
});
})

View File

@ -45,13 +45,22 @@
</div>
</div>
{% endblock %} {% block scripts %}
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
<script>
Vue.component(VueQrcode.name, VueQrcode)
new Vue({
el: '#vue',
mixins: [windowMixin]
mixins: [windowMixin],
data: function () {
return {
theurl: location.host,
}
},
})
</script>
{% endblock %}

View File

@ -274,7 +274,6 @@
>Shareable link</q-btn
>
<q-btn
v-if="!qrCodeDialog.data.is_unique"
outline
color="grey"
icon="print"

View File

@ -1,58 +1,79 @@
{% extends "print.html" %} {% block page %}
<!DOCTYPE html>
{% block page %}
<div class="row justify-center">
<div class="col-12 col-sm-8 col-lg-6 text-center">
{% for i in range(link.uses) %}
<div class="zimbabwe">
<div class="qr">
<qrcode value="{{ link.lnurl }}" :options="{width: 150}"></qrcode>
<br /><br />
<strong>{{ SITE_TITLE }}</strong><br />
<strong>{{ link.max_withdrawable }} FREE SATS</strong><br />
<small>Scan and follow link<br />or use Lightning wallet</small>
</div>
<img src="{{ url_for('static', filename='images/note.jpg') }}" />
</div>
<div class="col-12 col-sm-8 col-lg-6 text-center" id="vue">
{% for page in link %}
<page size="A4" id="pdfprint">
<table style="width: 100%;">
{% for threes in page %}
<tr style="height: 37.125mm;">
{% for one in threes %}
<td style="width: 52.5mm;">
<center>
<h3 class="q-mb-md"></h3>
<qrcode
:value="theurl + '/lnurlwallet?lightning={{one.lnurl}}'"
:options="{width: 110}"
></qrcode>
</center>
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</page>
{% endfor %}
</div>
</div>
{% endblock %} {% block styles %}
<style>
.zimbabwe {
page-break-inside: avoid;
height: 7cm;
width: 16cm;
position: relative;
margin-bottom: 10px;
overflow: hidden;
body {
background: rgb(204, 204, 204);
}
.zimbabwe img {
position: absolute;
top: 0;
left: 0;
z-index: 0;
width: 100%;
page {
background: white;
display: block;
margin: 0 auto;
margin-bottom: 0.5cm;
box-shadow: 0 0 0.5cm rgba(0, 0, 0, 0.5);
}
.zimbabwe .qr {
position: absolute;
top: 0;
bottom: 0;
right: 0;
z-index: 10;
background: rgb(255, 255, 255, 0.7);
padding: 10px;
text-align: center;
line-height: 1.1;
page[size='A4'] {
width: 21cm;
height: 29.7cm;
}
@media print {
body,
page {
margin: 0;
box-shadow: 0;
}
}
</style>
{% endblock %} {% block scripts %}
<script src="{{ url_for('static', filename='vendor/vue@2.6.11/vue.js') }}"></script>
<script src="{{ url_for('static', filename='vendor/vue-router@3.1.6/vue-router.js') }}"></script>
<script src="{{ url_for('static', filename='vendor/vuex@3.1.3/vuex.js') }}"></script>
<script src="{{ url_for('static', filename='vendor/quasar@1.10.4/quasar.umd.js') }}"></script>
<script
type="text/javascript"
src="/static/__bundle__/base.js?a52a989e"
></script>
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
<script>
Vue.component(VueQrcode.name, VueQrcode)
new Vue({
el: '#vue',
created: function () {
window.print()
mixins: [windowMixin],
data: function () {
return {
theurl: location.host,
printDialog: {
show: true,
data: null
}
}
}
})
</script>

View File

@ -4,7 +4,7 @@ from http import HTTPStatus
from lnbits.decorators import check_user_exists, validate_uuids
from lnbits.extensions.withdraw import withdraw_ext
from .crud import get_withdraw_link
from .crud import get_withdraw_link, chunks
@withdraw_ext.route("/")
@ -17,6 +17,7 @@ def index():
@withdraw_ext.route("/<link_id>")
def display(link_id):
link = get_withdraw_link(link_id) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
link = get_withdraw_link(link_id, len(link.usescsv.split(","))) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
return render_template("withdraw/display.html", link=link)
@ -24,5 +25,12 @@ def display(link_id):
@withdraw_ext.route("/print/<link_id>")
def print_qr(link_id):
link = get_withdraw_link(link_id) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
return render_template("withdraw/print_qr.html", link=link)
if link.uses == 0:
return render_template("withdraw/print_qr.html", link=link, unique=False)
links = []
for x in link.usescsv.split(","):
linkk = get_withdraw_link(link_id, x) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
links.append(linkk)
page_link = list(chunks(links, 4))
linked = list(chunks(page_link, 8))
return render_template("withdraw/print_qr.html", link=linked, unique=True)

View File

@ -2,6 +2,7 @@ from datetime import datetime
from flask import g, jsonify, request
from http import HTTPStatus
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
import shortuuid # type: ignore
from lnbits.core.crud import get_user
from lnbits.core.services import pay_invoice
@ -26,10 +27,9 @@ def api_links():
if "all_wallets" in request.args:
wallet_ids = get_user(g.wallet.user).wallet_ids
try:
return (
jsonify([{**link._asdict(), **{"lnurl": link.lnurl}} for link in get_withdraw_links(wallet_ids)]),
jsonify([{**link._asdict(), **{"lnurl": link.lnurl}} for link in get_withdraw_links(wallet_ids)[0]]),
HTTPStatus.OK,
)
except LnurlInvalidUrl:
@ -49,7 +49,7 @@ def api_link_retrieve(link_id):
if link.wallet != g.wallet.id:
return jsonify({"message": "Not your withdraw link."}), HTTPStatus.FORBIDDEN
return jsonify({**link._asdict(), **{"lnurl": link.lnurl}}), HTTPStatus.OK
@ -76,6 +76,15 @@ def api_link_create_or_update(link_id=None):
if (g.data["max_withdrawable"] * g.data["uses"] * 1000) > g.wallet.balance_msat:
return jsonify({"message": "Insufficient balance."}), HTTPStatus.FORBIDDEN
usescsv = ""
for i in range(g.data["uses"]):
if g.data["is_unique"]:
usescsv += "," + str(i + 1)
else:
usescsv += "," + str(1)
usescsv = usescsv[1:]
if link_id:
link = get_withdraw_link(link_id)
@ -85,9 +94,9 @@ def api_link_create_or_update(link_id=None):
if link.wallet != g.wallet.id:
return jsonify({"message": "Not your withdraw link."}), HTTPStatus.FORBIDDEN
link = update_withdraw_link(link_id, **g.data)
link = update_withdraw_link(link_id, **g.data, usescsv=usescsv, used=0)
else:
link = create_withdraw_link(wallet_id=g.wallet.id, **g.data)
link = create_withdraw_link(wallet_id=g.wallet.id, **g.data, usescsv=usescsv)
return jsonify({**link._asdict(), **{"lnurl": link.lnurl}}), HTTPStatus.OK if link_id else HTTPStatus.CREATED
@ -107,6 +116,7 @@ def api_link_delete(link_id):
return "", HTTPStatus.NO_CONTENT
#FOR LNURLs WHICH ARE NOT UNIQUE
@withdraw_ext.route("/api/v1/lnurl/<unique_hash>", methods=["GET"])
def api_lnurl_response(unique_hash):
@ -115,10 +125,51 @@ def api_lnurl_response(unique_hash):
if not link:
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
link = update_withdraw_link(link.id, k1=urlsafe_short_hash())
if link.is_unique == 1:
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
usescsv = ""
for x in range(1, link.uses - link.used):
usescsv += "," + str(1)
usescsv = usescsv[1:]
link = update_withdraw_link(link.id, used=link.used + 1, usescsv=usescsv)
return jsonify(link.lnurl_response.dict()), HTTPStatus.OK
#FOR LNURLs WHICH ARE UNIQUE
@withdraw_ext.route("/api/v1/lnurl/<unique_hash>/<id_unique_hash>", methods=["GET"])
def api_lnurl_multi_response(unique_hash, id_unique_hash):
link = get_withdraw_link_by_hash(unique_hash)
if not link:
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
useslist = link.usescsv.split(",")
usescsv = ""
hashed = []
found = False
print(link.uses - link.used)
print("link.uses - link.used")
print("svfsfv")
if link.is_unique == 0:
for x in range(link.uses - link.used):
usescsv += "," + str(1)
else:
for x in useslist:
tohash = link.id + link.unique_hash + str(x)
if id_unique_hash == shortuuid.uuid(name=tohash):
found = True
else:
usescsv += "," + x
print(x)
print("usescsv: " + usescsv)
if not found:
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
usescsv = usescsv[1:]
link = update_withdraw_link(link.id, used=link.used + 1, usescsv=usescsv)
return jsonify(link.lnurl_response.dict()), HTTPStatus.OK
@withdraw_ext.route("/api/v1/lnurl/cb/<unique_hash>", methods=["GET"])
def api_lnurl_callback(unique_hash):
@ -143,13 +194,9 @@ def api_lnurl_callback(unique_hash):
pay_invoice(wallet_id=link.wallet, bolt11=payment_request, max_sat=link.max_withdrawable)
changes = {
"used": link.used + 1,
"open_time": link.wait_time + now,
}
if link.is_unique:
changes["unique_hash"] = urlsafe_short_hash()
update_withdraw_link(link.id, **changes)
except ValueError as e: