Merge remote-tracking branch 'origin/main' into universalwebsocket
This commit is contained in:
commit
00123d6c16
|
@ -6,6 +6,7 @@ PORT=5000
|
|||
|
||||
DEBUG=false
|
||||
|
||||
# Allow users and admins by user IDs (comma separated list)
|
||||
LNBITS_ALLOWED_USERS=""
|
||||
LNBITS_ADMIN_USERS=""
|
||||
# Extensions only admin can access
|
||||
|
|
|
@ -157,30 +157,29 @@ class CreateInvoiceData(BaseModel):
|
|||
|
||||
|
||||
async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
|
||||
if data.description_hash:
|
||||
if data.description_hash or data.unhashed_description:
|
||||
try:
|
||||
description_hash = binascii.unhexlify(data.description_hash)
|
||||
description_hash = (
|
||||
binascii.unhexlify(data.description_hash)
|
||||
if data.description_hash
|
||||
else b""
|
||||
)
|
||||
unhashed_description = (
|
||||
binascii.unhexlify(data.unhashed_description)
|
||||
if data.unhashed_description
|
||||
else b""
|
||||
)
|
||||
except binascii.Error:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail="'description_hash' must be a valid hex string",
|
||||
detail="'description_hash' and 'unhashed_description' must be a valid hex strings",
|
||||
)
|
||||
unhashed_description = b""
|
||||
memo = ""
|
||||
elif data.unhashed_description:
|
||||
try:
|
||||
unhashed_description = binascii.unhexlify(data.unhashed_description)
|
||||
except binascii.Error:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail="'unhashed_description' must be a valid hex string",
|
||||
)
|
||||
description_hash = b""
|
||||
memo = ""
|
||||
else:
|
||||
description_hash = b""
|
||||
unhashed_description = b""
|
||||
memo = data.memo or LNBITS_SITE_TITLE
|
||||
|
||||
if data.unit == "sat":
|
||||
amount = int(data.amount)
|
||||
else:
|
||||
|
|
|
@ -487,6 +487,17 @@
|
|||
@click="copyText(lnurlValue, 'LNURL copied to clipboard!')"
|
||||
>Copy LNURL</q-btn
|
||||
>
|
||||
<q-chip
|
||||
v-if="websocketMessage == 'WebSocket NOT supported by your Browser!' || websocketMessage == 'Connection closed'"
|
||||
clickable
|
||||
color="red"
|
||||
text-color="white"
|
||||
icon="error"
|
||||
>{% raw %}{{ wsMessage }}{% endraw %}</q-chip
|
||||
>
|
||||
<q-chip v-else clickable color="green" text-color="white" icon="check"
|
||||
>{% raw %}{{ wsMessage }}{% endraw %}</q-chip
|
||||
>
|
||||
<br />
|
||||
<div class="row q-mt-lg q-gutter-sm">
|
||||
<q-btn
|
||||
|
@ -534,6 +545,7 @@
|
|||
filter: '',
|
||||
currency: 'USD',
|
||||
lnurlValue: '',
|
||||
websocketMessage: '',
|
||||
switches: 0,
|
||||
lnurldeviceLinks: [],
|
||||
lnurldeviceLinksObj: [],
|
||||
|
@ -622,6 +634,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wsMessage: function () {
|
||||
return this.websocketMessage
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openQrCodeDialog: function (lnurldevice_id) {
|
||||
var lnurldevice = _.findWhere(this.lnurldeviceLinks, {
|
||||
|
@ -631,11 +648,17 @@
|
|||
this.qrCodeDialog.data = _.clone(lnurldevice)
|
||||
this.qrCodeDialog.data.url =
|
||||
window.location.protocol + '//' + window.location.host
|
||||
this.lnurlValueFetch(this.qrCodeDialog.data.switches[0][3])
|
||||
this.lnurlValueFetch(
|
||||
this.qrCodeDialog.data.switches[0][3],
|
||||
this.qrCodeDialog.data.id
|
||||
)
|
||||
this.qrCodeDialog.show = true
|
||||
},
|
||||
lnurlValueFetch: function (lnurl) {
|
||||
lnurlValueFetch: function (lnurl, switchId) {
|
||||
this.lnurlValue = lnurl
|
||||
this.websocketConnector(
|
||||
'wss://' + window.location.host + '/lnurldevice/ws/' + switchId
|
||||
)
|
||||
},
|
||||
addSwitch: function () {
|
||||
var self = this
|
||||
|
@ -797,6 +820,25 @@
|
|||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
websocketConnector: function (websocketUrl) {
|
||||
if ('WebSocket' in window) {
|
||||
self = this
|
||||
var ws = new WebSocket(websocketUrl)
|
||||
self.updateWsMessage('Websocket connected')
|
||||
ws.onmessage = function (evt) {
|
||||
var received_msg = evt.data
|
||||
self.updateWsMessage('Message recieved: ' + received_msg)
|
||||
}
|
||||
ws.onclose = function () {
|
||||
self.updateWsMessage('Connection closed')
|
||||
}
|
||||
} else {
|
||||
self.updateWsMessage('WebSocket NOT supported by your Browser!')
|
||||
}
|
||||
},
|
||||
updateWsMessage: function (message) {
|
||||
this.websocketMessage = message
|
||||
},
|
||||
clearFormDialoglnurldevice() {
|
||||
this.formDialoglnurldevice.data = {
|
||||
lnurl_toggle: false,
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import json
|
||||
from typing import List, Optional
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.core.services import create_invoice
|
||||
from lnbits.core.views.api import api_payment
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
|
||||
from ..watchonly.crud import get_config, get_fresh_address
|
||||
|
||||
# from lnbits.db import open_ext_db
|
||||
from . import db
|
||||
from .helpers import fetch_onchain_balance
|
||||
from .models import Charges, CreateCharge
|
||||
|
||||
###############CHARGES##########################
|
||||
|
@ -18,6 +18,10 @@ from .models import Charges, CreateCharge
|
|||
async def create_charge(user: str, data: CreateCharge) -> Charges:
|
||||
charge_id = urlsafe_short_hash()
|
||||
if data.onchainwallet:
|
||||
config = await get_config(user)
|
||||
data.extra = json.dumps(
|
||||
{"mempool_endpoint": config.mempool_endpoint, "network": config.network}
|
||||
)
|
||||
onchain = await get_fresh_address(data.onchainwallet)
|
||||
onchainaddress = onchain.address
|
||||
else:
|
||||
|
@ -48,9 +52,10 @@ async def create_charge(user: str, data: CreateCharge) -> Charges:
|
|||
completelinktext,
|
||||
time,
|
||||
amount,
|
||||
balance
|
||||
balance,
|
||||
extra
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
charge_id,
|
||||
|
@ -67,6 +72,7 @@ async def create_charge(user: str, data: CreateCharge) -> Charges:
|
|||
data.time,
|
||||
data.amount,
|
||||
0,
|
||||
data.extra,
|
||||
),
|
||||
)
|
||||
return await get_charge(charge_id)
|
||||
|
@ -98,34 +104,20 @@ async def delete_charge(charge_id: str) -> None:
|
|||
await db.execute("DELETE FROM satspay.charges WHERE id = ?", (charge_id,))
|
||||
|
||||
|
||||
async def check_address_balance(charge_id: str) -> List[Charges]:
|
||||
async def check_address_balance(charge_id: str) -> Optional[Charges]:
|
||||
charge = await get_charge(charge_id)
|
||||
|
||||
if not charge.paid:
|
||||
if charge.onchainaddress:
|
||||
config = await get_charge_config(charge_id)
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.get(
|
||||
config.mempool_endpoint
|
||||
+ "/api/address/"
|
||||
+ charge.onchainaddress
|
||||
)
|
||||
respAmount = r.json()["chain_stats"]["funded_txo_sum"]
|
||||
respAmount = await fetch_onchain_balance(charge)
|
||||
if respAmount > charge.balance:
|
||||
await update_charge(charge_id=charge_id, balance=respAmount)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.warning(e)
|
||||
if charge.lnbitswallet:
|
||||
invoice_status = await api_payment(charge.payment_hash)
|
||||
|
||||
if invoice_status["paid"]:
|
||||
return await update_charge(charge_id=charge_id, balance=charge.amount)
|
||||
row = await db.fetchone("SELECT * FROM satspay.charges WHERE id = ?", (charge_id,))
|
||||
return Charges.from_row(row) if row else None
|
||||
|
||||
|
||||
async def get_charge_config(charge_id: str):
|
||||
row = await db.fetchone(
|
||||
"""SELECT "user" FROM satspay.charges WHERE id = ?""", (charge_id,)
|
||||
)
|
||||
return await get_config(row.user)
|
||||
return await get_charge(charge_id)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import httpx
|
||||
from loguru import logger
|
||||
|
||||
from .models import Charges
|
||||
|
||||
|
||||
def compact_charge(charge: Charges):
|
||||
return {
|
||||
def public_charge(charge: Charges):
|
||||
c = {
|
||||
"id": charge.id,
|
||||
"description": charge.description,
|
||||
"onchainaddress": charge.onchainaddress,
|
||||
|
@ -13,5 +16,38 @@ def compact_charge(charge: Charges):
|
|||
"balance": charge.balance,
|
||||
"paid": charge.paid,
|
||||
"timestamp": charge.timestamp,
|
||||
"completelink": charge.completelink, # should be secret?
|
||||
"time_elapsed": charge.time_elapsed,
|
||||
"time_left": charge.time_left,
|
||||
"paid": charge.paid,
|
||||
}
|
||||
|
||||
if charge.paid:
|
||||
c["completelink"] = charge.completelink
|
||||
|
||||
return c
|
||||
|
||||
|
||||
async def call_webhook(charge: Charges):
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
r = await client.post(
|
||||
charge.webhook,
|
||||
json=public_charge(charge),
|
||||
timeout=40,
|
||||
)
|
||||
return {"webhook_success": r.is_success, "webhook_message": r.reason_phrase}
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to call webhook for charge {charge.id}")
|
||||
logger.warning(e)
|
||||
return {"webhook_success": False, "webhook_message": str(e)}
|
||||
|
||||
|
||||
async def fetch_onchain_balance(charge: Charges):
|
||||
endpoint = (
|
||||
f"{charge.config.mempool_endpoint}/testnet"
|
||||
if charge.config.network == "Testnet"
|
||||
else charge.config.mempool_endpoint
|
||||
)
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.get(endpoint + "/api/address/" + charge.onchainaddress)
|
||||
return r.json()["chain_stats"]["funded_txo_sum"]
|
||||
|
|
|
@ -26,3 +26,14 @@ async def m001_initial(db):
|
|||
);
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
async def m002_add_charge_extra_data(db):
|
||||
"""
|
||||
Add 'extra' column for storing various config about the charge (JSON format)
|
||||
"""
|
||||
await db.execute(
|
||||
"""ALTER TABLE satspay.charges
|
||||
ADD COLUMN extra TEXT DEFAULT '{"mempool_endpoint": "https://mempool.space", "network": "Mainnet"}';
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from sqlite3 import Row
|
||||
from typing import Optional
|
||||
|
@ -15,6 +16,14 @@ class CreateCharge(BaseModel):
|
|||
completelinktext: str = Query(None)
|
||||
time: int = Query(..., ge=1)
|
||||
amount: int = Query(..., ge=1)
|
||||
extra: str = "{}"
|
||||
|
||||
|
||||
class ChargeConfig(BaseModel):
|
||||
mempool_endpoint: Optional[str]
|
||||
network: Optional[str]
|
||||
webhook_success: Optional[bool] = False
|
||||
webhook_message: Optional[str]
|
||||
|
||||
|
||||
class Charges(BaseModel):
|
||||
|
@ -28,6 +37,7 @@ class Charges(BaseModel):
|
|||
webhook: Optional[str]
|
||||
completelink: Optional[str]
|
||||
completelinktext: Optional[str] = "Back to Merchant"
|
||||
extra: str = "{}"
|
||||
time: int
|
||||
amount: int
|
||||
balance: int
|
||||
|
@ -54,3 +64,11 @@ class Charges(BaseModel):
|
|||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def config(self) -> ChargeConfig:
|
||||
charge_config = json.loads(self.extra)
|
||||
return ChargeConfig(**charge_config)
|
||||
|
||||
def must_call_webhook(self):
|
||||
return self.webhook and self.paid and self.config.webhook_success == False
|
||||
|
|
|
@ -14,15 +14,14 @@ const retryWithDelay = async function (fn, retryCount = 0) {
|
|||
}
|
||||
|
||||
const mapCharge = (obj, oldObj = {}) => {
|
||||
const charge = _.clone(obj)
|
||||
const charge = {...oldObj, ...obj}
|
||||
|
||||
charge.progress = obj.time_left < 0 ? 1 : 1 - obj.time_left / obj.time
|
||||
charge.time = minutesToTime(obj.time)
|
||||
charge.timeLeft = minutesToTime(obj.time_left)
|
||||
|
||||
charge.expanded = false
|
||||
charge.displayUrl = ['/satspay/', obj.id].join('')
|
||||
charge.expanded = oldObj.expanded
|
||||
charge.expanded = oldObj.expanded || false
|
||||
charge.pendingBalance = oldObj.pendingBalance || 0
|
||||
return charge
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import asyncio
|
||||
import json
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
@ -7,7 +8,8 @@ from lnbits.extensions.satspay.crud import check_address_balance, get_charge
|
|||
from lnbits.helpers import get_current_extension_name
|
||||
from lnbits.tasks import register_invoice_listener
|
||||
|
||||
# from .crud import get_ticket, set_ticket_paid
|
||||
from .crud import update_charge
|
||||
from .helpers import call_webhook
|
||||
|
||||
|
||||
async def wait_for_paid_invoices():
|
||||
|
@ -30,4 +32,9 @@ async def on_invoice_paid(payment: Payment) -> None:
|
|||
return
|
||||
|
||||
await payment.set_pending(False)
|
||||
await check_address_balance(charge_id=charge.id)
|
||||
charge = await check_address_balance(charge_id=charge.id)
|
||||
|
||||
if charge.must_call_webhook():
|
||||
resp = await call_webhook(charge)
|
||||
extra = {**charge.config.dict(), **resp}
|
||||
await update_charge(charge_id=charge.id, extra=json.dumps(extra))
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
<q-btn
|
||||
flat
|
||||
disable
|
||||
v-if="!charge.lnbitswallet || charge.time_elapsed"
|
||||
v-if="!charge.payment_request || charge.time_elapsed"
|
||||
style="color: primary; width: 100%"
|
||||
label="lightning⚡"
|
||||
>
|
||||
|
@ -131,7 +131,7 @@
|
|||
<q-btn
|
||||
flat
|
||||
disable
|
||||
v-if="!charge.onchainwallet || charge.time_elapsed"
|
||||
v-if="!charge.onchainaddress || charge.time_elapsed"
|
||||
style="color: primary; width: 100%"
|
||||
label="onchain⛓️"
|
||||
>
|
||||
|
@ -170,6 +170,8 @@
|
|||
name="check"
|
||||
style="color: green; font-size: 21.4em"
|
||||
></q-icon>
|
||||
<div class="row text-center q-mt-lg">
|
||||
<div class="col text-center">
|
||||
<q-btn
|
||||
outline
|
||||
v-if="charge.webhook"
|
||||
|
@ -178,6 +180,8 @@
|
|||
:label="charge.completelinktext"
|
||||
></q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="row text-center q-mb-sm">
|
||||
<div class="col text-center">
|
||||
|
@ -218,7 +222,7 @@
|
|||
<div class="col text-center">
|
||||
<a
|
||||
style="color: unset"
|
||||
:href="mempool_endpoint + '/address/' + charge.onchainaddress"
|
||||
:href="'https://' + mempoolHostname + '/address/' + charge.onchainaddress"
|
||||
target="_blank"
|
||||
><span
|
||||
class="text-subtitle1"
|
||||
|
@ -241,6 +245,8 @@
|
|||
name="check"
|
||||
style="color: green; font-size: 21.4em"
|
||||
></q-icon>
|
||||
<div class="row text-center q-mt-lg">
|
||||
<div class="col text-center">
|
||||
<q-btn
|
||||
outline
|
||||
v-if="charge.webhook"
|
||||
|
@ -249,6 +255,8 @@
|
|||
:label="charge.completelinktext"
|
||||
></q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="row items-center q-mb-sm">
|
||||
<div class="col text-center">
|
||||
|
@ -303,7 +311,8 @@
|
|||
data() {
|
||||
return {
|
||||
charge: JSON.parse('{{charge_data | tojson}}'),
|
||||
mempool_endpoint: '{{mempool_endpoint}}',
|
||||
mempoolEndpoint: '{{mempool_endpoint}}',
|
||||
network: '{{network}}',
|
||||
pendingFunds: 0,
|
||||
ws: null,
|
||||
newProgress: 0.4,
|
||||
|
@ -316,19 +325,19 @@
|
|||
cancelListener: () => {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startPaymentNotifier() {
|
||||
this.cancelListener()
|
||||
if (!this.lnbitswallet) return
|
||||
this.cancelListener = LNbits.events.onInvoicePaid(
|
||||
this.wallet,
|
||||
payment => {
|
||||
this.checkInvoiceBalance()
|
||||
computed: {
|
||||
mempoolHostname: function () {
|
||||
let hostname = new URL(this.mempoolEndpoint).hostname
|
||||
if (this.network === 'Testnet') {
|
||||
hostname += '/testnet'
|
||||
}
|
||||
return hostname
|
||||
}
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
checkBalances: async function () {
|
||||
if (this.charge.hasStaleBalance) return
|
||||
if (!this.charge.payment_request && this.charge.hasOnchainStaleBalance)
|
||||
return
|
||||
try {
|
||||
const {data} = await LNbits.api.request(
|
||||
'GET',
|
||||
|
@ -345,7 +354,7 @@
|
|||
const {
|
||||
bitcoin: {addresses: addressesAPI}
|
||||
} = mempoolJS({
|
||||
hostname: new URL(this.mempool_endpoint).hostname
|
||||
hostname: new URL(this.mempoolEndpoint).hostname
|
||||
})
|
||||
|
||||
try {
|
||||
|
@ -353,7 +362,8 @@
|
|||
address: this.charge.onchainaddress
|
||||
})
|
||||
const newBalance = utxos.reduce((t, u) => t + u.value, 0)
|
||||
this.charge.hasStaleBalance = this.charge.balance === newBalance
|
||||
this.charge.hasOnchainStaleBalance =
|
||||
this.charge.balance === newBalance
|
||||
|
||||
this.pendingFunds = utxos
|
||||
.filter(u => !u.status.confirmed)
|
||||
|
@ -388,10 +398,10 @@
|
|||
const {
|
||||
bitcoin: {websocket}
|
||||
} = mempoolJS({
|
||||
hostname: new URL(this.mempool_endpoint).hostname
|
||||
hostname: new URL(this.mempoolEndpoint).hostname
|
||||
})
|
||||
|
||||
this.ws = new WebSocket('wss://mempool.space/api/v1/ws')
|
||||
this.ws = new WebSocket(`wss://${this.mempoolHostname}/api/v1/ws`)
|
||||
this.ws.addEventListener('open', x => {
|
||||
if (this.charge.onchainaddress) {
|
||||
this.trackAddress(this.charge.onchainaddress)
|
||||
|
@ -428,13 +438,10 @@
|
|||
}
|
||||
},
|
||||
created: async function () {
|
||||
if (this.charge.lnbitswallet) this.payInvoice()
|
||||
if (this.charge.payment_request) this.payInvoice()
|
||||
else this.payOnchain()
|
||||
await this.checkBalances()
|
||||
|
||||
// empty for onchain
|
||||
this.wallet.inkey = '{{ wallet_inkey }}'
|
||||
this.startPaymentNotifier()
|
||||
await this.checkBalances()
|
||||
|
||||
if (!this.charge.paid) {
|
||||
this.loopRefresh()
|
||||
|
|
|
@ -203,9 +203,14 @@
|
|||
:href="props.row.webhook"
|
||||
target="_blank"
|
||||
style="color: unset; text-decoration: none"
|
||||
>{{props.row.webhook || props.row.webhook}}</a
|
||||
>{{props.row.webhook}}</a
|
||||
>
|
||||
</div>
|
||||
<div class="col-4 q-pr-lg">
|
||||
<q-badge v-if="props.row.webhook_message" color="blue">
|
||||
{{props.row.webhook_message }}
|
||||
</q-badge>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row items-center q-mt-md q-mb-lg">
|
||||
<div class="col-2 q-pr-lg">ID:</div>
|
||||
|
@ -409,10 +414,11 @@
|
|||
balance: null,
|
||||
walletLinks: [],
|
||||
chargeLinks: [],
|
||||
onchainwallet: '',
|
||||
onchainwallet: null,
|
||||
rescanning: false,
|
||||
mempool: {
|
||||
endpoint: ''
|
||||
endpoint: '',
|
||||
network: 'Mainnet'
|
||||
},
|
||||
|
||||
chargesTable: {
|
||||
|
@ -505,6 +511,7 @@
|
|||
methods: {
|
||||
cancelCharge: function (data) {
|
||||
this.formDialogCharge.data.description = ''
|
||||
this.formDialogCharge.data.onchain = false
|
||||
this.formDialogCharge.data.onchainwallet = ''
|
||||
this.formDialogCharge.data.lnbitswallet = ''
|
||||
this.formDialogCharge.data.time = null
|
||||
|
@ -518,7 +525,7 @@
|
|||
try {
|
||||
const {data} = await LNbits.api.request(
|
||||
'GET',
|
||||
'/watchonly/api/v1/wallet',
|
||||
`/watchonly/api/v1/wallet?network=${this.mempool.network}`,
|
||||
this.g.user.wallets[0].inkey
|
||||
)
|
||||
this.walletLinks = data.map(w => ({
|
||||
|
@ -538,6 +545,7 @@
|
|||
this.g.user.wallets[0].inkey
|
||||
)
|
||||
this.mempool.endpoint = data.mempool_endpoint
|
||||
this.mempool.network = data.network || 'Mainnet'
|
||||
const url = new URL(this.mempool.endpoint)
|
||||
this.mempool.hostname = url.hostname
|
||||
} catch (error) {
|
||||
|
@ -577,7 +585,8 @@
|
|||
const data = this.formDialogCharge.data
|
||||
data.amount = parseInt(data.amount)
|
||||
data.time = parseInt(data.time)
|
||||
data.onchainwallet = this.onchainwallet?.id
|
||||
data.lnbitswallet = data.lnbits ? data.lnbitswallet : null
|
||||
data.onchainwallet = data.onchain ? this.onchainwallet?.id : null
|
||||
this.createCharge(wallet, data)
|
||||
},
|
||||
refreshActiveChargesBalance: async function () {
|
||||
|
@ -695,8 +704,8 @@
|
|||
},
|
||||
created: async function () {
|
||||
await this.getCharges()
|
||||
await this.getWalletLinks()
|
||||
await this.getWalletConfig()
|
||||
await this.getWalletLinks()
|
||||
setInterval(() => this.refreshActiveChargesBalance(), 10 * 2000)
|
||||
await this.rescanOnchainAddresses()
|
||||
setInterval(() => this.rescanOnchainAddresses(), 10 * 1000)
|
||||
|
|
|
@ -6,12 +6,12 @@ from starlette.exceptions import HTTPException
|
|||
from starlette.requests import Request
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
from lnbits.core.crud import get_wallet
|
||||
from lnbits.core.models import User
|
||||
from lnbits.decorators import check_user_exists
|
||||
from lnbits.extensions.satspay.helpers import public_charge
|
||||
|
||||
from . import satspay_ext, satspay_renderer
|
||||
from .crud import get_charge, get_charge_config
|
||||
from .crud import get_charge
|
||||
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
@ -30,18 +30,13 @@ async def display(request: Request, charge_id: str):
|
|||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="Charge link does not exist."
|
||||
)
|
||||
wallet = await get_wallet(charge.lnbitswallet)
|
||||
onchainwallet_config = await get_charge_config(charge_id)
|
||||
inkey = wallet.inkey if wallet else None
|
||||
mempool_endpoint = (
|
||||
onchainwallet_config.mempool_endpoint if onchainwallet_config else None
|
||||
)
|
||||
|
||||
return satspay_renderer().TemplateResponse(
|
||||
"satspay/display.html",
|
||||
{
|
||||
"request": request,
|
||||
"charge_data": charge.dict(),
|
||||
"wallet_inkey": inkey,
|
||||
"mempool_endpoint": mempool_endpoint,
|
||||
"charge_data": public_charge(charge),
|
||||
"mempool_endpoint": charge.config.mempool_endpoint,
|
||||
"network": charge.config.network,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
from http import HTTPStatus
|
||||
|
||||
import httpx
|
||||
from fastapi.params import Depends
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
|
@ -20,7 +20,7 @@ from .crud import (
|
|||
get_charges,
|
||||
update_charge,
|
||||
)
|
||||
from .helpers import compact_charge
|
||||
from .helpers import call_webhook, public_charge
|
||||
from .models import CreateCharge
|
||||
|
||||
#############################CHARGES##########################
|
||||
|
@ -58,6 +58,7 @@ async def api_charges_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
|
|||
**{"time_elapsed": charge.time_elapsed},
|
||||
**{"time_left": charge.time_left},
|
||||
**{"paid": charge.paid},
|
||||
**{"webhook_message": charge.config.webhook_message},
|
||||
}
|
||||
for charge in await get_charges(wallet.wallet.user)
|
||||
]
|
||||
|
@ -119,19 +120,9 @@ async def api_charge_balance(charge_id):
|
|||
status_code=HTTPStatus.NOT_FOUND, detail="Charge does not exist."
|
||||
)
|
||||
|
||||
if charge.paid and charge.webhook:
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
r = await client.post(
|
||||
charge.webhook,
|
||||
json=compact_charge(charge),
|
||||
timeout=40,
|
||||
)
|
||||
except AssertionError:
|
||||
charge.webhook = None
|
||||
return {
|
||||
**compact_charge(charge),
|
||||
**{"time_elapsed": charge.time_elapsed},
|
||||
**{"time_left": charge.time_left},
|
||||
**{"paid": charge.paid},
|
||||
}
|
||||
if charge.must_call_webhook():
|
||||
resp = await call_webhook(charge)
|
||||
extra = {**charge.config.dict(), **resp}
|
||||
await update_charge(charge_id=charge.id, extra=json.dumps(extra))
|
||||
|
||||
return {**public_charge(charge)}
|
||||
|
|
|
@ -124,7 +124,7 @@ async def check_pending_payments():
|
|||
|
||||
while True:
|
||||
async with db.connect() as conn:
|
||||
logger.debug(
|
||||
logger.info(
|
||||
f"Task: checking all pending payments (incoming={incoming}, outgoing={outgoing}) of last 15 days"
|
||||
)
|
||||
start_time: float = time.time()
|
||||
|
@ -140,15 +140,15 @@ async def check_pending_payments():
|
|||
for payment in pending_payments:
|
||||
await payment.check_status(conn=conn)
|
||||
|
||||
logger.debug(
|
||||
logger.info(
|
||||
f"Task: pending check finished for {len(pending_payments)} payments (took {time.time() - start_time:0.3f} s)"
|
||||
)
|
||||
# we delete expired invoices once upon the first pending check
|
||||
if incoming:
|
||||
logger.debug("Task: deleting all expired invoices")
|
||||
logger.info("Task: deleting all expired invoices")
|
||||
start_time: float = time.time()
|
||||
await delete_expired_invoices(conn=conn)
|
||||
logger.debug(
|
||||
logger.info(
|
||||
f"Task: expired invoice deletion finished (took {time.time() - start_time:0.3f} s)"
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user