payment notification webhook

This commit is contained in:
Gene Takavic 2022-09-23 15:17:21 +02:00
parent 30e0537270
commit 632d35682d
6 changed files with 53 additions and 6 deletions

View file

@ -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)

View file

@ -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"}

View file

@ -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 '';
"""
)

View file

@ -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):

View file

@ -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(

View file

@ -283,7 +283,7 @@
v-model="toggleAdvanced"
label="Show advanced options"
></q-toggle>
<div v-show="toggleAdvanced">
<div v-show="toggleAdvanced" class="q-gutter-y-md">
<q-input
filled
dense
@ -322,6 +322,14 @@
>Zero if you don't know.</q-tooltip
>
</q-input>
<q-input
filled
dense
v-model.number="cardDialog.data.webhook_url"
type="text"
label="Notification webhook"
>
</q-input>
<q-btn
unelevated
color="primary"
@ -380,6 +388,8 @@
<strong>Lock key:</strong> {{ qrCodeDialog.data.k0 }}<br />
<strong>Meta key:</strong> {{ qrCodeDialog.data.k1 }}<br />
<strong>File key:</strong> {{ qrCodeDialog.data.k2 }}<br />
<strong>Notification webhook:</strong> {{ qrCodeDialog.data.webhook_url
}}<br />
</p>
<br />
<q-btn