Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: lnaddress funding for pos #112

Merged
merged 3 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def tpos_start():
__all__ = [
"db",
"tpos_ext",
"tpos_static_files",
"tpos_start",
"tpos_static_files",
"tpos_stop",
]
6 changes: 5 additions & 1 deletion crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from lnbits.db import Database
from lnbits.helpers import urlsafe_short_hash
from loguru import logger

from .models import CreateTposData, LnurlCharge, Tpos, TposClean

Expand Down Expand Up @@ -78,9 +79,12 @@ async def get_tposs(wallet_ids: Union[str, list[str]]) -> list[Tpos]:
if isinstance(wallet_ids, str):
wallet_ids = [wallet_ids]
q = ",".join([f"'{wallet_id}'" for wallet_id in wallet_ids])
return await db.fetchall(
tposs = await db.fetchall(
f"SELECT * FROM tpos.pos WHERE wallet IN ({q})", model=Tpos
)
logger.debug("tposs")
logger.debug(tposs)
return tposs


async def delete_tpos(tpos_id: str) -> None:
Expand Down
18 changes: 18 additions & 0 deletions helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import httpx
from lnbits.core.views.api import api_lnurlscan


async def get_pr(ln_address, amount):
try:
data = await api_lnurlscan(ln_address)
if data.get("status") == "ERROR":
return
async with httpx.AsyncClient() as client:
response = await client.get(
url=f"{data['callback']}?amount={int(amount) * 1000}"
)
if response.status_code != 200:
return
return response.json()["pr"]
except Exception:
return None
22 changes: 22 additions & 0 deletions migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,25 @@ async def m010_rename_tpos_withdraw_columns(db: Database):
)
await db.execute("DROP TABLE tpos.pos")
await db.execute("ALTER TABLE tpos.pos_backup RENAME TO pos")


async def m011_lnaddress(db: Database):
"""
Add lnaddress to tpos table
"""
await db.execute(
"""
ALTER TABLE tpos.pos ADD lnaddress BOOLEAN DEFAULT false;
"""
)


async def m012_addlnaddress(db: Database):
"""
Add lnaddress_cut to tpos table
"""
await db.execute(
"""
ALTER TABLE tpos.pos ADD lnaddress_cut TEXT NULL;
"""
)
9 changes: 9 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class CreateTposInvoice(BaseModel):
memo: Optional[str] = Query(None)
details: Optional[dict] = Query(None)
tip_amount: Optional[int] = Query(None, ge=1)
user_lnaddress: Optional[str] = Query(None)


class CreateTposData(BaseModel):
Expand All @@ -32,6 +33,8 @@ class CreateTposData(BaseModel):
withdraw_time_option: Optional[str] = Field(None)
withdraw_premium: Optional[float] = Field(None)
withdraw_pin_disabled: bool = Field(False)
lnaddress: bool = Field(False)
lnaddress_cut: Optional[int] = Field(0)


class TposClean(BaseModel):
Expand All @@ -47,6 +50,8 @@ class TposClean(BaseModel):
withdraw_premium: Optional[float] = None
withdraw_pin_disabled: Optional[bool] = None
withdrawn_amount: int = 0
lnaddress: Optional[bool] = None
lnaddress_cut: int = 0
items: Optional[str] = None
tip_options: Optional[str] = None

Expand Down Expand Up @@ -89,6 +94,10 @@ class PayLnurlWData(BaseModel):
lnurl: str


class LNaddress(BaseModel):
lnaddress: str


class Item(BaseModel):
image: Optional[str]
price: float
Expand Down
71 changes: 36 additions & 35 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ authors = ["Alan Bits <[email protected]>"]
[tool.poetry.dependencies]
python = "^3.10 | ^3.9"
lnbits = {version = "*", allow-prereleases = true}
mypy = "^1.13.0"

[tool.poetry.group.dev.dependencies]
black = "^24.3.0"
Expand Down
17 changes: 15 additions & 2 deletions static/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ window.app = Vue.createApp({
align: 'left',
label: 'atm pin disabled',
field: 'withdraw_pin_disabled'
},
{
name: 'lnaddress',
align: 'left',
label: 'LNaddress',
field: 'lnaddress'
},
{
name: 'lnaddress_cut',
align: 'left',
label: 'LNaddress Cut',
field: 'lnaddress_cut'
}
],
pagination: {
Expand All @@ -71,7 +83,9 @@ window.app = Vue.createApp({
withdraw_between: 10,
withdraw_time_option: '',
withdraw_pin_disabled: false,
tax_inclusive: true
tax_inclusive: true,
lnaddress: false,
lnaddress_cut: 2
},
advanced: {
tips: false,
Expand Down Expand Up @@ -170,7 +184,6 @@ window.app = Vue.createApp({
},
getTposs: function () {
var self = this

LNbits.api
.request(
'GET',
Expand Down
60 changes: 59 additions & 1 deletion static/js/tpos.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ window.app = Vue.createApp({
atmMode: false,
atmToken: '',
nfcTagReading: false,
lnaddressDialog: {
show: false,
lnaddress: ''
},
lastPaymentsDialog: {
show: false,
data: []
Expand Down Expand Up @@ -283,6 +287,29 @@ window.app = Vue.createApp({
LNbits.utils.notifyApiError(error)
})
},
lnaddressSubmit() {
LNbits.api
.request(
'GET',
`/tpos/api/v1/tposs/lnaddresscheck?lnaddress=${encodeURIComponent(this.lnaddressDialog.lnaddress)}`,
null
)
.then(response => {
if (response.data) {
this.$q.localStorage.set(
'tpos.lnaddress',
this.lnaddressDialog.lnaddress
)
this.lnaddressDialog.show = false
this.lnaddress = true
}
})
.catch(error => {
const errorMessage =
error.response?.data?.detail || 'An unknown error occurred.'
LNbits.utils.notifyApiError(errorMessage)
})
},
atmGetWithdraw() {
var dialog = this.invoiceDialog
if (this.sat > this.withdrawMaximum) {
Expand Down Expand Up @@ -452,7 +479,9 @@ window.app = Vue.createApp({
taxValue: this.cartTax
}
}

if (this.lnaddress) {
params.user_lnaddress = this.lnaddressDialog.lnaddress
}
axios
.post(`/tpos/api/v1/tposs/${this.tposId}/invoices`, params)
.then(response => {
Expand Down Expand Up @@ -630,6 +659,7 @@ window.app = Vue.createApp({
.request('GET', `/tpos/api/v1/rate/${this.currency}`)
.then(response => {
this.exchangeRate = response.data.rate
console.log(this.exchangeRate)
Quasar.Loading.hide()
})
.catch(e => console.error(e))
Expand Down Expand Up @@ -669,6 +699,15 @@ window.app = Vue.createApp({
handleColorScheme(val) {
this.$q.localStorage.set('lnbits.tpos.color', val)
},
clearLNaddress() {
this.$q.localStorage.remove('tpos.lnaddress')
this.lnaddressDialog.lnaddress = ''
const url = new URL(window.location.href)
url.searchParams.delete('lnaddress')
window.history.replaceState({}, document.title, url.toString())
this.lnaddressDialog.show = true
this.lnaddress = false
},
extractCategories(items) {
let categories = new Set()
items
Expand Down Expand Up @@ -718,6 +757,8 @@ window.app = Vue.createApp({
this.pinDisabled = tpos.withdraw_pin_disabled
this.taxInclusive = tpos.tax_inclusive
this.taxDefault = tpos.tax_default
this.tposLNaddress = tpos.lnaddress
this.tposLNaddressCut = tpos.lnaddress_cut

this.tip_options = tpos.tip_options == 'null' ? null : tpos.tip_options

Expand All @@ -735,6 +776,23 @@ window.app = Vue.createApp({
this.showPoS = false
this.categories = this.extractCategories(this.items)
}
if (this.tposLNaddress) {
this.lnaddressDialog.lnaddress =
this.$q.localStorage.getItem('tpos.lnaddress')
if (lnaddressparam != '') {
this.lnaddressDialog.lnaddress = lnaddressparam
this.$q.localStorage.set(
'tpos.lnaddress',
this.lnaddressDialog.lnaddress
)
this.lnaddress = true
} else if (!this.lnaddressDialog.lnaddress) {
this.lnaddress = false
this.lnaddressDialog.show = true
} else {
this.lnaddress = true
}
}

window.addEventListener('keyup', event => {
// do nothing if the event was already processed
Expand Down
12 changes: 12 additions & 0 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from loguru import logger

from .crud import get_tpos
from .helpers import get_pr


async def wait_for_paid_invoices():
Expand Down Expand Up @@ -40,6 +41,17 @@ async def on_invoice_paid(payment: Payment) -> None:

tpos = await get_tpos(tpos_id)
assert tpos
if payment.extra.get("lnaddress") and payment.extra["lnaddress"] != "":
calc_amount = payment.amount - ((payment.amount / 100) * tpos.lnaddress_cut)
pr = await get_pr(payment.extra.get("lnaddress"), calc_amount / 1000)
if pr:
payment.extra["lnaddress"] = ""
paid_payment = await pay_invoice(
payment_request=pr,
wallet_id=payment.wallet_id,
extra={**payment.extra},
)
logger.debug(f"tpos: LNaddress paid cut: {paid_payment.checking_id}")

await websocket_updater(tpos_id, str(stripped_payment))

Expand Down
Loading
Loading