From b68b8a0292516604c5a14f082cf0d1fce05e0cca Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 21 Dec 2022 14:56:27 +0200 Subject: [PATCH 1/8] fix: do not loose error, log it --- lnbits/core/views/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index ebce4b85c..2bd19978d 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -214,7 +214,8 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): lnurl_response = resp["reason"] else: lnurl_response = True - except (httpx.ConnectError, httpx.RequestError): + except (httpx.ConnectError, httpx.RequestError) as ex: + logger.error(ex) lnurl_response = False return { From dd4a9f10cf4c965f8efcf7285503bdbf57d04727 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 21 Dec 2022 14:57:00 +0200 Subject: [PATCH 2/8] feat: add function to update the `extra` JSON values --- lnbits/core/crud.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 1c8c71ad3..771ff3973 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -451,6 +451,36 @@ async def update_payment_details( return +async def update_payment_extra( + payment_hash: str, + extra: dict, + conn: Optional[Connection] = None, +) -> None: + """ + Only update the `extra` field for the payment. + Old values in the `extra` JSON object will be kept unless the new `extra` overwrites them. + """ + + row = await (conn or db).fetchone( + "SELECT hash, extra from apipayments WHERE hash = ?", + (payment_hash), + ) + if not row: + return + existing_extra = json.loads(row["extra"] if row["extra"] else "{}") + new_extra = { + **existing_extra, + **extra, + } + await (conn or db).execute( + """ + UPDATE apipayments SET extra = ? + WHERE hash = ? + """, + (json.dumps(new_extra), payment_hash), + ) + + async def delete_payment(checking_id: str, conn: Optional[Connection] = None) -> None: await (conn or db).execute( "DELETE FROM apipayments WHERE checking_id = ?", (checking_id,) From 8fae90bb9dcd5976231fc7942f030f00adc8a75e Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 21 Dec 2022 15:03:22 +0200 Subject: [PATCH 3/8] feat: add `webhook_headers` and `webhook_body` to withdraws --- lnbits/extensions/withdraw/crud.py | 6 ++- lnbits/extensions/withdraw/migrations.py | 10 +++++ lnbits/extensions/withdraw/models.py | 4 ++ lnbits/extensions/withdraw/static/js/index.js | 37 +++++++++++++------ .../withdraw/templates/withdraw/index.html | 24 ++++++++++++ 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/lnbits/extensions/withdraw/crud.py b/lnbits/extensions/withdraw/crud.py index 9868b0570..83404c629 100644 --- a/lnbits/extensions/withdraw/crud.py +++ b/lnbits/extensions/withdraw/crud.py @@ -27,9 +27,11 @@ async def create_withdraw_link( open_time, usescsv, webhook_url, + webhook_headers, + webhook_body, custom_url ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( link_id, @@ -45,6 +47,8 @@ async def create_withdraw_link( int(datetime.now().timestamp()) + data.wait_time, usescsv, data.webhook_url, + data.webhook_headers, + data.webhook_body, data.custom_url, ), ) diff --git a/lnbits/extensions/withdraw/migrations.py b/lnbits/extensions/withdraw/migrations.py index 0c6ed4fc1..95805ae75 100644 --- a/lnbits/extensions/withdraw/migrations.py +++ b/lnbits/extensions/withdraw/migrations.py @@ -122,3 +122,13 @@ async def m005_add_custom_print_design(db): Adds custom print design """ await db.execute("ALTER TABLE withdraw.withdraw_link ADD COLUMN custom_url TEXT;") + + +async def m006_webhook_headers_and_body(db): + """ + Add headers and body to webhooks + """ + await db.execute( + "ALTER TABLE withdraw.withdraw_link ADD COLUMN webhook_headers TEXT;" + ) + await db.execute("ALTER TABLE withdraw.withdraw_link ADD COLUMN webhook_body TEXT;") diff --git a/lnbits/extensions/withdraw/models.py b/lnbits/extensions/withdraw/models.py index 2672537fa..51c6a1cf9 100644 --- a/lnbits/extensions/withdraw/models.py +++ b/lnbits/extensions/withdraw/models.py @@ -16,6 +16,8 @@ class CreateWithdrawData(BaseModel): wait_time: int = Query(..., ge=1) is_unique: bool webhook_url: str = Query(None) + webhook_headers: str = Query(None) + webhook_body: str = Query(None) custom_url: str = Query(None) @@ -35,6 +37,8 @@ class WithdrawLink(BaseModel): usescsv: str = Query(None) number: int = Query(0) webhook_url: str = Query(None) + webhook_headers: str = Query(None) + webhook_body: str = Query(None) custom_url: str = Query(None) @property diff --git a/lnbits/extensions/withdraw/static/js/index.js b/lnbits/extensions/withdraw/static/js/index.js index a3eaa593a..ced78439d 100644 --- a/lnbits/extensions/withdraw/static/js/index.js +++ b/lnbits/extensions/withdraw/static/js/index.js @@ -63,7 +63,8 @@ new Vue({ secondMultiplierOptions: ['seconds', 'minutes', 'hours'], data: { is_unique: false, - use_custom: false + use_custom: false, + has_webhook: false } }, simpleformDialog: { @@ -188,23 +189,35 @@ new Vue({ }, updateWithdrawLink: function (wallet, data) { var self = this + const body = _.pick( + data, + 'title', + 'min_withdrawable', + 'max_withdrawable', + 'uses', + 'wait_time', + 'is_unique', + 'webhook_url', + 'webhook_headers', + 'webhook_body', + 'custom_url' + ) + + if (data.has_webhook) { + body = { + ...body, + webhook_url: data.webhook_url, + webhook_headers: data.webhook_headers, + webhook_body: data.webhook_body + } + } LNbits.api .request( 'PUT', '/withdraw/api/v1/links/' + data.id, wallet.adminkey, - _.pick( - data, - 'title', - 'min_withdrawable', - 'max_withdrawable', - 'uses', - 'wait_time', - 'is_unique', - 'webhook_url', - 'custom_url' - ) + body ) .then(function (response) { self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) { diff --git a/lnbits/extensions/withdraw/templates/withdraw/index.html b/lnbits/extensions/withdraw/templates/withdraw/index.html index 27684f6b5..3ae244e67 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/index.html +++ b/lnbits/extensions/withdraw/templates/withdraw/index.html @@ -209,7 +209,13 @@ + + + From 0ab99bef414d47bce4413a4f3fb012b11a055a87 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 21 Dec 2022 15:04:49 +0200 Subject: [PATCH 4/8] refactor: use `update_payment_extra` --- lnbits/extensions/lnurlp/tasks.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lnbits/extensions/lnurlp/tasks.py b/lnbits/extensions/lnurlp/tasks.py index 5de47f2ef..72805603b 100644 --- a/lnbits/extensions/lnurlp/tasks.py +++ b/lnbits/extensions/lnurlp/tasks.py @@ -5,6 +5,7 @@ import httpx from loguru import logger from lnbits.core import db as core_db +from lnbits.core.crud import update_payment_extra from lnbits.core.models import Payment from lnbits.helpers import get_current_extension_name from lnbits.tasks import register_invoice_listener @@ -66,10 +67,4 @@ async def mark_webhook_sent( payment.extra["wh_message"] = reason_phrase payment.extra["wh_response"] = text - await core_db.execute( - """ - UPDATE apipayments SET extra = ? - WHERE hash = ? - """, - (json.dumps(payment.extra), payment.payment_hash), - ) + await update_payment_extra(payment.payment_hash, payment.extra) From 60cd40d5e524d77945196d266056f7d24a0e8880 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 21 Dec 2022 15:05:28 +0200 Subject: [PATCH 5/8] feat: `update_payment_extra` with webhook response --- lnbits/extensions/withdraw/lnurl.py | 38 ++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index 5737e54f5..17ef64649 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -11,6 +11,7 @@ from loguru import logger from starlette.requests import Request from starlette.responses import HTMLResponse +from lnbits.core.crud import update_payment_extra from lnbits.core.services import pay_invoice from . import withdraw_ext @@ -44,7 +45,11 @@ async def api_lnurl_response(request: Request, unique_hash): "minWithdrawable": link.min_withdrawable * 1000, "maxWithdrawable": link.max_withdrawable * 1000, "defaultDescription": link.title, + "webhook_url": link.webhook_url, + "webhook_headers": link.webhook_headers, + "webhook_body": link.webhook_body, } + return json.dumps(withdrawResponse) @@ -56,7 +61,7 @@ async def api_lnurl_response(request: Request, unique_hash): name="withdraw.api_lnurl_callback", summary="lnurl withdraw callback", description=""" - This enpoints allows you to put unique_hash, k1 + This endpoints allows you to put unique_hash, k1 and a payment_request to get your payment_request paid. """, response_description="JSON with status", @@ -143,18 +148,39 @@ async def api_lnurl_callback( if link.webhook_url: async with httpx.AsyncClient() as client: try: - r = await client.post( - link.webhook_url, - json={ + kwargs = { + "json": { "payment_hash": payment_hash, "payment_request": payment_request, "lnurlw": link.id, }, - timeout=40, + "timeout": 40, + } + if link.webhook_body: + kwargs["json"]["body"] = json.loads(link.webhook_body) + if link.webhook_headers: + kwargs["headers"] = json.loads(link.webhook_headers) + + r: httpx.Response = await client.post(link.webhook_url, **kwargs) + await update_payment_extra( + payment_hash, + dict( + { + "wh_success": r.is_success, + "wh_message": r.reason_phrase, + "wh_response": r.text, + } + ), ) except Exception as exc: # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid - logger.error("Caught exception when dispatching webhook url:", exc) + logger.error( + "Caught exception when dispatching webhook url: " + str(exc) + ) + await update_payment_extra( + payment_hash, + dict({"wh_success": False, "wh_message": str(exc)}), + ) return {"status": "OK"} From ec01c74da183a45b4218f89503a35ad4e1826556 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 21 Dec 2022 15:10:12 +0200 Subject: [PATCH 6/8] fix: `mypy` --- lnbits/core/crud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 771ff3973..8f514beae 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -463,7 +463,7 @@ async def update_payment_extra( row = await (conn or db).fetchone( "SELECT hash, extra from apipayments WHERE hash = ?", - (payment_hash), + (payment_hash,), ) if not row: return From f3c884111d6aed64dfdb8c0a83e6ca92f920ae1d Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 21 Dec 2022 15:47:22 +0200 Subject: [PATCH 7/8] refactor: use `dict.update()` --- lnbits/core/crud.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 8f514beae..7d9f2a5bc 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -467,17 +467,15 @@ async def update_payment_extra( ) if not row: return - existing_extra = json.loads(row["extra"] if row["extra"] else "{}") - new_extra = { - **existing_extra, - **extra, - } + db_extra = json.loads(row["extra"] if row["extra"] else "{}") + db_extra.update(extra) + await (conn or db).execute( """ UPDATE apipayments SET extra = ? WHERE hash = ? """, - (json.dumps(new_extra), payment_hash), + (json.dumps(db_extra), payment_hash), ) From 7b0f9f61fc2092f6dcf16a354d42e058e7e65c0c Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 21 Dec 2022 15:47:35 +0200 Subject: [PATCH 8/8] fix: remove double dict --- lnbits/extensions/withdraw/lnurl.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index 17ef64649..7260df1e2 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -164,13 +164,11 @@ async def api_lnurl_callback( r: httpx.Response = await client.post(link.webhook_url, **kwargs) await update_payment_extra( payment_hash, - dict( - { - "wh_success": r.is_success, - "wh_message": r.reason_phrase, - "wh_response": r.text, - } - ), + { + "wh_success": r.is_success, + "wh_message": r.reason_phrase, + "wh_response": r.text, + }, ) except Exception as exc: # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid @@ -179,7 +177,7 @@ async def api_lnurl_callback( ) await update_payment_extra( payment_hash, - dict({"wh_success": False, "wh_message": str(exc)}), + {"wh_success": False, "wh_message": str(exc)}, ) return {"status": "OK"}