Merge pull request #697 from leesalminen/tpos-tips
Enhancements to TPOS Extension
This commit is contained in:
commit
c2399cf9aa
10 changed files with 298 additions and 39 deletions
|
|
@ -1,7 +1,10 @@
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from lnbits.db import Database
|
from lnbits.db import Database
|
||||||
from lnbits.helpers import template_renderer
|
from lnbits.helpers import template_renderer
|
||||||
|
from lnbits.tasks import catch_everything_and_restart
|
||||||
|
|
||||||
db = Database("ext_tpos")
|
db = Database("ext_tpos")
|
||||||
|
|
||||||
|
|
@ -11,6 +14,10 @@ tpos_ext: APIRouter = APIRouter(prefix="/tpos", tags=["TPoS"])
|
||||||
def tpos_renderer():
|
def tpos_renderer():
|
||||||
return template_renderer(["lnbits/extensions/tpos/templates"])
|
return template_renderer(["lnbits/extensions/tpos/templates"])
|
||||||
|
|
||||||
|
from .tasks import wait_for_paid_invoices
|
||||||
from .views_api import * # noqa
|
from .views_api import * # noqa
|
||||||
from .views import * # noqa
|
from .views import * # noqa
|
||||||
|
|
||||||
|
def tpos_start():
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
"name": "TPoS",
|
"name": "TPoS",
|
||||||
"short_description": "A shareable PoS terminal!",
|
"short_description": "A shareable PoS terminal!",
|
||||||
"icon": "dialpad",
|
"icon": "dialpad",
|
||||||
"contributors": ["talvasconcelos", "arcbtc"]
|
"contributors": ["talvasconcelos", "arcbtc", "leesalminen"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ async def create_tpos(wallet_id: str, data: CreateTposData) -> TPoS:
|
||||||
tpos_id = urlsafe_short_hash()
|
tpos_id = urlsafe_short_hash()
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO tpos.tposs (id, wallet, name, currency)
|
INSERT INTO tpos.tposs (id, wallet, name, currency, tip_options, tip_wallet)
|
||||||
VALUES (?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(tpos_id, wallet_id, data.name, data.currency),
|
(tpos_id, wallet_id, data.name, data.currency, data.tip_options, data.tip_wallet),
|
||||||
)
|
)
|
||||||
|
|
||||||
tpos = await get_tpos(tpos_id)
|
tpos = await get_tpos(tpos_id)
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,23 @@ async def m001_initial(db):
|
||||||
);
|
);
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def m002_addtip_wallet(db):
|
||||||
|
"""
|
||||||
|
Add tips to tposs table
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE tpos.tposs ADD tip_wallet TEXT NULL;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
async def m003_addtip_options(db):
|
||||||
|
"""
|
||||||
|
Add tips to tposs table
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE tpos.tposs ADD tip_options TEXT NULL;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
@ -6,6 +6,8 @@ from pydantic import BaseModel
|
||||||
class CreateTposData(BaseModel):
|
class CreateTposData(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
currency: str
|
currency: str
|
||||||
|
tip_options: str
|
||||||
|
tip_wallet: str
|
||||||
|
|
||||||
|
|
||||||
class TPoS(BaseModel):
|
class TPoS(BaseModel):
|
||||||
|
|
@ -13,6 +15,8 @@ class TPoS(BaseModel):
|
||||||
wallet: str
|
wallet: str
|
||||||
name: str
|
name: str
|
||||||
currency: str
|
currency: str
|
||||||
|
tip_options: str
|
||||||
|
tip_wallet: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_row(cls, row: Row) -> "TPoS":
|
def from_row(cls, row: Row) -> "TPoS":
|
||||||
|
|
|
||||||
70
lnbits/extensions/tpos/tasks.py
Normal file
70
lnbits/extensions/tpos/tasks.py
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
|
||||||
|
from lnbits.core import db as core_db
|
||||||
|
from lnbits.core.crud import create_payment
|
||||||
|
from lnbits.core.models import Payment
|
||||||
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
from lnbits.tasks import internal_invoice_queue, register_invoice_listener
|
||||||
|
|
||||||
|
from .crud import get_tpos
|
||||||
|
|
||||||
|
|
||||||
|
async def wait_for_paid_invoices():
|
||||||
|
invoice_queue = asyncio.Queue()
|
||||||
|
register_invoice_listener(invoice_queue)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
payment = await invoice_queue.get()
|
||||||
|
await on_invoice_paid(payment)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_invoice_paid(payment: Payment) -> None:
|
||||||
|
if "tpos" == payment.extra.get("tag") and payment.extra.get("tipSplitted"):
|
||||||
|
# already splitted, ignore
|
||||||
|
return
|
||||||
|
|
||||||
|
# now we make some special internal transfers (from no one to the receiver)
|
||||||
|
tpos = await get_tpos(payment.extra.get("tposId"))
|
||||||
|
|
||||||
|
tipAmount = payment.extra.get("tipAmount")
|
||||||
|
|
||||||
|
if tipAmount is None:
|
||||||
|
#no tip amount
|
||||||
|
return
|
||||||
|
|
||||||
|
tipAmount = tipAmount * 1000
|
||||||
|
|
||||||
|
# mark the original payment with one extra key, "splitted"
|
||||||
|
# (this prevents us from doing this process again and it's informative)
|
||||||
|
# and reduce it by the amount we're going to send to the producer
|
||||||
|
await core_db.execute(
|
||||||
|
"""
|
||||||
|
UPDATE apipayments
|
||||||
|
SET extra = ?, amount = amount - ?
|
||||||
|
WHERE hash = ?
|
||||||
|
AND checking_id NOT LIKE 'internal_%'
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
json.dumps(dict(**payment.extra, tipSplitted=True)),
|
||||||
|
tipAmount,
|
||||||
|
payment.payment_hash,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# perform the internal transfer using the same payment_hash
|
||||||
|
internal_checking_id = f"internal_{urlsafe_short_hash()}"
|
||||||
|
await create_payment(
|
||||||
|
wallet_id=tpos.tip_wallet,
|
||||||
|
checking_id=internal_checking_id,
|
||||||
|
payment_request="",
|
||||||
|
payment_hash=payment.payment_hash,
|
||||||
|
amount=tipAmount,
|
||||||
|
memo=payment.memo,
|
||||||
|
pending=False,
|
||||||
|
extra={"tipSplitted": True},
|
||||||
|
)
|
||||||
|
|
||||||
|
# manually send this for now
|
||||||
|
await internal_invoice_queue.put(internal_checking_id)
|
||||||
|
return
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
></q-btn>
|
></q-btn>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
{{ col.value }}
|
{{ (col.name == 'tip_options' ? JSON.parse(col.value).join(", ") : col.value) }}
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td auto-width>
|
<q-td auto-width>
|
||||||
<q-btn
|
<q-btn
|
||||||
|
|
@ -116,6 +116,29 @@
|
||||||
:options="currencyOptions"
|
:options="currencyOptions"
|
||||||
label="Currency *"
|
label="Currency *"
|
||||||
></q-select>
|
></q-select>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
emit-value
|
||||||
|
v-model="formDialog.data.tip_wallet"
|
||||||
|
:options="g.user.walletOptions"
|
||||||
|
label="Tip Wallet"
|
||||||
|
></q-select>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
multiple
|
||||||
|
dense
|
||||||
|
emit-value
|
||||||
|
v-model="formDialog.data.tip_options"
|
||||||
|
v-if="formDialog.data.tip_wallet"
|
||||||
|
use-input
|
||||||
|
use-chips
|
||||||
|
multiple
|
||||||
|
hide-dropdown-icon
|
||||||
|
input-debounce="0"
|
||||||
|
new-value-mode="add-unique"
|
||||||
|
label="Tip % Options"
|
||||||
|
></q-select>
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
|
|
@ -333,7 +356,19 @@
|
||||||
align: 'left',
|
align: 'left',
|
||||||
label: 'Currency',
|
label: 'Currency',
|
||||||
field: 'currency'
|
field: 'currency'
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
name: 'tip_wallet',
|
||||||
|
align: 'left',
|
||||||
|
label: "Tip Wallet",
|
||||||
|
field: "tip_wallet",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tip_options',
|
||||||
|
align: 'left',
|
||||||
|
label: "Tip Options %",
|
||||||
|
field: "tip_options",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
pagination: {
|
pagination: {
|
||||||
rowsPerPage: 10
|
rowsPerPage: 10
|
||||||
|
|
@ -367,7 +402,9 @@
|
||||||
createTPoS: function () {
|
createTPoS: function () {
|
||||||
var data = {
|
var data = {
|
||||||
name: this.formDialog.data.name,
|
name: this.formDialog.data.name,
|
||||||
currency: this.formDialog.data.currency
|
currency: this.formDialog.data.currency,
|
||||||
|
tip_options: (this.formDialog.data.tip_options ? JSON.stringify(this.formDialog.data.tip_options.map(str => parseInt(str))) : JSON.stringify([])),
|
||||||
|
tip_wallet: this.formDialog.data.tip_wallet || "",
|
||||||
}
|
}
|
||||||
var self = this
|
var self = this
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,17 @@
|
||||||
{% extends "public.html" %} {% block toolbar_title %}{{ tpos.name }}{% endblock
|
<link rel="manifest" href="/tpos/manifest/{{ tpos.id }}.webmanifest" />
|
||||||
%} {% block footer %}{% endblock %} {% block page_container %}
|
{% extends "public.html" %}
|
||||||
|
{% block toolbar_title %}
|
||||||
|
{{ tpos.name }}
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
size="md"
|
||||||
|
@click.prevent="urlDialog.show = true"
|
||||||
|
icon="share"
|
||||||
|
color="white"
|
||||||
|
></q-btn>
|
||||||
|
{% endblock %}
|
||||||
|
{% block footer %}{% endblock %} {% block page_container %}
|
||||||
<q-page-container>
|
<q-page-container>
|
||||||
<q-page>
|
<q-page>
|
||||||
<q-page-sticky v-if="exchangeRate" expand position="top">
|
<q-page-sticky v-if="exchangeRate" expand position="top">
|
||||||
|
|
@ -43,16 +55,6 @@
|
||||||
color="primary"
|
color="primary"
|
||||||
>3</q-btn
|
>3</q-btn
|
||||||
>
|
>
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
@click="stack = []"
|
|
||||||
size="xl"
|
|
||||||
:outline="!($q.dark.isActive)"
|
|
||||||
rounded
|
|
||||||
color="primary"
|
|
||||||
class="btn-cancel"
|
|
||||||
>C</q-btn
|
|
||||||
>
|
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
@click="stack.push(4)"
|
@click="stack.push(4)"
|
||||||
|
|
@ -107,17 +109,6 @@
|
||||||
color="primary"
|
color="primary"
|
||||||
>9</q-btn
|
>9</q-btn
|
||||||
>
|
>
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
:disabled="amount == 0"
|
|
||||||
@click="showInvoice()"
|
|
||||||
size="xl"
|
|
||||||
:outline="!($q.dark.isActive)"
|
|
||||||
rounded
|
|
||||||
color="primary"
|
|
||||||
class="btn-confirm"
|
|
||||||
>OK</q-btn
|
|
||||||
>
|
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
@click="stack.splice(-1, 1)"
|
@click="stack.splice(-1, 1)"
|
||||||
|
|
@ -138,12 +129,24 @@
|
||||||
>
|
>
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
@click="urlDialog.show = true"
|
@click="stack = []"
|
||||||
size="xl"
|
size="xl"
|
||||||
:outline="!($q.dark.isActive)"
|
:outline="!($q.dark.isActive)"
|
||||||
rounded
|
rounded
|
||||||
color="primary"
|
color="primary"
|
||||||
>#</q-btn
|
class="btn-cancel"
|
||||||
|
>C</q-btn
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
:disabled="amount == 0"
|
||||||
|
@click="submitForm()"
|
||||||
|
size="xl"
|
||||||
|
:outline="!($q.dark.isActive)"
|
||||||
|
rounded
|
||||||
|
color="primary"
|
||||||
|
class="btn-confirm"
|
||||||
|
>OK</q-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -176,6 +179,38 @@
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog
|
||||||
|
v-model="tipDialog.show"
|
||||||
|
position="top"
|
||||||
|
>
|
||||||
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
|
<div class="text-center q-mb-xl">
|
||||||
|
<b style="font-size: 24px;">Would you like to leave a tip?</b>
|
||||||
|
</div>
|
||||||
|
<div class="text-center q-mb-xl">
|
||||||
|
<q-btn
|
||||||
|
style="padding: 10px; margin: 3px;"
|
||||||
|
unelevated
|
||||||
|
@click="processTipSelection(tip)"
|
||||||
|
size="xl"
|
||||||
|
:outline="!($q.dark.isActive)"
|
||||||
|
rounded
|
||||||
|
color="primary"
|
||||||
|
v-for="tip in this.tip_options"
|
||||||
|
:key="tip"
|
||||||
|
>{% raw %}{{ tip }}{% endraw %}%</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="text-center q-mb-xl">
|
||||||
|
<p><a @click="processTipSelection(0)"> No, thanks</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
<q-dialog v-model="urlDialog.show" position="top">
|
<q-dialog v-model="urlDialog.show" position="top">
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
|
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
|
||||||
|
|
@ -214,6 +249,10 @@
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
{% endblock %} {% block styles %}
|
{% endblock %} {% block styles %}
|
||||||
<style>
|
<style>
|
||||||
|
* {
|
||||||
|
touch-action: manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
.keypad {
|
.keypad {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 8px;
|
grid-gap: 8px;
|
||||||
|
|
@ -225,9 +264,8 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-cancel,
|
.keypad .btn-confirm {
|
||||||
.btn-confirm {
|
grid-area: 1 / 4 / 5 / 4;
|
||||||
grid-row: auto/span 2;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %} {% block scripts %}
|
{% endblock %} {% block scripts %}
|
||||||
|
|
@ -241,14 +279,19 @@
|
||||||
return {
|
return {
|
||||||
tposId: '{{ tpos.id }}',
|
tposId: '{{ tpos.id }}',
|
||||||
currency: '{{ tpos.currency }}',
|
currency: '{{ tpos.currency }}',
|
||||||
|
tip_options: JSON.parse('{{ tpos.tip_options }}'),
|
||||||
exchangeRate: null,
|
exchangeRate: null,
|
||||||
stack: [],
|
stack: [],
|
||||||
|
tipAmount: 0.00,
|
||||||
invoiceDialog: {
|
invoiceDialog: {
|
||||||
show: false,
|
show: false,
|
||||||
data: null,
|
data: null,
|
||||||
dismissMsg: null,
|
dismissMsg: null,
|
||||||
paymentChecker: null
|
paymentChecker: null
|
||||||
},
|
},
|
||||||
|
tipDialog: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
urlDialog: {
|
urlDialog: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
|
|
@ -269,6 +312,10 @@
|
||||||
if (!this.exchangeRate) return 0
|
if (!this.exchangeRate) return 0
|
||||||
return Math.ceil((this.amount / this.exchangeRate) * 100000000)
|
return Math.ceil((this.amount / this.exchangeRate) * 100000000)
|
||||||
},
|
},
|
||||||
|
tipAmountSat: function () {
|
||||||
|
if (!this.exchangeRate) return 0
|
||||||
|
return Math.ceil((this.tipAmount / this.exchangeRate) * 100000000)
|
||||||
|
},
|
||||||
fsat: function () {
|
fsat: function () {
|
||||||
console.log('sat', this.sat, LNbits.utils.formatSat(this.sat))
|
console.log('sat', this.sat, LNbits.utils.formatSat(this.sat))
|
||||||
return LNbits.utils.formatSat(this.sat)
|
return LNbits.utils.formatSat(this.sat)
|
||||||
|
|
@ -277,12 +324,46 @@
|
||||||
methods: {
|
methods: {
|
||||||
closeInvoiceDialog: function () {
|
closeInvoiceDialog: function () {
|
||||||
this.stack = []
|
this.stack = []
|
||||||
|
this.tipAmount = 0.00
|
||||||
var dialog = this.invoiceDialog
|
var dialog = this.invoiceDialog
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
clearInterval(dialog.paymentChecker)
|
clearInterval(dialog.paymentChecker)
|
||||||
dialog.dismissMsg()
|
dialog.dismissMsg()
|
||||||
}, 3000)
|
}, 3000)
|
||||||
},
|
},
|
||||||
|
processTipSelection: function (selectedTipOption) {
|
||||||
|
this.tipDialog.show = false
|
||||||
|
|
||||||
|
if(selectedTipOption) {
|
||||||
|
const tipAmount = parseFloat(parseFloat((selectedTipOption / 100) * this.amount))
|
||||||
|
const subtotal = parseFloat(this.amount)
|
||||||
|
const grandTotal = parseFloat((tipAmount + subtotal).toFixed(2))
|
||||||
|
const totalString = grandTotal.toFixed(2).toString()
|
||||||
|
|
||||||
|
this.stack = []
|
||||||
|
for (var i = 0; i < totalString.length; i++) {
|
||||||
|
const char = totalString[i]
|
||||||
|
|
||||||
|
if(char !== ".") {
|
||||||
|
this.stack.push(char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tipAmount = tipAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showInvoice()
|
||||||
|
},
|
||||||
|
submitForm: function() {
|
||||||
|
if(this.tip_options.length) {
|
||||||
|
this.showTipModal()
|
||||||
|
} else {
|
||||||
|
this.showInvoice()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showTipModal: function() {
|
||||||
|
this.tipDialog.show = true
|
||||||
|
},
|
||||||
showInvoice: function () {
|
showInvoice: function () {
|
||||||
var self = this
|
var self = this
|
||||||
var dialog = this.invoiceDialog
|
var dialog = this.invoiceDialog
|
||||||
|
|
@ -290,7 +371,8 @@
|
||||||
axios
|
axios
|
||||||
.post('/tpos/api/v1/tposs/' + this.tposId + '/invoices', null, {
|
.post('/tpos/api/v1/tposs/' + this.tposId + '/invoices', null, {
|
||||||
params: {
|
params: {
|
||||||
amount: this.sat
|
amount: this.sat,
|
||||||
|
tipAmount: this.tipAmountSat,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@ from starlette.responses import HTMLResponse
|
||||||
|
|
||||||
from lnbits.core.models import User
|
from lnbits.core.models import User
|
||||||
from lnbits.decorators import check_user_exists
|
from lnbits.decorators import check_user_exists
|
||||||
|
from lnbits.settings import (
|
||||||
|
LNBITS_CUSTOM_LOGO,
|
||||||
|
LNBITS_SITE_TITLE,
|
||||||
|
)
|
||||||
|
|
||||||
from . import tpos_ext, tpos_renderer
|
from . import tpos_ext, tpos_renderer
|
||||||
from .crud import get_tpos
|
from .crud import get_tpos
|
||||||
|
|
@ -33,3 +37,37 @@ async def tpos(request: Request, tpos_id):
|
||||||
return tpos_renderer().TemplateResponse(
|
return tpos_renderer().TemplateResponse(
|
||||||
"tpos/tpos.html", {"request": request, "tpos": tpos}
|
"tpos/tpos.html", {"request": request, "tpos": tpos}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@tpos_ext.get("/manifest/{tpos_id}.webmanifest")
|
||||||
|
async def manifest(tpos_id: str):
|
||||||
|
tpos = await get_tpos(tpos_id)
|
||||||
|
if not tpos:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"short_name": LNBITS_SITE_TITLE,
|
||||||
|
"name": tpos.name + ' - ' + LNBITS_SITE_TITLE,
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": LNBITS_CUSTOM_LOGO if LNBITS_CUSTOM_LOGO else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "900x900",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/tpos/" + tpos_id,
|
||||||
|
"background_color": "#1F2234",
|
||||||
|
"description": "Bitcoin Lightning tPOS",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/tpos/" + tpos_id,
|
||||||
|
"theme_color": "#1F2234",
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": tpos.name + ' - ' + LNBITS_SITE_TITLE,
|
||||||
|
"short_name": tpos.name,
|
||||||
|
"description": tpos.name + ' - ' + LNBITS_SITE_TITLE,
|
||||||
|
"url": "/tpos/" + tpos_id,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ async def api_tpos_delete(
|
||||||
|
|
||||||
|
|
||||||
@tpos_ext.post("/api/v1/tposs/{tpos_id}/invoices", status_code=HTTPStatus.CREATED)
|
@tpos_ext.post("/api/v1/tposs/{tpos_id}/invoices", status_code=HTTPStatus.CREATED)
|
||||||
async def api_tpos_create_invoice(amount: int = Query(..., ge=1), tpos_id: str = None):
|
async def api_tpos_create_invoice(amount: int = Query(..., ge=1), tipAmount: int = None, tpos_id: str = None):
|
||||||
tpos = await get_tpos(tpos_id)
|
tpos = await get_tpos(tpos_id)
|
||||||
|
|
||||||
if not tpos:
|
if not tpos:
|
||||||
|
|
@ -65,7 +65,7 @@ async def api_tpos_create_invoice(amount: int = Query(..., ge=1), tpos_id: str =
|
||||||
wallet_id=tpos.wallet,
|
wallet_id=tpos.wallet,
|
||||||
amount=amount,
|
amount=amount,
|
||||||
memo=f"{tpos.name}",
|
memo=f"{tpos.name}",
|
||||||
extra={"tag": "tpos"},
|
extra={"tag": "tpos", "tipAmount": tipAmount, "tposId": tpos_id},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
|
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
|
||||||
|
|
@ -84,6 +84,7 @@ async def api_tpos_check_invoice(tpos_id: str, payment_hash: str):
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
status = await api_payment(payment_hash)
|
status = await api_payment(payment_hash)
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(exc)
|
print(exc)
|
||||||
return {"paid": False}
|
return {"paid": False}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue