Add webhook_api_key and webhook_custom_data to lnurlp webhook (#1065)

* add webhook_api_key and webhook_custom_data to lnurlp

* formatting

* add json validation to lnurlp custom_data

* motorina0 improvements
This commit is contained in:
dni ⚡ 2022-11-19 21:44:21 +01:00 committed by GitHub
parent 604e52c45d
commit 41aa074350
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 6 deletions

View file

@ -21,13 +21,15 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
served_meta, served_meta,
served_pr, served_pr,
webhook_url, webhook_url,
webhook_headers,
webhook_body,
success_text, success_text,
success_url, success_url,
comment_chars, comment_chars,
currency, currency,
fiat_base_multiplier fiat_base_multiplier
) )
VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?)
{returning} {returning}
""", """,
( (
@ -36,6 +38,8 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
data.min, data.min,
data.max, data.max,
data.webhook_url, data.webhook_url,
data.webhook_headers,
data.webhook_body,
data.success_text, data.success_text,
data.success_url, data.success_url,
data.comment_chars, data.comment_chars,

View file

@ -60,3 +60,11 @@ async def m004_fiat_base_multiplier(db):
await db.execute( await db.execute(
"ALTER TABLE lnurlp.pay_links ADD COLUMN fiat_base_multiplier INTEGER DEFAULT 1;" "ALTER TABLE lnurlp.pay_links ADD COLUMN fiat_base_multiplier INTEGER DEFAULT 1;"
) )
async def m005_webhook_headers_and_body(db):
"""
Add headers and body to webhooks
"""
await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN webhook_headers TEXT;")
await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN webhook_body TEXT;")

View file

@ -18,6 +18,8 @@ class CreatePayLinkData(BaseModel):
currency: str = Query(None) currency: str = Query(None)
comment_chars: int = Query(0, ge=0, lt=800) comment_chars: int = Query(0, ge=0, lt=800)
webhook_url: str = Query(None) webhook_url: str = Query(None)
webhook_headers: str = Query(None)
webhook_body: str = Query(None)
success_text: str = Query(None) success_text: str = Query(None)
success_url: str = Query(None) success_url: str = Query(None)
fiat_base_multiplier: int = Query(100, ge=1) fiat_base_multiplier: int = Query(100, ge=1)
@ -31,6 +33,8 @@ class PayLink(BaseModel):
served_meta: int served_meta: int
served_pr: int served_pr: int
webhook_url: Optional[str] webhook_url: Optional[str]
webhook_headers: Optional[str]
webhook_body: Optional[str]
success_text: Optional[str] success_text: Optional[str]
success_url: Optional[str] success_url: Optional[str]
currency: Optional[str] currency: Optional[str]

View file

@ -33,17 +33,22 @@ async def on_invoice_paid(payment: Payment) -> None:
if pay_link and pay_link.webhook_url: if pay_link and pay_link.webhook_url:
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
try: try:
r = await client.post( kwargs = {
pay_link.webhook_url, "json": {
json={
"payment_hash": payment.payment_hash, "payment_hash": payment.payment_hash,
"payment_request": payment.bolt11, "payment_request": payment.bolt11,
"amount": payment.amount, "amount": payment.amount,
"comment": payment.extra.get("comment"), "comment": payment.extra.get("comment"),
"lnurlp": pay_link.id, "lnurlp": pay_link.id,
}, },
timeout=40, "timeout": 40,
) }
if pay_link.webhook_body:
kwargs["json"]["body"] = json.loads(pay_link.webhook_body)
if pay_link.webhook_headers:
kwargs["headers"] = json.loads(pay_link.webhook_headers)
r = await client.post(pay_link.webhook_url, **kwargs)
await mark_webhook_sent(payment, r.status_code) await mark_webhook_sent(payment, r.status_code)
except (httpx.ConnectError, httpx.RequestError): except (httpx.ConnectError, httpx.RequestError):
await mark_webhook_sent(payment, -1) await mark_webhook_sent(payment, -1)

View file

@ -213,6 +213,24 @@
label="Webhook URL (optional)" label="Webhook URL (optional)"
hint="A URL to be called whenever this link receives a payment." hint="A URL to be called whenever this link receives a payment."
></q-input> ></q-input>
<q-input
filled
dense
v-if="formDialog.data.webhook_url"
v-model="formDialog.data.webhook_headers"
type="text"
label="Webhook headers (optional)"
hint="Custom data as JSON string, send headers along with the webhook."
></q-input>
<q-input
filled
dense
v-if="formDialog.data.webhook_url"
v-model="formDialog.data.webhook_body"
type="text"
label="Webhook custom data (optional)"
hint="Custom data as JSON string, will get posted along with webhook 'body' field."
></q-input>
<q-input <q-input
filled filled
dense dense

View file

@ -1,3 +1,4 @@
import json
from http import HTTPStatus from http import HTTPStatus
from fastapi import Request from fastapi import Request
@ -90,6 +91,24 @@ async def api_link_create_or_update(
detail="Must use full satoshis.", status_code=HTTPStatus.BAD_REQUEST detail="Must use full satoshis.", status_code=HTTPStatus.BAD_REQUEST
) )
if data.webhook_headers:
try:
json.loads(data.webhook_headers)
except ValueError:
raise HTTPException(
detail="Invalid JSON in webhook_headers.",
status_code=HTTPStatus.BAD_REQUEST,
)
if data.webhook_body:
try:
json.loads(data.webhook_body)
except ValueError:
raise HTTPException(
detail="Invalid JSON in webhook_body.",
status_code=HTTPStatus.BAD_REQUEST,
)
# database only allows int4 entries for min and max. For fiat currencies, # database only allows int4 entries for min and max. For fiat currencies,
# we multiply by data.fiat_base_multiplier (usually 100) to save the value in cents. # we multiply by data.fiat_base_multiplier (usually 100) to save the value in cents.
if data.currency and data.fiat_base_multiplier: if data.currency and data.fiat_base_multiplier: