Compare commits
2 commits
cc2adcb808
...
05d2797a92
| Author | SHA1 | Date | |
|---|---|---|---|
| 05d2797a92 | |||
| 9c9a29f93a |
1 changed files with 54 additions and 64 deletions
|
|
@ -352,37 +352,11 @@ export default class WalletService extends BaseService {
|
|||
const payments = await response.json()
|
||||
|
||||
// Transform to our transaction format
|
||||
this._transactions.value = payments.map((payment: any) => {
|
||||
let timestamp = new Date()
|
||||
|
||||
if (payment.time) {
|
||||
// Check if it's an ISO string or Unix timestamp
|
||||
if (typeof payment.time === 'string' && payment.time.includes('T')) {
|
||||
// ISO string format (e.g., "2025-09-14T16:49:40.378877+00:00")
|
||||
timestamp = new Date(payment.time)
|
||||
} else if (typeof payment.time === 'number' || !isNaN(Number(payment.time))) {
|
||||
// Unix timestamp (seconds) - multiply by 1000 for milliseconds
|
||||
timestamp = new Date(Number(payment.time) * 1000)
|
||||
} else {
|
||||
// Try to parse as-is
|
||||
timestamp = new Date(payment.time)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
id: payment.payment_hash,
|
||||
amount: Math.abs(payment.amount) / 1000,
|
||||
description: payment.memo || payment.description || 'No description',
|
||||
timestamp: timestamp,
|
||||
type: payment.amount > 0 ? 'received' : 'sent',
|
||||
status: payment.pending ? 'pending' : 'confirmed',
|
||||
fee: payment.fee ? payment.fee / 1000 : undefined,
|
||||
tag: payment.tag || (payment.extra && payment.extra.tag) || null
|
||||
}
|
||||
}).sort((a: PaymentTransaction, b: PaymentTransaction) =>
|
||||
b.timestamp.getTime() - a.timestamp.getTime()
|
||||
)
|
||||
this._transactions.value = payments
|
||||
.map((payment: any) => this.mapPaymentToTransaction(payment))
|
||||
.sort((a: PaymentTransaction, b: PaymentTransaction) =>
|
||||
b.timestamp.getTime() - a.timestamp.getTime()
|
||||
)
|
||||
|
||||
console.log(`Loaded ${payments.length} transactions`)
|
||||
}
|
||||
|
|
@ -478,51 +452,67 @@ export default class WalletService extends BaseService {
|
|||
* Map LNbits payment object to our transaction format
|
||||
*/
|
||||
private mapPaymentToTransaction(payment: any): PaymentTransaction {
|
||||
// Handle timestamp parsing - try different formats
|
||||
// `time` is serialized by pydantic as an ISO-8601 string
|
||||
// ("2025-09-14T16:49:40.378877+00:00"). Older payloads and some
|
||||
// extensions still hand back a unix-seconds number, so accept both.
|
||||
let timestamp = new Date()
|
||||
if (payment.time) {
|
||||
if (typeof payment.time === 'string') {
|
||||
// ISO string format
|
||||
timestamp = new Date(payment.time)
|
||||
} else if (typeof payment.time === 'number') {
|
||||
// Unix timestamp (seconds)
|
||||
if (typeof payment.time === 'number') {
|
||||
timestamp = new Date(payment.time * 1000)
|
||||
} else if (typeof payment.time === 'string' && !payment.time.includes('T')) {
|
||||
// Numeric string = unix seconds
|
||||
const asNumber = Number(payment.time)
|
||||
timestamp = isNaN(asNumber) ? new Date(payment.time) : new Date(asNumber * 1000)
|
||||
} else {
|
||||
timestamp = new Date(payment.time)
|
||||
}
|
||||
}
|
||||
|
||||
// For the transaction display, convert amount from millisats to sats
|
||||
const amountSats = Math.abs(payment.amount) / 1000
|
||||
|
||||
// Map status correctly - be more explicit about the mapping
|
||||
let status: 'pending' | 'confirmed' | 'failed' = 'pending'
|
||||
|
||||
// Check for pending first
|
||||
if (payment.pending === true) {
|
||||
status = 'pending'
|
||||
}
|
||||
// Check for success status
|
||||
else if (payment.status === 'success' || payment.status === 'settled' || payment.status === 'confirmed') {
|
||||
status = 'confirmed'
|
||||
}
|
||||
// Check for failed status
|
||||
else if (payment.status === 'failed') {
|
||||
status = 'failed'
|
||||
}
|
||||
// If status is success but no pending field, assume confirmed
|
||||
else if (payment.status === 'success' && payment.pending !== true) {
|
||||
status = 'confirmed'
|
||||
}
|
||||
// `amount` is signed millisats: positive incoming, negative outgoing.
|
||||
const amountMsat = Number(payment.amount ?? 0)
|
||||
const amountSats = Math.abs(amountMsat) / 1000
|
||||
|
||||
// `fee` is signed millisats as well. A zero fee is left undefined so the
|
||||
// UI can omit the "Fee:" line entirely rather than printing "0 sats".
|
||||
const feeMsat = Math.abs(Number(payment.fee ?? 0))
|
||||
|
||||
return {
|
||||
id: payment.payment_hash || payment.checking_id || payment.id,
|
||||
amount: amountSats,
|
||||
description: payment.description || payment.memo || 'Payment',
|
||||
description: payment.memo || payment.description || 'No description',
|
||||
timestamp: timestamp,
|
||||
type: payment.amount > 0 ? 'received' : 'sent',
|
||||
status: status,
|
||||
fee: payment.fee_msat ? payment.fee_msat / 1000 : undefined,
|
||||
tag: payment.tag || null
|
||||
type: amountMsat > 0 ? 'received' : 'sent',
|
||||
status: this.mapPaymentStatus(payment),
|
||||
fee: feeMsat > 0 ? feeMsat / 1000 : undefined,
|
||||
tag: payment.tag || payment.extra?.tag || null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an LNbits payment status onto our three display states.
|
||||
*
|
||||
* LNbits serializes `PaymentState` as "pending" | "success" | "failed"
|
||||
* (lnbits/core/models/payments.py). `Payment.pending` is a Python
|
||||
* `@property`, and LNbits pins pydantic 1.x, which never serializes
|
||||
* properties - so `pending` is absent from every REST and WebSocket
|
||||
* payload. Reading it returned `undefined` for every payment, which is
|
||||
* falsy, so everything rendered as "confirmed": pending payments looked
|
||||
* settled and failed payments looked successful.
|
||||
*
|
||||
* An unrecognized status is treated as pending rather than confirmed:
|
||||
* showing a payment as still in flight is the safe direction to be wrong.
|
||||
*/
|
||||
private mapPaymentStatus(payment: any): 'pending' | 'confirmed' | 'failed' {
|
||||
switch (payment.status) {
|
||||
case 'success':
|
||||
return 'confirmed'
|
||||
case 'failed':
|
||||
return 'failed'
|
||||
case 'pending':
|
||||
return 'pending'
|
||||
default:
|
||||
console.warn('WalletService: unrecognized payment status', payment.status)
|
||||
return 'pending'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue