watchonly working, satspay broken

This commit is contained in:
Ben Arc 2021-03-31 23:49:36 +01:00
parent b05b8c0115
commit 0db516b6e0
9 changed files with 237 additions and 89 deletions

View file

@ -27,8 +27,6 @@ from .crud import (
update_payment_status, update_payment_status,
get_wallet_payment, get_wallet_payment,
) )
async def create_invoice( async def create_invoice(
*, *,
wallet_id: str, wallet_id: str,

View file

@ -8,34 +8,56 @@ from lnbits.helpers import urlsafe_short_hash
from quart import jsonify from quart import jsonify
import httpx import httpx
from lnbits.core.services import create_invoice, check_invoice_status
from ..watchonly.crud import get_watch_wallet, get_derive_address
###############CHARGES########################## ###############CHARGES##########################
async def create_charge(walletid: str, user: str, title: Optional[str] = None, time: Optional[int] = None, amount: Optional[int] = None) -> Charges: async def create_charge(user: str, description: Optional[str] = None, onchainwallet: Optional[str] = None, lnbitswallet: Optional[str] = None, webhook: Optional[str] = None, time: Optional[int] = None, amount: Optional[int] = None) -> Charges:
wallet = await get_watch_wallet(walletid)
address = await get_derive_address(walletid, wallet[4] + 1)
charge_id = urlsafe_short_hash() charge_id = urlsafe_short_hash()
if onchainwallet:
wallet = await get_watch_wallet(onchainwallet)
onchainaddress = await get_derive_address(onchainwallet, wallet[4] + 1)
else:
onchainaddress = None
if lnbitswallet:
payment_hash, payment_request = await create_invoice(
wallet_id=lnbitswallet,
amount=amount,
memo=charge_id)
else:
payment_hash = None
payment_request = None
await db.execute( await db.execute(
""" """
INSERT INTO charges ( INSERT INTO charges (
id, id,
user, user,
title, description,
wallet, onchainwallet,
address, onchainaddress,
time_to_pay, lnbitswallet,
payment_request,
payment_hash,
webhook,
time,
amount, amount,
balance balance,
paid
) )
VALUES (?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", """,
(charge_id, user, title, walletid, address, time, amount, 0), (charge_id, user, description, onchainwallet, onchainaddress, lnbitswallet, payment_request, payment_hash, webhook, time, amount, 0, False),
) )
return await get_charge(charge_id) return await get_charge(charge_id)
async def update_charge(charge_id: str, **kwargs) -> Optional[Charges]:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(f"UPDATE charges SET {q} WHERE id = ?", (*kwargs.values(), wallet_id))
row = await db.fetchone("SELECT * FROM charges WHERE id = ?", (wallet_id,))
return Charges.from_row(row) if row else None
async def get_charge(charge_id: str) -> Charges: async def get_charge(charge_id: str) -> Charges:
row = await db.fetchone("SELECT * FROM charges WHERE id = ?", (charge_id,)) row = await db.fetchone("SELECT * FROM charges WHERE id = ?", (charge_id,))
@ -45,7 +67,7 @@ async def get_charge(charge_id: str) -> Charges:
async def get_charges(user: str) -> List[Charges]: async def get_charges(user: str) -> List[Charges]:
rows = await db.fetchall("SELECT * FROM charges WHERE user = ?", (user,)) rows = await db.fetchall("SELECT * FROM charges WHERE user = ?", (user,))
for row in rows: for row in rows:
await check_address_balance(row.address) await check_address_balance(row.id)
rows = await db.fetchall("SELECT * FROM charges WHERE user = ?", (user,)) rows = await db.fetchall("SELECT * FROM charges WHERE user = ?", (user,))
return [charges.from_row(row) for row in rows] return [charges.from_row(row) for row in rows]
@ -53,15 +75,23 @@ async def get_charges(user: str) -> List[Charges]:
async def delete_charge(charge_id: str) -> None: async def delete_charge(charge_id: str) -> None:
await db.execute("DELETE FROM charges WHERE id = ?", (charge_id,)) await db.execute("DELETE FROM charges WHERE id = ?", (charge_id,))
async def check_address_balance(address: str) -> List[Charges]: async def check_address_balance(charge_id: str) -> List[Charges]:
address_data = await get_address(address) charge = await get_charge(charge_id)
mempool = await get_mempool(address_data.user) if charge.onchainaddress:
mempool = await get_mempool(charge.user)
try: try:
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
r = await client.get(mempool.endpoint + "/api/address/" + address) r = await client.get(mempool.endpoint + "/api/address/" + charge.onchainaddress)
except Exception: respAmount = r.json()['chain_stats']['funded_txo_sum']
pass if (charge.balance + respAmount) >= charge.balance:
return await update_charge(charge_id = charge_id, balance = (charge.balance + respAmount), paid = True)
amount_paid = r.json()['chain_stats']['funded_txo_sum'] - r.json()['chain_stats']['spent_txo_sum'] else:
print(amount_paid) return await update_charge(charge_id = charge_id, balance = (charge.balance + respAmount), paid = False)
except Exception:
pass
if charge.lnbitswallet:
invoice_status = await check_invoice_status(charge.lnbitswallet, charge.payment_hash)
if invoice_status.paid:
return await update_charge(charge_id = charge_id, balance = charge.balance, paid = True)
row = await db.fetchone("SELECT * FROM charges WHERE id = ?", (charge_id,))
return Charges.from_row(row) if row else None

View file

@ -9,13 +9,19 @@ async def m001_initial(db):
CREATE TABLE IF NOT EXISTS charges ( CREATE TABLE IF NOT EXISTS charges (
id TEXT NOT NULL PRIMARY KEY, id TEXT NOT NULL PRIMARY KEY,
user TEXT, user TEXT,
title TEXT, description TEXT,
wallet TEXT NOT NULL, onchainwallet TEXT,
address TEXT NOT NULL, onchainaddress TEXT,
time_to_pay INTEGER, lnbitswallet TEXT,
lnbitskey TEXT,
payment_request TEXT,
payment_hash TEXT,
webhook TEXT,
time INTEGER,
amount INTEGER, amount INTEGER,
balance INTEGER DEFAULT 0, balance INTEGER DEFAULT 0,
time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now')) paid BOOLEAN,
timestamp TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now'))
); );
""" """
) )

View file

@ -4,13 +4,18 @@ from typing import NamedTuple
class Charges(NamedTuple): class Charges(NamedTuple):
id: str id: str
user: str user: str
wallet: str description: str
title: str onchainwallet: str
address: str onchainaddress: str
time_to_pay: str lnbitswallet: str
payment_request: str
payment_hash: str
webhook: str
time: str
amount: int amount: int
balance: int balance: int
time: int paid: bool
timestamp: int
@classmethod @classmethod
def from_row(cls, row: Row) -> "Payments": def from_row(cls, row: Row) -> "Payments":

View file

@ -215,7 +215,7 @@
<div v-if="formDialogCharge.data.onchain"> <div v-if="formDialogCharge.data.onchain">
<q-select filled dense emit-value v-model="onchainwallet" :options="walletLinks" label="Onchain Wallet" /> <q-select filled dense emit-value v-model="formDialogCharge.data.onchainwallet" :options="walletLinks" label="Onchain Wallet" />
</div> </div>
<div v-if="formDialogCharge.data.lnbits"> <div v-if="formDialogCharge.data.lnbits">
@ -223,7 +223,7 @@
filled filled
dense dense
emit-value emit-value
v-model="formDialog.data.wallet" v-model="formDialogCharge.data.lnbitswallet"
:options="g.user.walletOptions" :options="g.user.walletOptions"
label="Wallet *" label="Wallet *"
> >
@ -442,10 +442,11 @@
LNbits.api LNbits.api
.request( .request(
'GET', 'GET',
'/satspay/api/v1/ChargeLinks', '/satspay/api/v1/charges',
this.g.user.wallets[0].inkey this.g.user.wallets[0].inkey
) )
.then(function (response) { .then(function (response) {
console.log(response.data)
var i var i
var now = parseInt(new Date() / 1000) var now = parseInt(new Date() / 1000)
for (i = 0; i < response.data.length; i++) { for (i = 0; i < response.data.length; i++) {
@ -468,8 +469,9 @@
}, },
sendFormDataCharge: function () { sendFormDataCharge: function () {
var self = this var self = this
var wallet = self.g.user.wallets[0] var wallet = self.g.user.wallets[0].inkey
var data = self.formDialogCharge.data var data = self.formDialogCharge.data
console.log(data)
data.amount = parseInt(data.amount) data.amount = parseInt(data.amount)
data.time = parseInt(data.time) data.time = parseInt(data.time)
if (data.id) { if (data.id) {
@ -501,10 +503,11 @@
var self = this var self = this
LNbits.api LNbits.api
.request('POST', '/satspay/api/v1/Charge', wallet.inkey, data) .request('POST', '/satspay/api/v1/charge', wallet, data)
.then(function (response) { .then(function (response) {
self.ChargeLinks.push(mapCharge(response.data)) self.ChargeLinks.push(mapCharge(response.data))
self.formDialogCharge.show = false self.formDialogCharge.show = false
self.formDialogCharge.data = null
}) })
.catch(function (error) { .catch(function (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)

View file

@ -81,6 +81,7 @@ async def api_wallet_delete(wallet_id):
async def api_charges_retrieve(): async def api_charges_retrieve():
charges = await get_charges(g.wallet.user) charges = await get_charges(g.wallet.user)
print(charges)
if not charges: if not charges:
return ( return (
jsonify(""), jsonify(""),
@ -106,8 +107,10 @@ async def api_charge_retrieve(charge_id):
@api_check_wallet_key("invoice") @api_check_wallet_key("invoice")
@api_validate_post_request( @api_validate_post_request(
schema={ schema={
"walletid": {"type": "string", "empty": False, "required": True}, "onchainwallet": {"type": "string", "empty": False, "required": True},
"title": {"type": "string", "empty": False, "required": True}, "lnbitswallet": {"type": "string", "empty": False, "required": True},
"description": {"type": "string", "empty": False, "required": True},
"webhook": {"type": "string", "empty": False, "required": True},
"time": {"type": "integer", "min": 1, "required": True}, "time": {"type": "integer", "min": 1, "required": True},
"amount": {"type": "integer", "min": 1, "required": True}, "amount": {"type": "integer", "min": 1, "required": True},
} }
@ -117,7 +120,6 @@ async def api_charge_create_or_update(charge_id=None):
if not charge_id: if not charge_id:
charge = await create_charge(user = g.wallet.user, **g.data) charge = await create_charge(user = g.wallet.user, **g.data)
return jsonify(charge), HTTPStatus.CREATED return jsonify(charge), HTTPStatus.CREATED
else: else:
charge = await update_charge(user = g.wallet.user, **g.data) charge = await update_charge(user = g.wallet.user, **g.data)
return jsonify(charge), HTTPStatus.OK return jsonify(charge), HTTPStatus.OK

View file

@ -8,11 +8,12 @@ class Wallets(NamedTuple):
title: str title: str
address_no: int address_no: int
balance: int balance: int
@classmethod @classmethod
def from_row(cls, row: Row) -> "Wallets": def from_row(cls, row: Row) -> "Wallets":
return cls(**dict(row)) return cls(**dict(row))
class Mempool(NamedTuple): class Mempool(NamedTuple):
user: str user: str
endpoint: str endpoint: str

View file

@ -285,6 +285,22 @@
) )
return obj return obj
} }
var mapAddresses = function (obj) {
obj._data = _.clone(obj)
obj.date = Quasar.utils.date.formatDate(
new Date(obj.time * 1000),
'YYYY-MM-DD HH:mm'
)
return obj
}
var mapTxs = function (obj) {
obj._data = _.clone(obj)
obj.date = Quasar.utils.date.formatDate(
new Date(obj.time * 1000),
'YYYY-MM-DD HH:mm'
)
return obj
}
var mapCharge = function (obj) { var mapCharge = function (obj) {
obj._data = _.clone(obj) obj._data = _.clone(obj)
obj.date = Quasar.utils.date.formatDate( obj.date = Quasar.utils.date.formatDate(
@ -304,6 +320,8 @@
balance: null, balance: null,
checker: null, checker: null,
walletLinks: [], walletLinks: [],
AddressesLinks: [],
txsLinks: [],
ChargeLinks: [], ChargeLinks: [],
currentaddress: "", currentaddress: "",
Addresses: { Addresses: {
@ -322,13 +340,6 @@
label: 'Title', label: 'Title',
field: 'title' field: 'title'
}, },
{
name: 'amount',
align: 'left',
label: 'Amount',
field: 'amount'
},
{ {
name: 'masterpub', name: 'masterpub',
align: 'left', align: 'left',
@ -397,26 +408,64 @@
}, },
methods: { methods: {
getAddressDetails: function (address){
LNbits.api
.request(
'GET',
'/watchonly/api/v1/mempool/' + address,
this.g.user.wallets[0].inkey
)
.then(function (response) {
return reponse.data
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
getAddressTxs: function (address){
LNbits.api
.request(
'GET',
'/watchonly/api/v1/mempool/txs/' + address,
this.g.user.wallets[0].inkey
)
.then(function (response) {
self.txsLinks = response.data.map(function (obj) {
console.log(obj)
return mapTxs(obj)
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
getAddresses: function (walletID) { getAddresses: function (walletID) {
var self = this var self = this
LNbits.api LNbits.api
.request( .request(
'GET', 'GET',
'/watchonly/api/v1/addresses/' + walletID, '/watchonly/api/v1/addresses/' + walletID,
this.g.user.wallets[0].inkey this.g.user.wallets[0].inkey
) )
.then(function (response) { .then(function (response) {
self.Addresses.data = response.data
self.currentaddress = self.Addresses.data[self.Addresses.data.length - 1].address
self.AddressesLinks = response.data.map(function (obj) {
console.log(obj)
return mapAddresses(obj)
self.Addresses.data = response.data })
self.currentaddress = self.Addresses.data[self.Addresses.data.length - 1].address })
console.log(self.currentaddress) .catch(function (error) {
}) LNbits.utils.notifyApiError(error)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
}, },
getFreshAddress: function (walletID) { getFreshAddress: function (walletID) {
var self = this var self = this
@ -428,16 +477,11 @@
this.g.user.wallets[0].inkey this.g.user.wallets[0].inkey
) )
.then(function (response) { .then(function (response) {
console.log(response.data)
self.Addresses.data = response.data self.Addresses.data = response.data
self.currentaddress = self.Addresses.data[self.Addresses.data.length - 1].address self.currentaddress = self.Addresses.data[self.Addresses.data.length - 1].address
}) })
}, },
getMempool: function () {
getMempool: function () {
var self = this var self = this
LNbits.api LNbits.api
@ -447,9 +491,7 @@
this.g.user.wallets[0].inkey this.g.user.wallets[0].inkey
) )
.then(function (response) { .then(function (response) {
console.log(response.data.endpoint)
self.mempool.endpoint = response.data.endpoint self.mempool.endpoint = response.data.endpoint
console.log(this.mempool.endpoint)
}) })
.catch(function (error) { .catch(function (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
@ -482,15 +524,19 @@
this.g.user.wallets[0].inkey this.g.user.wallets[0].inkey
) )
.then(function (response) { .then(function (response) {
console.log(response)
self.walletLinks = response.data.map(function (obj) { self.walletLinks = response.data.map(function (obj) {
self.getAddresses(obj.id)
return mapWalletLink(obj) return mapWalletLink(obj)
}) })
}) })
.catch(function (error) { .catch(function (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
}) })
}, },
closeFormDialog: function () { closeFormDialog: function () {
this.formDialog.data = { this.formDialog.data = {
is_unique: false is_unique: false
@ -547,7 +593,6 @@
.then(function (response) { .then(function (response) {
self.walletLinks.push(mapWalletLink(response.data)) self.walletLinks.push(mapWalletLink(response.data))
self.formDialog.show = false self.formDialog.show = false
console.log(response.data[1][1])
}) })
.catch(function (error) { .catch(function (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
@ -556,7 +601,6 @@
deleteWalletLink: function (linkId) { deleteWalletLink: function (linkId) {
var self = this var self = this
var link = _.findWhere(this.walletLinks, {id: linkId}) var link = _.findWhere(this.walletLinks, {id: linkId})
console.log(self.g.user.wallets[0].adminkey)
LNbits.utils LNbits.utils
.confirmDialog('Are you sure you want to delete this pay link?') .confirmDialog('Are you sure you want to delete this pay link?')
.onOk(function () { .onOk(function () {
@ -582,13 +626,13 @@
}, },
created: function () { created: function () {
if (this.g.user.wallets.length) { if (this.g.user.wallets.length) {
var getWalletLinks = this.getWalletLinks
getWalletLinks()
var getMempool = this.getMempool var getMempool = this.getMempool
getMempool() getMempool()
var getWalletLinks = this.getWalletLinks
getWalletLinks()
} }
} }
}) })
</script> </script>

View file

@ -1,8 +1,7 @@
import hashlib import hashlib
from quart import g, jsonify, url_for from quart import g, jsonify, url_for, request
from http import HTTPStatus from http import HTTPStatus
import httpx import httpx, json
from lnbits.core.crud import get_user from lnbits.core.crud import get_user
from lnbits.decorators import api_check_wallet_key, api_validate_post_request from lnbits.decorators import api_check_wallet_key, api_validate_post_request
@ -38,6 +37,7 @@ async def api_wallets_retrieve():
@api_check_wallet_key("invoice") @api_check_wallet_key("invoice")
async def api_wallet_retrieve(wallet_id): async def api_wallet_retrieve(wallet_id):
wallet = await get_watch_wallet(wallet_id) wallet = await get_watch_wallet(wallet_id)
addresses = await api_get_addresses(wallet_id)
if not wallet: if not wallet:
return jsonify({"message": "wallet does not exist"}), HTTPStatus.NOT_FOUND return jsonify({"message": "wallet does not exist"}), HTTPStatus.NOT_FOUND
@ -130,4 +130,63 @@ async def api_get_mempool():
mempool = await get_mempool(g.wallet.user) mempool = await get_mempool(g.wallet.user)
if not mempool: if not mempool:
mempool = await create_mempool(user=g.wallet.user) mempool = await create_mempool(user=g.wallet.user)
return jsonify(mempool._asdict()), HTTPStatus.OK return jsonify(mempool._asdict()), HTTPStatus.OK
@watchonly_ext.route("/api/v1/mempool/<address>", methods=["GET"])
@api_check_wallet_key("invoice")
async def api_get_mempool_wallet_balance(address):
mempool = await get_mempool(g.wallet.user)
if not mempool:
mempool = await create_mempool(user=g.wallet.user)
url = (
mempool.endpoint
+ "/api/address/"
+ address
)
header = {
"Content-Type": "application/json",
}
async with httpx.AsyncClient() as client:
try:
r = await client.get(
url,
headers=header,
timeout=40,
)
mp_response = json.loads(r.text)
print(mp_response)
except AssertionError:
mp_response = "Error occured"
return jsonify(mp_response), HTTPStatus.OK
@watchonly_ext.route("/api/v1/mempool/txs/<address>", methods=["GET"])
@api_check_wallet_key("invoice")
async def api_get_mempool_wallet_txs(address):
mempool = await get_mempool(g.wallet.user)
if not mempool:
mempool = await create_mempool(user=g.wallet.user)
url = (
mempool.endpoint
+ "/api/address/"
+ address
+ "/txs"
)
header = {
"Content-Type": "application/json",
}
async with httpx.AsyncClient() as client:
try:
r = await client.get(
url,
headers=header,
timeout=40,
)
mp_response = json.loads(r.text)
print(mp_response)
except AssertionError:
mp_response = "Error occured"
return jsonify(mp_response), HTTPStatus.OK