feat: Allow custom memo on pay invoice (#3236)
Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
This commit is contained in:
parent
9aa2194d94
commit
88cf1ac853
9 changed files with 204 additions and 58 deletions
|
|
@ -10,6 +10,7 @@ class CreateLnurl(BaseModel):
|
||||||
comment: Optional[str] = None
|
comment: Optional[str] = None
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
unit: Optional[str] = None
|
unit: Optional[str] = None
|
||||||
|
internal_memo: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class CreateLnurlAuth(BaseModel):
|
class CreateLnurlAuth(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -667,8 +667,36 @@
|
||||||
dense
|
dense
|
||||||
type="textarea"
|
type="textarea"
|
||||||
rows="2"
|
rows="2"
|
||||||
v-model.trim="receive.data.memo"
|
v-model="receive.data.memo"
|
||||||
:label="$t('memo')"
|
:label="$t('memo')"
|
||||||
|
>
|
||||||
|
<template v-if="receive.data.internalMemo === null" v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="add_comment"
|
||||||
|
@click.stop.prevent="receive.data.internalMemo = ''"
|
||||||
|
class="cursor-pointer"
|
||||||
|
></q-icon>
|
||||||
|
<q-tooltip>
|
||||||
|
<span v-text="$t('internal_memo')"></span>
|
||||||
|
</q-tooltip>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
v-if="receive.data.internalMemo !== null"
|
||||||
|
autogrow
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="receive.data.internalMemo"
|
||||||
|
class="q-mb-lg"
|
||||||
|
:label="$t('internal_memo')"
|
||||||
|
:hint="$t('internal_memo_hint_receive')"
|
||||||
|
:rules="[ val => !val || val.length <= 512 || 'Please use maximum 512 characters' ]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="cancel"
|
||||||
|
@click.stop.prevent="receive.data.internalMemo = null"
|
||||||
|
class="cursor-pointer"
|
||||||
|
/> </template
|
||||||
></q-input>
|
></q-input>
|
||||||
<div v-if="g.user.fiat_providers?.length" class="q-mt-md">
|
<div v-if="g.user.fiat_providers?.length" class="q-mt-md">
|
||||||
<q-list bordered dense class="rounded-borders">
|
<q-list bordered dense class="rounded-borders">
|
||||||
|
|
@ -848,6 +876,21 @@
|
||||||
</div>
|
</div>
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
<h6 class="text-center" v-text="parse.invoice.description"></h6>
|
<h6 class="text-center" v-text="parse.invoice.description"></h6>
|
||||||
|
<q-input
|
||||||
|
autogrow
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="parse.data.internalMemo"
|
||||||
|
:label="$t('internal_memo')"
|
||||||
|
:hint="$t('internal_memo_hint_pay')"
|
||||||
|
class="q-mb-lg"
|
||||||
|
:rules="[ val => !val || val.length <= 512 || 'Please use maximum 512 characters' ]"
|
||||||
|
><template v-if="parse.data.internalMemo" v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="cancel"
|
||||||
|
@click.stop.prevent="parse.data.internalMemo = null"
|
||||||
|
class="cursor-pointer" /></template
|
||||||
|
></q-input>
|
||||||
<q-list separator bordered dense class="q-mb-md">
|
<q-list separator bordered dense class="q-mb-md">
|
||||||
<q-expansion-item expand-separator icon="info" label="Details">
|
<q-expansion-item expand-separator icon="info" label="Details">
|
||||||
<q-list separator>
|
<q-list separator>
|
||||||
|
|
@ -1041,7 +1084,7 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col q-mb-lg">
|
||||||
<q-select
|
<q-select
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
|
|
@ -1075,9 +1118,39 @@
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model="parse.data.comment"
|
v-model="parse.data.comment"
|
||||||
:type="parse.lnurlpay.commentAllowed > 64 ? 'textarea' : 'text'"
|
:type="parse.lnurlpay.commentAllowed > 512 ? 'textarea' : 'text'"
|
||||||
label="Comment (optional)"
|
label="Comment (optional)"
|
||||||
:maxlength="parse.lnurlpay.commentAllowed"
|
:maxlength="parse.lnurlpay.commentAllowed"
|
||||||
|
><template
|
||||||
|
v-if="parse.data.internalMemo === null"
|
||||||
|
v-slot:append
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
name="add_comment"
|
||||||
|
@click.stop.prevent="parse.data.internalMemo = ''"
|
||||||
|
class="cursor-pointer"
|
||||||
|
></q-icon>
|
||||||
|
<q-tooltip>
|
||||||
|
<span v-text="$t('internal_memo')"></span>
|
||||||
|
</q-tooltip> </template
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
<q-input
|
||||||
|
v-if="parse.data.internalMemo !== null"
|
||||||
|
autogrow
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="parse.data.internalMemo"
|
||||||
|
:label="$t('internal_memo')"
|
||||||
|
:hint="$t('internal_memo_hint_pay')"
|
||||||
|
class=""
|
||||||
|
:rules="[ val => !val || val.length <= 512 || 'Please use maximum 512 characters' ]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="cancel"
|
||||||
|
@click.stop.prevent="parse.data.internalMemo = null"
|
||||||
|
class="cursor-pointer"
|
||||||
|
/> </template
|
||||||
></q-input>
|
></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,6 @@ async def api_payments_counting_stats(
|
||||||
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
||||||
user: User = Depends(check_user_exists),
|
user: User = Depends(check_user_exists),
|
||||||
):
|
):
|
||||||
|
|
||||||
if user.admin:
|
if user.admin:
|
||||||
# admin user can see payments from all wallets
|
# admin user can see payments from all wallets
|
||||||
for_user_id = None
|
for_user_id = None
|
||||||
|
|
@ -142,7 +141,6 @@ async def api_payments_wallets_stats(
|
||||||
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
||||||
user: User = Depends(check_user_exists),
|
user: User = Depends(check_user_exists),
|
||||||
):
|
):
|
||||||
|
|
||||||
if user.admin:
|
if user.admin:
|
||||||
# admin user can see payments from all wallets
|
# admin user can see payments from all wallets
|
||||||
for_user_id = None
|
for_user_id = None
|
||||||
|
|
@ -163,7 +161,6 @@ async def api_payments_daily_stats(
|
||||||
user: User = Depends(check_user_exists),
|
user: User = Depends(check_user_exists),
|
||||||
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
||||||
):
|
):
|
||||||
|
|
||||||
if user.admin:
|
if user.admin:
|
||||||
# admin user can see payments from all wallets
|
# admin user can see payments from all wallets
|
||||||
for_user_id = None
|
for_user_id = None
|
||||||
|
|
@ -285,23 +282,36 @@ async def api_payments_fee_reserve(invoice: str = Query("invoice")) -> JSONRespo
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@payment_router.post("/lnurl")
|
def _validate_lnurl_response(
|
||||||
async def api_payments_pay_lnurl(
|
params: dict, domain: str, amount_msat: int
|
||||||
data: CreateLnurl, wallet: WalletTypeInfo = Depends(require_admin_key)
|
) -> bolt11.Invoice:
|
||||||
) -> Payment:
|
if params.get("status") == "ERROR":
|
||||||
domain = urlparse(data.callback).netloc
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail=f"{domain} said: '{params.get('reason', '')}'",
|
||||||
|
)
|
||||||
|
if not params.get("pr"):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail=f"{domain} did not return a payment request.",
|
||||||
|
)
|
||||||
|
invoice = bolt11.decode(params["pr"])
|
||||||
|
if invoice.amount_msat != amount_msat:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail=(
|
||||||
|
f"{domain} returned an invalid invoice. Expected"
|
||||||
|
f" {amount_msat} msat, got {invoice.amount_msat}."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return invoice
|
||||||
|
|
||||||
|
|
||||||
|
async def _fetch_lnurl_params(data: CreateLnurl, amount_msat: int, domain: str) -> dict:
|
||||||
headers = {"User-Agent": settings.user_agent}
|
headers = {"User-Agent": settings.user_agent}
|
||||||
async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
|
async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
|
||||||
try:
|
try:
|
||||||
if data.unit and data.unit != "sat":
|
r: httpx.Response = await client.get(
|
||||||
amount_msat = await fiat_amount_as_satoshis(data.amount, data.unit)
|
|
||||||
# no msat precision
|
|
||||||
amount_msat = ceil(amount_msat // 1000) * 1000
|
|
||||||
else:
|
|
||||||
amount_msat = data.amount
|
|
||||||
check_callback_url(data.callback)
|
|
||||||
r = await client.get(
|
|
||||||
data.callback,
|
data.callback,
|
||||||
params={"amount": amount_msat, "comment": data.comment},
|
params={"amount": amount_msat, "comment": data.comment},
|
||||||
timeout=40,
|
timeout=40,
|
||||||
|
|
@ -315,29 +325,24 @@ async def api_payments_pay_lnurl(
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
detail=f"Failed to connect to {domain}.",
|
detail=f"Failed to connect to {domain}.",
|
||||||
) from exc
|
) from exc
|
||||||
|
return json.loads(r.text)
|
||||||
|
|
||||||
params = json.loads(r.text)
|
|
||||||
if params.get("status") == "ERROR":
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
|
||||||
detail=f"{domain} said: '{params.get('reason', '')}'",
|
|
||||||
)
|
|
||||||
|
|
||||||
if not params.get("pr"):
|
@payment_router.post("/lnurl")
|
||||||
raise HTTPException(
|
async def api_payments_pay_lnurl(
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
data: CreateLnurl, wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||||
detail=f"{domain} did not return a payment request.",
|
) -> Payment:
|
||||||
)
|
domain = urlparse(data.callback).netloc
|
||||||
|
check_callback_url(data.callback)
|
||||||
|
|
||||||
invoice = bolt11.decode(params["pr"])
|
if data.unit and data.unit != "sat":
|
||||||
if invoice.amount_msat != amount_msat:
|
amount_msat = await fiat_amount_as_satoshis(data.amount, data.unit)
|
||||||
raise HTTPException(
|
amount_msat = ceil(amount_msat // 1000) * 1000
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
else:
|
||||||
detail=(
|
amount_msat = data.amount
|
||||||
f"{domain} returned an invalid invoice. Expected"
|
|
||||||
f" {amount_msat} msat, got {invoice.amount_msat}."
|
params = await _fetch_lnurl_params(data, amount_msat, domain)
|
||||||
),
|
_validate_lnurl_response(params, domain, amount_msat)
|
||||||
)
|
|
||||||
|
|
||||||
extra = {}
|
extra = {}
|
||||||
if params.get("successAction"):
|
if params.get("successAction"):
|
||||||
|
|
@ -347,6 +352,11 @@ async def api_payments_pay_lnurl(
|
||||||
if data.unit and data.unit != "sat":
|
if data.unit and data.unit != "sat":
|
||||||
extra["fiat_currency"] = data.unit
|
extra["fiat_currency"] = data.unit
|
||||||
extra["fiat_amount"] = data.amount / 1000
|
extra["fiat_amount"] = data.amount / 1000
|
||||||
|
if data.internal_memo is not None:
|
||||||
|
assert (
|
||||||
|
len(data.internal_memo) <= 512
|
||||||
|
), "Internal memo must be 512 characters or less."
|
||||||
|
extra["internal_memo"] = data.internal_memo
|
||||||
assert data.description is not None, "description is required"
|
assert data.description is not None, "description is required"
|
||||||
|
|
||||||
payment = await pay_invoice(
|
payment = await pay_invoice(
|
||||||
|
|
|
||||||
4
lnbits/static/bundle.min.js
vendored
4
lnbits/static/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -101,6 +101,11 @@ window.localisation.en = {
|
||||||
memo: 'Memo',
|
memo: 'Memo',
|
||||||
date: 'Date',
|
date: 'Date',
|
||||||
path: 'Path',
|
path: 'Path',
|
||||||
|
internal_memo: 'Internal memo (optional)',
|
||||||
|
internal_memo_hint_receive:
|
||||||
|
"This memo is not shown to the payer but it's stored in the invoice for your reference.",
|
||||||
|
internal_memo_hint_pay:
|
||||||
|
"This memo is not shown to the payee but it's stored in the payment for your reference.",
|
||||||
payment_processing: 'Processing payment...',
|
payment_processing: 'Processing payment...',
|
||||||
payment_processing: 'Processing payment...',
|
payment_processing: 'Processing payment...',
|
||||||
payment_successful: 'Payment successful!',
|
payment_successful: 'Payment successful!',
|
||||||
|
|
|
||||||
|
|
@ -20,22 +20,35 @@ window.LNbits = {
|
||||||
memo,
|
memo,
|
||||||
unit = 'sat',
|
unit = 'sat',
|
||||||
lnurlCallback = null,
|
lnurlCallback = null,
|
||||||
fiatProvider = null
|
fiatProvider = null,
|
||||||
|
internalMemo = null
|
||||||
) {
|
) {
|
||||||
return this.request('post', '/api/v1/payments', wallet.inkey, {
|
const data = {
|
||||||
out: false,
|
out: false,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
memo: memo,
|
memo: memo,
|
||||||
lnurl_callback: lnurlCallback,
|
|
||||||
unit: unit,
|
unit: unit,
|
||||||
|
lnurl_callback: lnurlCallback,
|
||||||
fiat_provider: fiatProvider
|
fiat_provider: fiatProvider
|
||||||
})
|
}
|
||||||
|
if (internalMemo) {
|
||||||
|
data.extra = {
|
||||||
|
internal_memo: String(internalMemo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.request('post', '/api/v1/payments', wallet.inkey, data)
|
||||||
},
|
},
|
||||||
payInvoice(wallet, bolt11) {
|
payInvoice(wallet, bolt11, internalMemo = null) {
|
||||||
return this.request('post', '/api/v1/payments', wallet.adminkey, {
|
const data = {
|
||||||
out: true,
|
out: true,
|
||||||
bolt11: bolt11
|
bolt11: bolt11
|
||||||
})
|
}
|
||||||
|
if (internalMemo) {
|
||||||
|
data.extra = {
|
||||||
|
internal_memo: String(internalMemo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.request('post', '/api/v1/payments', wallet.adminkey, data)
|
||||||
},
|
},
|
||||||
payLnurl(
|
payLnurl(
|
||||||
wallet,
|
wallet,
|
||||||
|
|
@ -44,16 +57,28 @@ window.LNbits = {
|
||||||
amount,
|
amount,
|
||||||
description = '',
|
description = '',
|
||||||
comment = '',
|
comment = '',
|
||||||
unit = ''
|
unit = '',
|
||||||
|
internalMemo = null
|
||||||
) {
|
) {
|
||||||
return this.request('post', '/api/v1/payments/lnurl', wallet.adminkey, {
|
const data = {
|
||||||
callback,
|
callback,
|
||||||
description_hash,
|
description_hash,
|
||||||
amount,
|
amount,
|
||||||
comment,
|
comment,
|
||||||
description,
|
description,
|
||||||
unit
|
unit
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if (internalMemo) {
|
||||||
|
data.internal_memo = String(internalMemo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.request(
|
||||||
|
'post',
|
||||||
|
'/api/v1/payments/lnurl',
|
||||||
|
wallet.adminkey,
|
||||||
|
data
|
||||||
|
)
|
||||||
},
|
},
|
||||||
authLnurl(wallet, callback) {
|
authLnurl(wallet, callback) {
|
||||||
return this.request('post', '/api/v1/lnurlauth', wallet.adminkey, {
|
return this.request('post', '/api/v1/lnurlauth', wallet.adminkey, {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ window.PaymentsPageLogic = {
|
||||||
searchData: {
|
searchData: {
|
||||||
wallet_id: null,
|
wallet_id: null,
|
||||||
payment_hash: null,
|
payment_hash: null,
|
||||||
memo: null
|
memo: null,
|
||||||
|
internal_memo: null
|
||||||
},
|
},
|
||||||
statusFilters: {
|
statusFilters: {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -82,6 +83,14 @@ window.PaymentsPageLogic = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
max_length: 20
|
max_length: 20
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'internal_memo',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Internal Memo',
|
||||||
|
field: 'internal_memo',
|
||||||
|
sortable: false,
|
||||||
|
max_length: 20
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'wallet_id',
|
name: 'wallet_id',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
|
@ -166,6 +175,9 @@ window.PaymentsPageLogic = {
|
||||||
p.extra.wallet_fiat_currency
|
p.extra.wallet_fiat_currency
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (p.extra?.internal_memo) {
|
||||||
|
p.internal_memo = p.extra.internal_memo
|
||||||
|
}
|
||||||
p.fee_sats =
|
p.fee_sats =
|
||||||
new Intl.NumberFormat(window.LOCALE).format(p.fee / 1000) + ' sats'
|
new Intl.NumberFormat(window.LOCALE).format(p.fee / 1000) + ' sats'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ window.WalletPageLogic = {
|
||||||
request: '',
|
request: '',
|
||||||
amount: 0,
|
amount: 0,
|
||||||
comment: '',
|
comment: '',
|
||||||
|
internalMemo: null,
|
||||||
unit: 'sat'
|
unit: 'sat'
|
||||||
},
|
},
|
||||||
paymentChecker: null,
|
paymentChecker: null,
|
||||||
|
|
@ -38,7 +39,8 @@ window.WalletPageLogic = {
|
||||||
fiatProvider: '',
|
fiatProvider: '',
|
||||||
data: {
|
data: {
|
||||||
amount: null,
|
amount: null,
|
||||||
memo: ''
|
memo: '',
|
||||||
|
internalMemo: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
invoiceQrCode: '',
|
invoiceQrCode: '',
|
||||||
|
|
@ -197,6 +199,7 @@ window.WalletPageLogic = {
|
||||||
this.receive.paymentHash = null
|
this.receive.paymentHash = null
|
||||||
this.receive.data.amount = null
|
this.receive.data.amount = null
|
||||||
this.receive.data.memo = null
|
this.receive.data.memo = null
|
||||||
|
this.receive.data.internalMemo = null
|
||||||
this.receive.unit = this.isFiatPriority
|
this.receive.unit = this.isFiatPriority
|
||||||
? this.g.wallet.currency || 'sat'
|
? this.g.wallet.currency || 'sat'
|
||||||
: 'sat'
|
: 'sat'
|
||||||
|
|
@ -218,6 +221,7 @@ window.WalletPageLogic = {
|
||||||
window.isSecureContext && navigator.clipboard?.readText !== undefined
|
window.isSecureContext && navigator.clipboard?.readText !== undefined
|
||||||
this.parse.data.request = ''
|
this.parse.data.request = ''
|
||||||
this.parse.data.comment = ''
|
this.parse.data.comment = ''
|
||||||
|
this.parse.data.internalMemo = null
|
||||||
this.parse.data.paymentChecker = null
|
this.parse.data.paymentChecker = null
|
||||||
this.parse.camera.show = false
|
this.parse.camera.show = false
|
||||||
this.focusInput('textArea')
|
this.focusInput('textArea')
|
||||||
|
|
@ -253,7 +257,8 @@ window.WalletPageLogic = {
|
||||||
this.receive.data.memo,
|
this.receive.data.memo,
|
||||||
this.receive.unit,
|
this.receive.unit,
|
||||||
this.receive.lnurl && this.receive.lnurl.callback,
|
this.receive.lnurl && this.receive.lnurl.callback,
|
||||||
this.receive.fiatProvider
|
this.receive.fiatProvider,
|
||||||
|
this.receive.data.internalMemo
|
||||||
)
|
)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.g.updatePayments = !this.g.updatePayments
|
this.g.updatePayments = !this.g.updatePayments
|
||||||
|
|
@ -477,7 +482,11 @@ window.WalletPageLogic = {
|
||||||
})
|
})
|
||||||
|
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.payInvoice(this.g.wallet, this.parse.data.request)
|
.payInvoice(
|
||||||
|
this.g.wallet,
|
||||||
|
this.parse.data.request,
|
||||||
|
this.parse.data.internalMemo
|
||||||
|
)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
dismissPaymentMsg()
|
dismissPaymentMsg()
|
||||||
this.updatePayments = !this.updatePayments
|
this.updatePayments = !this.updatePayments
|
||||||
|
|
@ -515,7 +524,8 @@ window.WalletPageLogic = {
|
||||||
this.parse.data.amount * 1000,
|
this.parse.data.amount * 1000,
|
||||||
this.parse.lnurlpay.description.slice(0, 120),
|
this.parse.lnurlpay.description.slice(0, 120),
|
||||||
this.parse.data.comment,
|
this.parse.data.comment,
|
||||||
this.parse.data.unit
|
this.parse.data.unit,
|
||||||
|
this.parse.data.internalMemo
|
||||||
)
|
)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.parse.show = false
|
this.parse.show = false
|
||||||
|
|
|
||||||
|
|
@ -880,14 +880,24 @@
|
||||||
:props="props"
|
:props="props"
|
||||||
style="white-space: normal; word-break: break-all"
|
style="white-space: normal; word-break: break-all"
|
||||||
>
|
>
|
||||||
<q-badge v-if="props.row.tag" color="yellow" text-color="black">
|
<q-badge
|
||||||
|
v-if="props.row.tag"
|
||||||
|
color="yellow"
|
||||||
|
text-color="black"
|
||||||
|
class="q-mr-sm"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
v-text="'#' + props.row.tag"
|
v-text="'#' + props.row.tag"
|
||||||
class="inherit"
|
class="inherit"
|
||||||
:href="['/', props.row.tag].join('')"
|
:href="['/', props.row.tag].join('')"
|
||||||
></a>
|
></a>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
<span class="q-ml-sm" v-text="props.row.memo"></span>
|
<span v-text="props.row.memo"></span>
|
||||||
|
<span
|
||||||
|
class="text-grey-5 q-ml-sm ellipsis"
|
||||||
|
v-if="props.row.extra.internal_memo"
|
||||||
|
v-text="`(${props.row.extra.internal_memo})`"
|
||||||
|
></span>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<i>
|
<i>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue