diff --git a/lnbits/core/__init__.py b/lnbits/core/__init__.py index ca0959a8..12dcded8 100644 --- a/lnbits/core/__init__.py +++ b/lnbits/core/__init__.py @@ -14,6 +14,7 @@ core_app: Blueprint = Blueprint( from .views.api import * # noqa from .views.generic import * # noqa +from .views.public_api import * # noqa from .tasks import register_listeners from lnbits.tasks import record_async diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 20740c05..3a296e66 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -8,7 +8,7 @@ from . import db from .crud import get_balance_notify from .models import Payment -sse_listeners: List[trio.MemorySendChannel] = [] +api_invoice_listeners: List[trio.MemorySendChannel] = [] async def register_listeners(): @@ -20,7 +20,7 @@ async def register_listeners(): async def wait_for_paid_invoices(invoice_paid_chan: trio.MemoryReceiveChannel): async for payment in invoice_paid_chan: # send information to sse channel - await dispatch_sse(payment) + await dispatch_invoice_listener(payment) # dispatch webhook if payment.webhook and not payment.webhook_status: @@ -40,13 +40,13 @@ async def wait_for_paid_invoices(invoice_paid_chan: trio.MemoryReceiveChannel): pass -async def dispatch_sse(payment: Payment): - for send_channel in sse_listeners: +async def dispatch_invoice_listener(payment: Payment): + for send_channel in api_invoice_listeners: try: send_channel.send_nowait(payment) except trio.WouldBlock: print("removing sse listener", send_channel) - sse_listeners.remove(send_channel) + api_invoice_listeners.remove(send_channel) async def dispatch_webhook(payment: Payment): diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 2547435e..9a81a1fe 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -20,7 +20,7 @@ from ..services import ( pay_invoice, perform_lnurlauth, ) -from ..tasks import sse_listeners +from ..tasks import api_invoice_listeners @core_app.route("/api/v1/wallet", methods=["GET"]) @@ -295,7 +295,7 @@ async def api_payments_sse(): send_payment, receive_payment = trio.open_memory_channel(0) print("adding sse listener", send_payment) - sse_listeners.append(send_payment) + api_invoice_listeners.append(send_payment) send_event, event_to_send = trio.open_memory_channel(0) diff --git a/lnbits/core/views/public_api.py b/lnbits/core/views/public_api.py new file mode 100644 index 00000000..5149d90c --- /dev/null +++ b/lnbits/core/views/public_api.py @@ -0,0 +1,37 @@ +import trio # type: ignore +import datetime +from http import HTTPStatus +from quart import jsonify + +from lnbits import bolt11 + +from .. import core_app +from ..crud import get_standalone_payment +from ..tasks import api_invoice_listeners + + +@core_app.route("/public/v1/payment/", methods=["GET"]) +async def api_public_payment_longpolling(payment_hash): + payment = await get_standalone_payment(payment_hash) + + if not payment: + return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND + elif not payment.pending: + return jsonify({"status": "paid"}), HTTPStatus.OK + + try: + invoice = bolt11.decode(payment.bolt11) + expiration = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) + if expiration < datetime.datetime.now(): + return jsonify({"status": "expired"}), HTTPStatus.OK + except: + return jsonify({"message": "Invalid bolt11 invoice."}), HTTPStatus.BAD_REQUEST + + send_payment, receive_payment = trio.open_memory_channel(0) + + print("adding standalone invoice listener", payment_hash, send_payment) + api_invoice_listeners.append(send_payment) + + async for payment in receive_payment: + if payment.payment_hash == payment_hash: + return jsonify({"status": "paid"}), HTTPStatus.OK