lnbits-legend/lnbits/extensions/offlineshop/lnurl.py

89 lines
2.9 KiB
Python
Raw Normal View History

2023-01-04 12:21:12 +01:00
from fastapi import Query
2023-01-04 13:36:08 +01:00
from lnurl import LnurlErrorResponse, LnurlPayActionResponse, LnurlPayResponse
from lnurl.models import ClearnetUrl, LightningInvoice, MilliSatoshi
from starlette.requests import Request
from lnbits.core.services import create_invoice
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
from . import offlineshop_ext
from .crud import get_item, get_shop
2021-09-16 19:42:05 +02:00
@offlineshop_ext.get("/lnurl/{item_id}", name="offlineshop.lnurl_response")
2023-01-04 12:21:12 +01:00
async def lnurl_response(req: Request, item_id: int = Query(...)) -> dict:
item = await get_item(item_id)
if not item:
2021-08-21 00:34:48 +01:00
return {"status": "ERROR", "reason": "Item not found."}
if not item.enabled:
2021-08-21 00:34:48 +01:00
return {"status": "ERROR", "reason": "Item disabled."}
price_msat = (
await fiat_amount_as_satoshis(item.price, item.unit)
if item.unit != "sat"
else item.price
) * 1000
resp = LnurlPayResponse(
2023-01-04 13:36:08 +01:00
callback=ClearnetUrl(
req.url_for("offlineshop.lnurl_callback", item_id=item.id), scheme="https"
),
2023-01-04 12:21:12 +01:00
minSendable=MilliSatoshi(price_msat),
maxSendable=MilliSatoshi(price_msat),
metadata=await item.lnurlpay_metadata(),
)
2021-08-21 00:34:48 +01:00
return resp.dict()
2021-09-19 13:34:31 +02:00
@offlineshop_ext.get("/lnurl/cb/{item_id}", name="offlineshop.lnurl_callback")
2021-09-16 19:42:05 +02:00
async def lnurl_callback(request: Request, item_id: int):
2023-01-04 12:21:12 +01:00
item = await get_item(item_id)
if not item:
2021-08-21 00:34:48 +01:00
return {"status": "ERROR", "reason": "Couldn't find item."}
if item.unit == "sat":
min = item.price * 1000
max = item.price * 1000
else:
price = await fiat_amount_as_satoshis(item.price, item.unit)
# allow some fluctuation (the fiat price may have changed between the calls)
min = price * 995
max = price * 1010
2021-09-19 13:34:31 +02:00
amount_received = int(request.query_params.get("amount") or 0)
if amount_received < min:
2021-08-21 00:34:48 +01:00
return LnurlErrorResponse(
2021-09-16 19:42:05 +02:00
reason=f"Amount {amount_received} is smaller than minimum {min}."
).dict()
elif amount_received > max:
2021-08-21 00:34:48 +01:00
return LnurlErrorResponse(
2021-09-16 19:42:05 +02:00
reason=f"Amount {amount_received} is greater than maximum {max}."
).dict()
shop = await get_shop(item.shop)
2023-01-04 12:21:12 +01:00
assert shop
try:
payment_hash, payment_request = await create_invoice(
wallet_id=shop.wallet,
amount=int(amount_received / 1000),
memo=item.name,
unhashed_description=(await item.lnurlpay_metadata()).encode(),
extra={"tag": "offlineshop", "item": item.id},
)
except Exception as exc:
2023-01-04 12:21:12 +01:00
return LnurlErrorResponse(reason=str(exc)).dict()
if shop.method:
success_action = item.success_action(shop, payment_hash, request)
assert success_action
resp = LnurlPayActionResponse(
pr=LightningInvoice(payment_request),
successAction=success_action,
routes=[],
)
2023-01-04 12:21:12 +01:00
return resp.dict()