diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 17b46a949..2c3ae7a53 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import List, Optional, Dict from uuid import uuid4 from lnbits.db import open_db @@ -136,18 +136,18 @@ def get_wallet_for_key(key: str, key_type: str = "invoice") -> Optional[Wallet]: # --------------- -def get_wallet_payment(wallet_id: str, checking_id: str) -> Optional[Payment]: +def get_wallet_payment(wallet_id: str, payment_hash: str) -> Optional[Payment]: with open_db() as db: row = db.fetchone( """ - SELECT id as checking_id, amount, fee, pending, memo, time - FROM apipayment - WHERE wallet = ? AND id = ? + SELECT * + FROM apipayments + WHERE wallet = ? AND hash = ? """, - (wallet_id, checking_id), + (wallet_id, payment_hash), ) - return Payment(**row) if row else None + return Payment.from_row(row) if row else None def get_wallet_payments( @@ -179,7 +179,7 @@ def get_wallet_payments( with open_db() as db: rows = db.fetchall( f""" - SELECT id as checking_id, amount, fee, pending, memo, time + SELECT * FROM apipayments WHERE wallet = ? {clause} ORDER BY time DESC @@ -187,7 +187,7 @@ def get_wallet_payments( (wallet_id,), ) - return [Payment(**row) for row in rows] + return [Payment.from_row(row) for row in rows] def delete_wallet_payments_expired(wallet_id: str, *, seconds: int = 86400) -> None: @@ -195,7 +195,7 @@ def delete_wallet_payments_expired(wallet_id: str, *, seconds: int = 86400) -> N db.execute( """ DELETE - FROM apipayment WHERE wallet = ? AND pending = 1 AND time < strftime('%s', 'now') - ? + FROM apipayments WHERE wallet = ? AND pending = 1 AND time < strftime('%s', 'now') - ? """, (wallet_id, seconds), ) @@ -206,18 +206,30 @@ def delete_wallet_payments_expired(wallet_id: str, *, seconds: int = 86400) -> N def create_payment( - *, wallet_id: str, checking_id: str, payment_hash: str, amount: int, memo: str, fee: int = 0, pending: bool = True + *, + wallet_id: str, + checking_id: str, + payment_request: str, + payment_hash: str, + amount: int, + memo: str, + fee: int = 0, + preimage: Optional[str] = None, + pending: bool = True, + extra: Optional[Dict] = None, ) -> Payment: with open_db() as db: db.execute( """ - INSERT INTO apipayment (wallet, id, payment_hash, amount, pending, memo, fee) - VALUES (?, ?, ?, ?, ?, ?, ?) + INSERT INTO apipayments + (wallet, checking_id, bolt11, hash, preimage, + amount, pending, memo, fee, extra) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, - (wallet_id, checking_id, payment_hash, amount, int(pending), memo, fee), + (wallet_id, checking_id, payment_request, payment_hash, preimage, amount, int(pending), memo, fee, extra), ) - new_payment = get_wallet_payment(wallet_id, checking_id) + new_payment = get_wallet_payment(wallet_id, payment_hash) assert new_payment, "Newly created payment couldn't be retrieved" return new_payment @@ -225,18 +237,18 @@ def create_payment( def update_payment_status(checking_id: str, pending: bool) -> None: with open_db() as db: - db.execute("UPDATE apipayment SET pending = ? WHERE id = ?", (int(pending), checking_id,)) + db.execute("UPDATE apipayments SET pending = ? WHERE checking_id = ?", (int(pending), checking_id,)) def delete_payment(checking_id: str) -> None: with open_db() as db: - db.execute("DELETE FROM apipayment WHERE id = ?", (checking_id,)) + db.execute("DELETE FROM apipayments WHERE checking_id = ?", (checking_id,)) def check_internal(payment_hash: str) -> None: with open_db() as db: - row = db.fetchone("SELECT * FROM apipayment WHERE payment_hash = ?", (payment_hash,)) + row = db.fetchone("SELECT checking_id FROM apipayments WHERE hash = ?", (payment_hash,)) if not row: return False else: - return row['id'] + return row["checking_id"] diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index 29b03718e..826b21769 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -69,74 +69,22 @@ def m001_initial(db): GROUP BY wallet; """ ) - db.execute("DROP VIEW balances") - db.execute( - """ - CREATE VIEW IF NOT EXISTS balances AS - SELECT wallet, COALESCE(SUM(s), 0) AS balance FROM ( - SELECT wallet, SUM(amount) AS s -- incoming - FROM apipayment - WHERE amount > 0 AND pending = 0 -- don't sum pending - GROUP BY wallet - UNION ALL - SELECT wallet, SUM(amount + fee) AS s -- outgoing, sum fees - FROM apipayment - WHERE amount < 0 -- do sum pending - GROUP BY wallet - ) - GROUP BY wallet; + + +def m002_add_fields_to_apipayments(db): """ - ) - -def m002_changed(db): - - db.execute( - """ - CREATE TABLE IF NOT EXISTS apipayment ( - id TEXT NOT NULL, - payment_hash TEXT NOT NULL, - amount INTEGER NOT NULL, - fee INTEGER NOT NULL DEFAULT 0, - wallet TEXT NOT NULL, - pending BOOLEAN NOT NULL, - memo TEXT, - time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now')), - - UNIQUE (wallet, id) - ); + Adding fields to apipayments for better accounting, + and renaming payhash to checking_id since that is what it really is. """ - ) + db.execute("ALTER TABLE apipayments RENAME COLUMN payhash TO checking_id") + db.execute("ALTER TABLE apipayments ADD COLUMN hash TEXT") + db.execute("CREATE INDEX by_hash ON apipayments (hash)") + db.execute("ALTER TABLE apipayments ADD COLUMN preimage TEXT") + db.execute("ALTER TABLE apipayments ADD COLUMN bolt11 TEXT") + db.execute("ALTER TABLE apipayments ADD COLUMN extra TEXT") - for row in [list(row) for row in db.fetchall("SELECT * FROM apipayments")]: - db.execute( - """ - INSERT INTO apipayment ( - id, - payment_hash, - amount, - fee, - wallet, - pending, - memo, - time - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - row[0], - "oldinvoice", - row[1], - row[2], - row[3], - row[4], - row[5], - row[6], - ), - ) - db.execute("DROP TABLE apipayments") - def migrate(): with open_db() as db: m001_initial(db) - m002_changed(db) + m002_add_fields_to_apipayments(db) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 10a87ad1b..9dec751e6 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -1,4 +1,6 @@ -from typing import List, NamedTuple, Optional +import json +from typing import List, NamedTuple, Optional, Dict +from sqlite3 import Row class User(NamedTuple): @@ -29,10 +31,10 @@ class Wallet(NamedTuple): def balance(self) -> int: return self.balance_msat // 1000 - def get_payment(self, checking_id: str) -> Optional["Payment"]: + def get_payment(self, payment_hash: str) -> Optional["Payment"]: from .crud import get_wallet_payment - return get_wallet_payment(self.id, checking_id) + return get_wallet_payment(self.id, payment_hash) def get_payments( self, *, complete: bool = True, pending: bool = False, outgoing: bool = True, incoming: bool = True @@ -54,6 +56,29 @@ class Payment(NamedTuple): fee: int memo: str time: int + bolt11: str + preimage: str + payment_hash: str + extra: Dict + + @classmethod + def from_row(cls, row: Row): + return cls( + checking_id=row["checking_id"], + payment_hash=row["hash"], + bolt11=row["bolt11"], + preimage=row["preimage"], + extra=json.loads(row["extra"] or "{}"), + pending=row["pending"], + amount=row["amount"], + fee=row["fee"], + memo=row["memo"], + time=row["time"], + ) + + @property + def tag(self) -> Optional[str]: + return self.extra.get("tag") @property def msat(self) -> int: diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 3ff3b3274..e074a34a4 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -1,14 +1,20 @@ -from typing import Optional, Tuple +from typing import Optional, Tuple, Dict -from lnbits.bolt11 import decode as bolt11_decode # type: ignore +from lnbits import bolt11 from lnbits.helpers import urlsafe_short_hash from lnbits.settings import WALLET -from .crud import get_wallet, create_payment, delete_payment, check_internal, update_payment_status +from .crud import get_wallet, create_payment, delete_payment, check_internal, update_payment_status, get_wallet_payment -def create_invoice(*, wallet_id: str, amount: int, memo: str, description_hash: bytes = None) -> Tuple[str, str]: - +def create_invoice( + *, + wallet_id: str, + amount: int, + memo: Optional[str] = None, + description_hash: Optional[bytes] = None, + extra: Optional[Dict] = None, +) -> Tuple[str, str]: try: ok, checking_id, payment_request, error_message = WALLET.create_invoice( amount=amount, memo=memo, description_hash=description_hash @@ -18,77 +24,81 @@ def create_invoice(*, wallet_id: str, amount: int, memo: str, description_hash: if not ok: raise Exception(error_message or "Unexpected backend error.") - invoice = bolt11_decode(payment_request) + + invoice = bolt11.decode(payment_request) amount_msat = amount * 1000 - create_payment(wallet_id=wallet_id, checking_id=checking_id, payment_hash=invoice.payment_hash, amount=amount_msat, memo=memo) + create_payment( + wallet_id=wallet_id, + checking_id=checking_id, + payment_request=payment_request, + payment_hash=invoice.payment_hash, + amount=amount_msat, + memo=memo, + extra=extra, + ) - return checking_id, payment_request + return invoice.payment_hash, payment_request -def pay_invoice(*, wallet_id: str, bolt11: str, max_sat: Optional[int] = None) -> str: +def pay_invoice( + *, wallet_id: str, payment_request: str, max_sat: Optional[int] = None, extra: Optional[Dict] = None +) -> str: temp_id = f"temp_{urlsafe_short_hash()}" try: - invoice = bolt11_decode(bolt11) - internal = check_internal(invoice.payment_hash) - + invoice = bolt11.decode(payment_request) if invoice.amount_msat == 0: raise ValueError("Amountless invoices not supported.") - if max_sat and invoice.amount_msat > max_sat * 1000: raise ValueError("Amount in invoice is too high.") - fee_reserve = max(1000, int(invoice.amount_msat * 0.01)) + # put all parameters that don't change here + payment_kwargs = dict( + wallet_id=wallet_id, + payment_request=payment_request, + payment_hash=invoice.payment_hash, + amount=-invoice.amount_msat, + memo=invoice.description, + extra=extra, + ) - if not internal: - create_payment( - wallet_id=wallet_id, - checking_id=temp_id, - payment_hash=invoice.payment_hash, - amount=-invoice.amount_msat, - fee=-fee_reserve, - memo=temp_id, - ) + # check_internal() returns the checking_id of the invoice we're waiting for + internal = check_internal(invoice.payment_hash) + if internal: + # create a new payment from this wallet + create_payment(checking_id=temp_id, fee=0, pending=False, **payment_kwargs) + else: + # create a temporary payment here so we can check if + # the balance is enough in the next step + fee_reserve = max(1000, int(invoice.amount_msat * 0.01)) + create_payment(checking_id=temp_id, fee=-fee_reserve, **payment_kwargs) + # do the balance check wallet = get_wallet(wallet_id) assert wallet, "invalid wallet id" if wallet.balance_msat < 0: raise PermissionError("Insufficient balance.") if internal: - create_payment( - wallet_id=wallet_id, - checking_id=temp_id, - payment_hash=invoice.payment_hash, - amount=-invoice.amount_msat, - fee=0, - pending=False, - memo=invoice.description, - ) + # mark the invoice from the other side as not pending anymore + # so the other side only has access to his new money when we are sure + # the payer has enough to deduct from update_payment_status(checking_id=internal, pending=False) - return temp_id - - ok, checking_id, fee_msat, error_message = WALLET.pay_invoice(bolt11) - if ok: - create_payment( - wallet_id=wallet_id, - checking_id=checking_id, - payment_hash=invoice.payment_hash, - amount=-invoice.amount_msat, - fee=fee_msat, - memo=invoice.description, - ) + else: + # actually pay the external invoice + ok, checking_id, fee_msat, error_message = WALLET.pay_invoice(payment_request) + if ok: + create_payment(checking_id=checking_id, fee=fee_msat, **payment_kwargs) + delete_payment(temp_id) except Exception as e: ok, error_message = False, str(e) - - delete_payment(temp_id) - if not ok: raise Exception(error_message or "Unexpected backend error.") - return checking_id + return invoice.payment_hash -def check_payment(*, checking_id: str) -> str: - pass +def check_invoice_status(wallet_id: str, payment_hash: str) -> str: + payment = get_wallet_payment(wallet_id, payment_hash) + return WALLET.get_invoice_status(payment.checking_id) diff --git a/lnbits/core/static/js/wallet.js b/lnbits/core/static/js/wallet.js index 20e7778fc..ae0116271 100644 --- a/lnbits/core/static/js/wallet.js +++ b/lnbits/core/static/js/wallet.js @@ -1,3 +1,5 @@ +/* globals Vue, VueQrcodeReader, VueQrcode, Quasar, LNbits, _ */ + Vue.component(VueQrcode.name, VueQrcode) Vue.use(VueQrcodeReader) @@ -12,10 +14,10 @@ function generateChart(canvas, payments) { } _.each( - payments.slice(0).sort(function (a, b) { + payments.slice(0).sort(function(a, b) { return a.time - b.time }), - function (tx) { + function(tx) { txs.push({ hour: Quasar.utils.date.formatDate(tx.date, 'YYYY-MM-DDTHH:00'), sat: tx.sat @@ -23,17 +25,17 @@ function generateChart(canvas, payments) { } ) - _.each(_.groupBy(txs, 'hour'), function (value, day) { + _.each(_.groupBy(txs, 'hour'), function(value, day) { var income = _.reduce( value, - function (memo, tx) { + function(memo, tx) { return tx.sat >= 0 ? memo + tx.sat : memo }, 0 ) var outcome = _.reduce( value, - function (memo, tx) { + function(memo, tx) { return tx.sat < 0 ? memo + Math.abs(tx.sat) : memo }, 0 @@ -65,14 +67,20 @@ function generateChart(canvas, payments) { type: 'bar', label: 'in', barPercentage: 0.75, - backgroundColor: window.Color('rgb(76,175,80)').alpha(0.5).rgbString() // green + backgroundColor: window + .Color('rgb(76,175,80)') + .alpha(0.5) + .rgbString() // green }, { data: data.outcome, type: 'bar', label: 'out', barPercentage: 0.75, - backgroundColor: window.Color('rgb(233,30,99)').alpha(0.5).rgbString() // pink + backgroundColor: window + .Color('rgb(233,30,99)') + .alpha(0.5) + .rgbString() // pink } ] }, @@ -113,7 +121,7 @@ function generateChart(canvas, payments) { new Vue({ el: '#vue', mixins: [windowMixin], - data: function () { + data: function() { return { receive: { show: false, @@ -169,49 +177,49 @@ new Vue({ } }, computed: { - filteredPayments: function () { + filteredPayments: function() { var q = this.paymentsTable.filter if (!q || q == '') return this.payments return LNbits.utils.search(this.payments, q) }, - balance: function () { + balance: function() { if (this.payments.length) { return ( - _.pluck(this.payments, 'amount').reduce(function (a, b) { + _.pluck(this.payments, 'amount').reduce(function(a, b) { return a + b }, 0) / 1000 ) } return this.g.wallet.sat }, - fbalance: function () { + fbalance: function() { return LNbits.utils.formatSat(this.balance) }, - canPay: function () { + canPay: function() { if (!this.send.invoice) return false return this.send.invoice.sat <= this.balance }, - pendingPaymentsExist: function () { + pendingPaymentsExist: function() { return this.payments ? _.where(this.payments, {pending: 1}).length > 0 : false } }, methods: { - closeCamera: function () { + closeCamera: function() { this.sendCamera.show = false }, - showCamera: function () { + showCamera: function() { this.sendCamera.show = true }, - showChart: function () { + showChart: function() { this.paymentsChart.show = true - this.$nextTick(function () { + this.$nextTick(function() { generateChart(this.$refs.canvas, this.payments) }) }, - showReceiveDialog: function () { + showReceiveDialog: function() { this.receive = { show: true, status: 'pending', @@ -223,7 +231,7 @@ new Vue({ paymentChecker: null } }, - showSendDialog: function () { + showSendDialog: function() { this.send = { show: true, invoice: null, @@ -233,20 +241,20 @@ new Vue({ paymentChecker: null } }, - closeReceiveDialog: function () { + closeReceiveDialog: function() { var checker = this.receive.paymentChecker - setTimeout(function () { + setTimeout(function() { clearInterval(checker) }, 10000) }, - closeSendDialog: function () { + closeSendDialog: function() { this.sendCamera.show = false var checker = this.send.paymentChecker - setTimeout(function () { + setTimeout(function() { clearInterval(checker) }, 1000) }, - createInvoice: function () { + createInvoice: function() { var self = this this.receive.status = 'loading' LNbits.api @@ -255,14 +263,14 @@ new Vue({ this.receive.data.amount, this.receive.data.memo ) - .then(function (response) { + .then(function(response) { self.receive.status = 'success' self.receive.paymentReq = response.data.payment_request - self.receive.paymentChecker = setInterval(function () { + self.receive.paymentChecker = setInterval(function() { LNbits.api - .getPayment(self.g.wallet, response.data.checking_id) - .then(function (response) { + .getPayment(self.g.wallet, response.data.payment_hash) + .then(function(response) { if (response.data.paid) { self.fetchPayments() self.receive.show = false @@ -271,17 +279,17 @@ new Vue({ }) }, 2000) }) - .catch(function (error) { + .catch(function(error) { LNbits.utils.notifyApiError(error) self.receive.status = 'pending' }) }, - decodeQR: function (res) { + decodeQR: function(res) { this.send.data.bolt11 = res this.decodeInvoice() this.sendCamera.show = false }, - decodeInvoice: function () { + decodeInvoice: function() { if (this.send.data.bolt11.startsWith('lightning:')) { this.send.data.bolt11 = this.send.data.bolt11.slice(10) } @@ -306,7 +314,7 @@ new Vue({ fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000) } - _.each(invoice.data.tags, function (tag) { + _.each(invoice.data.tags, function(tag) { if (_.isObject(tag) && _.has(tag, 'description')) { if (tag.description == 'payment_hash') { cleanInvoice.hash = tag.value @@ -327,10 +335,10 @@ new Vue({ this.send.invoice = Object.freeze(cleanInvoice) }, - payInvoice: function () { + payInvoice: function() { var self = this - dismissPaymentMsg = this.$q.notify({ + let dismissPaymentMsg = this.$q.notify({ timeout: 0, message: 'Processing payment...', icon: null @@ -338,11 +346,11 @@ new Vue({ LNbits.api .payInvoice(this.g.wallet, this.send.data.bolt11) - .then(function (response) { - self.send.paymentChecker = setInterval(function () { + .then(function(response) { + self.send.paymentChecker = setInterval(function() { LNbits.api - .getPayment(self.g.wallet, response.data.checking_id) - .then(function (res) { + .getPayment(self.g.wallet, response.data.payment_hash) + .then(function(res) { if (res.data.paid) { self.send.show = false clearInterval(self.send.paymentChecker) @@ -352,58 +360,58 @@ new Vue({ }) }, 2000) }) - .catch(function (error) { + .catch(function(error) { dismissPaymentMsg() LNbits.utils.notifyApiError(error) }) }, - deleteWallet: function (walletId, user) { + deleteWallet: function(walletId, user) { LNbits.utils .confirmDialog('Are you sure you want to delete this wallet?') - .onOk(function () { + .onOk(function() { LNbits.href.deleteWallet(walletId, user) }) }, - fetchPayments: function (checkPending) { + fetchPayments: function(checkPending) { var self = this return LNbits.api .getPayments(this.g.wallet, checkPending) - .then(function (response) { + .then(function(response) { self.payments = response.data - .map(function (obj) { + .map(function(obj) { return LNbits.map.payment(obj) }) - .sort(function (a, b) { + .sort(function(a, b) { return b.time - a.time }) }) }, - checkPendingPayments: function () { + checkPendingPayments: function() { var dismissMsg = this.$q.notify({ timeout: 0, message: 'Checking pending transactions...', icon: null }) - this.fetchPayments(true).then(function () { + this.fetchPayments(true).then(function() { dismissMsg() }) }, - exportCSV: function () { + exportCSV: function() { LNbits.utils.exportCSV(this.paymentsTable.columns, this.payments) } }, watch: { - payments: function () { + payments: function() { EventHub.$emit('update-wallet-balance', [this.g.wallet.id, this.balance]) } }, - created: function () { + created: function() { this.fetchPayments() setTimeout(this.checkPendingPayments(), 1200) }, - mounted: function () { + mounted: function() { if ( this.$refs.disclaimer && !this.$q.localStorage.getItem('lnbits.disclaimerShown') diff --git a/lnbits/core/templates/core/_api_docs.html b/lnbits/core/templates/core/_api_docs.html index 38c356859..f1fddd5e5 100644 --- a/lnbits/core/templates/core/_api_docs.html +++ b/lnbits/core/templates/core/_api_docs.html @@ -23,7 +23,7 @@ Returns 201 CREATED (application/json) {"checking_id": <string>, "payment_request": + >{"payment_hash": <string>, "payment_request": <string>}
Curl example
@@ -51,7 +51,7 @@
Returns 201 CREATED (application/json)
- {"checking_id": <string>} + {"payment_hash": <string>}
Curl example
curl -X POST {{ request.url_root }}api/v1/payments -d '{"out": true, @@ -73,7 +73,7 @@ GET - /api/v1/payments/<checking_id>
Headers
{"X-Api-Key": "{{ wallet.inkey }}"} @@ -83,9 +83,9 @@ {"paid": <bool>}
Curl example
curl -X GET {{ request.url_root }}api/v1/payments/<checking_id> - -H "X-Api-Key: {{ wallet.inkey }}" -H "Content-type: - application/json"curl -X GET {{ request.url_root + }}api/v1/payments/<payment_hash> -H "X-Api-Key: + {{ wallet.inkey }}" -H "Content-type: application/json"
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index 6d19bcaaf..bc2487629 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -84,7 +84,7 @@ dense flat :data="filteredPayments" - row-key="payhash" + row-key="checking_id" :columns="paymentsTable.columns" :pagination.sync="paymentsTable.pagination" > diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 056703180..4018fbdd1 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -2,12 +2,12 @@ from flask import g, jsonify, request from http import HTTPStatus from binascii import unhexlify +from lnbits import bolt11 from lnbits.core import core_app +from lnbits.core.services import create_invoice, pay_invoice from lnbits.decorators import api_check_wallet_key, api_validate_post_request from lnbits.settings import WALLET -from ..services import create_invoice, pay_invoice - @core_app.route("/api/v1/payments", methods=["GET"]) @api_check_wallet_key("invoice") @@ -41,20 +41,31 @@ def api_payments_create_invoice(): memo = g.data["memo"] try: - checking_id, payment_request = create_invoice( + payment_hash, payment_request = create_invoice( wallet_id=g.wallet.id, amount=g.data["amount"], memo=memo, description_hash=description_hash ) except Exception as e: return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR - return jsonify({"checking_id": checking_id, "payment_request": payment_request}), HTTPStatus.CREATED + invoice = bolt11.decode(payment_request) + return ( + jsonify( + { + "payment_hash": invoice.payment_hash, + "payment_request": payment_request, + # maintain backwards compatibility with API clients: + "checking_id": invoice.payment_hash, + } + ), + HTTPStatus.CREATED, + ) @api_check_wallet_key("admin") @api_validate_post_request(schema={"bolt11": {"type": "string", "empty": False, "required": True}}) def api_payments_pay_invoice(): try: - checking_id = pay_invoice(wallet_id=g.wallet.id, bolt11=g.data["bolt11"]) + payment_hash = pay_invoice(wallet_id=g.wallet.id, payment_request=g.data["bolt11"]) except ValueError as e: return jsonify({"message": str(e)}), HTTPStatus.BAD_REQUEST except PermissionError as e: @@ -62,7 +73,16 @@ def api_payments_pay_invoice(): except Exception as e: return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR - return jsonify({"checking_id": checking_id}), HTTPStatus.CREATED + return ( + jsonify( + { + "payment_hash": payment_hash, + # maintain backwards compatibility with API clients: + "checking_id": payment_hash, + } + ), + HTTPStatus.CREATED, + ) @core_app.route("/api/v1/payments", methods=["POST"]) @@ -73,10 +93,10 @@ def api_payments_create(): return api_payments_create_invoice() -@core_app.route("/api/v1/payments/", methods=["GET"]) +@core_app.route("/api/v1/payments/", methods=["GET"]) @api_check_wallet_key("invoice") -def api_payment(checking_id): - payment = g.wallet.get_payment(checking_id) +def api_payment(payment_hash): + payment = g.wallet.get_payment(payment_hash) if not payment: return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND @@ -85,9 +105,9 @@ def api_payment(checking_id): try: if payment.is_out: - is_paid = not WALLET.get_payment_status(checking_id).pending + is_paid = not WALLET.get_payment_status(payment.checking_id).pending elif payment.is_in: - is_paid = not WALLET.get_invoice_status(checking_id).pending + is_paid = not WALLET.get_invoice_status(payment.checking_id).pending except Exception: return jsonify({"paid": False}), HTTPStatus.OK diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index f712cb48c..62b194e3b 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -64,7 +64,7 @@ def wallet(): allowed_users = getenv("LNBITS_ALLOWED_USERS", "all") if allowed_users != "all" and user_id not in allowed_users.split(","): - abort(HTTPStatus.UNAUTHORIZED, f"User not authorized.") + abort(HTTPStatus.UNAUTHORIZED, "User not authorized.") if not wallet_id: if user.wallets and not wallet_name: diff --git a/lnbits/extensions/amilk/views_api.py b/lnbits/extensions/amilk/views_api.py index 6d51da0b0..dccad42dd 100644 --- a/lnbits/extensions/amilk/views_api.py +++ b/lnbits/extensions/amilk/views_api.py @@ -1,19 +1,16 @@ -from flask import g, jsonify, request +import requests +from flask import g, jsonify, request, abort from http import HTTPStatus - -from lnbits.core.crud import get_user -from lnbits.decorators import api_check_wallet_key, api_validate_post_request - -from lnbits.extensions.amilk import amilk_ext -from .crud import create_amilk, get_amilk, get_amilks, delete_amilk -from lnbits.core.services import create_invoice - -from flask import abort, redirect, request, url_for from lnurl import LnurlWithdrawResponse, handle as handle_lnurl from lnurl.exceptions import LnurlException from time import sleep -import requests -from lnbits.settings import WALLET + +from lnbits.core.crud import get_user +from lnbits.decorators import api_check_wallet_key, api_validate_post_request +from lnbits.core.services import create_invoice, check_invoice_status + +from lnbits.extensions.amilk import amilk_ext +from .crud import create_amilk, get_amilk, get_amilks, delete_amilk @amilk_ext.route("/api/v1/amilk", methods=["GET"]) @@ -36,13 +33,8 @@ def api_amilkit(amilk_id): withdraw_res = handle_lnurl(milk.lnurl, response_class=LnurlWithdrawResponse) except LnurlException: abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.") - print(withdraw_res.max_sats) - try: - checking_id, payment_request = create_invoice(wallet_id=milk.wallet, amount=withdraw_res.max_sats, memo=memo) - # print(payment_request) - except Exception as e: - error_message = False, str(e) + payment_hash, payment_request = create_invoice(wallet_id=milk.wallet, amount=withdraw_res.max_sats, memo=memo) r = requests.get( withdraw_res.callback.base, @@ -50,19 +42,17 @@ def api_amilkit(amilk_id): ) if not r.ok: - abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.") for i in range(10): - invoice_status = WALLET.get_invoice_status(checking_id) sleep(i) - if not invoice_status.paid: - continue + invoice_status = check_invoice_status(milk.wallet, payment_hash) + if invoice_status.paid: + return jsonify({"paid": True}), HTTPStatus.OK else: - return jsonify({"paid": False}), HTTPStatus.OK - break + continue - return jsonify({"paid": True}), HTTPStatus.OK + return jsonify({"paid": False}), HTTPStatus.OK @amilk_ext.route("/api/v1/amilk", methods=["POST"]) diff --git a/lnbits/extensions/events/crud.py b/lnbits/extensions/events/crud.py index 1c8c968bb..b7219f282 100644 --- a/lnbits/extensions/events/crud.py +++ b/lnbits/extensions/events/crud.py @@ -9,31 +9,31 @@ from .models import Tickets, Events #######TICKETS######## -def create_ticket(checking_id: str, wallet: str, event: str, name: str, email: str) -> Tickets: +def create_ticket(payment_hash: str, wallet: str, event: str, name: str, email: str) -> Tickets: with open_ext_db("events") as db: db.execute( """ INSERT INTO ticket (id, wallet, event, name, email, registered, paid) VALUES (?, ?, ?, ?, ?, ?, ?) """, - (checking_id, wallet, event, name, email, False, False), + (payment_hash, wallet, event, name, email, False, False), ) - return get_ticket(checking_id) + return get_ticket(payment_hash) -def update_ticket(paid: bool, checking_id: str) -> Tickets: +def update_ticket(paid: bool, payment_hash: str) -> Tickets: with open_ext_db("events") as db: - row = db.fetchone("SELECT * FROM ticket WHERE id = ?", (checking_id,)) + row = db.fetchone("SELECT * FROM ticket WHERE id = ?", (payment_hash,)) if row[6] == True: - return get_ticket(checking_id) + return get_ticket(payment_hash) db.execute( """ UPDATE ticket SET paid = ? WHERE id = ? """, - (paid, checking_id), + (paid, payment_hash), ) eventdata = get_event(row[2]) @@ -47,12 +47,12 @@ def update_ticket(paid: bool, checking_id: str) -> Tickets: """, (sold, amount_tickets, row[2]), ) - return get_ticket(checking_id) + return get_ticket(payment_hash) -def get_ticket(checking_id: str) -> Optional[Tickets]: +def get_ticket(payment_hash: str) -> Optional[Tickets]: with open_ext_db("events") as db: - row = db.fetchone("SELECT * FROM ticket WHERE id = ?", (checking_id,)) + row = db.fetchone("SELECT * FROM ticket WHERE id = ?", (payment_hash,)) return Tickets(**row) if row else None @@ -68,9 +68,9 @@ def get_tickets(wallet_ids: Union[str, List[str]]) -> List[Tickets]: return [Tickets(**row) for row in rows] -def delete_ticket(checking_id: str) -> None: +def delete_ticket(payment_hash: str) -> None: with open_ext_db("events") as db: - db.execute("DELETE FROM ticket WHERE id = ?", (checking_id,)) + db.execute("DELETE FROM ticket WHERE id = ?", (payment_hash,)) ########EVENTS######### diff --git a/lnbits/extensions/events/templates/events/display.html b/lnbits/extensions/events/templates/events/display.html index 59a06ef24..542feb91f 100644 --- a/lnbits/extensions/events/templates/events/display.html +++ b/lnbits/extensions/events/templates/events/display.html @@ -144,7 +144,7 @@ ) .then(function (response) { self.paymentReq = response.data.payment_request - self.paymentCheck = response.data.checking_id + self.paymentCheck = response.data.payment_hash dismissMsg = self.$q.notify({ timeout: 0, diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py index cfc67ffdb..0680cbe93 100644 --- a/lnbits/extensions/events/views_api.py +++ b/lnbits/extensions/events/views_api.py @@ -2,9 +2,8 @@ from flask import g, jsonify, request from http import HTTPStatus from lnbits.core.crud import get_user, get_wallet -from lnbits.core.services import create_invoice +from lnbits.core.services import create_invoice, check_invoice_status from lnbits.decorators import api_check_wallet_key, api_validate_post_request -from lnbits.settings import WALLET from lnbits.extensions.events import events_ext from .crud import ( @@ -108,39 +107,37 @@ def api_tickets(): } ) def api_ticket_make_ticket(event_id, sats): - event = get_event(event_id) - if not event: return jsonify({"message": "LNTicket does not exist."}), HTTPStatus.NOT_FOUND try: - checking_id, payment_request = create_invoice( + payment_hash, payment_request = create_invoice( wallet_id=event.wallet, amount=int(sats), memo=f"#lnticket {event_id}" ) except Exception as e: return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR - ticket = create_ticket(checking_id=checking_id, wallet=event.wallet, event=event_id, **g.data) + ticket = create_ticket(payment_hash=payment_hash, wallet=event.wallet, event=event_id, **g.data) if not ticket: return jsonify({"message": "LNTicket could not be fetched."}), HTTPStatus.NOT_FOUND - return jsonify({"checking_id": checking_id, "payment_request": payment_request}), HTTPStatus.OK + return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.OK -@events_ext.route("/api/v1/tickets/", methods=["GET"]) -def api_ticket_send_ticket(checking_id): - theticket = get_ticket(checking_id) +@events_ext.route("/api/v1/tickets/", methods=["GET"]) +def api_ticket_send_ticket(payment_hash): + ticket = get_ticket(payment_hash) try: - is_paid = not WALLET.get_invoice_status(checking_id).pending + is_paid = not check_invoice_status(ticket.wallet, payment_hash).pending except Exception: return jsonify({"message": "Not paid."}), HTTPStatus.NOT_FOUND if is_paid: - wallet = get_wallet(theticket.wallet) - payment = wallet.get_payment(checking_id) + wallet = get_wallet(ticket.wallet) + payment = wallet.get_payment(payment_hash) payment.set_pending(False) - ticket = update_ticket(paid=True, checking_id=checking_id) + ticket = update_ticket(paid=True, payment_hash=payment_hash) return jsonify({"paid": True, "ticket_id": ticket.id}), HTTPStatus.OK diff --git a/lnbits/extensions/lnticket/crud.py b/lnbits/extensions/lnticket/crud.py index 5a532fc39..561137d92 100644 --- a/lnbits/extensions/lnticket/crud.py +++ b/lnbits/extensions/lnticket/crud.py @@ -9,31 +9,31 @@ from .models import Tickets, Forms #######TICKETS######## -def create_ticket(checking_id: str, wallet: str, form: str, name: str, email: str, ltext: str, sats: int) -> Tickets: +def create_ticket(payment_hash: str, wallet: str, form: str, name: str, email: str, ltext: str, sats: int) -> Tickets: with open_ext_db("lnticket") as db: db.execute( """ INSERT INTO ticket (id, form, email, ltext, name, wallet, sats, paid) VALUES (?, ?, ?, ?, ?, ?, ?, ?) """, - (checking_id, form, email, ltext, name, wallet, sats, False), + (payment_hash, form, email, ltext, name, wallet, sats, False), ) - return get_ticket(checking_id) + return get_ticket(payment_hash) -def update_ticket(paid: bool, checking_id: str) -> Tickets: +def update_ticket(paid: bool, payment_hash: str) -> Tickets: with open_ext_db("lnticket") as db: - row = db.fetchone("SELECT * FROM ticket WHERE id = ?", (checking_id,)) + row = db.fetchone("SELECT * FROM ticket WHERE id = ?", (payment_hash,)) if row[7] == True: - return get_ticket(checking_id) + return get_ticket(payment_hash) db.execute( """ UPDATE ticket SET paid = ? WHERE id = ? """, - (paid, checking_id), + (paid, payment_hash), ) formdata = get_form(row[1]) @@ -46,7 +46,7 @@ def update_ticket(paid: bool, checking_id: str) -> Tickets: """, (amount, row[1]), ) - return get_ticket(checking_id) + return get_ticket(payment_hash) def get_ticket(ticket_id: str) -> Optional[Tickets]: diff --git a/lnbits/extensions/lnticket/templates/lnticket/display.html b/lnbits/extensions/lnticket/templates/lnticket/display.html index 1b72615f3..900151915 100644 --- a/lnbits/extensions/lnticket/templates/lnticket/display.html +++ b/lnbits/extensions/lnticket/templates/lnticket/display.html @@ -152,7 +152,7 @@ ) .then(function (response) { self.paymentReq = response.data.payment_request - self.paymentCheck = response.data.checking_id + self.paymentCheck = response.data.payment_hash dismissMsg = self.$q.notify({ timeout: 0, diff --git a/lnbits/extensions/lnticket/views_api.py b/lnbits/extensions/lnticket/views_api.py index 69a2ac8ca..be5cb8ad3 100644 --- a/lnbits/extensions/lnticket/views_api.py +++ b/lnbits/extensions/lnticket/views_api.py @@ -2,9 +2,8 @@ from flask import g, jsonify, request from http import HTTPStatus from lnbits.core.crud import get_user, get_wallet -from lnbits.core.services import create_invoice +from lnbits.core.services import create_invoice, check_invoice_status from lnbits.decorators import api_check_wallet_key, api_validate_post_request -from lnbits.settings import WALLET from lnbits.extensions.lnticket import lnticket_ext from .crud import ( @@ -104,40 +103,38 @@ def api_tickets(): } ) def api_ticket_make_ticket(form_id, sats): - event = get_form(form_id) if not event: return jsonify({"message": "LNTicket does not exist."}), HTTPStatus.NOT_FOUND try: - checking_id, payment_request = create_invoice( + payment_hash, payment_request = create_invoice( wallet_id=event.wallet, amount=int(sats), memo=f"#lnticket {form_id}" ) except Exception as e: return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR - ticket = create_ticket(checking_id=checking_id, wallet=event.wallet, **g.data) + ticket = create_ticket(payment_hash=payment_hash, wallet=event.wallet, **g.data) if not ticket: return jsonify({"message": "LNTicket could not be fetched."}), HTTPStatus.NOT_FOUND - return jsonify({"checking_id": checking_id, "payment_request": payment_request}), HTTPStatus.OK + return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.OK -@lnticket_ext.route("/api/v1/tickets/", methods=["GET"]) -def api_ticket_send_ticket(checking_id): - theticket = get_ticket(checking_id) +@lnticket_ext.route("/api/v1/tickets/", methods=["GET"]) +def api_ticket_send_ticket(payment_hash): + ticket = get_ticket(payment_hash) try: - is_paid = not WALLET.get_invoice_status(checking_id).pending + is_paid = not check_invoice_status(ticket.wallet, payment_hash).pending except Exception: return jsonify({"message": "Not paid."}), HTTPStatus.NOT_FOUND if is_paid: - wallet = get_wallet(theticket.wallet) - payment = wallet.get_payment(checking_id) + wallet = get_wallet(ticket.wallet) + payment = wallet.get_payment(payment_hash) payment.set_pending(False) - ticket = update_ticket(paid=True, checking_id=checking_id) - + ticket = update_ticket(paid=True, payment_hash=payment_hash) return jsonify({"paid": True, "ticket_id": ticket.id}), HTTPStatus.OK return jsonify({"paid": False}), HTTPStatus.OK diff --git a/lnbits/extensions/paywall/templates/paywall/_api_docs.html b/lnbits/extensions/paywall/templates/paywall/_api_docs.html index 60e610817..56dbf5645 100644 --- a/lnbits/extensions/paywall/templates/paywall/_api_docs.html +++ b/lnbits/extensions/paywall/templates/paywall/_api_docs.html @@ -75,7 +75,7 @@ Returns 201 CREATED (application/json) {"checking_id": <string>, "payment_request": + >{"payment_hash": <string>, "payment_request": <string>}
Curl example
@@ -100,7 +100,7 @@ /paywall/api/v1/paywalls/<paywall_id>/check_invoice
Body (application/json)
- {"checking_id": <string>} + {"payment_hash": <string>}
Returns 200 OK (application/json)
@@ -113,7 +113,7 @@ curl -X POST {{ request.url_root }}paywall/api/v1/paywalls/<paywall_id>/check_invoice -d - '{"checking_id": <string>}' -H "Content-type: application/json" + '{"payment_hash": <string>}' -H "Content-type: application/json" diff --git a/lnbits/extensions/paywall/templates/paywall/display.html b/lnbits/extensions/paywall/templates/paywall/display.html index f3b7c6ff4..b9248ed38 100644 --- a/lnbits/extensions/paywall/templates/paywall/display.html +++ b/lnbits/extensions/paywall/templates/paywall/display.html @@ -121,7 +121,7 @@ axios .post( '/paywall/api/v1/paywalls/{{ paywall.id }}/check_invoice', - {checking_id: response.data.checking_id} + {payment_hash: response.data.payment_hash} ) .then(function (res) { if (res.data.paid) { diff --git a/lnbits/extensions/paywall/views_api.py b/lnbits/extensions/paywall/views_api.py index 012d355d5..96b616ca6 100644 --- a/lnbits/extensions/paywall/views_api.py +++ b/lnbits/extensions/paywall/views_api.py @@ -2,9 +2,8 @@ from flask import g, jsonify, request from http import HTTPStatus from lnbits.core.crud import get_user, get_wallet -from lnbits.core.services import create_invoice +from lnbits.core.services import create_invoice, check_invoice_status from lnbits.decorators import api_check_wallet_key, api_validate_post_request -from lnbits.settings import WALLET from lnbits.extensions.paywall import paywall_ext from .crud import create_paywall, get_paywall, get_paywalls, delete_paywall @@ -64,17 +63,17 @@ def api_paywall_create_invoice(paywall_id): try: amount = g.data["amount"] if g.data["amount"] > paywall.amount else paywall.amount - checking_id, payment_request = create_invoice( + payment_hash, payment_request = create_invoice( wallet_id=paywall.wallet, amount=amount, memo=f"#paywall {paywall.memo}" ) except Exception as e: return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR - return jsonify({"checking_id": checking_id, "payment_request": payment_request}), HTTPStatus.CREATED + return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.CREATED @paywall_ext.route("/api/v1/paywalls//check_invoice", methods=["POST"]) -@api_validate_post_request(schema={"checking_id": {"type": "string", "empty": False, "required": True}}) +@api_validate_post_request(schema={"payment_hash": {"type": "string", "empty": False, "required": True}}) def api_paywal_check_invoice(paywall_id): paywall = get_paywall(paywall_id) @@ -82,13 +81,13 @@ def api_paywal_check_invoice(paywall_id): return jsonify({"message": "Paywall does not exist."}), HTTPStatus.NOT_FOUND try: - is_paid = not WALLET.get_invoice_status(g.data["checking_id"]).pending + is_paid = not check_invoice_status(paywall.wallet, g.data["payment_hash"]).pending except Exception: return jsonify({"paid": False}), HTTPStatus.OK if is_paid: wallet = get_wallet(paywall.wallet) - payment = wallet.get_payment(g.data["checking_id"]) + payment = wallet.get_payment(g.data["payment_hash"]) payment.set_pending(False) return jsonify({"paid": True, "url": paywall.url, "remembers": paywall.remembers}), HTTPStatus.OK diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 6d96233e1..800114a16 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -224,7 +224,7 @@ '/tpos/api/v1/tposs/' + self.tposId + '/invoices/' + - response.data.checking_id + response.data.payment_hash ) .then(function (res) { if (res.data.paid) { diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py index c273ab155..c80fed8c2 100644 --- a/lnbits/extensions/tpos/views_api.py +++ b/lnbits/extensions/tpos/views_api.py @@ -2,9 +2,8 @@ from flask import g, jsonify, request from http import HTTPStatus from lnbits.core.crud import get_user, get_wallet -from lnbits.core.services import create_invoice +from lnbits.core.services import create_invoice, check_invoice_status from lnbits.decorators import api_check_wallet_key, api_validate_post_request -from lnbits.settings import WALLET from lnbits.extensions.tpos import tpos_ext from .crud import create_tpos, get_tpos, get_tposs, delete_tpos @@ -60,30 +59,31 @@ def api_tpos_create_invoice(tpos_id): return jsonify({"message": "TPoS does not exist."}), HTTPStatus.NOT_FOUND try: - checking_id, payment_request = create_invoice( + payment_hash, payment_request = create_invoice( wallet_id=tpos.wallet, amount=g.data["amount"], memo=f"#tpos {tpos.name}" ) except Exception as e: return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR - return jsonify({"checking_id": checking_id, "payment_request": payment_request}), HTTPStatus.CREATED + return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.CREATED -@tpos_ext.route("/api/v1/tposs//invoices/", methods=["GET"]) -def api_tpos_check_invoice(tpos_id, checking_id): +@tpos_ext.route("/api/v1/tposs//invoices/", methods=["GET"]) +def api_tpos_check_invoice(tpos_id, payment_hash): tpos = get_tpos(tpos_id) if not tpos: return jsonify({"message": "TPoS does not exist."}), HTTPStatus.NOT_FOUND try: - is_paid = not WALLET.get_invoice_status(checking_id).pending - except Exception: + is_paid = not check_invoice_status(tpos.wallet, payment_hash).pending + except Exception as exc: + print(exc) return jsonify({"paid": False}), HTTPStatus.OK if is_paid: wallet = get_wallet(tpos.wallet) - payment = wallet.get_payment(checking_id) + payment = wallet.get_payment(payment_hash) payment.set_pending(False) return jsonify({"paid": True}), HTTPStatus.OK diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html index c9f80e5ac..6a0980c98 100644 --- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html +++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html @@ -122,7 +122,8 @@ Returns 201 CREATED (application/json) {"checking_id": <string>,"payment_request": + >{"id": <string>, "name": <string>, "admin": + <string>, "email": <string>, "password": <string>}
Curl example
@@ -158,8 +159,9 @@ Returns 201 CREATED (application/json) {"checking_id": <string>,"payment_request": - <string>}{"id": <string>, "admin": <string>, "name": + <string>, "user": <string>, "adminkey": <string>, + "inkey": <string>}
Curl example
0 obj.isOut = obj.amount < 0 - obj.isPaid = obj.pending == 0 + obj.isPaid = obj.pending === 0 obj._q = [obj.memo, obj.sat].join(' ').toLowerCase() return obj } }, utils: { - confirmDialog: function (msg) { + confirmDialog: function(msg) { return Quasar.plugins.Dialog.create({ message: msg, ok: { @@ -119,16 +125,16 @@ var LNbits = { } }) }, - formatCurrency: function (value, currency) { + formatCurrency: function(value, currency) { return new Intl.NumberFormat(LOCALE, { style: 'currency', currency: currency }).format(value) }, - formatSat: function (value) { + formatSat: function(value) { return new Intl.NumberFormat(LOCALE).format(value) }, - notifyApiError: function (error) { + notifyApiError: function(error) { var types = { 400: 'warning', 401: 'warning', @@ -145,24 +151,22 @@ var LNbits = { icon: null }) }, - search: function (data, q, field, separator) { - var field = field || '_q' - + search: function(data, q, field, separator) { try { var queries = q.toLowerCase().split(separator || ' ') - return data.filter(function (obj) { + return data.filter(function(obj) { var matches = 0 - _.each(queries, function (q) { + _.each(queries, function(q) { if (obj[field].indexOf(q) !== -1) matches++ }) - return matches == queries.length + return matches === queries.length }) } catch (err) { return data } }, - exportCSV: function (columns, data) { - var wrapCsvValue = function (val, formatFn) { + exportCSV: function(columns, data) { + var wrapCsvValue = function(val, formatFn) { var formatted = formatFn !== void 0 ? formatFn(val) : val formatted = @@ -174,14 +178,14 @@ var LNbits = { } var content = [ - columns.map(function (col) { + columns.map(function(col) { return wrapCsvValue(col.label) }) ] .concat( - data.map(function (row) { + data.map(function(row) { return columns - .map(function (col) { + .map(function(col) { return wrapCsvValue( typeof col.field === 'function' ? col.field(row) @@ -212,7 +216,7 @@ var LNbits = { } var windowMixin = { - data: function () { + data: function() { return { g: { visibleDrawer: false, @@ -224,13 +228,13 @@ var windowMixin = { } }, methods: { - toggleDarkMode: function () { + toggleDarkMode: function() { this.$q.dark.toggle() this.$q.localStorage.set('lnbits.darkMode', this.$q.dark.isActive) }, - copyText: function (text, message, position) { + copyText: function(text, message, position) { var notify = this.$q.notify - Quasar.utils.copyToClipboard(text).then(function () { + Quasar.utils.copyToClipboard(text).then(function() { notify({ message: message || 'Copied to clipboard!', position: position || 'bottom' @@ -238,7 +242,7 @@ var windowMixin = { }) } }, - created: function () { + created: function() { this.$q.dark.set(this.$q.localStorage.getItem('lnbits.darkMode')) if (window.user) { this.g.user = Object.freeze(LNbits.map.user(window.user)) @@ -250,18 +254,18 @@ var windowMixin = { var user = this.g.user this.g.extensions = Object.freeze( window.extensions - .map(function (data) { + .map(function(data) { return LNbits.map.extension(data) }) - .map(function (obj) { + .map(function(obj) { if (user) { - obj.isEnabled = user.extensions.indexOf(obj.code) != -1 + obj.isEnabled = user.extensions.indexOf(obj.code) !== -1 } else { obj.isEnabled = false } return obj }) - .sort(function (a, b) { + .sort(function(a, b) { return a.name > b.name }) )