diff --git a/crud.py b/crud.py
index 19d9c19..3c3b895 100644
--- a/crud.py
+++ b/crud.py
@@ -11,21 +11,15 @@
async def create_lnpos(data: CreateLnpos) -> Lnpos:
- if data.device == "pos" or data.device == "atm":
- lnpos_id = shortuuid.uuid()[:5]
- else:
- lnpos_id = urlsafe_short_hash()
-
+ lnpos_id = shortuuid.uuid()[:5]
lnpos_key = urlsafe_short_hash()
-
device = Lnpos(
id=lnpos_id,
key=lnpos_key,
title=data.title,
wallet=data.wallet,
- profit=data.profit,
- currency=data.currency,
- device=data.device,
+ profit=data.profit or 0.0,
+ currency=data.currency or "sat",
)
await db.insert("lnpos.lnpos", device)
@@ -80,6 +74,7 @@ async def get_lnpos_payment(
return await db.fetchone(
"SELECT * FROM lnpos.lnpos_payment WHERE id = :id",
{"id": lnpos_payment_id},
+ LnposPayment,
)
@@ -104,6 +99,7 @@ async def get_lnpos_payment_by_payhash(
return await db.fetchone(
"SELECT * FROM lnpos.lnpos_payment WHERE payhash = :payhash",
{"payhash": payhash},
+ LnposPayment,
)
diff --git a/migrations.py b/migrations.py
index ca11cb4..f4543bd 100644
--- a/migrations.py
+++ b/migrations.py
@@ -15,7 +15,6 @@ async def m001_initial(db):
title TEXT NOT NULL,
wallet TEXT NOT NULL,
currency TEXT NOT NULL,
- device TEXT NOT NULL,
profit FLOAT NOT NULL,
timestamp TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
);
diff --git a/models.py b/models.py
index 385b137..cd6cbc3 100644
--- a/models.py
+++ b/models.py
@@ -1,4 +1,5 @@
import json
+from typing import Optional
from lnurl.types import LnurlPayMetadata
from pydantic import BaseModel
@@ -7,9 +8,8 @@
class CreateLnpos(BaseModel):
title: str
wallet: str
- currency: str
- device: str
- profit: float
+ currency: Optional[str] = "sat"
+ profit: Optional[float] = None
class Lnpos(BaseModel):
@@ -19,7 +19,6 @@ class Lnpos(BaseModel):
wallet: str
profit: float
currency: str
- device: str
@property
def lnurlpay_metadata(self) -> LnurlPayMetadata:
@@ -33,7 +32,3 @@ class LnposPayment(BaseModel):
payload: str
pin: int
sats: int
-
-
-class Lnurlencode(BaseModel):
- url: str
diff --git a/static/js/index.js b/static/js/index.js
index ad71493..1f2fcac 100644
--- a/static/js/index.js
+++ b/static/js/index.js
@@ -7,10 +7,8 @@ window.app = Vue.createApp({
location: window.location.hostname,
filter: '',
currency: 'USD',
- lnurlValue: '',
+ deviceString: '',
lnposs: [],
- atmLinks: [],
- lnpossObj: [],
lnposTable: {
columns: [
{
@@ -48,7 +46,6 @@ window.app = Vue.createApp({
rowsPerPage: 10
}
},
- passedlnurldevice: {},
settingsDialog: {
show: false,
data: {}
@@ -56,26 +53,13 @@ window.app = Vue.createApp({
formDialog: {
show: false,
data: {}
- },
- formDialog: {
- show: false,
- data: {
- lnurl_toggle: false,
- show_message: false,
- show_ack: false,
- show_price: 'None',
- device: 'pos',
- profit: 1,
- amount: 1,
- title: ''
- }
}
}
},
methods: {
cancelLnpos() {
- self.formDialog.show = false
- self.clearFormDialog()
+ this.formDialog.show = false
+ this.clearFormDialog()
},
closeFormDialog() {
this.clearFormDialog()
@@ -84,17 +68,17 @@ window.app = Vue.createApp({
}
},
sendFormData() {
- if (!self.formDialog.data.profit) {
- self.formDialog.data.profit = 0
+ if (!this.formDialog.data.profit) {
+ this.formDialog.data.profit = 0
}
- if (self.formDialog.data.id) {
- this.updateLnpos(self.g.user.wallets[0].adminkey, self.formDialog.data)
+ if (this.formDialog.data.id) {
+ this.updateLnpos(this.g.user.wallets[0].adminkey, this.formDialog.data)
} else {
- this.createLnpos(self.g.user.wallets[0].adminkey, self.formDialog.data)
+ this.createLnpos(this.g.user.wallets[0].adminkey, this.formDialog.data)
}
},
- createLnpos: function (wallet, data) {
+ createLnpos(wallet, data) {
const updatedData = {}
for (const property in data) {
if (data[property]) {
@@ -102,88 +86,71 @@ window.app = Vue.createApp({
}
}
LNbits.api
- .request('POST', '/lnurldevice/api/v1/lnurlpos', wallet, updatedData)
- .then(function (response) {
- self.lnposs.push(response.data)
- self.formDialog.show = false
- self.clearFormDialog()
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
+ .request('POST', '/lnpos/api/v1', wallet, updatedData)
+ .then(response => {
+ this.lnposs.push(response.data)
+ this.formDialog.show = false
+ this.clearFormDialog()
})
+ .catch(LNbits.utils.notifyApiError)
},
getLnposs() {
LNbits.api
- .request(
- 'GET',
- '/lnurldevice/api/v1/lnurlpos',
- self.g.user.wallets[0].adminkey
- )
- .then(function (response) {
+ .request('GET', '/lnpos/api/v1', this.g.user.wallets[0].adminkey)
+ .then(response => {
if (response.data) {
- self.lnposs = response.data.map(maplnurldevice)
+ this.lnposs = response.data
}
})
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
+ .catch(LNbits.utils.notifyApiError)
},
- getLnpos: function (lnurldevice_id) {
+ getLnpos: lnpos_id => {
LNbits.api
.request(
'GET',
- '/lnurldevice/api/v1/lnurlpos/' + lnurldevice_id,
- self.g.user.wallets[0].adminkey
+ '/lnpos/api/v1/' + lnpos_id,
+ this.g.user.wallets[0].adminkey
)
- .then(function (response) {
- localStorage.setItem('lnurldevice', JSON.stringify(response.data))
- localStorage.setItem('inkey', self.g.user.wallets[0].inkey)
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
+ .then(response => {
+ localStorage.setItem('lnpos', JSON.stringify(response.data))
+ localStorage.setItem('inkey', this.g.user.wallets[0].inkey)
})
+ .catch(LNbits.utils.notifyApiError)
},
- deleteLnpos: function (lnurldeviceId) {
- const link = _.findWhere(this.lnposs, {id: lnurldeviceId})
+ deleteLnpos(lnposId) {
LNbits.utils
.confirmDialog('Are you sure you want to delete this pay link?')
- .onOk(function () {
+ .onOk(() => {
LNbits.api
.request(
'DELETE',
- '/lnurldevice/api/v1/lnurlpos/' + lnurldeviceId,
- self.g.user.wallets[0].adminkey
+ '/lnpos/api/v1/' + lnposId,
+ this.g.user.wallets[0].adminkey
)
- .then(function (response) {
- self.lnposs = _.reject(self.lnposs, function (obj) {
- return obj.id === lnurldeviceId
+ .then(response => {
+ this.lnposs = _.reject(this.lnposs, obj => {
+ return obj.id === lnposId
})
})
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
+ .catch(LNbits.utils.notifyApiError)
})
},
- openUpdateLnpos: function (lnurldeviceId) {
- const lnurldevice = _.findWhere(this.lnposs, {
- id: lnurldeviceId
+ openUpdateLnpos(lnposId) {
+ const lnpos = _.findWhere(this.lnposs, {
+ id: lnposId
})
- self.formDialog.data = _.clone(lnurldevice._data)
- if (lnurldevice.device == 'atm' && lnurldevice.extra == 'boltz') {
- self.boltzToggleState = true
- } else {
- self.boltzToggleState = false
- }
- self.formDialog.show = true
+ this.formDialog.data = _.clone(lnpos)
+ this.formDialog.show = true
},
- openSettings: function (lnurldeviceId) {
- const lnurldevice = _.findWhere(this.lnposs, {
- id: lnurldeviceId
+ openSettings(lnposId) {
+ const lnpos = _.findWhere(this.lnposs, {
+ id: lnposId
})
- self.settingsDialog.data = _.clone(lnurldevice._data)
- self.settingsDialog.show = true
+ this.deviceString = `${this.protocol}//${this.location}/lnpos/api/v1/lnurl/${lnpos.id},${lnpos.key},${lnpos.currency}`
+ this.settingsDialog.data = _.clone(lnpos)
+ this.settingsDialog.show = true
},
- updateLnpos: function (wallet, data) {
+ updateLnpos(wallet, data) {
const updatedData = {}
for (const property in data) {
if (data[property]) {
@@ -192,23 +159,16 @@ window.app = Vue.createApp({
}
LNbits.api
- .request(
- 'PUT',
- '/lnurldevice/api/v1/lnurlpos/' + updatedData.id,
- wallet,
- updatedData
- )
- .then(function (response) {
- self.lnposs = _.reject(self.lnposs, function (obj) {
+ .request('PUT', '/lnpos/api/v1/' + updatedData.id, wallet, updatedData)
+ .then(response => {
+ this.lnposs = _.reject(this.lnposs, obj => {
return obj.id === updatedData.id
})
- self.lnposs.push(response.data)
- self.formDialog.show = false
- self.clearFormDialog()
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
+ this.lnposs.push(response.data)
+ this.formDialog.show = false
+ this.clearFormDialog()
})
+ .catch(LNbits.utils.notifyApiError)
},
clearFormDialog() {
this.formDialog.data = {
@@ -220,7 +180,7 @@ window.app = Vue.createApp({
}
},
exportCSV() {
- LNbits.utils.exportCSV(self.lnposTable.columns, this.lnposs)
+ LNbits.utils.exportCSV(this.lnposTable.columns, this.lnposs)
}
},
created() {
@@ -228,10 +188,8 @@ window.app = Vue.createApp({
LNbits.api
.request('GET', '/api/v1/currencies')
.then(response => {
- this.currency = ['sat', 'USD', ...response.data]
- })
- .catch(err => {
- LNbits.utils.notifyApiError(err)
+ this.currency = response.data
})
+ .catch(LNbits.utils.notifyApiError)
}
})
diff --git a/templates/lnpos/error.html b/templates/lnpos/error.html
index ca5b6db..a50094d 100644
--- a/templates/lnpos/error.html
+++ b/templates/lnpos/error.html
@@ -17,15 +17,12 @@
LNURL-pay not paid
-
- {% endblock %} {% block scripts %}
-
-
-
- {% endblock %}
+{% endblock %} {% block scripts %}
+
+{% endblock %}
diff --git a/templates/lnpos/index.html b/templates/lnpos/index.html
index 6729ede..8bd5343 100644
--- a/templates/lnpos/index.html
+++ b/templates/lnpos/index.html
@@ -128,6 +128,27 @@ {{SITE_TITLE}} LNPoS Extension
+
+
+ Device string
+
+
+ Click to copy URL
+
+
+
+
diff --git a/templates/lnpos/paid.html b/templates/lnpos/paid.html
index c185ecc..e8991c5 100644
--- a/templates/lnpos/paid.html
+++ b/templates/lnpos/paid.html
@@ -10,18 +10,12 @@ {{ pin }}
-
- {% endblock %} {% block scripts %}
-
-
-
- {% endblock %}
+{% endblock %} {% block scripts %}
+
+{% endblock %}
diff --git a/views_api.py b/views_api.py
index 38eb106..da9229b 100644
--- a/views_api.py
+++ b/views_api.py
@@ -1,52 +1,31 @@
from http import HTTPStatus
-import bolt11
-import httpx
from fastapi import APIRouter, Depends, HTTPException
-from lnbits.core.crud import get_user, get_wallet
+from lnbits.core.crud import get_user
from lnbits.core.models import WalletTypeInfo
-from lnbits.core.services import pay_invoice
-from lnbits.core.views.api import api_lnurlscan
from lnbits.decorators import (
- check_user_extension_access,
require_admin_key,
require_invoice_key,
)
-from lnbits.settings import settings
-from lnbits.utils.exchange_rates import currencies
-from lnurl import encode as lnurl_encode
-from loguru import logger
from .crud import (
create_lnpos,
- delete_atm_payment_link,
delete_lnpos,
get_lnpos,
- get_lnpos_payment,
- get_lnpos_payments,
get_lnposs,
update_lnpos,
- update_lnpos_payment,
)
-from .helpers import register_atm_payment
-from .models import CreateLnpos, Lnurlencode
+from .models import CreateLnpos
lnpos_api_router = APIRouter()
-@lnpos_api_router.get("/api/v1/currencies")
-async def api_list_currencies_available():
- return list(currencies.keys())
-
-
-@lnpos_api_router.post("/api/v1/lnurlpos", dependencies=[Depends(require_admin_key)])
+@lnpos_api_router.post("/api/v1", dependencies=[Depends(require_admin_key)])
async def api_lnpos_create(data: CreateLnpos):
return await create_lnpos(data)
-@lnpos_api_router.put(
- "/api/v1/lnurlpos/{lnpos_id}", dependencies=[Depends(require_admin_key)]
-)
+@lnpos_api_router.put("/api/v1/{lnpos_id}", dependencies=[Depends(require_admin_key)])
async def api_lnpos_update(data: CreateLnpos, lnpos_id: str):
lnpos = await get_lnpos(lnpos_id)
if not lnpos:
@@ -58,241 +37,31 @@ async def api_lnpos_update(data: CreateLnpos, lnpos_id: str):
return await update_lnpos(lnpos)
-@lnpos_api_router.get("/api/v1/lnurlpos")
-async def api_lnposs_retrieve(wallet: WalletTypeInfo = Depends(require_invoice_key)):
+@lnpos_api_router.get("/api/v1")
+async def api_lnposs_get(wallet: WalletTypeInfo = Depends(require_invoice_key)):
user = await get_user(wallet.wallet.user)
assert user, "Lnpos cannot retrieve user"
return await get_lnposs(user.wallet_ids)
-@lnpos_api_router.get(
- "/api/v1/lnurlpos/{lnpos_id}", dependencies=[Depends(require_invoice_key)]
-)
-async def api_lnpos_retrieve(lnpos_id: str):
+@lnpos_api_router.get("/api/v1/{lnpos_id}", dependencies=[Depends(require_invoice_key)])
+async def api_lnpos_get(lnpos_id: str):
lnpos = await get_lnpos(lnpos_id)
if not lnpos:
raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="lnpos does not exist"
+ status_code=HTTPStatus.NOT_FOUND, detail="LNPoS does not exist"
)
return lnpos
@lnpos_api_router.delete(
- "/api/v1/lnurlpos/{lnpos_id}", dependencies=[Depends(require_admin_key)]
+ "/api/v1/{lnpos_id}", dependencies=[Depends(require_admin_key)]
)
async def api_lnpos_delete(lnpos_id: str):
lnpos = await get_lnpos(lnpos_id)
if not lnpos:
raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Lnpos does not exist."
+ status_code=HTTPStatus.NOT_FOUND, detail="LNPoS does not exist."
)
await delete_lnpos(lnpos_id)
-
-
-#########ATM API#########
-
-
-@lnpos_api_router.get("/api/v1/atm")
-async def api_atm_payments_retrieve(
- wallet: WalletTypeInfo = Depends(require_invoice_key),
-):
- user = await get_user(wallet.wallet.user)
- assert user, "Lnpos cannot retrieve user"
- lnposs = await get_lnposs(user.wallet_ids)
- deviceids = []
- for lnpos in lnposs:
- if lnpos.device == "atm":
- deviceids.append(lnpos.id)
- return await get_lnpos_payments(deviceids)
-
-
-@lnpos_api_router.post(
- "/api/v1/lnurlencode", dependencies=[Depends(require_invoice_key)]
-)
-async def api_lnurlencode(data: Lnurlencode):
- lnurl = lnurl_encode(data.url)
- logger.debug(lnurl)
- if not lnurl:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Lnurl could not be encoded."
- )
- return lnurl
-
-
-@lnpos_api_router.delete(
- "/api/v1/atm/{atm_id}", dependencies=[Depends(require_admin_key)]
-)
-async def api_atm_payment_delete(atm_id: str):
- lnpos = await get_lnpos_payment(atm_id)
- if not lnpos:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="ATM payment does not exist."
- )
-
- await delete_atm_payment_link(atm_id)
-
-
-@lnpos_api_router.get("/api/v1/ln/{lnpos_id}/{p}/{ln}")
-async def get_lnpos_payment_lightning(lnpos_id: str, p: str, ln: str) -> str:
- """
- Handle Lightning payments for atms via invoice, lnaddress, lnurlp.
- """
- ln = ln.strip().lower()
-
- lnpos = await get_lnpos(lnpos_id)
- if not lnpos:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="lnpos does not exist"
- )
-
- wallet = await get_wallet(lnpos.wallet)
- if not wallet:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND,
- detail="Wallet does not exist connected to atm, payment could not be made",
- )
- lnpos_payment, price_msat = await register_atm_payment(lnpos, p)
- if not lnpos_payment:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Payment already claimed."
- )
-
- # If its an lnaddress or lnurlp get the request from callback
- elif ln[:5] == "lnurl" or "@" in ln and "." in ln.split("@")[-1]:
- data = await api_lnurlscan(ln)
- logger.debug(data)
- if data.get("status") == "ERROR":
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST, detail=data.get("reason")
- )
- async with httpx.AsyncClient() as client:
- response = await client.get(
- url=f"{data['callback']}?amount={lnpos_payment.sats * 1000}"
- )
- if response.status_code != 200:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST,
- detail="Could not get callback from lnurl",
- )
- ln = response.json()["pr"]
-
- # If just an invoice
- elif ln[:4] == "lnbc":
- ln = ln
-
- # If ln is gibberish, return an error
- else:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND,
- detail="""
- Wrong format for payment, could not be made.
- Use LNaddress or LNURLp
- """,
- )
-
- # If its an invoice check its a legit invoice
- if ln[:4] == "lnbc":
- invoice = bolt11.decode(ln)
- assert invoice.amount_msat, "Amountless invoices are not allowed"
- if not invoice.payment_hash:
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not valid payment request"
- )
- if not invoice.payment_hash:
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not valid payment request"
- )
- if int(invoice.amount_msat / 1000) != lnpos_payment.sats:
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN,
- detail="Request is not the same as withdraw amount",
- )
-
- # Finally log the payment and make the payment
- try:
- lnpos_payment, price_msat = await register_atm_payment(lnpos, p)
- assert lnpos_payment
- lnpos_payment.payment_hash = lnpos_payment.payload
- await update_lnpos_payment(lnpos_payment)
- if ln[:4] == "lnbc":
- await pay_invoice(
- wallet_id=lnpos.wallet,
- payment_request=ln,
- max_sat=price_msat,
- extra={"tag": "lnpos", "id": lnpos_payment.id},
- )
- except Exception as exc:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST, detail=f"{exc!s}"
- ) from exc
-
- return lnpos_payment.id
-
-
-@lnpos_api_router.get("/api/v1/boltz/{lnpos_id}/{payload}/{onchain_liquid}/{address}")
-async def get_lnpos_payment_boltz(
- lnpos_id: str, payload: str, onchain_liquid: str, address: str
-):
- """
- Handle Boltz payments for atms.
- """
- lnpos = await get_lnpos(lnpos_id)
- if not lnpos:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="lnpos does not exist"
- )
-
- lnpos_payment, _ = await register_atm_payment(lnpos, payload)
- assert lnpos_payment
- if lnpos_payment == "ERROR":
- return lnpos_payment
- if lnpos_payment.payload == lnpos_payment.payment_hash:
- return {"status": "ERROR", "reason": "Payment already claimed."}
- if lnpos_payment.payment_hash == "pending":
- return {
- "status": "ERROR",
- "reason": "Pending. If you are unable to withdraw contact vendor",
- }
- wallet = await get_wallet(lnpos.wallet)
- if not wallet:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND,
- detail="Wallet does not exist connected to atm, payment could not be made",
- )
- access = await check_user_extension_access(wallet.user, "boltz")
- if not access.success:
- return {"status": "ERROR", "reason": "Boltz not enabled"}
-
- data = {
- "wallet": lnpos.wallet,
- "asset": onchain_liquid.replace("temp", "/"),
- "amount": lnpos_payment.sats,
- "direction": "send",
- "instant_settlement": True,
- "onchain_address": address,
- "feerate": False,
- "feerate_value": 0,
- }
-
- try:
- lnpos_payment.payload = payload
- lnpos_payment.payment_hash = "pending"
- lnpos_payment_updated = await update_lnpos_payment(lnpos_payment)
- assert lnpos_payment_updated
- async with httpx.AsyncClient() as client:
- response = await client.post(
- url=f"http://{settings.host}:{settings.port}/boltz/api/v1/swap/reverse",
- headers={"X-API-KEY": wallet.adminkey},
- json=data,
- )
- lnpos_payment.payment_hash = lnpos_payment.payload
- lnpos_payment_updated = await update_lnpos_payment(lnpos_payment)
- assert lnpos_payment_updated
- resp = response.json()
- return resp
- except Exception as exc:
- lnpos_payment.payment_hash = "payment_hash"
- lnpos_payment_updated = await update_lnpos_payment(lnpos_payment)
- assert lnpos_payment_updated
- return {"status": "ERROR", "reason": str(exc)}