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:
@@ -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)],
|
||||
|
||||
Reference in New Issue
Block a user