diff --git a/lnbits/extensions/withdraw/README.md b/lnbits/extensions/withdraw/README.md
deleted file mode 100644
index fce2c6e5..00000000
--- a/lnbits/extensions/withdraw/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# LNURLw
-
-## Create a static QR code people can use to withdraw funds from a Lightning Network wallet
-
-LNURL is a range of lightning-network standards that allow us to use lightning-network differently. An LNURL withdraw is the permission for someone to pull a certain amount of funds from a lightning wallet.
-
-The most common use case for an LNURL withdraw is a faucet, although it is a very powerful technology, with much further reaching implications. For example, an LNURL withdraw could be minted to pay for a subscription service. Or you can have a LNURLw as an offline Lightning wallet (a pre paid "card"), you use to pay for something without having to even reach your smartphone.
-
-LNURL withdraw is a **very powerful tool** and should not have his use limited to just faucet applications. With LNURL withdraw, you have the ability to give someone the right to spend a range, once or multiple times. **This functionality has not existed in money before**.
-
-[**Wallets supporting LNURL**](https://github.com/fiatjaf/awesome-lnurl#wallets)
-
-## Usage
-
-#### Quick Vouchers
-
-LNbits Quick Vouchers allows you to easily create a batch of LNURLw's QR codes that you can print and distribute as rewards, onboarding people into Lightning Network, gifts, etc...
-
-1. Create Quick Vouchers\
- 
- - select wallet
- - set the amount each voucher will allow someone to withdraw
- - set the amount of vouchers you want to create - _have in mind you need to have a balance on the wallet that supports the amount \* number of vouchers_
-2. You can now print, share, display your LNURLw links or QR codes\
- 
- - on details you can print the vouchers\
- 
- - every printed LNURLw QR code is unique, it can only be used once
-3. Bonus: you can use an LNbits themed voucher, or use a custom one. There's a _template.svg_ file in `static/images` folder if you want to create your own.\
- 
-
-#### Advanced
-
-1. Create the Advanced LNURLw\
- 
- - set the wallet
- - set a title for the LNURLw (it will show up in users wallet)
- - define the minimum and maximum a user can withdraw, if you want a fixed amount set them both to an equal value
- - set how many times can the LNURLw be scanned, if it's a one time use or it can be scanned 100 times
- - LNbits has the "_Time between withdraws_" setting, you can define how long the LNURLw will be unavailable between scans
- - you can set the time in _seconds, minutes or hours_
- - the "_Use unique withdraw QR..._" reduces the chance of your LNURL withdraw being exploited and depleted by one person, by generating a new QR code every time it's scanned
-2. Print, share or display your LNURLw link or it's QR code\
- 
-
-**LNbits bonus:** If a user doesn't have a Lightning Network wallet and scans the LNURLw QR code with their smartphone camera, or a QR scanner app, they can follow the link provided to claim their satoshis and get an instant LNbits wallet!
-
-
diff --git a/lnbits/extensions/withdraw/__init__.py b/lnbits/extensions/withdraw/__init__.py
deleted file mode 100644
index cb5eb9c4..00000000
--- a/lnbits/extensions/withdraw/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from fastapi import APIRouter
-from fastapi.staticfiles import StaticFiles
-
-from lnbits.db import Database
-from lnbits.helpers import template_renderer
-
-db = Database("ext_withdraw")
-
-withdraw_static_files = [
- {
- "path": "/withdraw/static",
- "app": StaticFiles(packages=[("lnbits", "extensions/withdraw/static")]),
- "name": "withdraw_static",
- }
-]
-
-
-withdraw_ext: APIRouter = APIRouter(prefix="/withdraw", tags=["withdraw"])
-
-
-def withdraw_renderer():
- return template_renderer(["lnbits/extensions/withdraw/templates"])
-
-
-from .lnurl import * # noqa: F401,F403
-from .views import * # noqa: F401,F403
-from .views_api import * # noqa: F401,F403
diff --git a/lnbits/extensions/withdraw/config.json b/lnbits/extensions/withdraw/config.json
deleted file mode 100644
index c22d69c8..00000000
--- a/lnbits/extensions/withdraw/config.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "LNURLw",
- "short_description": "Make LNURL withdraw links",
- "tile": "/withdraw/static/image/lnurl-withdraw.png",
- "contributors": ["arcbtc", "eillarra"]
-}
diff --git a/lnbits/extensions/withdraw/crud.py b/lnbits/extensions/withdraw/crud.py
deleted file mode 100644
index 83dd0593..00000000
--- a/lnbits/extensions/withdraw/crud.py
+++ /dev/null
@@ -1,173 +0,0 @@
-from datetime import datetime
-from typing import List, Optional, Union
-
-import shortuuid
-
-from lnbits.helpers import urlsafe_short_hash
-
-from . import db
-from .models import CreateWithdrawData, HashCheck, WithdrawLink
-
-
-async def create_withdraw_link(
- data: CreateWithdrawData, wallet_id: str
-) -> WithdrawLink:
- link_id = urlsafe_short_hash()[:6]
- available_links = ",".join([str(i) for i in range(data.uses)])
- await db.execute(
- """
- INSERT INTO withdraw.withdraw_link (
- id,
- wallet,
- title,
- min_withdrawable,
- max_withdrawable,
- uses,
- wait_time,
- is_unique,
- unique_hash,
- k1,
- open_time,
- usescsv,
- webhook_url,
- webhook_headers,
- webhook_body,
- custom_url
- )
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- """,
- (
- link_id,
- wallet_id,
- data.title,
- data.min_withdrawable,
- data.max_withdrawable,
- data.uses,
- data.wait_time,
- int(data.is_unique),
- urlsafe_short_hash(),
- urlsafe_short_hash(),
- int(datetime.now().timestamp()) + data.wait_time,
- available_links,
- data.webhook_url,
- data.webhook_headers,
- data.webhook_body,
- data.custom_url,
- ),
- )
- link = await get_withdraw_link(link_id, 0)
- assert link, "Newly created link couldn't be retrieved"
- return link
-
-
-async def get_withdraw_link(link_id: str, num=0) -> Optional[WithdrawLink]:
- row = await db.fetchone(
- "SELECT * FROM withdraw.withdraw_link WHERE id = ?", (link_id,)
- )
- if not row:
- return None
-
- link = dict(**row)
- link["number"] = num
-
- return WithdrawLink.parse_obj(link)
-
-
-async def get_withdraw_link_by_hash(unique_hash: str, num=0) -> Optional[WithdrawLink]:
- row = await db.fetchone(
- "SELECT * FROM withdraw.withdraw_link WHERE unique_hash = ?", (unique_hash,)
- )
- if not row:
- return None
-
- link = dict(**row)
- link["number"] = num
-
- return WithdrawLink.parse_obj(link)
-
-
-async def get_withdraw_links(wallet_ids: Union[str, List[str]]) -> List[WithdrawLink]:
- if isinstance(wallet_ids, str):
- wallet_ids = [wallet_ids]
-
- q = ",".join(["?"] * len(wallet_ids))
- rows = await db.fetchall(
- f"SELECT * FROM withdraw.withdraw_link WHERE wallet IN ({q})", (*wallet_ids,)
- )
- return [WithdrawLink(**row) for row in rows]
-
-
-async def remove_unique_withdraw_link(link: WithdrawLink, unique_hash: str) -> None:
- unique_links = [
- x.strip()
- for x in link.usescsv.split(",")
- if unique_hash != shortuuid.uuid(name=link.id + link.unique_hash + x.strip())
- ]
- await update_withdraw_link(
- link.id,
- usescsv=",".join(unique_links),
- )
-
-
-async def increment_withdraw_link(link: WithdrawLink) -> None:
- await update_withdraw_link(
- link.id,
- used=link.used + 1,
- open_time=link.wait_time + int(datetime.now().timestamp()),
- )
-
-
-async def update_withdraw_link(link_id: str, **kwargs) -> Optional[WithdrawLink]:
- if "is_unique" in kwargs:
- kwargs["is_unique"] = int(kwargs["is_unique"])
- q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
- await db.execute(
- f"UPDATE withdraw.withdraw_link SET {q} WHERE id = ?",
- (*kwargs.values(), link_id),
- )
- row = await db.fetchone(
- "SELECT * FROM withdraw.withdraw_link WHERE id = ?", (link_id,)
- )
- return WithdrawLink(**row) if row else None
-
-
-async def delete_withdraw_link(link_id: str) -> None:
- await db.execute("DELETE FROM withdraw.withdraw_link WHERE id = ?", (link_id,))
-
-
-def chunks(lst, n):
- for i in range(0, len(lst), n):
- yield lst[i : i + n]
-
-
-async def create_hash_check(the_hash: str, lnurl_id: str) -> HashCheck:
- await db.execute(
- """
- INSERT INTO withdraw.hash_check (
- id,
- lnurl_id
- )
- VALUES (?, ?)
- """,
- (the_hash, lnurl_id),
- )
- hashCheck = await get_hash_check(the_hash, lnurl_id)
- return hashCheck
-
-
-async def get_hash_check(the_hash: str, lnurl_id: str) -> HashCheck:
- rowid = await db.fetchone(
- "SELECT * FROM withdraw.hash_check WHERE id = ?", (the_hash,)
- )
- rowlnurl = await db.fetchone(
- "SELECT * FROM withdraw.hash_check WHERE lnurl_id = ?", (lnurl_id,)
- )
- if not rowlnurl:
- await create_hash_check(the_hash, lnurl_id)
- return HashCheck(lnurl=True, hash=False)
- else:
- if not rowid:
- await create_hash_check(the_hash, lnurl_id)
- return HashCheck(lnurl=True, hash=False)
- else:
- return HashCheck(lnurl=True, hash=True)
diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py
deleted file mode 100644
index 5ef521fa..00000000
--- a/lnbits/extensions/withdraw/lnurl.py
+++ /dev/null
@@ -1,200 +0,0 @@
-import json
-from datetime import datetime
-from http import HTTPStatus
-
-import httpx
-import shortuuid
-from fastapi import HTTPException, Query, Request, Response
-from loguru import logger
-
-from lnbits.core.crud import update_payment_extra
-from lnbits.core.services import pay_invoice
-
-from . import withdraw_ext
-from .crud import (
- get_withdraw_link_by_hash,
- increment_withdraw_link,
- remove_unique_withdraw_link,
-)
-from .models import WithdrawLink
-
-
-@withdraw_ext.get(
- "/api/v1/lnurl/{unique_hash}",
- response_class=Response,
- name="withdraw.api_lnurl_response",
-)
-async def api_lnurl_response(request: Request, unique_hash):
- link = await get_withdraw_link_by_hash(unique_hash)
-
- if not link:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
- )
-
- if link.is_spent:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent."
- )
- url = request.url_for("withdraw.api_lnurl_callback", unique_hash=link.unique_hash)
- withdrawResponse = {
- "tag": "withdrawRequest",
- "callback": url,
- "k1": link.k1,
- "minWithdrawable": link.min_withdrawable * 1000,
- "maxWithdrawable": link.max_withdrawable * 1000,
- "defaultDescription": link.title,
- "webhook_url": link.webhook_url,
- "webhook_headers": link.webhook_headers,
- "webhook_body": link.webhook_body,
- }
-
- return json.dumps(withdrawResponse)
-
-
-@withdraw_ext.get(
- "/api/v1/lnurl/cb/{unique_hash}",
- name="withdraw.api_lnurl_callback",
- summary="lnurl withdraw callback",
- description="""
- This endpoints allows you to put unique_hash, k1
- and a payment_request to get your payment_request paid.
- """,
- response_description="JSON with status",
- responses={
- 200: {"description": "status: OK"},
- 400: {"description": "k1 is wrong or link open time or withdraw not working."},
- 404: {"description": "withdraw link not found."},
- 405: {"description": "withdraw link is spent."},
- },
-)
-async def api_lnurl_callback(
- unique_hash,
- k1: str = Query(...),
- pr: str = Query(...),
- id_unique_hash=None,
-):
- link = await get_withdraw_link_by_hash(unique_hash)
- now = int(datetime.now().timestamp())
- if not link:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="withdraw not found."
- )
-
- if link.is_spent:
- raise HTTPException(
- status_code=HTTPStatus.METHOD_NOT_ALLOWED, detail="withdraw is spent."
- )
-
- if link.k1 != k1:
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="k1 is wrong.")
-
- if now < link.open_time:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST,
- detail=f"wait link open_time {link.open_time - now} seconds.",
- )
-
- if id_unique_hash:
- if check_unique_link(link, id_unique_hash):
- await remove_unique_withdraw_link(link, id_unique_hash)
- else:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="withdraw not found."
- )
-
- try:
- payment_hash = await pay_invoice(
- wallet_id=link.wallet,
- payment_request=pr,
- max_sat=link.max_withdrawable,
- extra={"tag": "withdraw"},
- )
- await increment_withdraw_link(link)
- if link.webhook_url:
- await dispatch_webhook(link, payment_hash, pr)
- return {"status": "OK"}
- except Exception as e:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST, detail=f"withdraw not working. {str(e)}"
- )
-
-
-def check_unique_link(link: WithdrawLink, unique_hash: str) -> bool:
- return any(
- unique_hash == shortuuid.uuid(name=link.id + link.unique_hash + x.strip())
- for x in link.usescsv.split(",")
- )
-
-
-async def dispatch_webhook(
- link: WithdrawLink, payment_hash: str, payment_request: str
-) -> None:
- async with httpx.AsyncClient() as client:
- try:
- r: httpx.Response = await client.post(
- link.webhook_url,
- json={
- "payment_hash": payment_hash,
- "payment_request": payment_request,
- "lnurlw": link.id,
- "body": json.loads(link.webhook_body) if link.webhook_body else "",
- },
- headers=json.loads(link.webhook_headers)
- if link.webhook_headers
- else None,
- timeout=40,
- )
- await update_payment_extra(
- payment_hash=payment_hash,
- extra={
- "wh_success": r.is_success,
- "wh_message": r.reason_phrase,
- "wh_response": r.text,
- },
- outgoing=True,
- )
- except Exception as exc:
- # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid
- logger.error("Caught exception when dispatching webhook url: " + str(exc))
- await update_payment_extra(
- payment_hash=payment_hash,
- extra={"wh_success": False, "wh_message": str(exc)},
- outgoing=True,
- )
-
-
-# FOR LNURLs WHICH ARE UNIQUE
-@withdraw_ext.get(
- "/api/v1/lnurl/{unique_hash}/{id_unique_hash}",
- response_class=Response,
- name="withdraw.api_lnurl_multi_response",
-)
-async def api_lnurl_multi_response(request: Request, unique_hash, id_unique_hash):
- link = await get_withdraw_link_by_hash(unique_hash)
-
- if not link:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found."
- )
-
- if link.is_spent:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent."
- )
-
- if not check_unique_link(link, id_unique_hash):
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found."
- )
-
- url = request.url_for("withdraw.api_lnurl_callback", unique_hash=link.unique_hash)
- withdrawResponse = {
- "tag": "withdrawRequest",
- "callback": url + "?id_unique_hash=" + id_unique_hash,
- "k1": link.k1,
- "minWithdrawable": link.min_withdrawable * 1000,
- "maxWithdrawable": link.max_withdrawable * 1000,
- "defaultDescription": link.title,
- }
- return json.dumps(withdrawResponse)
diff --git a/lnbits/extensions/withdraw/migrations.py b/lnbits/extensions/withdraw/migrations.py
deleted file mode 100644
index 95805ae7..00000000
--- a/lnbits/extensions/withdraw/migrations.py
+++ /dev/null
@@ -1,134 +0,0 @@
-async def m001_initial(db):
- """
- Creates an improved withdraw table and migrates the existing data.
- """
- await db.execute(
- f"""
- CREATE TABLE withdraw.withdraw_links (
- id TEXT PRIMARY KEY,
- wallet TEXT,
- title TEXT,
- min_withdrawable {db.big_int} DEFAULT 1,
- max_withdrawable {db.big_int} DEFAULT 1,
- uses INTEGER DEFAULT 1,
- wait_time INTEGER,
- is_unique INTEGER DEFAULT 0,
- unique_hash TEXT UNIQUE,
- k1 TEXT,
- open_time INTEGER,
- used INTEGER DEFAULT 0,
- usescsv TEXT
- );
- """
- )
-
-
-async def m002_change_withdraw_table(db):
- """
- Creates an improved withdraw table and migrates the existing data.
- """
- await db.execute(
- f"""
- CREATE TABLE withdraw.withdraw_link (
- id TEXT PRIMARY KEY,
- wallet TEXT,
- title TEXT,
- min_withdrawable {db.big_int} DEFAULT 1,
- max_withdrawable {db.big_int} DEFAULT 1,
- uses INTEGER DEFAULT 1,
- wait_time INTEGER,
- is_unique INTEGER DEFAULT 0,
- unique_hash TEXT UNIQUE,
- k1 TEXT,
- open_time INTEGER,
- used INTEGER DEFAULT 0,
- usescsv TEXT
- );
- """
- )
-
- for row in [
- list(row) for row in await db.fetchall("SELECT * FROM withdraw.withdraw_links")
- ]:
- usescsv = ""
-
- for i in range(row[5]):
- if row[7]:
- usescsv += "," + str(i + 1)
- else:
- usescsv += "," + str(1)
- usescsv = usescsv[1:]
- await db.execute(
- """
- INSERT INTO withdraw.withdraw_link (
- id,
- wallet,
- title,
- min_withdrawable,
- max_withdrawable,
- uses,
- wait_time,
- is_unique,
- unique_hash,
- k1,
- open_time,
- used,
- usescsv
- )
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- """,
- (
- row[0],
- row[1],
- row[2],
- row[3],
- row[4],
- row[5],
- row[6],
- row[7],
- row[8],
- row[9],
- row[10],
- row[11],
- usescsv,
- ),
- )
- await db.execute("DROP TABLE withdraw.withdraw_links")
-
-
-async def m003_make_hash_check(db):
- """
- Creates a hash check table.
- """
- await db.execute(
- """
- CREATE TABLE withdraw.hash_check (
- id TEXT PRIMARY KEY,
- lnurl_id TEXT
- );
- """
- )
-
-
-async def m004_webhook_url(db):
- """
- Adds webhook_url
- """
- await db.execute("ALTER TABLE withdraw.withdraw_link ADD COLUMN webhook_url TEXT;")
-
-
-async def m005_add_custom_print_design(db):
- """
- Adds custom print design
- """
- await db.execute("ALTER TABLE withdraw.withdraw_link ADD COLUMN custom_url TEXT;")
-
-
-async def m006_webhook_headers_and_body(db):
- """
- Add headers and body to webhooks
- """
- await db.execute(
- "ALTER TABLE withdraw.withdraw_link ADD COLUMN webhook_headers TEXT;"
- )
- await db.execute("ALTER TABLE withdraw.withdraw_link ADD COLUMN webhook_body TEXT;")
diff --git a/lnbits/extensions/withdraw/models.py b/lnbits/extensions/withdraw/models.py
deleted file mode 100644
index 49421a79..00000000
--- a/lnbits/extensions/withdraw/models.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import shortuuid
-from fastapi import Query
-from lnurl import Lnurl, LnurlWithdrawResponse
-from lnurl import encode as lnurl_encode
-from lnurl.models import ClearnetUrl, MilliSatoshi
-from pydantic import BaseModel
-from starlette.requests import Request
-
-
-class CreateWithdrawData(BaseModel):
- title: str = Query(...)
- min_withdrawable: int = Query(..., ge=1)
- max_withdrawable: int = Query(..., ge=1)
- uses: int = Query(..., ge=1)
- wait_time: int = Query(..., ge=1)
- is_unique: bool
- webhook_url: str = Query(None)
- webhook_headers: str = Query(None)
- webhook_body: str = Query(None)
- custom_url: str = Query(None)
-
-
-class WithdrawLink(BaseModel):
- id: str
- wallet: str = Query(None)
- title: str = Query(None)
- min_withdrawable: int = Query(0)
- max_withdrawable: int = Query(0)
- uses: int = Query(0)
- wait_time: int = Query(0)
- is_unique: bool = Query(False)
- unique_hash: str = Query(0)
- k1: str = Query(None)
- open_time: int = Query(0)
- used: int = Query(0)
- usescsv: str = Query(None)
- number: int = Query(0)
- webhook_url: str = Query(None)
- webhook_headers: str = Query(None)
- webhook_body: str = Query(None)
- custom_url: str = Query(None)
-
- @property
- def is_spent(self) -> bool:
- return self.used >= self.uses
-
- def lnurl(self, req: Request) -> Lnurl:
- if self.is_unique:
- usescssv = self.usescsv.split(",")
- tohash = self.id + self.unique_hash + usescssv[self.number]
- multihash = shortuuid.uuid(name=tohash)
- url = req.url_for(
- "withdraw.api_lnurl_multi_response",
- unique_hash=self.unique_hash,
- id_unique_hash=multihash,
- )
- else:
- url = req.url_for(
- "withdraw.api_lnurl_response", unique_hash=self.unique_hash
- )
-
- return lnurl_encode(url)
-
- def lnurl_response(self, req: Request) -> LnurlWithdrawResponse:
- url = req.url_for(
- name="withdraw.api_lnurl_callback", unique_hash=self.unique_hash
- )
- return LnurlWithdrawResponse(
- callback=ClearnetUrl(url, scheme="https"),
- k1=self.k1,
- minWithdrawable=MilliSatoshi(self.min_withdrawable * 1000),
- maxWithdrawable=MilliSatoshi(self.max_withdrawable * 1000),
- defaultDescription=self.title,
- )
-
-
-class HashCheck(BaseModel):
- hash: bool
- lnurl: bool
diff --git a/lnbits/extensions/withdraw/static/image/lnurl-withdraw.png b/lnbits/extensions/withdraw/static/image/lnurl-withdraw.png
deleted file mode 100644
index 4f036423..00000000
Binary files a/lnbits/extensions/withdraw/static/image/lnurl-withdraw.png and /dev/null differ
diff --git a/lnbits/extensions/withdraw/static/js/index.js b/lnbits/extensions/withdraw/static/js/index.js
deleted file mode 100644
index ced78439..00000000
--- a/lnbits/extensions/withdraw/static/js/index.js
+++ /dev/null
@@ -1,323 +0,0 @@
-/* global Vue, VueQrcode, _, Quasar, LOCALE, windowMixin, LNbits */
-
-Vue.component(VueQrcode.name, VueQrcode)
-
-var locationPath = [
- window.location.protocol,
- '//',
- window.location.host,
- window.location.pathname
-].join('')
-
-var mapWithdrawLink = function (obj) {
- obj._data = _.clone(obj)
- obj.date = Quasar.utils.date.formatDate(
- new Date(obj.time * 1000),
- 'YYYY-MM-DD HH:mm'
- )
- obj.min_fsat = new Intl.NumberFormat(LOCALE).format(obj.min_withdrawable)
- obj.max_fsat = new Intl.NumberFormat(LOCALE).format(obj.max_withdrawable)
- obj.uses_left = obj.uses - obj.used
- obj.print_url = [locationPath, 'print/', obj.id].join('')
- obj.withdraw_url = [locationPath, obj.id].join('')
- obj._data.use_custom = Boolean(obj.custom_url)
- return obj
-}
-
-const CUSTOM_URL = '/static/images/default_voucher.png'
-
-new Vue({
- el: '#vue',
- mixins: [windowMixin],
- data: function () {
- return {
- checker: null,
- withdrawLinks: [],
- withdrawLinksTable: {
- columns: [
- {name: 'id', align: 'left', label: 'ID', field: 'id'},
- {name: 'title', align: 'left', label: 'Title', field: 'title'},
- {
- name: 'wait_time',
- align: 'right',
- label: 'Wait',
- field: 'wait_time'
- },
- {
- name: 'uses_left',
- align: 'right',
- label: 'Uses left',
- field: 'uses_left'
- },
- {name: 'min', align: 'right', label: 'Min (sat)', field: 'min_fsat'},
- {name: 'max', align: 'right', label: 'Max (sat)', field: 'max_fsat'}
- ],
- pagination: {
- rowsPerPage: 10
- }
- },
- nfcTagWriting: false,
- formDialog: {
- show: false,
- secondMultiplier: 'seconds',
- secondMultiplierOptions: ['seconds', 'minutes', 'hours'],
- data: {
- is_unique: false,
- use_custom: false,
- has_webhook: false
- }
- },
- simpleformDialog: {
- show: false,
- data: {
- is_unique: true,
- use_custom: false,
- title: 'Vouchers',
- min_withdrawable: 0,
- wait_time: 1
- }
- },
- qrCodeDialog: {
- show: false,
- data: null
- }
- }
- },
- computed: {
- sortedWithdrawLinks: function () {
- return this.withdrawLinks.sort(function (a, b) {
- return b.uses_left - a.uses_left
- })
- }
- },
- methods: {
- getWithdrawLinks: function () {
- var self = this
-
- LNbits.api
- .request(
- 'GET',
- '/withdraw/api/v1/links?all_wallets=true',
- this.g.user.wallets[0].inkey
- )
- .then(function (response) {
- self.withdrawLinks = response.data.map(function (obj) {
- return mapWithdrawLink(obj)
- })
- })
- .catch(function (error) {
- clearInterval(self.checker)
- LNbits.utils.notifyApiError(error)
- })
- },
- closeFormDialog: function () {
- this.formDialog.data = {
- is_unique: false,
- use_custom: false
- }
- },
- simplecloseFormDialog: function () {
- this.simpleformDialog.data = {
- is_unique: false,
- use_custom: false
- }
- },
- openQrCodeDialog: function (linkId) {
- var link = _.findWhere(this.withdrawLinks, {id: linkId})
-
- this.qrCodeDialog.data = _.clone(link)
- this.qrCodeDialog.data.url =
- window.location.protocol + '//' + window.location.host
- this.qrCodeDialog.show = true
- },
- openUpdateDialog: function (linkId) {
- var link = _.findWhere(this.withdrawLinks, {id: linkId})
- this.formDialog.data = _.clone(link._data)
- this.formDialog.show = true
- },
- sendFormData: function () {
- var wallet = _.findWhere(this.g.user.wallets, {
- id: this.formDialog.data.wallet
- })
- var data = _.omit(this.formDialog.data, 'wallet')
-
- if (!data.use_custom) {
- data.custom_url = null
- }
-
- if (data.use_custom && !data?.custom_url) {
- data.custom_url = CUSTOM_URL
- }
-
- data.wait_time =
- data.wait_time *
- {
- seconds: 1,
- minutes: 60,
- hours: 3600
- }[this.formDialog.secondMultiplier]
- if (data.id) {
- this.updateWithdrawLink(wallet, data)
- } else {
- this.createWithdrawLink(wallet, data)
- }
- },
- simplesendFormData: function () {
- var wallet = _.findWhere(this.g.user.wallets, {
- id: this.simpleformDialog.data.wallet
- })
- var data = _.omit(this.simpleformDialog.data, 'wallet')
-
- data.wait_time = 1
- data.min_withdrawable = data.max_withdrawable
- data.title = 'vouchers'
- data.is_unique = true
-
- if (!data.use_custom) {
- data.custom_url = null
- }
-
- if (data.use_custom && !data?.custom_url) {
- data.custom_url = '/static/images/default_voucher.png'
- }
-
- if (data.id) {
- this.updateWithdrawLink(wallet, data)
- } else {
- this.createWithdrawLink(wallet, data)
- }
- },
- updateWithdrawLink: function (wallet, data) {
- var self = this
- const body = _.pick(
- data,
- 'title',
- 'min_withdrawable',
- 'max_withdrawable',
- 'uses',
- 'wait_time',
- 'is_unique',
- 'webhook_url',
- 'webhook_headers',
- 'webhook_body',
- 'custom_url'
- )
-
- if (data.has_webhook) {
- body = {
- ...body,
- webhook_url: data.webhook_url,
- webhook_headers: data.webhook_headers,
- webhook_body: data.webhook_body
- }
- }
-
- LNbits.api
- .request(
- 'PUT',
- '/withdraw/api/v1/links/' + data.id,
- wallet.adminkey,
- body
- )
- .then(function (response) {
- self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) {
- return obj.id === data.id
- })
- self.withdrawLinks.push(mapWithdrawLink(response.data))
- self.formDialog.show = false
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- createWithdrawLink: function (wallet, data) {
- var self = this
-
- LNbits.api
- .request('POST', '/withdraw/api/v1/links', wallet.adminkey, data)
- .then(function (response) {
- self.withdrawLinks.push(mapWithdrawLink(response.data))
- self.formDialog.show = false
- self.simpleformDialog.show = false
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- deleteWithdrawLink: function (linkId) {
- var self = this
- var link = _.findWhere(this.withdrawLinks, {id: linkId})
-
- LNbits.utils
- .confirmDialog('Are you sure you want to delete this withdraw link?')
- .onOk(function () {
- LNbits.api
- .request(
- 'DELETE',
- '/withdraw/api/v1/links/' + linkId,
- _.findWhere(self.g.user.wallets, {id: link.wallet}).adminkey
- )
- .then(function (response) {
- self.withdrawLinks = _.reject(self.withdrawLinks, function (obj) {
- return obj.id === linkId
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- })
- },
- writeNfcTag: async function (lnurl) {
- try {
- if (typeof NDEFReader == 'undefined') {
- throw {
- toString: function () {
- return 'NFC not supported on this device or browser.'
- }
- }
- }
-
- const ndef = new NDEFReader()
-
- this.nfcTagWriting = true
- this.$q.notify({
- message: 'Tap your NFC tag to write the LNURL-withdraw link to it.'
- })
-
- await ndef.write({
- records: [{recordType: 'url', data: 'lightning:' + lnurl, lang: 'en'}]
- })
-
- this.nfcTagWriting = false
- this.$q.notify({
- type: 'positive',
- message: 'NFC tag written successfully.'
- })
- } catch (error) {
- this.nfcTagWriting = false
- this.$q.notify({
- type: 'negative',
- message: error
- ? error.toString()
- : 'An unexpected error has occurred.'
- })
- }
- },
- exportCSV() {
- LNbits.utils.exportCSV(
- this.withdrawLinksTable.columns,
- this.withdrawLinks,
- 'withdraw-links'
- )
- }
- },
- created: function () {
- if (this.g.user.wallets.length) {
- var getWithdrawLinks = this.getWithdrawLinks
- getWithdrawLinks()
- this.checker = setInterval(function () {
- getWithdrawLinks()
- }, 300000)
- }
- }
-})
diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html
deleted file mode 100644
index ff88189d..00000000
--- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html
+++ /dev/null
@@ -1,204 +0,0 @@
-
- WARNING: LNURL must be used over https or TOR
- Exploring LNURL and finding use cases, is really helping inform
- lightning protocol development, rather than the protocol dictating how
- lightning-network should be engaged with.
- GET /withdraw/api/v1/links
- Headers
- {"X-Api-Key": <invoice_key>}
- Body (application/json)
-
- Returns 200 OK (application/json)
-
- [<withdraw_link_object>, ...]
- Curl example
- curl -X GET {{ request.base_url }}withdraw/api/v1/links -H
- "X-Api-Key: {{ user.wallets[0].inkey }}"
-
- GET
- /withdraw/api/v1/links/<withdraw_id>
- Headers
- {"X-Api-Key": <invoice_key>}
- Body (application/json)
-
- Returns 201 CREATED (application/json)
-
- {"lnurl": <string>}
- Curl example
- curl -X GET {{ request.base_url
- }}withdraw/api/v1/links/<withdraw_id> -H "X-Api-Key: {{
- user.wallets[0].inkey }}"
-
- POST /withdraw/api/v1/links
- Headers
- {"X-Api-Key": <admin_key>}
- Body (application/json)
- {"title": <string>, "min_withdrawable": <integer>,
- "max_withdrawable": <integer>, "uses": <integer>,
- "wait_time": <integer>, "is_unique": <boolean>,
- "webhook_url": <string>}
-
- Returns 201 CREATED (application/json)
-
- {"lnurl": <string>}
- Curl example
- curl -X POST {{ request.base_url }}withdraw/api/v1/links -d
- '{"title": <string>, "min_withdrawable": <integer>,
- "max_withdrawable": <integer>, "uses": <integer>,
- "wait_time": <integer>, "is_unique": <boolean>,
- "webhook_url": <string>}' -H "Content-type: application/json" -H
- "X-Api-Key: {{ user.wallets[0].adminkey }}"
-
- PUT
- /withdraw/api/v1/links/<withdraw_id>
- Headers
- {"X-Api-Key": <admin_key>}
- Body (application/json)
- {"title": <string>, "min_withdrawable": <integer>,
- "max_withdrawable": <integer>, "uses": <integer>,
- "wait_time": <integer>, "is_unique": <boolean>}
-
- Returns 200 OK (application/json)
-
- {"lnurl": <string>}
- Curl example
- curl -X PUT {{ request.base_url
- }}withdraw/api/v1/links/<withdraw_id> -d '{"title":
- <string>, "min_withdrawable": <integer>,
- "max_withdrawable": <integer>, "uses": <integer>,
- "wait_time": <integer>, "is_unique": <boolean>}' -H
- "Content-type: application/json" -H "X-Api-Key: {{
- user.wallets[0].adminkey }}"
-
- DELETE
- /withdraw/api/v1/links/<withdraw_id>
- Headers
- {"X-Api-Key": <admin_key>}
- Returns 204 NO CONTENT
-
- Curl example
- curl -X DELETE {{ request.base_url
- }}withdraw/api/v1/links/<withdraw_id> -H "X-Api-Key: {{
- user.wallets[0].adminkey }}"
-
- GET
- /withdraw/api/v1/links/<the_hash>/<lnurl_id>
- Headers
- {"X-Api-Key": <invoice_key>}
- Body (application/json)
-
- Returns 201 CREATED (application/json)
-
- {"status": <bool>}
- Curl example
- curl -X GET {{ request.base_url
- }}withdraw/api/v1/links/<the_hash>/<lnurl_id> -H
- "X-Api-Key: {{ user.wallets[0].inkey }}"
-
- GET
- /withdraw/img/<lnurl_id>
- Curl example
- curl -X GET {{ request.base_url }}withdraw/img/<lnurl_id>"
-
-
- LNURL is a range of lightning-network standards that allow us to use
- lightning-network differently. An LNURL withdraw is the permission for
- someone to pull a certain amount of funds from a lightning wallet. In
- this extension time is also added - an amount can be withdraw over a
- period of time. A typical use case for an LNURL withdraw is a faucet,
- although it is a very powerful technology, with much further reaching
- implications. For example, an LNURL withdraw could be minted to pay for
- a subscription service.
-
- Use a LNURL compatible bitcoin wallet to claim the sats. -
-
- ID: {{ qrCodeDialog.data.id }}
- Unique: {{ qrCodeDialog.data.is_unique }}
- (QR code will change after each withdrawal)
- Max. withdrawable: {{
- qrCodeDialog.data.max_withdrawable }} sat
- Wait time: {{ qrCodeDialog.data.wait_time }} seconds
- Withdraws: {{ qrCodeDialog.data.used }} / {{
- qrCodeDialog.data.uses }}
-
|
- |
- {% endfor %}
-