mirror of
https://github.com/lnbits/lnbits.git
synced 2026-08-14 12:52:47 +02:00
Merge ef868e4d1a into 9495854d5c
This commit is contained in:
commit
cd41d8f537
4 changed files with 122 additions and 0 deletions
|
|
@ -65,6 +65,7 @@ async def pay_invoice(
|
|||
tag: str = "",
|
||||
labels: list[str] | None = None,
|
||||
external_id: str | None = None,
|
||||
webhook: str | None = None,
|
||||
conn: Connection | None = None,
|
||||
) -> Payment:
|
||||
if settings.lnbits_only_allow_incoming_payments:
|
||||
|
|
@ -99,6 +100,7 @@ async def pay_invoice(
|
|||
extra=extra,
|
||||
labels=labels,
|
||||
external_id=external_id,
|
||||
webhook=webhook,
|
||||
)
|
||||
|
||||
async with db.reuse_conn(conn) if conn else db.connect() as new_conn:
|
||||
|
|
|
|||
|
|
@ -256,6 +256,8 @@ async def api_all_payments_paginated(
|
|||
`amount`, `unit`, and `memo`. To pay an arbitrary invoice from the funds
|
||||
already in the authorized account, specify `out: true` and use the `bolt11`
|
||||
field to supply the BOLT11 invoice to be paid.
|
||||
The optional `webhook` field is honoured in both directions: LNbits POSTs
|
||||
the payment to that URL once the payment reaches `success`.
|
||||
""",
|
||||
status_code=HTTPStatus.CREATED,
|
||||
responses={
|
||||
|
|
@ -281,6 +283,7 @@ async def api_payments_create(
|
|||
extra=invoice_data.extra,
|
||||
labels=invoice_data.labels,
|
||||
external_id=invoice_data.external_id,
|
||||
webhook=invoice_data.webhook,
|
||||
)
|
||||
return payment
|
||||
|
||||
|
|
|
|||
|
|
@ -341,6 +341,38 @@ async def test_pay_invoice(
|
|||
# assert payment.payment_hash == invoice["payment_hash"]
|
||||
|
||||
|
||||
# check POST /api/v1/payments: the webhook is kept on an outgoing payment
|
||||
@pytest.mark.anyio
|
||||
async def test_pay_invoice_with_webhook(
|
||||
client,
|
||||
inkey_headers_to,
|
||||
adminkey_headers_from,
|
||||
mocker: MockerFixture,
|
||||
):
|
||||
mocker.patch(
|
||||
"lnbits.core.services.notifications.dispatch_webhook",
|
||||
AsyncMock(return_value=None),
|
||||
)
|
||||
|
||||
invoice_data = await get_random_invoice_data()
|
||||
create_response = await client.post(
|
||||
"/api/v1/payments", json=invoice_data, headers=inkey_headers_to
|
||||
)
|
||||
assert create_response.status_code < 300
|
||||
|
||||
webhook_url = "http://test.404.lnbits.com"
|
||||
data = {
|
||||
"out": True,
|
||||
"bolt11": create_response.json()["bolt11"],
|
||||
"webhook": webhook_url,
|
||||
}
|
||||
response = await client.post(
|
||||
"/api/v1/payments", json=data, headers=adminkey_headers_from
|
||||
)
|
||||
assert response.status_code < 300
|
||||
assert response.json()["webhook"] == webhook_url
|
||||
|
||||
|
||||
# check GET /api/v1/payments/<hash>: payment status
|
||||
@pytest.mark.anyio
|
||||
async def test_check_payment_without_key(client, invoice: Payment):
|
||||
|
|
|
|||
|
|
@ -533,6 +533,91 @@ async def test_pay_external_invoice_success(
|
|||
assert ws_notification.call_count == 1, "Websocket notification sent."
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pay_external_invoice_with_webhook(
|
||||
from_wallet: Wallet, mocker: MockerFixture, external_funding_source: FakeWallet
|
||||
):
|
||||
invoice_amount = 2105
|
||||
external_invoice = await external_funding_source.create_invoice(invoice_amount)
|
||||
assert external_invoice.payment_request
|
||||
assert external_invoice.checking_id
|
||||
|
||||
preimage = "0000000000000000000000000000000000000000000000000000000000002105"
|
||||
mocker.patch(
|
||||
"lnbits.wallets.FakeWallet.pay_invoice",
|
||||
AsyncMock(
|
||||
return_value=PaymentResponse(
|
||||
ok=True, checking_id=external_invoice.checking_id, preimage=preimage
|
||||
)
|
||||
),
|
||||
)
|
||||
dispatch_webhook_mock = mocker.patch(
|
||||
"lnbits.core.services.notifications.dispatch_webhook",
|
||||
AsyncMock(return_value=None),
|
||||
)
|
||||
|
||||
webhook_url = "http://test.404.lnbits.com"
|
||||
payment = await pay_invoice(
|
||||
wallet_id=from_wallet.id,
|
||||
payment_request=external_invoice.payment_request,
|
||||
webhook=webhook_url,
|
||||
)
|
||||
|
||||
_payment = await get_standalone_payment(payment.payment_hash)
|
||||
assert _payment
|
||||
assert _payment.status == PaymentState.SUCCESS.value
|
||||
assert _payment.amount == -invoice_amount * 1000
|
||||
assert _payment.webhook == webhook_url, "Webhook is stored on the outgoing payment."
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
assert (
|
||||
dispatch_webhook_mock.call_count == 1
|
||||
), "Webhook dispatched for the outgoing payment."
|
||||
dispatched_payment = dispatch_webhook_mock.call_args_list[0][0][0]
|
||||
assert dispatched_payment.payment_hash == payment.payment_hash
|
||||
assert dispatched_payment.amount < 0, "Dispatched payment is the outgoing one."
|
||||
assert dispatched_payment.webhook == webhook_url
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pay_external_invoice_without_webhook_does_not_dispatch(
|
||||
from_wallet: Wallet, mocker: MockerFixture, external_funding_source: FakeWallet
|
||||
):
|
||||
invoice_amount = 2106
|
||||
external_invoice = await external_funding_source.create_invoice(invoice_amount)
|
||||
assert external_invoice.payment_request
|
||||
assert external_invoice.checking_id
|
||||
|
||||
preimage = "0000000000000000000000000000000000000000000000000000000000002106"
|
||||
mocker.patch(
|
||||
"lnbits.wallets.FakeWallet.pay_invoice",
|
||||
AsyncMock(
|
||||
return_value=PaymentResponse(
|
||||
ok=True, checking_id=external_invoice.checking_id, preimage=preimage
|
||||
)
|
||||
),
|
||||
)
|
||||
dispatch_webhook_mock = mocker.patch(
|
||||
"lnbits.core.services.notifications.dispatch_webhook",
|
||||
AsyncMock(return_value=None),
|
||||
)
|
||||
|
||||
payment = await pay_invoice(
|
||||
wallet_id=from_wallet.id,
|
||||
payment_request=external_invoice.payment_request,
|
||||
)
|
||||
|
||||
_payment = await get_standalone_payment(payment.payment_hash)
|
||||
assert _payment
|
||||
assert _payment.status == PaymentState.SUCCESS.value
|
||||
assert _payment.webhook is None
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
assert dispatch_webhook_mock.call_count == 0, "No webhook, no dispatch."
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_retry_pay_success(
|
||||
from_wallet: Wallet, mocker: MockerFixture, external_funding_source: FakeWallet
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue