diff --git a/poetry.lock b/poetry.lock index 8a25f95..c39ad76 100644 --- a/poetry.lock +++ b/poetry.lock @@ -111,6 +111,7 @@ files = [ [package.dependencies] aiohappyeyeballs = ">=2.5.0" aiosignal = ">=1.4.0" +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -168,6 +169,7 @@ files = [ ] [package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} @@ -189,6 +191,19 @@ files = [ {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, ] +[[package]] +name = "async-timeout" +version = "5.0.1" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version == \"3.10\"" +files = [ + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, +] + [[package]] name = "asyncpg" version = "0.30.0" @@ -248,6 +263,9 @@ files = [ {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, ] +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.11.0\""} + [package.extras] docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] gssauth = ["gssapi ; platform_system != \"Windows\"", "sspilib ; platform_system == \"Windows\""] @@ -580,6 +598,8 @@ mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -1077,12 +1097,32 @@ files = [ [package.dependencies] marshmallow = ">=3.18.0" python-dotenv = "*" +typing-extensions = {version = "*", markers = "python_version < \"3.11\""} [package.extras] dev = ["environs[tests]", "pre-commit (>=4.0,<5.0)", "tox"] django = ["dj-database-url", "dj-email-url", "django-cache-url"] tests = ["backports.strenum ; python_version < \"3.11\"", "environs[django]", "packaging", "pytest"] +[[package]] +name = "exceptiongroup" +version = "1.3.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "fastapi" version = "0.116.1" @@ -1615,14 +1655,14 @@ valkey = ["valkey (>=6)"] [[package]] name = "lnbits" -version = "1.3.0rc3" +version = "1.3.0rc4" description = "LNbits, free and open-source Lightning wallet and accounts system." optional = false python-versions = "<3.13,>=3.10" groups = ["main"] files = [ - {file = "lnbits-1.3.0rc3-py3-none-any.whl", hash = "sha256:d3fc4f643314011a1b1050f84e16c540369d0ad2f3b290c72a617da479ed75c0"}, - {file = "lnbits-1.3.0rc3.tar.gz", hash = "sha256:dcc3e58062353e2012b9535446f23ac7ea012e5c12813220cb6ac38a7b7a8eb3"}, + {file = "lnbits-1.3.0rc4-py3-none-any.whl", hash = "sha256:eddff47480fa3160e0bd78bde28a73d216bebc741eed98cb054ba42fe4a068a8"}, + {file = "lnbits-1.3.0rc4.tar.gz", hash = "sha256:9f9d32f8c717f4b151ab4f54776d314e07b409503a738b2fd6a835cd5a89237c"}, ] [package.dependencies] @@ -1643,7 +1683,7 @@ httpx = "0.27.0" itsdangerous = "2.2.0" jinja2 = "3.1.6" jsonpath-ng = ">=1.7.0,<2.0.0" -lnurl = "0.7.2" +lnurl = "0.7.3" loguru = "0.7.3" nostr-sdk = ">=0.42.1,<0.43.0" packaging = "25.0" @@ -1676,14 +1716,14 @@ migration = ["psycopg2-binary (==2.9.10)"] [[package]] name = "lnurl" -version = "0.7.2" +version = "0.7.3" description = "LNURL implementation for Python." optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "lnurl-0.7.2-py3-none-any.whl", hash = "sha256:4323ac398d49e5b883000c166b4c2448df2acad2eddca99f656c67f7b97cd80f"}, - {file = "lnurl-0.7.2.tar.gz", hash = "sha256:9f0881cb5909512cadb35d238c27347d6a8afcec10b2b7aadd560083fd210c0b"}, + {file = "lnurl-0.7.3-py3-none-any.whl", hash = "sha256:1a6796cb5124047e2d289ca8ba2015bbf62e3f78d717802205dc6e303e31898a"}, + {file = "lnurl-0.7.3.tar.gz", hash = "sha256:b8e8036bc1d215d1d9b0cbb18b8d9b8d2e7e98850be1c98d33887e03c9f4b144"}, ] [package.dependencies] @@ -1962,6 +2002,9 @@ files = [ {file = "multidict-6.6.3.tar.gz", hash = "sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + [[package]] name = "mypy" version = "1.15.0" @@ -2006,6 +2049,7 @@ files = [ [package.dependencies] mypy_extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing_extensions = ">=4.6.0" [package.extras] @@ -2588,9 +2632,11 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -2798,6 +2844,7 @@ files = [ [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -3060,6 +3107,49 @@ files = [ {file = "tlv8-0.10.0.tar.gz", hash = "sha256:7930a590267b809952272ac2a27ee81b99ec5191fa2eba08050e0daee4262684"}, ] +[[package]] +name = "tomli" +version = "2.2.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, +] + [[package]] name = "tornado" version = "6.4.2" @@ -3171,6 +3261,7 @@ files = [ [package.dependencies] click = ">=7.0" h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] @@ -3570,5 +3661,5 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" -python-versions = "~3.12 | ~3.11" -content-hash = "75afffd289b93f5d7faef35f9d6fdbfd84772dddba9e02ae4ee76cef4c429a0b" +python-versions = "~3.12 | ~3.11 | ~3.10" +content-hash = "c0ca281d7ccef9d43c50c20ae2bb0f627389b4901fa26fc021ef65a9c5ef64c6" diff --git a/pyproject.toml b/pyproject.toml index 30d879f..10261cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Alan Bits "] package-mode = false [tool.poetry.dependencies] -python = "~3.12 | ~3.11" +python = "~3.12 | ~3.11 | ~3.10" lnbits = {version = "*", allow-prereleases = true} [tool.poetry.group.dev.dependencies] diff --git a/static/js/index.js b/static/js/index.js index e6a02f8..7d847a7 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -11,6 +11,11 @@ const mapPayLink = obj => { obj._data = _.clone(obj) obj.created_at = LNbits.utils.formatDateString(obj.created_at) obj.updated_at = LNbits.utils.formatDateString(obj.updated_at) + if (obj.currency) { + obj.min = obj.min / obj.fiat_base_multiplier + obj.max = obj.max / obj.fiat_base_multiplier + } + obj.print_url = [locationPath, 'print/', obj.id].join('') obj.pay_url = [locationPath, 'link/', obj.id].join('') return obj @@ -119,6 +124,7 @@ window.app = Vue.createApp({ ) .then(response => { this.payLinks = response.data.map(mapPayLink) + console.log('Pay links:', this.payLinks) }) .catch(err => { LNbits.utils.notifyApiError(err) @@ -161,7 +167,7 @@ window.app = Vue.createApp({ const link = _.findWhere(this.payLinks, {id: linkId}) if (link.currency) this.updateFiatRate(link.currency) - this.formDialog.data = _.clone(link._data) + this.formDialog.data = {...link} this.formDialog.show = true this.formDialog.fixedAmount = this.formDialog.data.min === this.formDialog.data.max @@ -241,9 +247,7 @@ window.app = Vue.createApp({ LNbits.api .request('GET', '/api/v1/rate/' + currency, null) .then(response => { - let rates = _.clone(this.fiatRates) - rates[currency] = response.data.rate - this.fiatRates = rates + this.fiatRates[currency] = response.data.rate }) .catch(err => { LNbits.utils.notifyApiError(err) diff --git a/templates/lnurlp/index.html b/templates/lnurlp/index.html index aa06bf4..6a56ae6 100644 --- a/templates/lnurlp/index.html +++ b/templates/lnurlp/index.html @@ -196,6 +196,7 @@ type="number" :step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'" :label="formDialog.fixedAmount ? 'Amount *' : 'Min *'" + :hint="formDialog.data.currency && fiatRates[formDialog.data.currency] && formDialog.data.min ? `approx. ${parseInt(Math.round(formDialog.data.min * fiatRates[formDialog.data.currency]))} sat` : ''" > - + :hint="formDialog.data.currency && fiatRates[formDialog.data.currency] && formDialog.data.max ? `approx. ${parseInt(Math.round(formDialog.data.max * fiatRates[formDialog.data.currency]))} sat` : ''" + >
diff --git a/views_api.py b/views_api.py index 56d496e..d87818d 100644 --- a/views_api.py +++ b/views_api.py @@ -87,12 +87,11 @@ async def api_link_create_or_update( detail="Min is greater than max.", status_code=HTTPStatus.BAD_REQUEST ) - if data.currency is None and ( - round(data.min) != data.min or round(data.max) != data.max or data.min < 1 - ): - raise HTTPException( - detail="Must use full satoshis.", status_code=HTTPStatus.BAD_REQUEST - ) + if not data.currency: + if round(data.min) != data.min or round(data.max) != data.max or data.min < 1: + raise HTTPException( + detail="Must use full satoshis.", status_code=HTTPStatus.BAD_REQUEST + ) if data.webhook_headers: try: diff --git a/views_lnurl.py b/views_lnurl.py index 82f470f..40803ef 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -52,7 +52,9 @@ async def api_lnurl_callback( maximum = link.max rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1 - if link.currency: + if link.currency and link.fiat_base_multiplier: + link.min = link.min / link.fiat_base_multiplier + link.max = link.max / link.fiat_base_multiplier # allow some fluctuation (as the fiat price may have changed between the calls) minimum = rate * 995 * link.min maximum = rate * 1010 * link.max @@ -160,6 +162,11 @@ async def api_lnurl_response( await update_pay_link(link) rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1 + + if link.currency and link.fiat_base_multiplier: + link.min = link.min / link.fiat_base_multiplier + link.max = link.max / link.fiat_base_multiplier + url = request.url_for("lnurlp.api_lnurl_callback", link_id=link.id) if webhook_data: url = url.include_query_params(webhook_data=webhook_data)