Add NFC Payment Support and Display Receive Amount in Receive Dialog (#2747)
* feat: add readNfcTag to core wallet * feat: added payments/ endpoint to pay invoice with lnurlw from nfc tag * feat: add notifications to nfc read and payment process * feat: display sat and fiat amount on receive invoice * feat: add notifications for non-lnurl nfc tags * removed unnecesary payment updates * fix: case when lnurlw was already used. lnurl_req status error * fix: lnurl response status error * fix: abort nfc reading on receive dialog hid * feat: dismiss tap suggestion when nfc tag read successfully * update: NFC supported chip * remove console.log * add: function return type * test: happy path for api_payment_pay_with_nfc * feat: follow LUD-17, no support for lightning: url schema * explicit lnurl withdraw for payment * test: add parametrized tests for all cases of api_payment_pay_with_nfc endpoint * fix: payment.amount in response comes already in milisats
This commit is contained in:
@@ -25,6 +25,7 @@ from lnbits.core.models import (
|
||||
CreateLnurl,
|
||||
DecodePayment,
|
||||
KeyType,
|
||||
PayLnurlWData,
|
||||
Payment,
|
||||
PaymentFilters,
|
||||
PaymentHistoryPoint,
|
||||
@@ -406,3 +407,59 @@ async def api_payments_decode(data: DecodePayment) -> JSONResponse:
|
||||
{"message": f"Failed to decode: {exc!s}"},
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
|
||||
|
||||
@payment_router.post("/{payment_request}/pay-with-nfc", status_code=HTTPStatus.OK)
|
||||
async def api_payment_pay_with_nfc(
|
||||
payment_request: str,
|
||||
lnurl_data: PayLnurlWData,
|
||||
) -> JSONResponse:
|
||||
|
||||
lnurl = lnurl_data.lnurl_w.lower()
|
||||
|
||||
# Follow LUD-17 -> https://github.com/lnurl/luds/blob/luds/17.md
|
||||
url = lnurl.replace("lnurlw://", "https://")
|
||||
|
||||
headers = {"User-Agent": settings.user_agent}
|
||||
async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
|
||||
try:
|
||||
lnurl_req = await client.get(url, timeout=10)
|
||||
if lnurl_req.is_error:
|
||||
return JSONResponse(
|
||||
{"success": False, "detail": "Error loading LNURL request"}
|
||||
)
|
||||
|
||||
lnurl_res = lnurl_req.json()
|
||||
|
||||
if lnurl_res.get("status") == "ERROR":
|
||||
return JSONResponse({"success": False, "detail": lnurl_res["reason"]})
|
||||
|
||||
if lnurl_res.get("tag") != "withdrawRequest":
|
||||
return JSONResponse(
|
||||
{"success": False, "detail": "Invalid LNURL-withdraw"}
|
||||
)
|
||||
|
||||
callback_url = lnurl_res["callback"]
|
||||
k1 = lnurl_res["k1"]
|
||||
|
||||
callback_req = await client.get(
|
||||
callback_url,
|
||||
params={"k1": k1, "pr": payment_request},
|
||||
timeout=10,
|
||||
)
|
||||
if callback_req.is_error:
|
||||
return JSONResponse(
|
||||
{"success": False, "detail": "Error loading callback request"}
|
||||
)
|
||||
|
||||
callback_res = callback_req.json()
|
||||
|
||||
if callback_res.get("status") == "ERROR":
|
||||
return JSONResponse(
|
||||
{"success": False, "detail": callback_res["reason"]}
|
||||
)
|
||||
else:
|
||||
return JSONResponse({"success": True, "detail": callback_res})
|
||||
|
||||
except Exception as e:
|
||||
return JSONResponse({"success": False, "detail": f"Unexpected error: {e}"})
|
||||
|
||||
Reference in New Issue
Block a user