mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-03-10 09:19:42 +01:00
track fiat value of payments (#1789)
* save fiat_amounts on payment creation * show fiat amount in frontend * add lnbits_default_accounting_currency * extract fiat calculation logic into service * move all currency conversions to calc_fiat_amounts move all conversion logic into create_invoice so extensions can benefit aswell * show user-defined and wallet-defined currency in frontend * remove sat from fiat units * make bundle * improve tests * debug log
This commit is contained in:
parent
7a37e72915
commit
2623e9247a
14 changed files with 233 additions and 42 deletions
|
@ -238,15 +238,25 @@ async def create_wallet(
|
||||||
|
|
||||||
|
|
||||||
async def update_wallet(
|
async def update_wallet(
|
||||||
wallet_id: str, new_name: str, conn: Optional[Connection] = None
|
wallet_id: str,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
currency: Optional[str] = None,
|
||||||
|
conn: Optional[Connection] = None,
|
||||||
) -> Optional[Wallet]:
|
) -> Optional[Wallet]:
|
||||||
|
set_clause = []
|
||||||
|
values = []
|
||||||
|
if name:
|
||||||
|
set_clause.append("name = ?")
|
||||||
|
values.append(name)
|
||||||
|
if currency is not None:
|
||||||
|
set_clause.append("currency = ?")
|
||||||
|
values.append(currency)
|
||||||
|
values.append(wallet_id)
|
||||||
await (conn or db).execute(
|
await (conn or db).execute(
|
||||||
"""
|
f"""
|
||||||
UPDATE wallets SET
|
UPDATE wallets SET {', '.join(set_clause)} WHERE id = ?
|
||||||
name = ?
|
|
||||||
WHERE id = ?
|
|
||||||
""",
|
""",
|
||||||
(new_name, wallet_id),
|
tuple(values),
|
||||||
)
|
)
|
||||||
wallet = await get_wallet(wallet_id=wallet_id, conn=conn)
|
wallet = await get_wallet(wallet_id=wallet_id, conn=conn)
|
||||||
assert wallet, "updated created wallet couldn't be retrieved"
|
assert wallet, "updated created wallet couldn't be retrieved"
|
||||||
|
@ -519,8 +529,8 @@ async def create_payment(
|
||||||
conn: Optional[Connection] = None,
|
conn: Optional[Connection] = None,
|
||||||
) -> Payment:
|
) -> Payment:
|
||||||
# todo: add this when tests are fixed
|
# todo: add this when tests are fixed
|
||||||
# previous_payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn)
|
previous_payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn)
|
||||||
# assert previous_payment is None, "Payment already exists"
|
assert previous_payment is None, "Payment already exists"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
invoice = bolt11.decode(payment_request)
|
invoice = bolt11.decode(payment_request)
|
||||||
|
|
|
@ -319,3 +319,11 @@ async def m011_optimize_balances_view(db):
|
||||||
GROUP BY wallet
|
GROUP BY wallet
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def m012_add_currency_to_wallet(db):
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE wallets ADD COLUMN currency TEXT
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
|
@ -27,6 +27,7 @@ class Wallet(BaseModel):
|
||||||
user: str
|
user: str
|
||||||
adminkey: str
|
adminkey: str
|
||||||
inkey: str
|
inkey: str
|
||||||
|
currency: Optional[str]
|
||||||
balance_msat: int
|
balance_msat: int
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -21,6 +21,7 @@ from lnbits.settings import (
|
||||||
send_admin_user_to_saas,
|
send_admin_user_to_saas,
|
||||||
settings,
|
settings,
|
||||||
)
|
)
|
||||||
|
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis, satoshis_amount_as_fiat
|
||||||
from lnbits.wallets import FAKE_WALLET, get_wallet_class, set_wallet_class
|
from lnbits.wallets import FAKE_WALLET, get_wallet_class, set_wallet_class
|
||||||
from lnbits.wallets.base import PaymentResponse, PaymentStatus
|
from lnbits.wallets.base import PaymentResponse, PaymentStatus
|
||||||
|
|
||||||
|
@ -55,10 +56,47 @@ class InvoiceFailure(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
async def calculate_fiat_amounts(
|
||||||
|
amount: float,
|
||||||
|
wallet_id: str,
|
||||||
|
currency: Optional[str] = None,
|
||||||
|
extra: Optional[Dict] = None,
|
||||||
|
conn: Optional[Connection] = None,
|
||||||
|
) -> Tuple[int, Optional[Dict]]:
|
||||||
|
wallet = await get_wallet(wallet_id, conn=conn)
|
||||||
|
assert wallet, "invalid wallet_id"
|
||||||
|
wallet_currency = wallet.currency or settings.lnbits_default_accounting_currency
|
||||||
|
|
||||||
|
if currency and currency != "sat":
|
||||||
|
amount_sat = await fiat_amount_as_satoshis(amount, currency)
|
||||||
|
extra = extra or {}
|
||||||
|
if currency != wallet_currency:
|
||||||
|
extra["fiat_currency"] = currency
|
||||||
|
extra["fiat_amount"] = round(amount, ndigits=3)
|
||||||
|
extra["fiat_rate"] = amount_sat / amount
|
||||||
|
else:
|
||||||
|
amount_sat = int(amount)
|
||||||
|
|
||||||
|
if wallet_currency:
|
||||||
|
if wallet_currency == currency:
|
||||||
|
fiat_amount = amount
|
||||||
|
else:
|
||||||
|
fiat_amount = await satoshis_amount_as_fiat(amount_sat, wallet_currency)
|
||||||
|
extra = extra or {}
|
||||||
|
extra["wallet_fiat_currency"] = wallet_currency
|
||||||
|
extra["wallet_fiat_amount"] = round(fiat_amount, ndigits=3)
|
||||||
|
extra["wallet_fiat_rate"] = amount_sat / fiat_amount
|
||||||
|
|
||||||
|
logger.debug(f"Calculated fiat amounts for {wallet}: {extra=}")
|
||||||
|
|
||||||
|
return amount_sat, extra
|
||||||
|
|
||||||
|
|
||||||
async def create_invoice(
|
async def create_invoice(
|
||||||
*,
|
*,
|
||||||
wallet_id: str,
|
wallet_id: str,
|
||||||
amount: int, # in satoshis
|
amount: float,
|
||||||
|
currency: Optional[str] = "sat",
|
||||||
memo: str,
|
memo: str,
|
||||||
description_hash: Optional[bytes] = None,
|
description_hash: Optional[bytes] = None,
|
||||||
unhashed_description: Optional[bytes] = None,
|
unhashed_description: Optional[bytes] = None,
|
||||||
|
@ -76,8 +114,12 @@ async def create_invoice(
|
||||||
# use the fake wallet if the invoice is for internal use only
|
# use the fake wallet if the invoice is for internal use only
|
||||||
wallet = FAKE_WALLET if internal else get_wallet_class()
|
wallet = FAKE_WALLET if internal else get_wallet_class()
|
||||||
|
|
||||||
|
amount_sat, extra = await calculate_fiat_amounts(
|
||||||
|
amount, wallet_id, currency=currency, extra=extra, conn=conn
|
||||||
|
)
|
||||||
|
|
||||||
ok, checking_id, payment_request, error_message = await wallet.create_invoice(
|
ok, checking_id, payment_request, error_message = await wallet.create_invoice(
|
||||||
amount=amount,
|
amount=amount_sat,
|
||||||
memo=invoice_memo,
|
memo=invoice_memo,
|
||||||
description_hash=description_hash,
|
description_hash=description_hash,
|
||||||
unhashed_description=unhashed_description,
|
unhashed_description=unhashed_description,
|
||||||
|
@ -88,7 +130,7 @@ async def create_invoice(
|
||||||
|
|
||||||
invoice = bolt11.decode(payment_request)
|
invoice = bolt11.decode(payment_request)
|
||||||
|
|
||||||
amount_msat = amount * 1000
|
amount_msat = 1000 * amount_sat
|
||||||
await create_payment(
|
await create_payment(
|
||||||
wallet_id=wallet_id,
|
wallet_id=wallet_id,
|
||||||
checking_id=checking_id,
|
checking_id=checking_id,
|
||||||
|
@ -134,6 +176,10 @@ async def pay_invoice(
|
||||||
if max_sat and invoice.amount_msat > max_sat * 1000:
|
if max_sat and invoice.amount_msat > max_sat * 1000:
|
||||||
raise ValueError("Amount in invoice is too high.")
|
raise ValueError("Amount in invoice is too high.")
|
||||||
|
|
||||||
|
_, extra = await calculate_fiat_amounts(
|
||||||
|
invoice.amount_msat, wallet_id, extra=extra, conn=conn
|
||||||
|
)
|
||||||
|
|
||||||
# put all parameters that don't change here
|
# put all parameters that don't change here
|
||||||
class PaymentKwargs(TypedDict):
|
class PaymentKwargs(TypedDict):
|
||||||
wallet_id: str
|
wallet_id: str
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// update cache version every time there is a new deployment
|
// update cache version every time there is a new deployment
|
||||||
// so the service worker reinitializes the cache
|
// so the service worker reinitializes the cache
|
||||||
const CACHE_VERSION = 52
|
const CACHE_VERSION = 53
|
||||||
const CURRENT_CACHE = `lnbits-${CACHE_VERSION}-`
|
const CURRENT_CACHE = `lnbits-${CACHE_VERSION}-`
|
||||||
|
|
||||||
const getApiKey = request => {
|
const getApiKey = request => {
|
||||||
|
|
|
@ -256,7 +256,10 @@ new Vue({
|
||||||
},
|
},
|
||||||
balance: 0,
|
balance: 0,
|
||||||
credit: 0,
|
credit: 0,
|
||||||
newName: ''
|
update: {
|
||||||
|
name: null,
|
||||||
|
currency: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -717,16 +720,12 @@ new Vue({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
updateWalletName: function () {
|
updateWallet: function (data) {
|
||||||
let newName = this.newName
|
|
||||||
let adminkey = this.g.wallet.adminkey
|
|
||||||
if (!newName || !newName.length) return
|
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request('PUT', '/api/v1/wallet/' + newName, adminkey, {})
|
.request('PATCH', '/api/v1/wallet', this.g.wallet.adminkey, data)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.newName = ''
|
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
message: `Wallet named updated.`,
|
message: `Wallet updated.`,
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
timeout: 3500
|
timeout: 3500
|
||||||
})
|
})
|
||||||
|
@ -737,7 +736,6 @@ new Vue({
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.newName = ''
|
|
||||||
LNbits.utils.notifyApiError(err)
|
LNbits.utils.notifyApiError(err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -811,6 +809,9 @@ new Vue({
|
||||||
this.fetchBalance()
|
this.fetchBalance()
|
||||||
this.fetchPayments()
|
this.fetchPayments()
|
||||||
|
|
||||||
|
this.update.name = this.g.wallet.name
|
||||||
|
this.update.currency = this.g.wallet.currency
|
||||||
|
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request('GET', '/api/v1/currencies')
|
.request('GET', '/api/v1/currencies')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
|
@ -67,6 +67,18 @@
|
||||||
></q-select>
|
></q-select>
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Default Accounting Currency</p>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
v-model="formData.lnbits_default_accounting_currency"
|
||||||
|
clearable
|
||||||
|
hint="Default currency for accounting"
|
||||||
|
label="Currency"
|
||||||
|
:options="formData.lnbits_allowed_currencies.length ? formData.lnbits_allowed_currencies : {{ currencies }}"
|
||||||
|
></q-select>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-md">
|
<div class="row q-col-gutter-md">
|
||||||
<div class="col-12 col-md-6">
|
<div class="col-12 col-md-6">
|
||||||
|
|
|
@ -211,10 +211,17 @@
|
||||||
parseFloat(String(props.row.fsat).replaceAll(",", "")) / 100
|
parseFloat(String(props.row.fsat).replaceAll(",", "")) / 100
|
||||||
}}
|
}}
|
||||||
</q-td>
|
</q-td>
|
||||||
|
|
||||||
<q-td auto-width key="amount" v-else :props="props">
|
<q-td auto-width key="amount" v-else :props="props">
|
||||||
{{ props.row.fsat }}<br />
|
{{ props.row.fsat }}<br />
|
||||||
<i>fee {{ props.row.fee/1000 }}</i>
|
<i v-if="props.row.extra.wallet_fiat_currency">
|
||||||
|
{{ props.row.extra.wallet_fiat_currency }} {{
|
||||||
|
props.row.extra.wallet_fiat_amount }}
|
||||||
|
<br />
|
||||||
|
</i>
|
||||||
|
<i v-if="props.row.extra.fiat_currency">
|
||||||
|
{{ props.row.extra.fiat_currency }} {{
|
||||||
|
props.row.extra.fiat_amount }}
|
||||||
|
</i>
|
||||||
</q-td>
|
</q-td>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
|
|
||||||
|
@ -361,23 +368,54 @@
|
||||||
<div class="" style="max-width: 320px">
|
<div class="" style="max-width: 320px">
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
v-model.trim="newName"
|
v-model.trim="update.name"
|
||||||
label="Label"
|
label="Name"
|
||||||
dense="dense"
|
dense
|
||||||
@update:model-value="(e) => console.log(e)"
|
@update:model-value="(e) => console.log(e)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<q-btn
|
<q-btn
|
||||||
:disable="!newName.length"
|
:disable="!update.name.length"
|
||||||
unelevated
|
unelevated
|
||||||
class="q-mt-sm"
|
class="q-mt-sm"
|
||||||
color="primary"
|
color="primary"
|
||||||
:label="$t('update_name')"
|
:label="$t('update_name')"
|
||||||
@click="updateWalletName()"
|
@click="updateWallet({ name: update.name })"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<q-expansion-item
|
||||||
|
group="extras"
|
||||||
|
icon="attach_money"
|
||||||
|
:label="$t('fiat_tracking')"
|
||||||
|
>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div style="max-width: 360px">
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
|
v-model="update.currency"
|
||||||
|
type="text"
|
||||||
|
:label="$t('currency')"
|
||||||
|
:options="receive.units.filter((u) => u !== 'sat')"
|
||||||
|
></q-select>
|
||||||
|
</div>
|
||||||
|
<q-btn
|
||||||
|
:disable="!update.name.length"
|
||||||
|
unelevated
|
||||||
|
class="q-mt-sm"
|
||||||
|
color="primary"
|
||||||
|
:label="$t('update_currency')"
|
||||||
|
@click="updateWallet({ currency: update.currency || '' })"
|
||||||
|
></q-btn>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
|
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
group="extras"
|
group="extras"
|
||||||
|
|
|
@ -114,7 +114,7 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||||
|
|
||||||
|
|
||||||
@core_app.put("/api/v1/wallet/{new_name}")
|
@core_app.put("/api/v1/wallet/{new_name}")
|
||||||
async def api_update_wallet(
|
async def api_update_wallet_name(
|
||||||
new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key)
|
new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||||
):
|
):
|
||||||
await update_wallet(wallet.wallet.id, new_name)
|
await update_wallet(wallet.wallet.id, new_name)
|
||||||
|
@ -125,6 +125,15 @@ async def api_update_wallet(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@core_app.patch("/api/v1/wallet", response_model=Wallet)
|
||||||
|
async def api_update_wallet(
|
||||||
|
name: Optional[str] = Body(None),
|
||||||
|
currency: Optional[str] = Body(None),
|
||||||
|
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||||
|
):
|
||||||
|
return await update_wallet(wallet.wallet.id, name, currency)
|
||||||
|
|
||||||
|
|
||||||
@core_app.get(
|
@core_app.get(
|
||||||
"/api/v1/payments",
|
"/api/v1/payments",
|
||||||
name="Payment List",
|
name="Payment List",
|
||||||
|
@ -187,7 +196,6 @@ async def api_payments_paginated(
|
||||||
|
|
||||||
|
|
||||||
async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
|
async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
|
||||||
data.extra = data.extra or {}
|
|
||||||
description_hash = b""
|
description_hash = b""
|
||||||
unhashed_description = b""
|
unhashed_description = b""
|
||||||
memo = data.memo or settings.lnbits_site_title
|
memo = data.memo or settings.lnbits_site_title
|
||||||
|
@ -211,20 +219,13 @@ async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
|
||||||
# do not save memo if description_hash or unhashed_description is set
|
# do not save memo if description_hash or unhashed_description is set
|
||||||
memo = ""
|
memo = ""
|
||||||
|
|
||||||
if data.unit == "sat":
|
|
||||||
amount = int(data.amount)
|
|
||||||
else:
|
|
||||||
assert data.unit is not None, "unit not set"
|
|
||||||
price_in_sats = await fiat_amount_as_satoshis(data.amount, data.unit)
|
|
||||||
amount = price_in_sats
|
|
||||||
data.extra.update({"fiat_amount": data.amount, "fiat_currency": data.unit})
|
|
||||||
|
|
||||||
async with db.connect() as conn:
|
async with db.connect() as conn:
|
||||||
try:
|
try:
|
||||||
payment_hash, payment_request = await create_invoice(
|
payment_hash, payment_request = await create_invoice(
|
||||||
wallet_id=wallet.id,
|
wallet_id=wallet.id,
|
||||||
amount=amount,
|
amount=data.amount,
|
||||||
memo=memo,
|
memo=memo,
|
||||||
|
currency=data.unit,
|
||||||
description_hash=description_hash,
|
description_hash=description_hash,
|
||||||
unhashed_description=unhashed_description,
|
unhashed_description=unhashed_description,
|
||||||
expiry=data.expiry,
|
expiry=data.expiry,
|
||||||
|
|
|
@ -90,6 +90,7 @@ class ThemesSettings(LNbitsSettings):
|
||||||
) # sneaky sneaky
|
) # sneaky sneaky
|
||||||
lnbits_ad_space_enabled: bool = Field(default=False)
|
lnbits_ad_space_enabled: bool = Field(default=False)
|
||||||
lnbits_allowed_currencies: List[str] = Field(default=[])
|
lnbits_allowed_currencies: List[str] = Field(default=[])
|
||||||
|
lnbits_default_accounting_currency: Optional[str] = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class OpsSettings(LNbitsSettings):
|
class OpsSettings(LNbitsSettings):
|
||||||
|
|
2
lnbits/static/bundle.min.js
vendored
2
lnbits/static/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -29,6 +29,9 @@ window.localisation.en = {
|
||||||
'This whole wallet will be deleted, the funds will be UNRECOVERABLE.',
|
'This whole wallet will be deleted, the funds will be UNRECOVERABLE.',
|
||||||
rename_wallet: 'Rename wallet',
|
rename_wallet: 'Rename wallet',
|
||||||
update_name: 'Update name',
|
update_name: 'Update name',
|
||||||
|
fiat_tracking: 'Fiat tracking',
|
||||||
|
currency: 'Currency',
|
||||||
|
update_currency: 'Update currency',
|
||||||
press_to_claim: 'Press to claim bitcoin',
|
press_to_claim: 'Press to claim bitcoin',
|
||||||
donate: 'Donate',
|
donate: 'Donate',
|
||||||
view_github: 'View on GitHub',
|
view_github: 'View on GitHub',
|
||||||
|
|
|
@ -178,7 +178,8 @@ window.LNbits = {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
adminkey: data.adminkey,
|
adminkey: data.adminkey,
|
||||||
inkey: data.inkey
|
inkey: data.inkey,
|
||||||
|
currency: data.currency
|
||||||
}
|
}
|
||||||
newWallet.msat = data.balance_msat
|
newWallet.msat = data.balance_msat
|
||||||
newWallet.sat = Math.round(data.balance_msat / 1000)
|
newWallet.sat = Math.round(data.balance_msat / 1000)
|
||||||
|
@ -203,7 +204,9 @@ window.LNbits = {
|
||||||
extra: data.extra,
|
extra: data.extra,
|
||||||
wallet_id: data.wallet_id,
|
wallet_id: data.wallet_id,
|
||||||
webhook: data.webhook,
|
webhook: data.webhook,
|
||||||
webhook_status: data.webhook_status
|
webhook_status: data.webhook_status,
|
||||||
|
fiat_amount: data.fiat_amount,
|
||||||
|
fiat_currency: data.fiat_currency
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.date = Quasar.utils.date.formatDate(
|
obj.date = Quasar.utils.date.formatDate(
|
||||||
|
|
|
@ -10,6 +10,7 @@ from lnbits.core.models import Payment
|
||||||
from lnbits.core.views.admin_api import api_auditor
|
from lnbits.core.views.admin_api import api_auditor
|
||||||
from lnbits.core.views.api import api_payment
|
from lnbits.core.views.api import api_payment
|
||||||
from lnbits.db import DB_TYPE, SQLITE
|
from lnbits.db import DB_TYPE, SQLITE
|
||||||
|
from lnbits.settings import settings
|
||||||
from lnbits.wallets import get_wallet_class
|
from lnbits.wallets import get_wallet_class
|
||||||
from tests.conftest import CreateInvoice, api_payments_create_invoice
|
from tests.conftest import CreateInvoice, api_payments_create_invoice
|
||||||
|
|
||||||
|
@ -96,6 +97,13 @@ async def test_create_invoice_fiat_amount(client, inkey_headers_to):
|
||||||
decode = bolt11.decode(invoice["payment_request"])
|
decode = bolt11.decode(invoice["payment_request"])
|
||||||
assert decode.amount_msat != data["amount"] * 1000
|
assert decode.amount_msat != data["amount"] * 1000
|
||||||
|
|
||||||
|
response = await client.get("/api/v1/payments?limit=1", headers=inkey_headers_to)
|
||||||
|
assert response.is_success
|
||||||
|
extra = response.json()[0]["extra"]
|
||||||
|
assert extra["fiat_amount"] == data["amount"]
|
||||||
|
assert extra["fiat_currency"] == data["unit"]
|
||||||
|
assert extra["fiat_rate"]
|
||||||
|
|
||||||
|
|
||||||
# check POST /api/v1/payments: invoice creation for internal payments only
|
# check POST /api/v1/payments: invoice creation for internal payments only
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
@ -357,6 +365,65 @@ async def test_create_invoice_with_unhashed_description(client, inkey_headers_to
|
||||||
return invoice
|
return invoice
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_update_wallet(client, adminkey_headers_from):
|
||||||
|
name = "new name"
|
||||||
|
currency = "EUR"
|
||||||
|
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/wallet", json={"name": name}, headers=adminkey_headers_from
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["name"] == name
|
||||||
|
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/wallet", json={"currency": currency}, headers=adminkey_headers_from
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["currency"] == currency
|
||||||
|
# name is not changed because updates are partial
|
||||||
|
assert response.json()["name"] == name
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_fiat_tracking(client, adminkey_headers_from):
|
||||||
|
async def create_invoice():
|
||||||
|
data = await get_random_invoice_data()
|
||||||
|
response = await client.post(
|
||||||
|
"/api/v1/payments", json=data, headers=adminkey_headers_from
|
||||||
|
)
|
||||||
|
assert response.is_success
|
||||||
|
|
||||||
|
response = await client.get(
|
||||||
|
f"/api/v1/payments/{response.json()['payment_hash']}",
|
||||||
|
headers=adminkey_headers_from,
|
||||||
|
)
|
||||||
|
assert response.is_success
|
||||||
|
return response.json()["details"]
|
||||||
|
|
||||||
|
async def update_currency(currency):
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/wallet", json={"currency": currency}, headers=adminkey_headers_from
|
||||||
|
)
|
||||||
|
assert response.is_success
|
||||||
|
assert response.json()["currency"] == currency
|
||||||
|
|
||||||
|
await update_currency("")
|
||||||
|
|
||||||
|
settings.lnbits_default_accounting_currency = "USD"
|
||||||
|
payment = await create_invoice()
|
||||||
|
assert payment["extra"]["wallet_fiat_currency"] == "USD"
|
||||||
|
assert payment["extra"]["wallet_fiat_amount"] != payment["amount"]
|
||||||
|
assert payment["extra"]["wallet_fiat_rate"]
|
||||||
|
|
||||||
|
await update_currency("EUR")
|
||||||
|
|
||||||
|
payment = await create_invoice()
|
||||||
|
assert payment["extra"]["wallet_fiat_currency"] == "EUR"
|
||||||
|
assert payment["extra"]["wallet_fiat_amount"] != payment["amount"]
|
||||||
|
assert payment["extra"]["wallet_fiat_rate"]
|
||||||
|
|
||||||
|
|
||||||
async def get_node_balance_sats():
|
async def get_node_balance_sats():
|
||||||
audit = await api_auditor()
|
audit = await api_auditor()
|
||||||
return audit["node_balance_msats"] / 1000
|
return audit["node_balance_msats"] / 1000
|
||||||
|
|
Loading…
Add table
Reference in a new issue