mirror of
https://github.com/lnbits/lnbits.git
synced 2025-04-06 19:08:12 +02:00
Merge pull request #1227 from motorina0/withdraw_callback_extra
Withdraw webhook improvements
This commit is contained in:
commit
4e8014a177
@ -451,6 +451,34 @@ 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
|
||||
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(db_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,)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
),
|
||||
)
|
||||
|
@ -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,37 @@ 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,
|
||||
{
|
||||
"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,
|
||||
{"wh_success": False, "wh_message": str(exc)},
|
||||
)
|
||||
|
||||
return {"status": "OK"}
|
||||
|
||||
|
@ -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;")
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -209,7 +209,13 @@
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
<q-toggle
|
||||
label="Webhook"
|
||||
color="secodary"
|
||||
v-model="formDialog.data.has_webhook"
|
||||
></q-toggle>
|
||||
<q-input
|
||||
v-if="formDialog.data.has_webhook"
|
||||
filled
|
||||
dense
|
||||
v-model="formDialog.data.webhook_url"
|
||||
@ -217,6 +223,24 @@
|
||||
label="Webhook URL (optional)"
|
||||
hint="A URL to be called whenever this link gets used."
|
||||
></q-input>
|
||||
<q-input
|
||||
v-if="formDialog.data.has_webhook"
|
||||
filled
|
||||
dense
|
||||
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
|
||||
v-if="formDialog.data.has_webhook"
|
||||
filled
|
||||
dense
|
||||
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-list>
|
||||
<q-item tag="label" class="rounded-borders">
|
||||
<q-item-section avatar>
|
||||
|
Loading…
x
Reference in New Issue
Block a user