- is requesting
+ is requesting
@@ -497,9 +512,7 @@
-
+
is requesting
between
and
@@ -516,7 +529,7 @@
Wallet:
return account.wallets[0]
-@api_router.get("/api/v1/lnurlscan/{code}")
-async def api_lnurlscan(
- code: str, wallet: WalletTypeInfo = Depends(require_invoice_key)
-):
- try:
- url = str(lnurl_decode(code))
- domain = urlparse(url).netloc
- except Exception as exc:
- # parse internet identifier (user@domain.com)
- name_domain = code.split("@")
- if len(name_domain) == 2 and len(name_domain[1].split(".")) >= 2:
- name, domain = name_domain
- url = (
- ("http://" if domain.endswith(".onion") else "https://")
- + domain
- + "/.well-known/lnurlp/"
- + name
- )
- # will proceed with these values
- else:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl"
- ) from exc
-
- # params is what will be returned to the client
- params: Dict = {"domain": domain}
-
- if "tag=login" in url:
- params.update(kind="auth")
- params.update(callback=url) # with k1 already in it
-
- lnurlauth_key = wallet.wallet.lnurlauth_key(domain)
- assert lnurlauth_key.verifying_key
- params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex())
- else:
- headers = {"User-Agent": settings.user_agent}
- async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
- r = await client.get(url, timeout=5)
- r.raise_for_status()
- if r.is_error:
- raise HTTPException(
- status_code=HTTPStatus.SERVICE_UNAVAILABLE,
- detail={"domain": domain, "message": "failed to get parameters"},
- )
-
- try:
- data = json.loads(r.text)
- except json.decoder.JSONDecodeError as exc:
- raise HTTPException(
- status_code=HTTPStatus.SERVICE_UNAVAILABLE,
- detail={
- "domain": domain,
- "message": f"got invalid response '{r.text[:200]}'",
- },
- ) from exc
-
- try:
- tag: str = data.get("tag")
- params.update(**data)
- if tag == "channelRequest":
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST,
- detail={
- "domain": domain,
- "kind": "channel",
- "message": "unsupported",
- },
- )
- elif tag == "withdrawRequest":
- params.update(kind="withdraw")
- params.update(fixed=data["minWithdrawable"] == data["maxWithdrawable"])
-
- # callback with k1 already in it
- parsed_callback: ParseResult = urlparse(data["callback"])
- qs: Dict = parse_qs(parsed_callback.query)
- qs["k1"] = data["k1"]
-
- # balanceCheck/balanceNotify
- if "balanceCheck" in data:
- params.update(balanceCheck=data["balanceCheck"])
-
- # format callback url and send to client
- parsed_callback = parsed_callback._replace(
- query=urlencode(qs, doseq=True)
- )
- params.update(callback=urlunparse(parsed_callback))
- elif tag == "payRequest":
- params.update(kind="pay")
- params.update(fixed=data["minSendable"] == data["maxSendable"])
-
- params.update(
- description_hash=hashlib.sha256(
- data["metadata"].encode()
- ).hexdigest()
- )
- metadata = json.loads(data["metadata"])
- for [k, v] in metadata:
- if k == "text/plain":
- params.update(description=v)
- if k in ("image/jpeg;base64", "image/png;base64"):
- data_uri = f"data:{k},{v}"
- params.update(image=data_uri)
- if k in ("text/email", "text/identifier"):
- params.update(targetUser=v)
- params.update(commentAllowed=data.get("commentAllowed", 0))
-
- except KeyError as exc:
- raise HTTPException(
- status_code=HTTPStatus.SERVICE_UNAVAILABLE,
- detail={
- "domain": domain,
- "message": f"lnurl JSON response invalid: {exc}",
- },
- ) from exc
-
- return params
-
-
-@api_router.post("/api/v1/lnurlauth")
-async def api_perform_lnurlauth(
- data: CreateLnurlAuth, wallet: WalletTypeInfo = Depends(require_admin_key)
-):
- err = await perform_lnurlauth(data.callback, wallet=wallet)
- if err:
- raise HTTPException(
- status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason
- )
- return ""
-
-
@api_router.get("/api/v1/currencies")
async def api_list_currencies_available() -> List[str]:
return allowed_currencies()
diff --git a/lnbits/core/views/lnurl_api.py b/lnbits/core/views/lnurl_api.py
new file mode 100644
index 000000000..ed2a13db7
--- /dev/null
+++ b/lnbits/core/views/lnurl_api.py
@@ -0,0 +1,156 @@
+import json
+from http import HTTPStatus
+
+from fastapi import (
+ APIRouter,
+ Depends,
+)
+from fastapi.exceptions import HTTPException
+from lnurl import (
+ LnurlPayActionResponse,
+ LnurlSuccessResponse,
+ execute_login,
+ execute_pay_request,
+)
+from lnurl import handle as lnurl_handle
+from lnurl.core import execute_withdraw
+from lnurl.exceptions import InvalidLnurl
+
+from lnbits.core.models import CreateLnurlAuth, CreateLnurlPay, CreateLnurlWithdraw
+from lnbits.core.services import create_invoice, pay_invoice
+from lnbits.decorators import WalletTypeInfo, require_admin_key
+from lnbits.settings import settings
+from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
+
+lnurl_router = APIRouter(tags=["LNURL"])
+
+
+@lnurl_router.get("/api/v1/lnurlscan/{code}")
+@lnurl_router.get("/lnurl/api/v1/scan/{code}")
+async def api_lnurl_scan(code: str) -> dict:
+ try:
+ handle = await lnurl_handle(code, user_agent=settings.user_agent)
+ return handle.dict()
+ except InvalidLnurl as exc:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail="Invalid LNURL",
+ ) from exc
+ except Exception as exc:
+ raise HTTPException(
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
+ detail="Error processing LNURL",
+ ) from exc
+
+
+@lnurl_router.post("/lnurl/api/v1/auth")
+async def api_lnurl_auth(
+ data: CreateLnurlAuth, key_type: WalletTypeInfo = Depends(require_admin_key)
+):
+ try:
+ res = await execute_login(data.auth_response, key_type.wallet.adminkey)
+ assert isinstance(
+ res, LnurlSuccessResponse
+ ), "unexpected response from execute_login"
+ return res
+ except Exception as exc:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail="Failed to auth, try new LNURL",
+ ) from exc
+
+
+@lnurl_router.post("/lnurl/api/v1/withdraw")
+async def api_lnurl_withdraw(
+ data: CreateLnurlWithdraw, key_type: WalletTypeInfo = Depends(require_admin_key)
+):
+ try:
+ payment_hash, payment_request = await create_invoice(
+ wallet_id=key_type.wallet.id,
+ amount=data.amount / 1000,
+ memo=data.memo or "",
+ extra={"tag": "lnurl-withdraw"},
+ )
+ res = await execute_withdraw(data.withdraw_response, payment_request)
+ assert isinstance(
+ res, LnurlSuccessResponse
+ ), "unexpected response from execute_withdraw"
+ return {"payment_hash": payment_hash}
+ except Exception as exc:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail=f"Failed to withdraw: {exc}",
+ ) from exc
+
+
+@lnurl_router.post("/api/v1/payments/lnurl")
+@lnurl_router.post("/lnurl/api/v1/pay")
+async def api_lnurl_pay(
+ data: CreateLnurlPay, key_type: WalletTypeInfo = Depends(require_admin_key)
+):
+ amount_msat = data.amount
+ if data.unit and data.unit != "sat":
+ amount_msat = await fiat_amount_as_satoshis(data.amount, data.unit)
+ # no msat precision, why?
+ amount_msat = int(amount_msat // 1000) * 1000
+
+ description = None
+ metadata = json.loads(data.pay_response.metadata)
+ for x in metadata:
+ if x[0] == "text/plain":
+ description = x[1]
+
+ if not description:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail="description required on LNURL pay_response.",
+ )
+
+ # pay
+ # params.update(
+ # description_hash=hashlib.sha256(
+ # data["metadata"].encode()
+ # ).hexdigest()
+ # )
+ # metadata = json.loads(data["metadata"])
+ # for [k, v] in metadata:
+ # if k == "text/plain":
+ # params.update(description=v)
+ # if k in ("image/jpeg;base64", "image/png;base64"):
+ # data_uri = f"data:{k},{v}"
+ # params.update(image=data_uri)
+ # if k in ("text/email", "text/identifier"):
+ # params.update(targetUser=v)
+
+ try:
+ res = await execute_pay_request(data.pay_response, str(amount_msat))
+ assert isinstance(
+ res, LnurlPayActionResponse
+ ), "unexpected response from execute_pay_request"
+ except Exception as exc:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail=f"Failed to fetch invoice: {exc}",
+ ) from exc
+
+ extra: dict = {}
+ if res.success_action:
+ extra["success_action"] = res.success_action.json()
+ if data.comment:
+ extra["comment"] = data.comment
+ if data.unit and data.unit != "sat":
+ extra["fiat_currency"] = data.unit
+ extra["fiat_amount"] = data.amount / 1000
+
+ payment_hash = await pay_invoice(
+ wallet_id=key_type.wallet.id,
+ payment_request=res.pr,
+ description=description,
+ extra=extra,
+ )
+ return {
+ "success_action": res.success_action,
+ "payment_hash": payment_hash,
+ # maintain backwards compatibility with API clients:
+ "checking_id": payment_hash,
+ }
diff --git a/lnbits/core/views/payment_api.py b/lnbits/core/views/payment_api.py
index c16f86858..e4a4dfaec 100644
--- a/lnbits/core/views/payment_api.py
+++ b/lnbits/core/views/payment_api.py
@@ -1,12 +1,8 @@
import asyncio
-import json
import uuid
from http import HTTPStatus
-from math import ceil
-from typing import List, Optional, Union
-from urllib.parse import urlparse
+from typing import List, Optional
-import httpx
from fastapi import (
APIRouter,
Body,
@@ -17,6 +13,7 @@ from fastapi import (
Request,
)
from fastapi.responses import JSONResponse
+from lnurl import decode as lnurl_decode
from loguru import logger
from sse_starlette.sse import EventSourceResponse
@@ -24,7 +21,6 @@ from lnbits import bolt11
from lnbits.core.db import db
from lnbits.core.models import (
CreateInvoice,
- CreateLnurl,
DecodePayment,
KeyType,
Payment,
@@ -37,13 +33,10 @@ from lnbits.decorators import (
WalletTypeInfo,
get_key_type,
parse_filters,
- require_admin_key,
require_invoice_key,
)
from lnbits.helpers import generate_filter_params_openapi
-from lnbits.lnurl import decode as lnurl_decode
from lnbits.settings import settings
-from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
from ..crud import (
DateTrunc,
@@ -168,36 +161,9 @@ async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
assert payment_db is not None, "payment not found"
checking_id = payment_db.checking_id
- invoice = bolt11.decode(payment_request)
-
- lnurl_response: Union[None, bool, str] = None
- if data.lnurl_callback:
- headers = {"User-Agent": settings.user_agent}
- async with httpx.AsyncClient(headers=headers) as client:
- try:
- r = await client.get(
- data.lnurl_callback,
- params={
- "pr": payment_request,
- },
- timeout=10,
- )
- if r.is_error:
- lnurl_response = r.text
- else:
- resp = json.loads(r.text)
- if resp["status"] != "OK":
- lnurl_response = resp["reason"]
- else:
- lnurl_response = True
- except (httpx.ConnectError, httpx.RequestError) as ex:
- logger.error(ex)
- lnurl_response = False
-
return {
- "payment_hash": invoice.payment_hash,
+ "payment_hash": payment_hash,
"payment_request": payment_request,
- "lnurl_response": lnurl_response,
# maintain backwards compatibility with API clients:
"checking_id": checking_id,
}
@@ -268,89 +234,10 @@ async def api_payments_fee_reserve(invoice: str = Query("invoice")) -> JSONRespo
)
-@payment_router.post("/lnurl")
-async def api_payments_pay_lnurl(
- data: CreateLnurl, wallet: WalletTypeInfo = Depends(require_admin_key)
-):
- domain = urlparse(data.callback).netloc
-
- headers = {"User-Agent": settings.user_agent}
- async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
- try:
- if data.unit and data.unit != "sat":
- amount_msat = await fiat_amount_as_satoshis(data.amount, data.unit)
- # no msat precision
- amount_msat = ceil(amount_msat // 1000) * 1000
- else:
- amount_msat = data.amount
- r = await client.get(
- data.callback,
- params={"amount": amount_msat, "comment": data.comment},
- timeout=40,
- )
- if r.is_error:
- raise httpx.ConnectError("LNURL callback connection error")
- r.raise_for_status()
- except (httpx.ConnectError, httpx.RequestError) as exc:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST,
- detail=f"Failed to connect to {domain}.",
- ) from exc
-
- params = json.loads(r.text)
- if params.get("status") == "ERROR":
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST,
- detail=f"{domain} said: '{params.get('reason', '')}'",
- )
-
- if not params.get("pr"):
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST,
- detail=f"{domain} did not return a payment request.",
- )
-
- invoice = bolt11.decode(params["pr"])
- if invoice.amount_msat != amount_msat:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST,
- detail=(
- (
- f"{domain} returned an invalid invoice. Expected"
- f" {amount_msat} msat, got {invoice.amount_msat}."
- ),
- ),
- )
-
- extra = {}
-
- if params.get("successAction"):
- extra["success_action"] = params["successAction"]
- if data.comment:
- extra["comment"] = data.comment
- if data.unit and data.unit != "sat":
- extra["fiat_currency"] = data.unit
- extra["fiat_amount"] = data.amount / 1000
- assert data.description is not None, "description is required"
- payment_hash = await pay_invoice(
- wallet_id=wallet.wallet.id,
- payment_request=params["pr"],
- description=data.description,
- extra=extra,
- )
-
- return {
- "success_action": params.get("successAction"),
- "payment_hash": payment_hash,
- # maintain backwards compatibility with API clients:
- "checking_id": payment_hash,
- }
-
-
async def subscribe_wallet_invoices(request: Request, wallet: Wallet):
"""
Subscribe to new invoices for a wallet. Can be wrapped in EventSourceResponse.
- Listenes invoming payments for a wallet and yields jsons with payment details.
+ Listenes incoming payments for a wallet and yields jsons with payment details.
"""
this_wallet_id = wallet.id
diff --git a/lnbits/static/js/base.js b/lnbits/static/js/base.js
index 363633594..a41a09224 100644
--- a/lnbits/static/js/base.js
+++ b/lnbits/static/js/base.js
@@ -22,18 +22,11 @@ window.LNbits = {
data: data
})
},
- createInvoice: async function (
- wallet,
- amount,
- memo,
- unit = 'sat',
- lnurlCallback = null
- ) {
+ createInvoice: async function (wallet, amount, memo, unit = 'sat') {
return this.request('post', '/api/v1/payments', wallet.inkey, {
out: false,
amount: amount,
memo: memo,
- lnurl_callback: lnurlCallback,
unit: unit
})
},
@@ -43,27 +36,24 @@ window.LNbits = {
bolt11: bolt11
})
},
- payLnurl: function (
- wallet,
- callback,
- description_hash,
- amount,
- description = '',
- comment = '',
- unit = ''
- ) {
- return this.request('post', '/api/v1/payments/lnurl', wallet.adminkey, {
- callback,
- description_hash,
+ payLnurl: function (wallet, pay_response, amount, comment = '', unit = '') {
+ return this.request('post', '/lnurl/api/v1/pay', wallet.adminkey, {
+ pay_response,
amount,
comment,
- description,
unit
})
},
- authLnurl: function (wallet, callback) {
- return this.request('post', '/api/v1/lnurlauth', wallet.adminkey, {
- callback
+ withdrawLnurl: function (wallet, withdraw_response, amount, memo) {
+ return this.request('post', '/lnurl/api/v1/withdraw', wallet.adminkey, {
+ withdraw_response,
+ amount,
+ memo
+ })
+ },
+ authLnurl: function (wallet, auth_response) {
+ return this.request('post', '/lnurl/api/v1/auth', wallet.adminkey, {
+ auth_response
})
},
createAccount: function (name) {
diff --git a/lnbits/static/js/wallet.js b/lnbits/static/js/wallet.js
index 38ad63aa7..7aa53481f 100644
--- a/lnbits/static/js/wallet.js
+++ b/lnbits/static/js/wallet.js
@@ -30,6 +30,7 @@ new Vue({
invoice: null,
lnurlpay: null,
lnurlauth: null,
+ lnurlwithdraw: null,
data: {
request: '',
amount: 0,
@@ -130,6 +131,12 @@ new Vue({
this.receive.paymentHash = null
}
},
+ getLnurlDescription: function (lnurl) {
+ const description = JSON.parse(lnurl.metadata).filter(
+ item => item[0] === 'text/plain'
+ )
+ return description[0][1]
+ },
createInvoice: function () {
this.receive.status = 'loading'
if (LNBITS_DENOMINATION != 'sats') {
@@ -147,30 +154,6 @@ new Vue({
this.receive.status = 'success'
this.receive.paymentReq = response.data.payment_request
this.receive.paymentHash = response.data.payment_hash
-
- if (response.data.lnurl_response !== null) {
- if (response.data.lnurl_response === false) {
- response.data.lnurl_response = `Unable to connect`
- }
-
- if (typeof response.data.lnurl_response === 'string') {
- // failure
- this.$q.notify({
- timeout: 5000,
- type: 'warning',
- message: `${this.receive.lnurl.domain} lnurl-withdraw call failed.`,
- caption: response.data.lnurl_response
- })
- return
- } else if (response.data.lnurl_response === true) {
- // success
- this.$q.notify({
- timeout: 5000,
- message: `Invoice sent to ${this.receive.lnurl.domain}!`,
- spinner: true
- })
- }
- }
})
.then(() => {
this.updatePayments = !this.updatePayments
@@ -232,11 +215,7 @@ new Vue({
this.parse.data.request.match(/[\w.+-~_]+@[\w.+-~_]/)
) {
LNbits.api
- .request(
- 'GET',
- '/api/v1/lnurlscan/' + this.parse.data.request,
- this.g.wallet.adminkey
- )
+ .request('GET', '/lnurl/api/v1/scan/' + this.parse.data.request, null)
.catch(err => {
LNbits.utils.notifyApiError(err)
})
@@ -253,28 +232,21 @@ new Vue({
return
}
- if (data.kind === 'pay') {
+ const url = new URL(data.callback)
+ this.parse.domain = url.host
+ if (data.tag === 'payRequest') {
this.parse.lnurlpay = Object.freeze(data)
this.parse.data.amount = data.minSendable / 1000
- } else if (data.kind === 'auth') {
+ } else if (data.tag === 'login') {
this.parse.lnurlauth = Object.freeze(data)
- } else if (data.kind === 'withdraw') {
- this.parse.show = false
- this.receive.show = true
- this.receive.status = 'pending'
- this.receive.paymentReq = null
- this.receive.paymentHash = null
- this.receive.data.amount = data.maxWithdrawable / 1000
- this.receive.data.memo = data.defaultDescription
- this.receive.minMax = [
+ } else if (data.tag === 'withdrawRequest') {
+ this.parse.lnurlwithdraw = Object.freeze(data)
+ this.parse.data.amount = data.maxWithdrawable / 1000
+ this.parse.data.memo = data.defaultDescription
+ this.parse.minMax = [
data.minWithdrawable / 1000,
data.maxWithdrawable / 1000
]
- this.receive.lnurl = {
- domain: data.domain,
- callback: data.callback,
- fixed: data.fixed
- }
}
})
return
@@ -364,6 +336,42 @@ new Vue({
this.parse.show = false
})
},
+ withdrawLnurl: function () {
+ let dismissPaymentMsg = this.$q.notify({
+ timeout: 0,
+ message: 'Processing withdraw...'
+ })
+ LNbits.api
+ .withdrawLnurl(
+ this.g.wallet,
+ this.parse.lnurlwithdraw,
+ this.parse.data.amount * 1000,
+ this.parse.data.memo
+ )
+ .then(response => {
+ this.parse.show = false
+ this.parse.lnurlwithdraw = null
+ clearInterval(this.parse.paymentChecker)
+ setTimeout(() => {
+ clearInterval(this.parse.paymentChecker)
+ }, 40000)
+ this.parse.paymentChecker = setInterval(() => {
+ LNbits.api
+ .getPayment(this.g.wallet, response.data.payment_hash)
+ .then(res => {
+ if (res.data.paid) {
+ dismissPaymentMsg()
+ clearInterval(this.parse.paymentChecker)
+ this.updatePayments = !this.updatePayments
+ }
+ })
+ }, 2000)
+ })
+ .catch(err => {
+ dismissPaymentMsg()
+ LNbits.utils.notifyApiError(err)
+ })
+ },
payLnurl: function () {
let dismissPaymentMsg = this.$q.notify({
timeout: 0,
@@ -373,10 +381,8 @@ new Vue({
LNbits.api
.payLnurl(
this.g.wallet,
- this.parse.lnurlpay.callback,
- this.parse.lnurlpay.description_hash,
+ this.parse.lnurlpay,
this.parse.data.amount * 1000,
- this.parse.lnurlpay.description.slice(0, 120),
this.parse.data.comment,
this.parse.data.unit
)
@@ -453,7 +459,7 @@ new Vue({
})
LNbits.api
- .authLnurl(this.g.wallet, this.parse.lnurlauth.callback)
+ .authLnurl(this.g.wallet, this.parse.lnurlauth)
.then(_ => {
dismissAuthMsg()
this.$q.notify({
diff --git a/poetry.lock b/poetry.lock
index 8f78ae336..f42276c19 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "anyio"
@@ -767,13 +767,13 @@ wmi = ["wmi (>=1.5.1)"]
[[package]]
name = "ecdsa"
-version = "0.18.0"
+version = "0.19.0"
description = "ECDSA cryptographic signature library (pure python)"
optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.6"
files = [
- {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"},
- {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"},
+ {file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"},
+ {file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"},
]
[package.dependencies]
@@ -987,39 +987,40 @@ cryptography = ">=2.5"
[[package]]
name = "httpcore"
-version = "0.18.0"
+version = "1.0.5"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
files = [
- {file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"},
- {file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"},
+ {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
+ {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"},
]
[package.dependencies]
-anyio = ">=3.0,<5.0"
certifi = "*"
h11 = ">=0.13,<0.15"
-sniffio = "==1.*"
[package.extras]
+asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
+trio = ["trio (>=0.22.0,<0.26.0)"]
[[package]]
name = "httpx"
-version = "0.25.0"
+version = "0.27.0"
description = "The next generation HTTP client."
optional = false
python-versions = ">=3.8"
files = [
- {file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"},
- {file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"},
+ {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
+ {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
]
[package.dependencies]
+anyio = "*"
certifi = "*"
-httpcore = ">=0.18.0,<0.19.0"
+httpcore = "==1.*"
idna = "*"
sniffio = "*"
@@ -1253,19 +1254,21 @@ rediscluster = ["redis (>=4.2.0,!=4.5.2,!=4.5.3)"]
[[package]]
name = "lnurl"
-version = "0.4.2"
+version = "0.5.1"
description = "LNURL implementation for Python."
optional = false
-python-versions = ">=3.9,<4.0"
+python-versions = "<4.0,>=3.9"
files = [
- {file = "lnurl-0.4.2-py3-none-any.whl", hash = "sha256:93f79ae7e0b0c66fed5b29ac1520e85e3e2c8648561a4b42974f0b7bffd34d84"},
- {file = "lnurl-0.4.2.tar.gz", hash = "sha256:c5e708b255d5333a0c08ceffe90ae4be6d2d09eb51dc8c35d19d8aa4cb21842a"},
+ {file = "lnurl-0.5.1-py3-none-any.whl", hash = "sha256:41a03eac08c32b9ee2c6d83b9f1e88bcc5b393b36d82a41d73a02180fa04f249"},
+ {file = "lnurl-0.5.1.tar.gz", hash = "sha256:a099899e622b23e6197c3f2e3ba38499658f1e6e9e2c456d55caa57373ca925b"},
]
[package.dependencies]
bech32 = ">=1.2.0,<2.0.0"
+bolt11 = ">=2.0.5,<3.0.0"
+ecdsa = ">=0.19.0,<0.20.0"
+httpx = ">=0.27.0,<0.28.0"
pydantic = ">=1,<2"
-requests = ">=2.31.0,<3.0.0"
[[package]]
name = "loguru"
@@ -3054,4 +3057,4 @@ liquid = ["wallycore"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10 | ^3.9"
-content-hash = "33f9d6ee851ae77b6e02cc8964d1a6ea233ba3ff4cfaeeb082c327654c9cd7e0"
+content-hash = "4e316d3fcc22d41ada4de66e57049011312664f6b71853b0be441e289a402595"
diff --git a/pyproject.toml b/pyproject.toml
index a9e016360..d0e0392d8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,11 +15,10 @@ packages = [
python = "^3.10 | ^3.9"
bech32 = "1.2.0"
click = "8.1.7"
-ecdsa = "0.18.0"
fastapi = "0.109.2"
-httpx = "0.25.0"
+httpx = "0.27.0"
jinja2 = "3.1.4"
-lnurl = "0.4.2"
+lnurl = "0.5.1"
psycopg2-binary = "2.9.7"
pydantic = "1.10.17"
pyqrcode = "1.2.1"
@@ -132,7 +131,6 @@ module = [
"lnurl.*",
"bolt11.*",
"bitstring.*",
- "ecdsa.*",
"psycopg2.*",
"pyngrok.*",
"pyln.client.*",