lnurlp: support success_text and success_url.

This commit is contained in:
fiatjaf 2020-10-09 16:17:16 -03:00
parent f6bcff01f4
commit ea3418c21d
6 changed files with 88 additions and 11 deletions

View File

@ -7,7 +7,13 @@ from .models import PayLink
def create_pay_link(
*, wallet_id: str, description: str, amount: int, webhook_url: Optional[str] = None
*,
wallet_id: str,
description: str,
amount: int,
webhook_url: Optional[str] = None,
success_text: Optional[str] = None,
success_url: Optional[str] = None,
) -> Optional[PayLink]:
with open_ext_db("lnurlp") as db:
db.execute(
@ -18,11 +24,13 @@ def create_pay_link(
amount,
served_meta,
served_pr,
webhook_url
webhook_url,
success_text,
success_url
)
VALUES (?, ?, ?, 0, 0, ?)
VALUES (?, ?, ?, 0, 0, ?, ?, ?)
""",
(wallet_id, description, amount, webhook_url),
(wallet_id, description, amount, webhook_url, success_text, success_url),
)
link_id = db.cursor.lastrowid
return get_pay_link(link_id)
@ -57,7 +65,13 @@ def get_pay_links(wallet_ids: Union[str, List[str]]) -> List[PayLink]:
with open_ext_db("lnurlp") as db:
q = ",".join(["?"] * len(wallet_ids))
rows = db.fetchall(f"SELECT * FROM pay_links WHERE wallet IN ({q})", (*wallet_ids,))
rows = db.fetchall(
f"""
SELECT * FROM pay_links WHERE wallet IN ({q})
ORDER BY Id
""",
(*wallet_ids,),
)
return [PayLink.from_row(row) for row in rows]

View File

@ -33,7 +33,7 @@ async def api_lnurl_callback(link_id):
if not link:
return jsonify({"status": "ERROR", "reason": "LNURL-pay not found."}), HTTPStatus.OK
_, payment_request = create_invoice(
payment_hash, payment_request = create_invoice(
wallet_id=link.wallet,
amount=link.amount,
memo=link.description,
@ -43,6 +43,10 @@ async def api_lnurl_callback(link_id):
save_link_invoice(link_id, payment_request)
resp = LnurlPayActionResponse(pr=payment_request, success_action=None, routes=[])
resp = LnurlPayActionResponse(
pr=payment_request,
success_action=link.success_action(payment_hash),
routes=[],
)
return jsonify(resp.dict()), HTTPStatus.OK

View File

@ -1,9 +1,11 @@
import json
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult
from quart import url_for
from typing import NamedTuple, Optional, Dict
from sqlite3 import Row
from lnurl import Lnurl, encode as lnurl_encode
from lnurl.types import LnurlPayMetadata
from sqlite3 import Row
from typing import NamedTuple
from lnurl.models import LnurlPaySuccessAction, MessageAction, UrlAction
class PayLink(NamedTuple):
@ -31,6 +33,18 @@ class PayLink(NamedTuple):
def lnurlpay_metadata(self) -> LnurlPayMetadata:
return LnurlPayMetadata(json.dumps([["text/plain", self.description]]))
def success_action(self, payment_hash: str) -> Optional[LnurlPaySuccessAction]:
if self.success_url:
url: ParseResult = urlparse(self.success_url)
qs: Dict = parse_qs(url.query)
qs["payment_hash"] = payment_hash
url = url._replace(query=urlencode(qs))
return UrlAction(url=urlunparse(url), description=self.success_text)
elif self.success_text:
return MessageAction(message=self.success_text)
else:
return None
class Invoice(NamedTuple):
payment_hash: str

View File

@ -27,7 +27,7 @@
<q-card-section>
<h6 class="text-subtitle1 q-mb-sm q-mt-none">LNbits LNURL-pay link</h6>
<p class="q-my-none">
Use a LNURL compatible bitcoin wallet to claim the sats.
Use an LNURL compatible bitcoin wallet to pay.
</p>
</q-card-section>
<q-card-section class="q-pa-none">

View File

@ -137,6 +137,23 @@
v-model="formDialog.data.webhook_url"
type="text"
label="Webhook URL (optional)"
hint="An URL to be called whenever this link receives a payment."
></q-input>
<q-input
filled
dense
v-model="formDialog.data.success_text"
type="text"
label="Success message (optional)"
hint="Will be shown to the user in his wallet after a successful payment."
></q-input>
<q-input
filled
dense
v-model="formDialog.data.success_url"
type="text"
label="Success URL (optional)"
hint="Will be shown as a clickable link to the user in his wallet after a successful payment, appended by the payment_hash as a query string."
></q-input>
<div class="row q-mt-lg">
<q-btn
@ -183,6 +200,9 @@
<strong>ID:</strong> {{ qrCodeDialog.data.id }}<br />
<strong>Amount:</strong> {{ qrCodeDialog.data.amount }} sat<br />
<strong>Webhook:</strong> {{ qrCodeDialog.data.webhook_url }}<br />
<strong>Success Message:</strong> {{ qrCodeDialog.data.success_text
}}<br />
<strong>Success URL:</strong> {{ qrCodeDialog.data.success_url }}<br />
</p>
{% endraw %}
<div class="row q-mt-lg q-gutter-sm">
@ -263,6 +283,13 @@
align: 'left',
label: 'Webhook URL',
field: 'webhook_url'
},
{
name: 'success_action',
align: 'center',
label: '',
format: (_, row) =>
row.success_text || row.success_url ? '💬' : ''
}
],
pagination: {
@ -341,12 +368,28 @@
updatePayLink: function (wallet, data) {
var self = this
let values = _.omit(
_.pick(
data,
'description',
'amount',
'webhook_url',
'success_text',
'success_url'
),
(value, key) =>
(key === 'webhook_url' ||
key === 'success_text' ||
key === 'success_url') &&
(value === null || value === '')
)
LNbits.api
.request(
'PUT',
'/lnurlp/api/v1/links/' + data.id,
wallet.adminkey,
_.pick(data, 'description', 'amount', 'webhook_url')
values
)
.then(function (response) {
self.payLinks = _.reject(self.payLinks, function (obj) {

View File

@ -57,6 +57,8 @@ async def api_link_retrieve(link_id):
"description": {"type": "string", "empty": False, "required": True},
"amount": {"type": "integer", "min": 1, "required": True},
"webhook_url": {"type": "string", "required": False},
"success_text": {"type": "string", "required": False},
"success_url": {"type": "string", "required": False},
}
)
async def api_link_create_or_update(link_id=None):