Extension: LNURLw webhook_url (#610)

* LNURLw web_hook

* crlf -> lf

* Fix typo

* LNURLw webhook api doc
This commit is contained in:
Tomas Bezouska 2022-06-01 15:24:17 +02:00 committed by GitHub
parent 7c4ce9bf96
commit 895d9d2e0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 7 deletions

View file

@ -25,9 +25,10 @@ async def create_withdraw_link(
unique_hash, unique_hash,
k1, k1,
open_time, open_time,
usescsv usescsv,
webhook_url
) )
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", """,
( (
link_id, link_id,
@ -42,6 +43,7 @@ async def create_withdraw_link(
urlsafe_short_hash(), urlsafe_short_hash(),
int(datetime.now().timestamp()) + data.wait_time, int(datetime.now().timestamp()) + data.wait_time,
usescsv, usescsv,
data.webhook_url
), ),
) )
link = await get_withdraw_link(link_id, 0) link = await get_withdraw_link(link_id, 0)

View file

@ -1,4 +1,7 @@
import json import json
import traceback
import httpx
from datetime import datetime from datetime import datetime
from http import HTTPStatus from http import HTTPStatus
@ -104,16 +107,34 @@ async def api_lnurl_callback(
payment_request = pr payment_request = pr
await pay_invoice( payment_hash = await pay_invoice(
wallet_id=link.wallet, wallet_id=link.wallet,
payment_request=payment_request, payment_request=payment_request,
max_sat=link.max_withdrawable, max_sat=link.max_withdrawable,
extra={"tag": "withdraw"}, extra={"tag": "withdraw"},
) )
if link.webhook_url:
async with httpx.AsyncClient() as client:
try:
r = await client.post(
link.webhook_url,
json={
"payment_hash": payment_hash,
"payment_request": payment_request,
"lnurlw": link.id,
},
timeout=40,
)
except Exception as exc:
# webhook fails shouldn't cause the lnurlw to fail since invoice is already paid
print("Caught exception when dispatching webhook url:", exc)
return {"status": "OK"} return {"status": "OK"}
except Exception as e: except Exception as e:
await update_withdraw_link(link.id, **changesback) await update_withdraw_link(link.id, **changesback)
print(traceback.format_exc())
return {"status": "ERROR", "reason": "Link not working"} return {"status": "ERROR", "reason": "Link not working"}

View file

@ -108,3 +108,9 @@ async def m003_make_hash_check(db):
); );
""" """
) )
async def m004_webhook_url(db):
"""
Adds webhook_url
"""
await db.execute("ALTER TABLE withdraw.withdraw_link ADD COLUMN webhook_url TEXT;")

View file

@ -15,6 +15,7 @@ class CreateWithdrawData(BaseModel):
uses: int = Query(..., ge=1) uses: int = Query(..., ge=1)
wait_time: int = Query(..., ge=1) wait_time: int = Query(..., ge=1)
is_unique: bool is_unique: bool
webhook_url: str = Query(None)
class WithdrawLink(BaseModel): class WithdrawLink(BaseModel):
@ -32,6 +33,7 @@ class WithdrawLink(BaseModel):
used: int = Query(0) used: int = Query(0)
usescsv: str = Query(None) usescsv: str = Query(None)
number: int = Query(0) number: int = Query(0)
webhook_url: str = Query(None)
@property @property
def is_spent(self) -> bool: def is_spent(self) -> bool:

View file

@ -179,7 +179,8 @@ new Vue({
'max_withdrawable', 'max_withdrawable',
'uses', 'uses',
'wait_time', 'wait_time',
'is_unique' 'is_unique',
'webhook_url'
) )
) )
.then(function (response) { .then(function (response) {

View file

@ -70,7 +70,8 @@
<code <code
>{"title": &lt;string&gt;, "min_withdrawable": &lt;integer&gt;, >{"title": &lt;string&gt;, "min_withdrawable": &lt;integer&gt;,
"max_withdrawable": &lt;integer&gt;, "uses": &lt;integer&gt;, "max_withdrawable": &lt;integer&gt;, "uses": &lt;integer&gt;,
"wait_time": &lt;integer&gt;, "is_unique": &lt;boolean&gt;}</code "wait_time": &lt;integer&gt;, "is_unique": &lt;boolean&gt;,
"webhook_url": &lt;string&gt;}</code
> >
<h5 class="text-caption q-mt-sm q-mb-none"> <h5 class="text-caption q-mt-sm q-mb-none">
Returns 201 CREATED (application/json) Returns 201 CREATED (application/json)
@ -81,7 +82,7 @@
>curl -X POST {{ request.base_url }}withdraw/api/v1/links -d '{"title": >curl -X POST {{ request.base_url }}withdraw/api/v1/links -d '{"title":
&lt;string&gt;, "min_withdrawable": &lt;integer&gt;, &lt;string&gt;, "min_withdrawable": &lt;integer&gt;,
"max_withdrawable": &lt;integer&gt;, "uses": &lt;integer&gt;, "max_withdrawable": &lt;integer&gt;, "uses": &lt;integer&gt;,
"wait_time": &lt;integer&gt;, "is_unique": &lt;boolean&gt;}' -H "wait_time": &lt;integer&gt;, "is_unique": &lt;boolean&gt;, "webhook_url": &lt;string&gt;}' -H
"Content-type: application/json" -H "X-Api-Key: {{ "Content-type: application/json" -H "X-Api-Key: {{
user.wallets[0].adminkey }}" user.wallets[0].adminkey }}"
</code> </code>

View file

@ -29,6 +29,7 @@
{{ col.label }} {{ col.label }}
</q-th> </q-th>
<q-th auto-width></q-th> <q-th auto-width></q-th>
<q-th auto-width></q-th>
</q-tr> </q-tr>
</template> </template>
<template v-slot:body="props"> <template v-slot:body="props">
@ -81,6 +82,11 @@
<q-td v-for="col in props.cols" :key="col.name" :props="props"> <q-td v-for="col in props.cols" :key="col.name" :props="props">
{{ col.value }} {{ col.value }}
</q-td> </q-td>
<q-td>
<q-icon v-if="props.row.webhook_url" size="14px" name="http">
<q-tooltip>Webhook to {{ props.row.webhook_url}}</q-tooltip>
</q-icon>
</q-td>
<q-td auto-width> <q-td auto-width>
<q-btn <q-btn
flat flat
@ -143,6 +149,14 @@
</q-select> </q-select>
</div> </div>
</div> </div>
<q-input
filled
dense
v-model="formDialog.data.webhook_url"
type="text"
label="Webhook URL (optional)"
hint="A URL to be called whenever this link gets used."
></q-input>
<q-list> <q-list>
<q-item tag="label" class="rounded-borders"> <q-item tag="label" class="rounded-borders">
<q-item-section avatar> <q-item-section avatar>