Compare commits
3
Commits
v1.3.0-rc1
...
v1.3.0-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
793c7c45ed | ||
|
|
ff535ae065 | ||
|
|
01fdc1defb |
@@ -7,11 +7,11 @@ from datetime import datetime, timezone
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from ecdsa import SECP256k1, SigningKey
|
from ecdsa import SECP256k1, SigningKey
|
||||||
|
from lnurl import encode as lnurl_encode
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from lnbits.db import FilterModel
|
from lnbits.db import FilterModel
|
||||||
from lnbits.helpers import url_for
|
from lnbits.helpers import url_for
|
||||||
from lnbits.lnurl import encode as lnurl_encode
|
|
||||||
from lnbits.settings import settings
|
from lnbits.settings import settings
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from .funding_source import (
|
|||||||
get_balance_delta,
|
get_balance_delta,
|
||||||
switch_to_voidwallet,
|
switch_to_voidwallet,
|
||||||
)
|
)
|
||||||
from .lnurl import fetch_lnurl_pay_request
|
from .lnurl import fetch_lnurl_pay_request, get_pr_from_lnurl
|
||||||
from .notifications import enqueue_admin_notification, send_payment_notification
|
from .notifications import enqueue_admin_notification, send_payment_notification
|
||||||
from .payments import (
|
from .payments import (
|
||||||
calculate_fiat_amounts,
|
calculate_fiat_amounts,
|
||||||
@@ -55,6 +55,7 @@ __all__ = [
|
|||||||
"fetch_lnurl_pay_request",
|
"fetch_lnurl_pay_request",
|
||||||
"get_balance_delta",
|
"get_balance_delta",
|
||||||
"get_payments_daily_stats",
|
"get_payments_daily_stats",
|
||||||
|
"get_pr_from_lnurl",
|
||||||
"pay_invoice",
|
"pay_invoice",
|
||||||
"send_payment_notification",
|
"send_payment_notification",
|
||||||
"service_fee",
|
"service_fee",
|
||||||
|
|||||||
@@ -1,11 +1,35 @@
|
|||||||
from lnurl import LnurlPayActionResponse
|
from lnurl import (
|
||||||
from lnurl import execute_pay_request as lnurlp
|
LnurlPayActionResponse,
|
||||||
|
LnurlPayResponse,
|
||||||
|
LnurlResponseException,
|
||||||
|
execute_pay_request,
|
||||||
|
handle,
|
||||||
|
)
|
||||||
|
|
||||||
from lnbits.core.models import CreateLnurlPayment
|
from lnbits.core.models import CreateLnurlPayment
|
||||||
from lnbits.settings import settings
|
from lnbits.settings import settings
|
||||||
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
|
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
|
||||||
|
|
||||||
|
|
||||||
|
async def get_pr_from_lnurl(lnurl: str, amount_msat: int) -> str:
|
||||||
|
res = await handle(lnurl, user_agent=settings.user_agent, timeout=10)
|
||||||
|
if not isinstance(res, LnurlPayResponse):
|
||||||
|
raise LnurlResponseException(
|
||||||
|
"Invalid LNURL response. Expected LnurlPayResponse."
|
||||||
|
)
|
||||||
|
res2 = await execute_pay_request(
|
||||||
|
res,
|
||||||
|
msat=str(amount_msat),
|
||||||
|
user_agent=settings.user_agent,
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
if not isinstance(res, LnurlPayActionResponse):
|
||||||
|
raise LnurlResponseException(
|
||||||
|
"Invalid LNURL pay response. Expected LnurlPayActionResponse."
|
||||||
|
)
|
||||||
|
return res2.pr
|
||||||
|
|
||||||
|
|
||||||
async def fetch_lnurl_pay_request(data: CreateLnurlPayment) -> LnurlPayActionResponse:
|
async def fetch_lnurl_pay_request(data: CreateLnurlPayment) -> LnurlPayActionResponse:
|
||||||
"""
|
"""
|
||||||
Pay an LNURL payment request.
|
Pay an LNURL payment request.
|
||||||
@@ -21,9 +45,9 @@ async def fetch_lnurl_pay_request(data: CreateLnurlPayment) -> LnurlPayActionRes
|
|||||||
else:
|
else:
|
||||||
amount_msat = data.amount
|
amount_msat = data.amount
|
||||||
|
|
||||||
return await lnurlp(
|
return await execute_pay_request(
|
||||||
data.res,
|
data.res,
|
||||||
msat=str(amount_msat),
|
msat=str(amount_msat),
|
||||||
user_agent=settings.user_agent,
|
user_agent=settings.user_agent,
|
||||||
timeout=5,
|
timeout=10,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
from http import HTTPStatus
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from fastapi import HTTPException, Request, Response
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
from fastapi.routing import APIRoute
|
|
||||||
from lnurl import LnurlErrorResponse, decode, encode, handle
|
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
from lnbits.exceptions import InvoiceError, PaymentError
|
|
||||||
|
|
||||||
|
|
||||||
class LnurlErrorResponseHandler(APIRoute):
|
|
||||||
"""
|
|
||||||
Custom APIRoute class to handle LNURL errors.
|
|
||||||
LNURL errors always return with status 200 and
|
|
||||||
a JSON response with `status="ERROR"` and a `reason` key.
|
|
||||||
Helps to catch HTTPException and return a valid lnurl error response
|
|
||||||
|
|
||||||
Example:
|
|
||||||
withdraw_lnurl_router = APIRouter(prefix="/api/v1/lnurl")
|
|
||||||
withdraw_lnurl_router.route_class = LnurlErrorResponseHandler
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_route_handler(self) -> Callable:
|
|
||||||
original_route_handler = super().get_route_handler()
|
|
||||||
|
|
||||||
async def lnurl_route_handler(request: Request) -> Response:
|
|
||||||
try:
|
|
||||||
response = await original_route_handler(request)
|
|
||||||
return response
|
|
||||||
except (InvoiceError, PaymentError) as exc:
|
|
||||||
logger.debug(f"Wallet Error: {exc}")
|
|
||||||
response = JSONResponse(
|
|
||||||
status_code=HTTPStatus.OK,
|
|
||||||
content={"status": "ERROR", "reason": f"{exc.message}"},
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
except HTTPException as exc:
|
|
||||||
logger.debug(f"HTTPException: {exc}")
|
|
||||||
response = JSONResponse(
|
|
||||||
status_code=HTTPStatus.OK,
|
|
||||||
content={"status": "ERROR", "reason": f"{exc.detail}"},
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
except Exception as exc:
|
|
||||||
logger.error("Unknown Error:", exc)
|
|
||||||
response = JSONResponse(
|
|
||||||
status_code=HTTPStatus.OK,
|
|
||||||
content={
|
|
||||||
"status": "ERROR",
|
|
||||||
"reason": f"UNKNOWN ERROR: {exc!s}",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
|
|
||||||
return lnurl_route_handler
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"LnurlErrorResponse",
|
|
||||||
"LnurlErrorResponseHandler",
|
|
||||||
"decode",
|
|
||||||
"encode",
|
|
||||||
"handle",
|
|
||||||
]
|
|
||||||
@@ -274,19 +274,18 @@ window.WalletPageLogic = {
|
|||||||
if (!this.receive.lnurl) {
|
if (!this.receive.lnurl) {
|
||||||
this.readNfcTag()
|
this.readNfcTag()
|
||||||
}
|
}
|
||||||
// TODO: lnurl_callback and lnurl_response
|
|
||||||
// WITHDRAW
|
// WITHDRAW
|
||||||
if (response.data.extra.lnurl_response !== null) {
|
if (response.data.extra.lnurl_response !== null) {
|
||||||
if (response.data.extra.lnurl_response === false) {
|
if (response.data.extra.lnurl_response === false) {
|
||||||
response.data.extra.lnurl_response = `Unable to connect`
|
response.data.extra.lnurl_response = `Unable to connect`
|
||||||
}
|
}
|
||||||
|
const domain = this.receive.lnurl.callback.split('/')[2]
|
||||||
if (typeof response.data.extra.lnurl_response === 'string') {
|
if (typeof response.data.extra.lnurl_response === 'string') {
|
||||||
// failure
|
// failure
|
||||||
Quasar.Notify.create({
|
Quasar.Notify.create({
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: `${this.receive.lnurl.domain} lnurl-withdraw call failed.`,
|
message: `${domain} lnurl-withdraw call failed.`,
|
||||||
caption: response.data.extra.lnurl_response
|
caption: response.data.extra.lnurl_response
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -294,7 +293,7 @@ window.WalletPageLogic = {
|
|||||||
// success
|
// success
|
||||||
Quasar.Notify.create({
|
Quasar.Notify.create({
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
message: `Invoice sent to ${this.receive.lnurl.domain}!`,
|
message: `Invoice sent to ${domain}!`,
|
||||||
spinner: true
|
spinner: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -350,12 +349,11 @@ window.WalletPageLogic = {
|
|||||||
)
|
)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const data = response.data
|
const data = response.data
|
||||||
|
|
||||||
if (data.status === 'ERROR') {
|
if (data.status === 'ERROR') {
|
||||||
Quasar.Notify.create({
|
Quasar.Notify.create({
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: `${data.domain} lnurl call failed.`,
|
message: `lnurl scan failed.`,
|
||||||
caption: data.reason
|
caption: data.reason
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "lnbits"
|
name = "lnbits"
|
||||||
version = "1.3.0-rc1"
|
version = "1.3.0-rc2"
|
||||||
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
||||||
authors = ["Alan Bits <alan@lnbits.com>"]
|
authors = ["Alan Bits <alan@lnbits.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
Reference in New Issue
Block a user