Paid extensions (#2229)

* fix: download archive file `async`

* feat: add `pay_link` property

* feat: basic install using internal wallet for payment

* fix: pop-up issues

* chore: refactor

* feat: detect paid extensions

* fix: payment check

* feat: small stuff

* feat: show external invoice

* fix: regression for extension install

* feat: store previos successful payments

* refactor: simplify, almost works

* chore: gugu gaga

* fix: pay and install

* fix: do not pay invoice on the back-end

* chore: code clean-up

* feat: basic websocker listener

* feat: use websocket to watch for invoice payment

* feat: remember hanging invoices

* refactor: extract `localStorage` methods

* chore: code format

* chore: code clean-up after test

* feat: remember previous payment_hashes

* chore: code format

* refactor: rename `ExtensionPaymentInfo` to `ReleasePaymentInfo`

* refactor: method rename

* fix: release version matters now

* chore: code format

* refactor: method rename

* refactor: extract method `_restore_payment_info`

* refactor: extract method

* chore: rollback `CACHE_VERSION`

* chore: code format

* feat: i18n

* chore: update bundle

* refactor: public method name

* chore: code format

* fix: websocket connection

* Update installation.md (#2259)

* Update installation.md (#2260)

* fix: try to fix `openapi` error

* chore: bundle

* chore:bundle

---------

Co-authored-by: benarc <ben@arc.wales>
Co-authored-by: Arc <33088785+arcbtc@users.noreply.github.com>
This commit is contained in:
Vlad Stan
2024-02-22 13:16:41 +00:00
committed by GitHub
co-authored by benarc Arc
parent 54c6faa4b6
commit d6c8ad1d0d
10 changed files with 1102 additions and 333 deletions
+54 -4
View File
@@ -62,6 +62,7 @@ from lnbits.extension_manager import (
ExtensionRelease,
InstallableExtension,
fetch_github_release_config,
fetch_release_payment_info,
get_valid_extensions,
)
from lnbits.helpers import generate_filter_params_openapi, url_for
@@ -83,6 +84,7 @@ from ..crud import (
delete_wallet,
drop_extension_db,
get_dbversions,
get_installed_extension,
get_payments,
get_payments_history,
get_payments_paginated,
@@ -796,7 +798,7 @@ async def api_install_extension(
access_token: Optional[str] = Depends(check_access_token),
):
release = await InstallableExtension.get_extension_release(
data.ext_id, data.source_repo, data.archive
data.ext_id, data.source_repo, data.archive, data.version
)
if not release:
raise HTTPException(
@@ -808,13 +810,17 @@ async def api_install_extension(
status_code=HTTPStatus.BAD_REQUEST, detail="Incompatible extension version"
)
release.payment_hash = data.payment_hash
ext_info = InstallableExtension(
id=data.ext_id, name=data.ext_id, installed_release=release, icon=release.icon
)
ext_info.download_archive()
try:
installed_ext = await get_installed_extension(data.ext_id)
ext_info.payments = installed_ext.payments if installed_ext else []
await ext_info.download_archive()
ext_info.extract_archive()
extension = Extension.from_installable_ext(ext_info)
@@ -838,7 +844,8 @@ async def api_install_extension(
ext_info.nofiy_upgrade()
return extension
except AssertionError as e:
raise HTTPException(HTTPStatus.BAD_REQUEST, str(e))
except Exception as ex:
logger.warning(ex)
ext_info.clean_extension_files()
@@ -907,6 +914,15 @@ async def get_extension_releases(ext_id: str):
ExtensionRelease
] = await InstallableExtension.get_extension_releases(ext_id)
installed_ext = await get_installed_extension(ext_id)
if not installed_ext:
return extension_releases
for release in extension_releases:
payment_info = installed_ext.find_existing_payment(release.pay_link)
if payment_info:
release.paid_sats = payment_info.amount
return extension_releases
except Exception as ex:
@@ -915,6 +931,40 @@ async def get_extension_releases(ext_id: str):
)
@api_router.put("/api/v1/extension/invoice", dependencies=[Depends(check_admin)])
async def get_extension_invoice(data: CreateExtension):
try:
assert data.cost_sats, "A non-zero amount must be specified"
release = await InstallableExtension.get_extension_release(
data.ext_id, data.source_repo, data.archive, data.version
)
assert release, "Release not found"
assert release.pay_link, "Pay link not found for release"
payment_info = await fetch_release_payment_info(
release.pay_link, data.cost_sats
)
assert payment_info and payment_info.payment_request, "Cannot request invoice"
invoice = bolt11.decode(payment_info.payment_request)
assert invoice.amount_msat is not None, "Invoic amount is missing"
invoice_amount = int(invoice.amount_msat / 1000)
assert (
invoice_amount == data.cost_sats
), f"Wrong invoice amount: {invoice_amount}."
assert (
payment_info.payment_hash == invoice.payment_hash
), "Wroong invoice payment hash"
return payment_info
except AssertionError as e:
raise HTTPException(HTTPStatus.BAD_REQUEST, str(e))
except Exception as ex:
logger.warning(ex)
raise HTTPException(HTTPStatus.INTERNAL_SERVER_ERROR, "Cannot request invoice")
@api_router.get(
"/api/v1/extension/release/{org}/{repo}/{tag_name}",
dependencies=[Depends(check_admin)],