From 632d35682d84c981b926ef47f8e6fc9d274b948e Mon Sep 17 00:00:00 2001 From: Gene Takavic Date: Fri, 23 Sep 2022 15:17:21 +0200 Subject: [PATCH] payment notification webhook --- lnbits/extensions/boltcards/crud.py | 6 +++-- lnbits/extensions/boltcards/lnurl.py | 23 ++++++++++++++++++- lnbits/extensions/boltcards/migrations.py | 8 +++++++ lnbits/extensions/boltcards/models.py | 2 ++ .../extensions/boltcards/static/js/index.js | 8 +++++-- .../boltcards/templates/boltcards/index.html | 12 +++++++++- 6 files changed, 53 insertions(+), 6 deletions(-) diff --git a/lnbits/extensions/boltcards/crud.py b/lnbits/extensions/boltcards/crud.py index c541346e..39ee3f40 100644 --- a/lnbits/extensions/boltcards/crud.py +++ b/lnbits/extensions/boltcards/crud.py @@ -27,9 +27,10 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card: k0, k1, k2, - otp + otp, + webhook_url ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( card_id, @@ -45,6 +46,7 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card: data.k1, data.k2, secrets.token_hex(16), + data.webhook_url, ), ) card = await get_card(card_id) diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py index 6fb9ad8d..be3e09d8 100644 --- a/lnbits/extensions/boltcards/lnurl.py +++ b/lnbits/extensions/boltcards/lnurl.py @@ -8,6 +8,7 @@ from io import BytesIO from typing import Optional from urllib.parse import urlparse +import httpx from embit import bech32, compact from fastapi import Request from fastapi.param_functions import Query @@ -119,12 +120,32 @@ async def lnurl_callback( invoice = bolt11.decode(pr) hit = await spend_hit(id=hit.id, amount=int(invoice.amount_msat / 1000)) try: - await pay_invoice( + payment_hash = await pay_invoice( wallet_id=card.wallet, payment_request=pr, max_sat=card.tx_limit, extra={"tag": "boltcard", "tag": hit.id}, ) + + if card.webhook_url: + async with httpx.AsyncClient() as client: + try: + r = await client.post( + card.webhook_url, + json={ + "notification": "card_payment", + "payment_hash": payment_hash, + "payment_request": pr, + "card_external_id": card.external_id, + "card_name": card.card_name, + "amount": int(invoice.amount_msat / 1000), + }, + timeout=40, + ) + 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) + return {"status": "OK"} except: return {"status": "ERROR", "reason": f"Payment failed"} diff --git a/lnbits/extensions/boltcards/migrations.py b/lnbits/extensions/boltcards/migrations.py index 08126013..25a59fdb 100644 --- a/lnbits/extensions/boltcards/migrations.py +++ b/lnbits/extensions/boltcards/migrations.py @@ -58,3 +58,11 @@ async def m001_initial(db): ); """ ) + + +async def m002_add_webhook(db): + await db.execute( + """ + ALTER TABLE boltcards.cards ADD COLUMN webhook_url TEXT NOT NULL DEFAULT ''; + """ + ) diff --git a/lnbits/extensions/boltcards/models.py b/lnbits/extensions/boltcards/models.py index 47ca1df0..8e6f77c9 100644 --- a/lnbits/extensions/boltcards/models.py +++ b/lnbits/extensions/boltcards/models.py @@ -30,6 +30,7 @@ class Card(BaseModel): prev_k1: str prev_k2: str otp: str + webhook_url: str time: int def from_row(cls, row: Row) -> "Card": @@ -56,6 +57,7 @@ class CreateCardData(BaseModel): prev_k0: str = Query(ZERO_KEY) prev_k1: str = Query(ZERO_KEY) prev_k2: str = Query(ZERO_KEY) + webhook_url: str = Query(...) class Hit(BaseModel): diff --git a/lnbits/extensions/boltcards/static/js/index.js b/lnbits/extensions/boltcards/static/js/index.js index 2ecde39d..e13c14fb 100644 --- a/lnbits/extensions/boltcards/static/js/index.js +++ b/lnbits/extensions/boltcards/static/js/index.js @@ -23,6 +23,7 @@ new Vue({ cardDialog: { show: false, data: { + webhook_url: '', counter: 1, k0: '', k1: '', @@ -270,7 +271,8 @@ new Vue({ k1: card.k1, k2: card.k2, k3: card.k1, - k4: card.k2 + k4: card.k2, + webhook_url: card.webhook_url } this.qrCodeDialog.show = true }, @@ -398,7 +400,9 @@ new Vue({ let cards = _.findWhere(this.cards, {id: cardId}) LNbits.utils - .confirmDialog('Are you sure you want to delete this card? Without access to the card keys you won\'t be able to reset them in the future!') + .confirmDialog( + "Are you sure you want to delete this card? Without access to the card keys you won't be able to reset them in the future!" + ) .onOk(function () { LNbits.api .request( diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html index 3e07024c..f795e454 100644 --- a/lnbits/extensions/boltcards/templates/boltcards/index.html +++ b/lnbits/extensions/boltcards/templates/boltcards/index.html @@ -283,7 +283,7 @@ v-model="toggleAdvanced" label="Show advanced options" > -
+
Zero if you don't know. + + Lock key: {{ qrCodeDialog.data.k0 }}
Meta key: {{ qrCodeDialog.data.k1 }}
File key: {{ qrCodeDialog.data.k2 }}
+ Notification webhook: {{ qrCodeDialog.data.webhook_url + }}