diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 04f0e2206..f7cf86ec9 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -171,6 +171,24 @@ async def require_admin_key( else: return wallet +async def require_invoice_key( + r: Request, + api_key_header: str = Security(api_key_header), + api_key_query: str = Security(api_key_query), +): + token = api_key_header if api_key_header else api_key_query + + wallet = await get_key_type(r, token) + + if wallet.wallet_type > 1: + # If wallet type is not invoice then return the unauthorized status + # This also covers when the user passes an invalid key type + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invoice (or Admin) key required." + ) + else: + return wallet + async def check_user_exists(usr: UUID4) -> User: g().user = await get_user(usr.hex) diff --git a/lnbits/extensions/satspay/__init__.py b/lnbits/extensions/satspay/__init__.py index 7cc5f64cd..ddbd1bb97 100644 --- a/lnbits/extensions/satspay/__init__.py +++ b/lnbits/extensions/satspay/__init__.py @@ -1,7 +1,10 @@ +import asyncio + from fastapi import APIRouter from lnbits.db import Database from lnbits.helpers import template_renderer +from lnbits.tasks import catch_everything_and_restart db = Database("ext_satspay") @@ -12,6 +15,11 @@ satspay_ext: APIRouter = APIRouter(prefix="/satspay", tags=["satspay"]) def satspay_renderer(): return template_renderer(["lnbits/extensions/satspay/templates"]) - +from .tasks import wait_for_paid_invoices from .views import * # noqa from .views_api import * # noqa + + +def satspay_start(): + loop = asyncio.get_event_loop() + loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/satspay/crud.py b/lnbits/extensions/satspay/crud.py index edbd39e7f..a4fe5f14d 100644 --- a/lnbits/extensions/satspay/crud.py +++ b/lnbits/extensions/satspay/crud.py @@ -25,7 +25,10 @@ async def create_charge(user: str, data: CreateCharge) -> Charges: onchainaddress = None if data.lnbitswallet: payment_hash, payment_request = await create_invoice( - wallet_id=data.lnbitswallet, amount=data.amount, memo=charge_id + wallet_id=data.lnbitswallet, + amount=data.amount, + memo=charge_id, + extra={"tag": "charge"}, ) else: payment_hash = None @@ -112,7 +115,7 @@ async def check_address_balance(charge_id: str) -> List[Charges]: pass if charge.lnbitswallet: invoice_status = await api_payment(charge.payment_hash) - + if invoice_status["paid"]: return await update_charge(charge_id=charge_id, balance=charge.amount) row = await db.fetchone("SELECT * FROM satspay.charges WHERE id = ?", (charge_id,)) diff --git a/lnbits/extensions/satspay/tasks.py b/lnbits/extensions/satspay/tasks.py new file mode 100644 index 000000000..bddd4ab2c --- /dev/null +++ b/lnbits/extensions/satspay/tasks.py @@ -0,0 +1,31 @@ +import asyncio + +from lnbits.core.models import Payment +from lnbits.extensions.satspay.crud import check_address_balance, get_charge +from lnbits.tasks import register_invoice_listener + +# from .crud import get_ticket, set_ticket_paid + + +async def wait_for_paid_invoices(): + invoice_queue = asyncio.Queue() + register_invoice_listener(invoice_queue) + + while True: + payment = await invoice_queue.get() + await on_invoice_paid(payment) + + +async def on_invoice_paid(payment: Payment) -> None: + if "charge" != payment.extra.get("tag"): + # not a charge invoice + return + + charge = await get_charge(payment.memo) + if not charge: + print("this should never happen", payment) + return + + await payment.set_pending(False) + await check_address_balance(charge_id=charge.id) + diff --git a/lnbits/extensions/satspay/templates/satspay/display.html b/lnbits/extensions/satspay/templates/satspay/display.html index 5b0282b61..7d7f2ab44 100644 --- a/lnbits/extensions/satspay/templates/satspay/display.html +++ b/lnbits/extensions/satspay/templates/satspay/display.html @@ -132,7 +132,7 @@ lightning-network invoice - + - + {} } }, methods: { + startPaymentNotifier(){ + this.cancelListener() + + this.cancelListener = LNbits.event.onInvoicePaid( + this.wallet, + payment => { + this.checkBalance() + } + ) + }, checkBalance: function () { var self = this LNbits.api @@ -307,12 +321,14 @@ this.lnbtc = false this.onbtc = true } + this.wallet.inkey = '{{ wallet_inkey }}' this.getTheTime() this.getThePercentage() var timerCount = this.timerCount if ('{{ charge.paid }}' == 'False') { timerCount() } + this.startPaymentNotifier() } }) diff --git a/lnbits/extensions/satspay/templates/satspay/index.html b/lnbits/extensions/satspay/templates/satspay/index.html index 7c5b21140..5be38cf6b 100644 --- a/lnbits/extensions/satspay/templates/satspay/index.html +++ b/lnbits/extensions/satspay/templates/satspay/index.html @@ -475,7 +475,7 @@ }, sendFormDataCharge: function () { var self = this - var wallet = this.g.user.wallets[0].adminkey + var wallet = this.g.user.wallets[0].inkey var data = this.formDialogCharge.data data.amount = parseInt(data.amount) data.time = parseInt(data.time) diff --git a/lnbits/extensions/satspay/views.py b/lnbits/extensions/satspay/views.py index 500f02123..07ed71ef8 100644 --- a/lnbits/extensions/satspay/views.py +++ b/lnbits/extensions/satspay/views.py @@ -6,6 +6,7 @@ from starlette.exceptions import HTTPException from starlette.requests import Request from starlette.responses import HTMLResponse +from lnbits.core.crud import get_wallet from lnbits.core.models import User from lnbits.decorators import check_user_exists @@ -29,6 +30,7 @@ async def display(request: Request, charge_id): raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="Charge link does not exist." ) + wallet = await get_wallet(charge.lnbitswallet) return satspay_renderer().TemplateResponse( - "satspay/display.html", {"request": request, "charge": charge} + "satspay/display.html", {"request": request, "charge": charge, "wallet_key": wallet.inkey} ) diff --git a/lnbits/extensions/satspay/views_api.py b/lnbits/extensions/satspay/views_api.py index a207113b6..428f2ef69 100644 --- a/lnbits/extensions/satspay/views_api.py +++ b/lnbits/extensions/satspay/views_api.py @@ -5,7 +5,12 @@ from fastapi import Query from fastapi.params import Depends from starlette.exceptions import HTTPException -from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key +from lnbits.decorators import ( + WalletTypeInfo, + get_key_type, + require_admin_key, + require_invoice_key, +) from lnbits.extensions.satspay import satspay_ext from .crud import ( @@ -20,20 +25,22 @@ from .models import CreateCharge #############################CHARGES########################## - @satspay_ext.post("/api/v1/charge") +async def api_charge_create( + data: CreateCharge, + wallet: WalletTypeInfo = Depends(require_invoice_key) +): + charge = await create_charge(user=wallet.wallet.user, data=data) + return charge.dict() + @satspay_ext.put("/api/v1/charge/{charge_id}") -async def api_charge_create_or_update( +async def api_charge_update( data: CreateCharge, wallet: WalletTypeInfo = Depends(require_admin_key), charge_id=None, ): - if not charge_id: - charge = await create_charge(user=wallet.wallet.user, data=data) - return charge.dict() - else: - charge = await update_charge(charge_id=charge_id, data=data) - return charge.dict() + charge = await update_charge(charge_id=charge_id, data=data) + return charge.dict() @satspay_ext.get("/api/v1/charges") diff --git a/lnbits/extensions/withdraw/templates/withdraw/display.html b/lnbits/extensions/withdraw/templates/withdraw/display.html index 245b3ed1a..5552c77ff 100644 --- a/lnbits/extensions/withdraw/templates/withdraw/display.html +++ b/lnbits/extensions/withdraw/templates/withdraw/display.html @@ -7,7 +7,7 @@ {% if link.is_spent %} Withdraw is spent. {% endif %} - +