bits bobs
This commit is contained in:
parent
b0db8d8f5a
commit
5e28183a24
7 changed files with 51 additions and 391 deletions
|
|
@ -5,87 +5,62 @@ from . import db
|
||||||
from .models import ScrubLink, CreateScrubLinkData
|
from .models import ScrubLink, CreateScrubLinkData
|
||||||
|
|
||||||
|
|
||||||
async def create_pay_link(data: CreateScrubLinkData, wallet_id: str) -> ScrubLink:
|
async def create_scrub_link(wallet_id: str, data: CreateSatsDiceLink) -> satsdiceLink:
|
||||||
|
satsdice_id = urlsafe_short_hash()
|
||||||
returning = "" if db.type == SQLITE else "RETURNING ID"
|
await db.execute(
|
||||||
method = db.execute if db.type == SQLITE else db.fetchone
|
"""
|
||||||
result = await (method)(
|
INSERT INTO scrub.scrub_links (
|
||||||
f"""
|
id,
|
||||||
INSERT INTO scrub.pay_links (
|
|
||||||
wallet,
|
wallet,
|
||||||
description,
|
description,
|
||||||
min,
|
payoraddress,
|
||||||
max,
|
|
||||||
served_meta,
|
|
||||||
served_pr,
|
|
||||||
webhook_url,
|
|
||||||
success_text,
|
|
||||||
success_url,
|
|
||||||
comment_chars,
|
|
||||||
currency
|
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
{returning}
|
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
wallet_id,
|
satsdice_id,
|
||||||
data.description,
|
wallet,
|
||||||
data.min,
|
description,
|
||||||
data.max,
|
payoraddress,
|
||||||
data.webhook_url,
|
|
||||||
data.success_text,
|
|
||||||
data.success_url,
|
|
||||||
data.comment_chars,
|
|
||||||
data.currency,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if db.type == SQLITE:
|
link = await get_satsdice_pay(satsdice_id)
|
||||||
link_id = result._result_proxy.lastrowid
|
|
||||||
else:
|
|
||||||
link_id = result[0]
|
|
||||||
|
|
||||||
link = await get_pay_link(link_id)
|
|
||||||
assert link, "Newly created link couldn't be retrieved"
|
assert link, "Newly created link couldn't be retrieved"
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
|
||||||
async def get_pay_link(link_id: int) -> Optional[ScrubLink]:
|
async def get_scrub_link(link_id: str) -> Optional[satsdiceLink]:
|
||||||
row = await db.fetchone("SELECT * FROM scrub.pay_links WHERE id = ?", (link_id,))
|
row = await db.fetchone(
|
||||||
return ScrubLink.from_row(row) if row else None
|
"SELECT * FROM scrub.scrub_links WHERE id = ?", (link_id,)
|
||||||
|
)
|
||||||
|
return satsdiceLink(**row) if row else None
|
||||||
|
|
||||||
|
|
||||||
async def get_pay_links(wallet_ids: Union[str, List[str]]) -> List[ScrubLink]:
|
async def get_scrub_links(wallet_ids: Union[str, List[str]]) -> List[satsdiceLink]:
|
||||||
if isinstance(wallet_ids, str):
|
if isinstance(wallet_ids, str):
|
||||||
wallet_ids = [wallet_ids]
|
wallet_ids = [wallet_ids]
|
||||||
|
|
||||||
q = ",".join(["?"] * len(wallet_ids))
|
q = ",".join(["?"] * len(wallet_ids))
|
||||||
rows = await db.fetchall(
|
rows = await db.fetchall(
|
||||||
f"""
|
f"""
|
||||||
SELECT * FROM scrub.pay_links WHERE wallet IN ({q})
|
SELECT * FROM scrub.scrub_links WHERE wallet IN ({q})
|
||||||
ORDER BY Id
|
ORDER BY id
|
||||||
""",
|
""",
|
||||||
(*wallet_ids,),
|
(*wallet_ids,),
|
||||||
)
|
)
|
||||||
return [ScrubLink.from_row(row) for row in rows]
|
return [satsdiceLink(**row) for row in rows]
|
||||||
|
|
||||||
|
|
||||||
async def update_pay_link(link_id: int, **kwargs) -> Optional[ScrubLink]:
|
async def update_scrub_link(link_id: int, **kwargs) -> Optional[satsdiceLink]:
|
||||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||||
await db.execute(
|
await db.execute(
|
||||||
f"UPDATE scrub.pay_links SET {q} WHERE id = ?", (*kwargs.values(), link_id)
|
f"UPDATE scrub.scrub_links SET {q} WHERE id = ?",
|
||||||
|
(*kwargs.values(), link_id),
|
||||||
)
|
)
|
||||||
row = await db.fetchone("SELECT * FROM scrub.pay_links WHERE id = ?", (link_id,))
|
row = await db.fetchone(
|
||||||
return ScrubLink.from_row(row) if row else None
|
"SELECT * FROM scrub.scrub_links WHERE id = ?", (link_id,)
|
||||||
|
|
||||||
|
|
||||||
async def increment_pay_link(link_id: int, **kwargs) -> Optional[ScrubLink]:
|
|
||||||
q = ", ".join([f"{field[0]} = {field[0]} + ?" for field in kwargs.items()])
|
|
||||||
await db.execute(
|
|
||||||
f"UPDATE scrub.pay_links SET {q} WHERE id = ?", (*kwargs.values(), link_id)
|
|
||||||
)
|
)
|
||||||
row = await db.fetchone("SELECT * FROM scrub.pay_links WHERE id = ?", (link_id,))
|
return satsdiceLink(**row) if row else None
|
||||||
return ScrubLink.from_row(row) if row else None
|
|
||||||
|
|
||||||
|
async def delete_scrub_link(link_id: int) -> None:
|
||||||
async def delete_pay_link(link_id: int) -> None:
|
await db.execute("DELETE FROM scrub.scrub_links WHERE id = ?", (link_id,))
|
||||||
await db.execute("DELETE FROM scrub.pay_links WHERE id = ?", (link_id,))
|
|
||||||
|
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
import hashlib
|
|
||||||
import math
|
|
||||||
from http import HTTPStatus
|
|
||||||
|
|
||||||
from fastapi import Request
|
|
||||||
from lnurl import ( # type: ignore
|
|
||||||
LnurlErrorResponse,
|
|
||||||
LnurlScrubActionResponse,
|
|
||||||
LnurlScrubResponse,
|
|
||||||
)
|
|
||||||
from starlette.exceptions import HTTPException
|
|
||||||
|
|
||||||
from lnbits.core.services import create_invoice
|
|
||||||
from lnbits.utils.exchange_rates import get_fiat_rate_satoshis
|
|
||||||
|
|
||||||
from . import scrub_ext
|
|
||||||
from .crud import increment_pay_link
|
|
||||||
|
|
||||||
|
|
||||||
@scrub_ext.get(
|
|
||||||
"/api/v1/lnurl/{link_id}",
|
|
||||||
status_code=HTTPStatus.OK,
|
|
||||||
name="scrub.api_lnurl_response",
|
|
||||||
)
|
|
||||||
async def api_lnurl_response(request: Request, link_id):
|
|
||||||
link = await increment_pay_link(link_id, served_meta=1)
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Scrub link does not exist."
|
|
||||||
)
|
|
||||||
|
|
||||||
rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1
|
|
||||||
|
|
||||||
resp = LnurlScrubResponse(
|
|
||||||
callback=request.url_for("scrub.api_lnurl_callback", link_id=link.id),
|
|
||||||
min_sendable=math.ceil(link.min * rate) * 1000,
|
|
||||||
max_sendable=round(link.max * rate) * 1000,
|
|
||||||
metadata=link.scrubay_metadata,
|
|
||||||
)
|
|
||||||
params = resp.dict()
|
|
||||||
|
|
||||||
if link.comment_chars > 0:
|
|
||||||
params["commentAllowed"] = link.comment_chars
|
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
@scrub_ext.get(
|
|
||||||
"/api/v1/lnurl/cb/{link_id}",
|
|
||||||
status_code=HTTPStatus.OK,
|
|
||||||
name="scrub.api_lnurl_callback",
|
|
||||||
)
|
|
||||||
async def api_lnurl_callback(request: Request, link_id):
|
|
||||||
link = await increment_pay_link(link_id, served_pr=1)
|
|
||||||
if not link:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Scrub link does not exist."
|
|
||||||
)
|
|
||||||
min, max = link.min, link.max
|
|
||||||
rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1
|
|
||||||
if link.currency:
|
|
||||||
# allow some fluctuation (as the fiat price may have changed between the calls)
|
|
||||||
min = rate * 995 * link.min
|
|
||||||
max = rate * 1010 * link.max
|
|
||||||
else:
|
|
||||||
min = link.min * 1000
|
|
||||||
max = link.max * 1000
|
|
||||||
|
|
||||||
amount_received = int(request.query_params.get("amount") or 0)
|
|
||||||
if amount_received < min:
|
|
||||||
return LnurlErrorResponse(
|
|
||||||
reason=f"Amount {amount_received} is smaller than minimum {min}."
|
|
||||||
).dict()
|
|
||||||
|
|
||||||
elif amount_received > max:
|
|
||||||
return LnurlErrorResponse(
|
|
||||||
reason=f"Amount {amount_received} is greater than maximum {max}."
|
|
||||||
).dict()
|
|
||||||
|
|
||||||
comment = request.query_params.get("comment")
|
|
||||||
if len(comment or "") > link.comment_chars:
|
|
||||||
return LnurlErrorResponse(
|
|
||||||
reason=f"Got a comment with {len(comment)} characters, but can only accept {link.comment_chars}"
|
|
||||||
).dict()
|
|
||||||
|
|
||||||
payment_hash, payment_request = await create_invoice(
|
|
||||||
wallet_id=link.wallet,
|
|
||||||
amount=int(amount_received / 1000),
|
|
||||||
memo=link.description,
|
|
||||||
description_hash=hashlib.sha256(
|
|
||||||
link.scrubay_metadata.encode("utf-8")
|
|
||||||
).digest(),
|
|
||||||
extra={
|
|
||||||
"tag": "scrub",
|
|
||||||
"link": link.id,
|
|
||||||
"comment": comment,
|
|
||||||
"extra": request.query_params.get("amount"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
success_action = link.success_action(payment_hash)
|
|
||||||
if success_action:
|
|
||||||
resp = LnurlScrubActionResponse(
|
|
||||||
pr=payment_request, success_action=success_action, routes=[]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
resp = LnurlScrubActionResponse(pr=payment_request, routes=[])
|
|
||||||
|
|
||||||
return resp.dict()
|
|
||||||
|
|
@ -1,51 +1,14 @@
|
||||||
async def m001_initial(db):
|
async def m001_initial(db):
|
||||||
"""
|
"""
|
||||||
Initial pay table.
|
Initial scrub table.
|
||||||
"""
|
"""
|
||||||
await db.execute(
|
await db.execute(
|
||||||
f"""
|
f"""
|
||||||
CREATE TABLE scrub.pay_links (
|
CREATE TABLE scrub.scrub_links (
|
||||||
id {db.serial_primary_key},
|
id TEXT PRIMARY KEY,
|
||||||
wallet TEXT NOT NULL,
|
wallet TEXT NOT NULL,
|
||||||
description TEXT NOT NULL,
|
description TEXT NOT NULL,
|
||||||
webhook INTEGER NOT NULL,
|
payoraddress TEXT NOT NULL
|
||||||
payoraddress INTEGER NOT NULL
|
|
||||||
);
|
);
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def m002_webhooks_and_success_actions(db):
|
|
||||||
"""
|
|
||||||
Webhooks and success actions.
|
|
||||||
"""
|
|
||||||
await db.execute("ALTER TABLE scrub.pay_links ADD COLUMN webhook_url TEXT;")
|
|
||||||
await db.execute("ALTER TABLE scrub.pay_links ADD COLUMN success_text TEXT;")
|
|
||||||
await db.execute("ALTER TABLE scrub.pay_links ADD COLUMN success_url TEXT;")
|
|
||||||
await db.execute(
|
|
||||||
f"""
|
|
||||||
CREATE TABLE scrub.invoices (
|
|
||||||
pay_link INTEGER NOT NULL REFERENCES {db.references_schema}pay_links (id),
|
|
||||||
payment_hash TEXT NOT NULL,
|
|
||||||
webhook_sent INT, -- null means not sent, otherwise store status
|
|
||||||
expiry INT
|
|
||||||
);
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def m003_min_max_comment_fiat(db):
|
|
||||||
"""
|
|
||||||
Support for min/max amounts, comments and fiat prices that get
|
|
||||||
converted automatically to satoshis based on some API.
|
|
||||||
"""
|
|
||||||
await db.execute(
|
|
||||||
"ALTER TABLE scrub.pay_links ADD COLUMN currency TEXT;"
|
|
||||||
) # null = satoshis
|
|
||||||
await db.execute(
|
|
||||||
"ALTER TABLE scrub.pay_links ADD COLUMN comment_chars INTEGER DEFAULT 0;"
|
|
||||||
)
|
|
||||||
await db.execute("ALTER TABLE scrub.pay_links RENAME COLUMN amount TO min;")
|
|
||||||
await db.execute("ALTER TABLE scrub.pay_links ADD COLUMN max INTEGER;")
|
|
||||||
await db.execute("UPDATE scrub.pay_links SET max = min;")
|
|
||||||
await db.execute("DROP TABLE scrub.invoices")
|
|
||||||
|
|
|
||||||
|
|
@ -8,31 +8,11 @@ from lnurl.types import LnurlScrubMetadata # type: ignore
|
||||||
from sqlite3 import Row
|
from sqlite3 import Row
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class CreateScrubLinkData(BaseModel):
|
|
||||||
description: str
|
|
||||||
min: int = Query(0.01, ge=0.01)
|
|
||||||
max: int = Query(0.01, ge=0.01)
|
|
||||||
currency: str = Query(None)
|
|
||||||
comment_chars: int = Query(0, ge=0, lt=800)
|
|
||||||
webhook_url: str = Query(None)
|
|
||||||
success_text: str = Query(None)
|
|
||||||
success_url: str = Query(None)
|
|
||||||
|
|
||||||
|
|
||||||
class ScrubLink(BaseModel):
|
class ScrubLink(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
wallet: str
|
wallet: str
|
||||||
description: str
|
description: str
|
||||||
min: int
|
payoraddress: str
|
||||||
served_meta: int
|
|
||||||
served_pr: int
|
|
||||||
webhook_url: Optional[str]
|
|
||||||
success_text: Optional[str]
|
|
||||||
success_url: Optional[str]
|
|
||||||
currency: Optional[str]
|
|
||||||
comment_chars: int
|
|
||||||
max: int
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_row(cls, row: Row) -> "ScrubLink":
|
def from_row(cls, row: Row) -> "ScrubLink":
|
||||||
|
|
@ -46,19 +26,3 @@ class ScrubLink(BaseModel):
|
||||||
@property
|
@property
|
||||||
def scrubay_metadata(self) -> LnurlScrubMetadata:
|
def scrubay_metadata(self) -> LnurlScrubMetadata:
|
||||||
return LnurlScrubMetadata(json.dumps([["text/plain", self.description]]))
|
return LnurlScrubMetadata(json.dumps([["text/plain", self.description]]))
|
||||||
|
|
||||||
def success_action(self, payment_hash: str) -> Optional[Dict]:
|
|
||||||
if self.success_url:
|
|
||||||
url: ParseResult = urlparse(self.success_url)
|
|
||||||
qs: Dict = parse_qs(url.query)
|
|
||||||
qs["payment_hash"] = payment_hash
|
|
||||||
url = url._replace(query=urlencode(qs, doseq=True))
|
|
||||||
return {
|
|
||||||
"tag": "url",
|
|
||||||
"description": self.success_text or "~",
|
|
||||||
"url": urlunparse(url),
|
|
||||||
}
|
|
||||||
elif self.success_text:
|
|
||||||
return {"tag": "message", "message": self.success_text}
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
|
||||||
|
|
@ -28,23 +28,7 @@ async def on_invoice_paid(payment: Scrubment) -> None:
|
||||||
return
|
return
|
||||||
|
|
||||||
pay_link = await get_pay_link(payment.extra.get("link", -1))
|
pay_link = await get_pay_link(payment.extra.get("link", -1))
|
||||||
if pay_link and pay_link.webhook_url:
|
# PAY LNURLP AND LNADDRESS
|
||||||
async with httpx.AsyncClient() as client:
|
|
||||||
try:
|
|
||||||
r = await client.post(
|
|
||||||
pay_link.webhook_url,
|
|
||||||
json={
|
|
||||||
"payment_hash": payment.payment_hash,
|
|
||||||
"payment_request": payment.bolt11,
|
|
||||||
"amount": payment.amount,
|
|
||||||
"comment": payment.extra.get("comment"),
|
|
||||||
"scrub": pay_link.id,
|
|
||||||
},
|
|
||||||
timeout=40,
|
|
||||||
)
|
|
||||||
await mark_webhook_sent(payment, r.status_code)
|
|
||||||
except (httpx.ConnectError, httpx.RequestError):
|
|
||||||
await mark_webhook_sent(payment, -1)
|
|
||||||
|
|
||||||
|
|
||||||
async def mark_webhook_sent(payment: Scrubment, status: int) -> None:
|
async def mark_webhook_sent(payment: Scrubment, status: int) -> None:
|
||||||
|
|
|
||||||
|
|
@ -154,76 +154,13 @@
|
||||||
type="text"
|
type="text"
|
||||||
label="Item description *"
|
label="Item description *"
|
||||||
></q-input>
|
></q-input>
|
||||||
<div class="row q-col-gutter-sm">
|
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.number="formDialog.data.min"
|
v-model.trim="formDialog.data.payoraddress"
|
||||||
type="number"
|
|
||||||
:step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'"
|
|
||||||
:label="formDialog.fixedAmount ? 'Amount *' : 'Min *'"
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
v-if="!formDialog.fixedAmount"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.number="formDialog.data.max"
|
|
||||||
type="number"
|
|
||||||
:step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'"
|
|
||||||
label="Max *"
|
|
||||||
></q-input>
|
|
||||||
</div>
|
|
||||||
<div class="row q-col-gutter-sm">
|
|
||||||
<div class="col">
|
|
||||||
<q-checkbox
|
|
||||||
dense
|
|
||||||
v-model="formDialog.fixedAmount"
|
|
||||||
label="Fixed amount"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<q-select
|
|
||||||
dense
|
|
||||||
:options="currencies"
|
|
||||||
v-model="formDialog.data.currency"
|
|
||||||
:display-value="formDialog.data.currency || 'satoshis'"
|
|
||||||
label="Currency"
|
|
||||||
:hint="'Amounts will be converted at use-time to satoshis. ' + (formDialog.data.currency && fiatRates[formDialog.data.currency] ? `Currently 1 ${formDialog.data.currency} = ${fiatRates[formDialog.data.currency]} sat` : '')"
|
|
||||||
@input="updateFiatRate"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.number="formDialog.data.comment_chars"
|
|
||||||
type="number"
|
|
||||||
label="Comment maximum characters"
|
|
||||||
hint="Tell wallets to prompt users for a comment that will be sent along with the payment. scrub will store the comment and send it in the webhook."
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="formDialog.data.webhook_url"
|
|
||||||
type="text"
|
type="text"
|
||||||
label="Webhook URL (optional)"
|
label="LNURLPay or LNAdress *"
|
||||||
hint="A URL to be called whenever this link receives a payment."
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="formDialog.data.success_text"
|
|
||||||
type="text"
|
|
||||||
label="Success message (optional)"
|
|
||||||
hint="Will be shown to the user in his wallet after a successful payment."
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="formDialog.data.success_url"
|
|
||||||
type="text"
|
|
||||||
label="Success URL (optional)"
|
|
||||||
hint="Will be shown as a clickable link to the user in his wallet after a successful payment, appended by the payment_hash as a query string."
|
|
||||||
></q-input>
|
></q-input>
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn
|
<q-btn
|
||||||
|
|
@ -240,10 +177,7 @@
|
||||||
:disable="
|
:disable="
|
||||||
formDialog.data.wallet == null ||
|
formDialog.data.wallet == null ||
|
||||||
formDialog.data.description == null ||
|
formDialog.data.description == null ||
|
||||||
(
|
formDialog.data.payoraddress == null
|
||||||
formDialog.data.min == null ||
|
|
||||||
formDialog.data.min <= 0
|
|
||||||
)
|
|
||||||
"
|
"
|
||||||
type="submit"
|
type="submit"
|
||||||
>Create pay link</q-btn
|
>Create pay link</q-btn
|
||||||
|
|
@ -255,57 +189,6 @@
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
<q-dialog v-model="qrCodeDialog.show" position="top">
|
|
||||||
<q-card v-if="qrCodeDialog.data" class="q-pa-lg lnbits__dialog-card">
|
|
||||||
{% raw %}
|
|
||||||
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
|
|
||||||
<qrcode
|
|
||||||
:value="qrCodeDialog.data.lnurl"
|
|
||||||
:options="{width: 800}"
|
|
||||||
class="rounded-borders"
|
|
||||||
></qrcode>
|
|
||||||
</q-responsive>
|
|
||||||
<p style="word-break: break-all">
|
|
||||||
<strong>ID:</strong> {{ qrCodeDialog.data.id }}<br />
|
|
||||||
<strong>Amount:</strong> {{ qrCodeDialog.data.amount }}<br />
|
|
||||||
<span v-if="qrCodeDialog.data.currency"
|
|
||||||
><strong>{{ qrCodeDialog.data.currency }} price:</strong> {{
|
|
||||||
fiatRates[qrCodeDialog.data.currency] ?
|
|
||||||
fiatRates[qrCodeDialog.data.currency] + ' sat' : 'Loading...' }}<br
|
|
||||||
/></span>
|
|
||||||
<strong>Accepts comments:</strong> {{ qrCodeDialog.data.comments }}<br />
|
|
||||||
<strong>Dispatches webhook to:</strong> {{ qrCodeDialog.data.webhook
|
|
||||||
}}<br />
|
|
||||||
<strong>On success:</strong> {{ qrCodeDialog.data.success }}<br />
|
|
||||||
</p>
|
|
||||||
{% endraw %}
|
|
||||||
<div class="row q-mt-lg q-gutter-sm">
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
@click="copyText(qrCodeDialog.data.lnurl, 'LNURL copied to clipboard!')"
|
|
||||||
class="q-ml-sm"
|
|
||||||
>Copy LNURL</q-btn
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
@click="copyText(qrCodeDialog.data.pay_url, 'Link copied to clipboard!')"
|
|
||||||
>Shareable link</q-btn
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
icon="print"
|
|
||||||
type="a"
|
|
||||||
:href="qrCodeDialog.data.print_url"
|
|
||||||
target="_blank"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
||||||
<script src="/scrub/static/js/index.js"></script>
|
<script src="/scrub/static/js/index.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ from lnbits.utils.exchange_rates import currencies, get_fiat_rate_satoshis
|
||||||
|
|
||||||
from . import scrub_ext
|
from . import scrub_ext
|
||||||
from .crud import (
|
from .crud import (
|
||||||
create_pay_link,
|
create_scrub_link,
|
||||||
delete_pay_link,
|
delete_scrub_link,
|
||||||
get_pay_link,
|
get_scrub_link,
|
||||||
get_pay_links,
|
get_scrub_links,
|
||||||
update_pay_link,
|
update_scrub_link,
|
||||||
)
|
)
|
||||||
from .models import CreateScrubLinkData
|
from .models import CreateScrubLinkData
|
||||||
|
|
||||||
|
|
@ -39,14 +39,14 @@ async def api_links(
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return [
|
return [
|
||||||
{**link.dict(), "lnurl": link.lnurl(req)}
|
{**link.dict()}
|
||||||
for link in await get_pay_links(wallet_ids)
|
for link in await get_pay_links(wallet_ids)
|
||||||
]
|
]
|
||||||
|
|
||||||
except LnurlInvalidUrl:
|
except:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.UPGRADE_REQUIRED,
|
status_code=HTTPStatus.NOT_FOUND,
|
||||||
detail="LNURLs need to be delivered over a publically accessible `https` domain or Tor.",
|
detail="No links available",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue