@breeztech/breez-sdk-spark 0.7.18 → 0.8.0-dev1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/breez-sdk-spark.tgz +0 -0
- package/bundler/breez_sdk_spark_wasm.d.ts +581 -532
- package/bundler/breez_sdk_spark_wasm_bg.js +74 -51
- package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/bundler/storage/index.js +550 -103
- package/deno/breez_sdk_spark_wasm.d.ts +581 -532
- package/deno/breez_sdk_spark_wasm.js +72 -50
- package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/nodejs/breez_sdk_spark_wasm.d.ts +581 -532
- package/nodejs/breez_sdk_spark_wasm.js +74 -51
- package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/nodejs/storage/index.cjs +175 -149
- package/nodejs/storage/migrations.cjs +27 -3
- package/package.json +1 -1
- package/web/breez_sdk_spark_wasm.d.ts +585 -534
- package/web/breez_sdk_spark_wasm.js +72 -50
- package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +4 -2
- package/web/storage/index.js +550 -103
package/bundler/storage/index.js
CHANGED
|
@@ -148,6 +148,8 @@ class MigrationManager {
|
|
|
148
148
|
{
|
|
149
149
|
name: "Add sync tables",
|
|
150
150
|
upgrade: (db, transaction) => {
|
|
151
|
+
// sync_revision: tracks the last committed revision (from server-acknowledged
|
|
152
|
+
// or server-received records). Does NOT include pending outgoing revisions.
|
|
151
153
|
if (!db.objectStoreNames.contains("sync_revision")) {
|
|
152
154
|
const syncRevisionStore = db.createObjectStore("sync_revision", {
|
|
153
155
|
keyPath: "id",
|
|
@@ -178,15 +180,17 @@ class MigrationManager {
|
|
|
178
180
|
if (!db.objectStoreNames.contains("sync_state")) {
|
|
179
181
|
db.createObjectStore("sync_state", { keyPath: ["type", "dataId"] });
|
|
180
182
|
}
|
|
181
|
-
}
|
|
183
|
+
},
|
|
182
184
|
},
|
|
183
185
|
{
|
|
184
186
|
name: "Create lnurl_receive_metadata store",
|
|
185
187
|
upgrade: (db) => {
|
|
186
188
|
if (!db.objectStoreNames.contains("lnurl_receive_metadata")) {
|
|
187
|
-
db.createObjectStore("lnurl_receive_metadata", {
|
|
189
|
+
db.createObjectStore("lnurl_receive_metadata", {
|
|
190
|
+
keyPath: "paymentHash",
|
|
191
|
+
});
|
|
188
192
|
}
|
|
189
|
-
}
|
|
193
|
+
},
|
|
190
194
|
},
|
|
191
195
|
{
|
|
192
196
|
// Delete all unclaimed deposits to clear old claim_error JSON format.
|
|
@@ -197,7 +201,7 @@ class MigrationManager {
|
|
|
197
201
|
const store = transaction.objectStore("unclaimed_deposits");
|
|
198
202
|
store.clear();
|
|
199
203
|
}
|
|
200
|
-
}
|
|
204
|
+
},
|
|
201
205
|
},
|
|
202
206
|
{
|
|
203
207
|
name: "Clear sync tables for BreezSigner backward compatibility",
|
|
@@ -236,6 +240,97 @@ class MigrationManager {
|
|
|
236
240
|
settings.delete("sync_initial_complete");
|
|
237
241
|
}
|
|
238
242
|
}
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: "Create parentPaymentId index for related payments lookup",
|
|
246
|
+
upgrade: (db, transaction) => {
|
|
247
|
+
if (db.objectStoreNames.contains("payment_metadata")) {
|
|
248
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
249
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
250
|
+
metadataStore.createIndex("parentPaymentId", "parentPaymentId", { unique: false });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "Add tx_type to token payments and trigger token re-sync",
|
|
257
|
+
upgrade: (db, transaction) => {
|
|
258
|
+
// Update all existing token payments to have a default txType
|
|
259
|
+
if (db.objectStoreNames.contains("payments")) {
|
|
260
|
+
const paymentStore = transaction.objectStore("payments");
|
|
261
|
+
const getAllRequest = paymentStore.getAll();
|
|
262
|
+
|
|
263
|
+
getAllRequest.onsuccess = () => {
|
|
264
|
+
const payments = getAllRequest.result;
|
|
265
|
+
|
|
266
|
+
payments.forEach((payment) => {
|
|
267
|
+
// Parse details if it's a string
|
|
268
|
+
let details = null;
|
|
269
|
+
if (payment.details && typeof payment.details === "string") {
|
|
270
|
+
try {
|
|
271
|
+
details = JSON.parse(payment.details);
|
|
272
|
+
} catch (e) {
|
|
273
|
+
return; // Skip this payment if parsing fails
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
details = payment.details;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Add default txType to token payments
|
|
280
|
+
if (details && details.type === "token" && !details.txType) {
|
|
281
|
+
details.txType = "transfer";
|
|
282
|
+
payment.details = JSON.stringify(details);
|
|
283
|
+
paymentStore.put(payment);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Reset sync cache to trigger token re-sync
|
|
290
|
+
if (db.objectStoreNames.contains("settings")) {
|
|
291
|
+
const settingsStore = transaction.objectStore("settings");
|
|
292
|
+
const getRequest = settingsStore.get("sync_offset");
|
|
293
|
+
|
|
294
|
+
getRequest.onsuccess = () => {
|
|
295
|
+
const syncCache = getRequest.result;
|
|
296
|
+
if (syncCache && syncCache.value) {
|
|
297
|
+
try {
|
|
298
|
+
const syncInfo = JSON.parse(syncCache.value);
|
|
299
|
+
// Reset only the token sync position, keep the bitcoin offset
|
|
300
|
+
syncInfo.last_synced_final_token_payment_id = null;
|
|
301
|
+
settingsStore.put({
|
|
302
|
+
key: "sync_offset",
|
|
303
|
+
value: JSON.stringify(syncInfo),
|
|
304
|
+
});
|
|
305
|
+
} catch (e) {
|
|
306
|
+
// If parsing fails, just continue
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: "Clear sync tables to force re-sync",
|
|
315
|
+
upgrade: (db, transaction) => {
|
|
316
|
+
if (db.objectStoreNames.contains("sync_outgoing")) {
|
|
317
|
+
transaction.objectStore("sync_outgoing").clear();
|
|
318
|
+
}
|
|
319
|
+
if (db.objectStoreNames.contains("sync_incoming")) {
|
|
320
|
+
transaction.objectStore("sync_incoming").clear();
|
|
321
|
+
}
|
|
322
|
+
if (db.objectStoreNames.contains("sync_state")) {
|
|
323
|
+
transaction.objectStore("sync_state").clear();
|
|
324
|
+
}
|
|
325
|
+
if (db.objectStoreNames.contains("sync_revision")) {
|
|
326
|
+
const syncRevision = transaction.objectStore("sync_revision");
|
|
327
|
+
syncRevision.clear();
|
|
328
|
+
syncRevision.put({ id: 1, revision: "0" });
|
|
329
|
+
}
|
|
330
|
+
if (db.objectStoreNames.contains("settings")) {
|
|
331
|
+
transaction.objectStore("settings").delete("sync_initial_complete");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
239
334
|
}
|
|
240
335
|
];
|
|
241
336
|
}
|
|
@@ -260,7 +355,7 @@ class IndexedDBStorage {
|
|
|
260
355
|
this.db = null;
|
|
261
356
|
this.migrationManager = null;
|
|
262
357
|
this.logger = logger;
|
|
263
|
-
this.dbVersion =
|
|
358
|
+
this.dbVersion = 10; // Current schema version
|
|
264
359
|
}
|
|
265
360
|
|
|
266
361
|
/**
|
|
@@ -418,6 +513,59 @@ class IndexedDBStorage {
|
|
|
418
513
|
|
|
419
514
|
// ===== Payment Operations =====
|
|
420
515
|
|
|
516
|
+
/**
|
|
517
|
+
* Gets the set of payment IDs that are related payments (have a parentPaymentId).
|
|
518
|
+
* Uses the parentPaymentId index for efficient lookup.
|
|
519
|
+
* @param {IDBObjectStore} metadataStore - The payment_metadata object store
|
|
520
|
+
* @returns {Promise<Set<string>>} Set of payment IDs that are related payments
|
|
521
|
+
*/
|
|
522
|
+
_getRelatedPaymentIds(metadataStore) {
|
|
523
|
+
return new Promise((resolve) => {
|
|
524
|
+
const relatedPaymentIds = new Set();
|
|
525
|
+
|
|
526
|
+
// Check if the parentPaymentId index exists (added in migration)
|
|
527
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
528
|
+
// Index doesn't exist yet, fall back to scanning all metadata
|
|
529
|
+
const cursorRequest = metadataStore.openCursor();
|
|
530
|
+
cursorRequest.onsuccess = (event) => {
|
|
531
|
+
const cursor = event.target.result;
|
|
532
|
+
if (cursor) {
|
|
533
|
+
if (cursor.value.parentPaymentId) {
|
|
534
|
+
relatedPaymentIds.add(cursor.value.paymentId);
|
|
535
|
+
}
|
|
536
|
+
cursor.continue();
|
|
537
|
+
} else {
|
|
538
|
+
resolve(relatedPaymentIds);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
cursorRequest.onerror = () => resolve(new Set());
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Use the parentPaymentId index to find all metadata entries with a parent
|
|
546
|
+
const index = metadataStore.index("parentPaymentId");
|
|
547
|
+
const cursorRequest = index.openCursor();
|
|
548
|
+
|
|
549
|
+
cursorRequest.onsuccess = (event) => {
|
|
550
|
+
const cursor = event.target.result;
|
|
551
|
+
if (cursor) {
|
|
552
|
+
// Only add if parentPaymentId is truthy (not null/undefined)
|
|
553
|
+
if (cursor.value.parentPaymentId) {
|
|
554
|
+
relatedPaymentIds.add(cursor.value.paymentId);
|
|
555
|
+
}
|
|
556
|
+
cursor.continue();
|
|
557
|
+
} else {
|
|
558
|
+
resolve(relatedPaymentIds);
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
cursorRequest.onerror = () => {
|
|
563
|
+
// If index lookup fails, return empty set and fall back to per-payment lookup
|
|
564
|
+
resolve(new Set());
|
|
565
|
+
};
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
421
569
|
async listPayments(request) {
|
|
422
570
|
if (!this.db) {
|
|
423
571
|
throw new StorageError("Database not initialized");
|
|
@@ -427,15 +575,18 @@ class IndexedDBStorage {
|
|
|
427
575
|
const actualOffset = request.offset !== null ? request.offset : 0;
|
|
428
576
|
const actualLimit = request.limit !== null ? request.limit : 4294967295; // u32::MAX
|
|
429
577
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
578
|
+
const transaction = this.db.transaction(
|
|
579
|
+
["payments", "payment_metadata", "lnurl_receive_metadata"],
|
|
580
|
+
"readonly"
|
|
581
|
+
);
|
|
582
|
+
const paymentStore = transaction.objectStore("payments");
|
|
583
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
584
|
+
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
438
585
|
|
|
586
|
+
// Build set of related payment IDs upfront for O(1) filtering
|
|
587
|
+
const relatedPaymentIds = await this._getRelatedPaymentIds(metadataStore);
|
|
588
|
+
|
|
589
|
+
return new Promise((resolve, reject) => {
|
|
439
590
|
const payments = [];
|
|
440
591
|
let count = 0;
|
|
441
592
|
let skipped = 0;
|
|
@@ -458,16 +609,23 @@ class IndexedDBStorage {
|
|
|
458
609
|
|
|
459
610
|
const payment = cursor.value;
|
|
460
611
|
|
|
612
|
+
// Skip related payments (those with a parentPaymentId)
|
|
613
|
+
if (relatedPaymentIds.has(payment.id)) {
|
|
614
|
+
cursor.continue();
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
461
618
|
if (skipped < actualOffset) {
|
|
462
619
|
skipped++;
|
|
463
620
|
cursor.continue();
|
|
464
621
|
return;
|
|
465
622
|
}
|
|
466
623
|
|
|
467
|
-
// Get metadata for this payment
|
|
624
|
+
// Get metadata for this payment (now only for non-related payments)
|
|
468
625
|
const metadataRequest = metadataStore.get(payment.id);
|
|
469
626
|
metadataRequest.onsuccess = () => {
|
|
470
627
|
const metadata = metadataRequest.result;
|
|
628
|
+
|
|
471
629
|
const paymentWithMetadata = this._mergePaymentMetadata(
|
|
472
630
|
payment,
|
|
473
631
|
metadata
|
|
@@ -478,9 +636,12 @@ class IndexedDBStorage {
|
|
|
478
636
|
cursor.continue();
|
|
479
637
|
return;
|
|
480
638
|
}
|
|
481
|
-
|
|
639
|
+
|
|
482
640
|
// Fetch lnurl receive metadata if it's a lightning payment
|
|
483
|
-
this._fetchLnurlReceiveMetadata(
|
|
641
|
+
this._fetchLnurlReceiveMetadata(
|
|
642
|
+
paymentWithMetadata,
|
|
643
|
+
lnurlReceiveMetadataStore
|
|
644
|
+
)
|
|
484
645
|
.then((mergedPayment) => {
|
|
485
646
|
payments.push(mergedPayment);
|
|
486
647
|
count++;
|
|
@@ -560,7 +721,9 @@ class IndexedDBStorage {
|
|
|
560
721
|
);
|
|
561
722
|
const paymentStore = transaction.objectStore("payments");
|
|
562
723
|
const metadataStore = transaction.objectStore("payment_metadata");
|
|
563
|
-
const lnurlReceiveMetadataStore = transaction.objectStore(
|
|
724
|
+
const lnurlReceiveMetadataStore = transaction.objectStore(
|
|
725
|
+
"lnurl_receive_metadata"
|
|
726
|
+
);
|
|
564
727
|
|
|
565
728
|
const paymentRequest = paymentStore.get(id);
|
|
566
729
|
|
|
@@ -579,9 +742,12 @@ class IndexedDBStorage {
|
|
|
579
742
|
payment,
|
|
580
743
|
metadata
|
|
581
744
|
);
|
|
582
|
-
|
|
745
|
+
|
|
583
746
|
// Fetch lnurl receive metadata if it's a lightning payment
|
|
584
|
-
this._fetchLnurlReceiveMetadata(
|
|
747
|
+
this._fetchLnurlReceiveMetadata(
|
|
748
|
+
paymentWithMetadata,
|
|
749
|
+
lnurlReceiveMetadataStore
|
|
750
|
+
)
|
|
585
751
|
.then(resolve)
|
|
586
752
|
.catch(() => {
|
|
587
753
|
// Continue without lnurl receive metadata if fetch fails
|
|
@@ -620,7 +786,9 @@ class IndexedDBStorage {
|
|
|
620
786
|
const paymentStore = transaction.objectStore("payments");
|
|
621
787
|
const invoiceIndex = paymentStore.index("invoice");
|
|
622
788
|
const metadataStore = transaction.objectStore("payment_metadata");
|
|
623
|
-
const lnurlReceiveMetadataStore = transaction.objectStore(
|
|
789
|
+
const lnurlReceiveMetadataStore = transaction.objectStore(
|
|
790
|
+
"lnurl_receive_metadata"
|
|
791
|
+
);
|
|
624
792
|
|
|
625
793
|
const paymentRequest = invoiceIndex.get(invoice);
|
|
626
794
|
|
|
@@ -639,9 +807,12 @@ class IndexedDBStorage {
|
|
|
639
807
|
payment,
|
|
640
808
|
metadata
|
|
641
809
|
);
|
|
642
|
-
|
|
810
|
+
|
|
643
811
|
// Fetch lnurl receive metadata if it's a lightning payment
|
|
644
|
-
this._fetchLnurlReceiveMetadata(
|
|
812
|
+
this._fetchLnurlReceiveMetadata(
|
|
813
|
+
paymentWithMetadata,
|
|
814
|
+
lnurlReceiveMetadataStore
|
|
815
|
+
)
|
|
645
816
|
.then(resolve)
|
|
646
817
|
.catch(() => {
|
|
647
818
|
// Continue without lnurl receive metadata if fetch fails
|
|
@@ -667,7 +838,173 @@ class IndexedDBStorage {
|
|
|
667
838
|
});
|
|
668
839
|
}
|
|
669
840
|
|
|
670
|
-
|
|
841
|
+
/**
|
|
842
|
+
* Checks if any related payments exist (payments with a parentPaymentId).
|
|
843
|
+
* Uses the parentPaymentId index for efficient lookup.
|
|
844
|
+
* @param {IDBObjectStore} metadataStore - The payment_metadata object store
|
|
845
|
+
* @returns {Promise<boolean>} True if any related payments exist
|
|
846
|
+
*/
|
|
847
|
+
_hasRelatedPayments(metadataStore) {
|
|
848
|
+
return new Promise((resolve) => {
|
|
849
|
+
// Check if the parentPaymentId index exists (added in migration)
|
|
850
|
+
if (!metadataStore.indexNames.contains("parentPaymentId")) {
|
|
851
|
+
// Index doesn't exist yet, fall back to scanning all metadata
|
|
852
|
+
const cursorRequest = metadataStore.openCursor();
|
|
853
|
+
cursorRequest.onsuccess = (event) => {
|
|
854
|
+
const cursor = event.target.result;
|
|
855
|
+
if (cursor) {
|
|
856
|
+
if (cursor.value.parentPaymentId) {
|
|
857
|
+
resolve(true);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
cursor.continue();
|
|
861
|
+
} else {
|
|
862
|
+
resolve(false);
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
cursorRequest.onerror = () => resolve(true); // Assume there might be related payments on error
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const index = metadataStore.index("parentPaymentId");
|
|
870
|
+
const cursorRequest = index.openCursor();
|
|
871
|
+
|
|
872
|
+
cursorRequest.onsuccess = (event) => {
|
|
873
|
+
const cursor = event.target.result;
|
|
874
|
+
if (cursor && cursor.value.parentPaymentId) {
|
|
875
|
+
// Found at least one related payment
|
|
876
|
+
resolve(true);
|
|
877
|
+
} else if (cursor) {
|
|
878
|
+
// Entry with null parentPaymentId, continue searching
|
|
879
|
+
cursor.continue();
|
|
880
|
+
} else {
|
|
881
|
+
// No more entries
|
|
882
|
+
resolve(false);
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
cursorRequest.onerror = () => {
|
|
887
|
+
// If index lookup fails, assume there might be related payments
|
|
888
|
+
resolve(true);
|
|
889
|
+
};
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Gets payments that have any of the specified parent payment IDs.
|
|
895
|
+
* @param {string[]} parentPaymentIds - Array of parent payment IDs
|
|
896
|
+
* @returns {Promise<Object>} Map of parentPaymentId -> array of RelatedPayment objects
|
|
897
|
+
*/
|
|
898
|
+
async getPaymentsByParentIds(parentPaymentIds) {
|
|
899
|
+
if (!this.db) {
|
|
900
|
+
throw new StorageError("Database not initialized");
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (!parentPaymentIds || parentPaymentIds.length === 0) {
|
|
904
|
+
return {};
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const transaction = this.db.transaction(
|
|
908
|
+
["payments", "payment_metadata", "lnurl_receive_metadata"],
|
|
909
|
+
"readonly"
|
|
910
|
+
);
|
|
911
|
+
const metadataStore = transaction.objectStore("payment_metadata");
|
|
912
|
+
|
|
913
|
+
// Early exit if no related payments exist
|
|
914
|
+
const hasRelated = await this._hasRelatedPayments(metadataStore);
|
|
915
|
+
if (!hasRelated) {
|
|
916
|
+
return {};
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const parentIdSet = new Set(parentPaymentIds);
|
|
920
|
+
const paymentStore = transaction.objectStore("payments");
|
|
921
|
+
const lnurlReceiveMetadataStore = transaction.objectStore("lnurl_receive_metadata");
|
|
922
|
+
|
|
923
|
+
return new Promise((resolve, reject) => {
|
|
924
|
+
const result = {};
|
|
925
|
+
const fetchedMetadata = [];
|
|
926
|
+
|
|
927
|
+
// Query all metadata records and filter by parentPaymentId
|
|
928
|
+
const cursorRequest = metadataStore.openCursor();
|
|
929
|
+
|
|
930
|
+
cursorRequest.onsuccess = (event) => {
|
|
931
|
+
const cursor = event.target.result;
|
|
932
|
+
if (!cursor) {
|
|
933
|
+
// All metadata processed, now fetch payment details
|
|
934
|
+
if (fetchedMetadata.length === 0) {
|
|
935
|
+
resolve(result);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
let processed = 0;
|
|
940
|
+
for (const metadata of fetchedMetadata) {
|
|
941
|
+
const parentId = metadata.parentPaymentId;
|
|
942
|
+
const paymentRequest = paymentStore.get(metadata.paymentId);
|
|
943
|
+
paymentRequest.onsuccess = () => {
|
|
944
|
+
const payment = paymentRequest.result;
|
|
945
|
+
if (payment) {
|
|
946
|
+
const paymentWithMetadata = this._mergePaymentMetadata(payment, metadata);
|
|
947
|
+
|
|
948
|
+
if (!result[parentId]) {
|
|
949
|
+
result[parentId] = [];
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Fetch lnurl receive metadata if applicable
|
|
953
|
+
this._fetchLnurlReceiveMetadata(paymentWithMetadata, lnurlReceiveMetadataStore)
|
|
954
|
+
.then((mergedPayment) => {
|
|
955
|
+
result[parentId].push(mergedPayment);
|
|
956
|
+
})
|
|
957
|
+
.catch(() => {
|
|
958
|
+
result[parentId].push(paymentWithMetadata);
|
|
959
|
+
})
|
|
960
|
+
.finally(() => {
|
|
961
|
+
processed++;
|
|
962
|
+
if (processed === fetchedMetadata.length) {
|
|
963
|
+
// Sort each parent's children by timestamp
|
|
964
|
+
for (const parentId of Object.keys(result)) {
|
|
965
|
+
result[parentId].sort((a, b) => a.timestamp - b.timestamp);
|
|
966
|
+
}
|
|
967
|
+
resolve(result);
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
} else {
|
|
971
|
+
processed++;
|
|
972
|
+
if (processed === fetchedMetadata.length) {
|
|
973
|
+
resolve(result);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
paymentRequest.onerror = () => {
|
|
978
|
+
processed++;
|
|
979
|
+
if (processed === fetchedMetadata.length) {
|
|
980
|
+
resolve(result);
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const metadata = cursor.value;
|
|
988
|
+
if (metadata.parentPaymentId && parentIdSet.has(metadata.parentPaymentId)) {
|
|
989
|
+
fetchedMetadata.push(metadata);
|
|
990
|
+
}
|
|
991
|
+
cursor.continue();
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
cursorRequest.onerror = () => {
|
|
995
|
+
reject(
|
|
996
|
+
new StorageError(
|
|
997
|
+
`Failed to get payments by parent ids: ${
|
|
998
|
+
cursorRequest.error?.message || "Unknown error"
|
|
999
|
+
}`,
|
|
1000
|
+
cursorRequest.error
|
|
1001
|
+
)
|
|
1002
|
+
);
|
|
1003
|
+
};
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
async insertPaymentMetadata(paymentId, metadata) {
|
|
671
1008
|
if (!this.db) {
|
|
672
1009
|
throw new StorageError("Database not initialized");
|
|
673
1010
|
}
|
|
@@ -676,30 +1013,47 @@ class IndexedDBStorage {
|
|
|
676
1013
|
const transaction = this.db.transaction("payment_metadata", "readwrite");
|
|
677
1014
|
const store = transaction.objectStore("payment_metadata");
|
|
678
1015
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
: null,
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
:
|
|
692
|
-
|
|
1016
|
+
// First get existing record to merge with
|
|
1017
|
+
const getRequest = store.get(paymentId);
|
|
1018
|
+
getRequest.onsuccess = () => {
|
|
1019
|
+
const existing = getRequest.result || {};
|
|
1020
|
+
|
|
1021
|
+
// Use COALESCE-like behavior: new value if non-null, otherwise keep existing
|
|
1022
|
+
const metadataToStore = {
|
|
1023
|
+
paymentId,
|
|
1024
|
+
parentPaymentId: metadata.parentPaymentId ?? existing.parentPaymentId ?? null,
|
|
1025
|
+
lnurlPayInfo: metadata.lnurlPayInfo
|
|
1026
|
+
? JSON.stringify(metadata.lnurlPayInfo)
|
|
1027
|
+
: existing.lnurlPayInfo ?? null,
|
|
1028
|
+
lnurlWithdrawInfo: metadata.lnurlWithdrawInfo
|
|
1029
|
+
? JSON.stringify(metadata.lnurlWithdrawInfo)
|
|
1030
|
+
: existing.lnurlWithdrawInfo ?? null,
|
|
1031
|
+
lnurlDescription: metadata.lnurlDescription ?? existing.lnurlDescription ?? null,
|
|
1032
|
+
conversionInfo: metadata.conversionInfo
|
|
1033
|
+
? JSON.stringify(metadata.conversionInfo)
|
|
1034
|
+
: existing.conversionInfo ?? null,
|
|
1035
|
+
};
|
|
693
1036
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1037
|
+
const putRequest = store.put(metadataToStore);
|
|
1038
|
+
putRequest.onsuccess = () => resolve();
|
|
1039
|
+
putRequest.onerror = () => {
|
|
1040
|
+
reject(
|
|
1041
|
+
new StorageError(
|
|
1042
|
+
`Failed to set payment metadata for '${paymentId}': ${
|
|
1043
|
+
putRequest.error?.message || "Unknown error"
|
|
1044
|
+
}`,
|
|
1045
|
+
putRequest.error
|
|
1046
|
+
)
|
|
1047
|
+
);
|
|
1048
|
+
};
|
|
1049
|
+
};
|
|
1050
|
+
getRequest.onerror = () => {
|
|
697
1051
|
reject(
|
|
698
1052
|
new StorageError(
|
|
699
|
-
`Failed to
|
|
700
|
-
|
|
1053
|
+
`Failed to get existing payment metadata for '${paymentId}': ${
|
|
1054
|
+
getRequest.error?.message || "Unknown error"
|
|
701
1055
|
}`,
|
|
702
|
-
|
|
1056
|
+
getRequest.error
|
|
703
1057
|
)
|
|
704
1058
|
);
|
|
705
1059
|
};
|
|
@@ -909,9 +1263,9 @@ class IndexedDBStorage {
|
|
|
909
1263
|
request.onerror = () => {
|
|
910
1264
|
reject(
|
|
911
1265
|
new StorageError(
|
|
912
|
-
`Failed to add lnurl metadata for payment hash '${
|
|
913
|
-
|
|
914
|
-
}`,
|
|
1266
|
+
`Failed to add lnurl metadata for payment hash '${
|
|
1267
|
+
item.paymentHash
|
|
1268
|
+
}': ${request.error?.message || "Unknown error"}`,
|
|
915
1269
|
request.error
|
|
916
1270
|
)
|
|
917
1271
|
);
|
|
@@ -931,63 +1285,74 @@ class IndexedDBStorage {
|
|
|
931
1285
|
"readwrite"
|
|
932
1286
|
);
|
|
933
1287
|
|
|
934
|
-
//
|
|
1288
|
+
// Compute next revision as max(committed, max outgoing) + 1, without updating sync_revision
|
|
935
1289
|
const revisionStore = transaction.objectStore("sync_revision");
|
|
1290
|
+
const outgoingStore = transaction.objectStore("sync_outgoing");
|
|
1291
|
+
|
|
936
1292
|
const getRevisionRequest = revisionStore.get(1);
|
|
1293
|
+
const getAllOutgoingRequest = outgoingStore.getAll();
|
|
937
1294
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
revision: "0",
|
|
942
|
-
};
|
|
943
|
-
const nextRevision = BigInt(revisionData.revision) + BigInt(1);
|
|
1295
|
+
let committedRevision = null;
|
|
1296
|
+
let maxOutgoingRevision = null;
|
|
1297
|
+
let resultsReady = 0;
|
|
944
1298
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
revision: nextRevision.toString(),
|
|
949
|
-
});
|
|
1299
|
+
const onBothReady = () => {
|
|
1300
|
+
resultsReady++;
|
|
1301
|
+
if (resultsReady < 2) return;
|
|
950
1302
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
type: record.id.type,
|
|
956
|
-
dataId: record.id.dataId,
|
|
957
|
-
revision: Number(nextRevision),
|
|
958
|
-
record: {
|
|
959
|
-
...record,
|
|
960
|
-
revision: nextRevision,
|
|
961
|
-
},
|
|
962
|
-
};
|
|
1303
|
+
const base = committedRevision > maxOutgoingRevision
|
|
1304
|
+
? committedRevision
|
|
1305
|
+
: maxOutgoingRevision;
|
|
1306
|
+
const nextRevision = base + BigInt(1);
|
|
963
1307
|
|
|
964
|
-
|
|
1308
|
+
const storeRecord = {
|
|
1309
|
+
type: record.id.type,
|
|
1310
|
+
dataId: record.id.dataId,
|
|
1311
|
+
revision: Number(nextRevision),
|
|
1312
|
+
record: {
|
|
1313
|
+
...record,
|
|
1314
|
+
revision: nextRevision,
|
|
1315
|
+
},
|
|
1316
|
+
};
|
|
965
1317
|
|
|
966
|
-
|
|
967
|
-
// Wait for transaction to complete before resolving
|
|
968
|
-
transaction.oncomplete = () => {
|
|
969
|
-
resolve(nextRevision);
|
|
970
|
-
};
|
|
971
|
-
};
|
|
1318
|
+
const addRequest = outgoingStore.add(storeRecord);
|
|
972
1319
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
`Failed to add outgoing change: ${event.target.error.message}`
|
|
977
|
-
)
|
|
978
|
-
);
|
|
1320
|
+
addRequest.onsuccess = () => {
|
|
1321
|
+
transaction.oncomplete = () => {
|
|
1322
|
+
resolve(nextRevision);
|
|
979
1323
|
};
|
|
980
1324
|
};
|
|
981
1325
|
|
|
982
|
-
|
|
1326
|
+
addRequest.onerror = (event) => {
|
|
983
1327
|
reject(
|
|
984
1328
|
new StorageError(
|
|
985
|
-
`Failed to
|
|
1329
|
+
`Failed to add outgoing change: ${event.target.error.message}`
|
|
986
1330
|
)
|
|
987
1331
|
);
|
|
988
1332
|
};
|
|
989
1333
|
};
|
|
990
1334
|
|
|
1335
|
+
getRevisionRequest.onsuccess = () => {
|
|
1336
|
+
const revisionData = getRevisionRequest.result || {
|
|
1337
|
+
id: 1,
|
|
1338
|
+
revision: "0",
|
|
1339
|
+
};
|
|
1340
|
+
committedRevision = BigInt(revisionData.revision);
|
|
1341
|
+
onBothReady();
|
|
1342
|
+
};
|
|
1343
|
+
|
|
1344
|
+
getAllOutgoingRequest.onsuccess = () => {
|
|
1345
|
+
const records = getAllOutgoingRequest.result;
|
|
1346
|
+
maxOutgoingRevision = BigInt(0);
|
|
1347
|
+
for (const storeRecord of records) {
|
|
1348
|
+
const rev = BigInt(storeRecord.record.revision);
|
|
1349
|
+
if (rev > maxOutgoingRevision) {
|
|
1350
|
+
maxOutgoingRevision = rev;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
onBothReady();
|
|
1354
|
+
};
|
|
1355
|
+
|
|
991
1356
|
getRevisionRequest.onerror = (event) => {
|
|
992
1357
|
reject(
|
|
993
1358
|
new StorageError(
|
|
@@ -996,6 +1361,14 @@ class IndexedDBStorage {
|
|
|
996
1361
|
);
|
|
997
1362
|
};
|
|
998
1363
|
|
|
1364
|
+
getAllOutgoingRequest.onerror = (event) => {
|
|
1365
|
+
reject(
|
|
1366
|
+
new StorageError(
|
|
1367
|
+
`Failed to get outgoing records: ${event.target.error.message}`
|
|
1368
|
+
)
|
|
1369
|
+
);
|
|
1370
|
+
};
|
|
1371
|
+
|
|
999
1372
|
transaction.onerror = (event) => {
|
|
1000
1373
|
reject(
|
|
1001
1374
|
new StorageError(`Transaction failed: ${event.target.error.message}`)
|
|
@@ -1004,23 +1377,24 @@ class IndexedDBStorage {
|
|
|
1004
1377
|
});
|
|
1005
1378
|
}
|
|
1006
1379
|
|
|
1007
|
-
async syncCompleteOutgoingSync(record) {
|
|
1380
|
+
async syncCompleteOutgoingSync(record, localRevision) {
|
|
1008
1381
|
if (!this.db) {
|
|
1009
1382
|
throw new StorageError("Database not initialized");
|
|
1010
1383
|
}
|
|
1011
1384
|
|
|
1012
1385
|
return new Promise((resolve, reject) => {
|
|
1013
1386
|
const transaction = this.db.transaction(
|
|
1014
|
-
["sync_outgoing", "sync_state"],
|
|
1387
|
+
["sync_outgoing", "sync_state", "sync_revision"],
|
|
1015
1388
|
"readwrite"
|
|
1016
1389
|
);
|
|
1017
1390
|
const outgoingStore = transaction.objectStore("sync_outgoing");
|
|
1018
1391
|
const stateStore = transaction.objectStore("sync_state");
|
|
1392
|
+
const revisionStore = transaction.objectStore("sync_revision");
|
|
1019
1393
|
|
|
1020
1394
|
const deleteRequest = outgoingStore.delete([
|
|
1021
1395
|
record.id.type,
|
|
1022
1396
|
record.id.dataId,
|
|
1023
|
-
Number(
|
|
1397
|
+
Number(localRevision),
|
|
1024
1398
|
]);
|
|
1025
1399
|
|
|
1026
1400
|
deleteRequest.onsuccess = () => {
|
|
@@ -1030,7 +1404,25 @@ class IndexedDBStorage {
|
|
|
1030
1404
|
record: record,
|
|
1031
1405
|
};
|
|
1032
1406
|
stateStore.put(stateRecord);
|
|
1033
|
-
|
|
1407
|
+
|
|
1408
|
+
// Update sync_revision to track the highest known revision
|
|
1409
|
+
const getRevisionRequest = revisionStore.get(1);
|
|
1410
|
+
getRevisionRequest.onsuccess = () => {
|
|
1411
|
+
const current = getRevisionRequest.result || { id: 1, revision: "0" };
|
|
1412
|
+
const currentRevision = BigInt(current.revision);
|
|
1413
|
+
const recordRevision = BigInt(record.revision);
|
|
1414
|
+
if (recordRevision > currentRevision) {
|
|
1415
|
+
revisionStore.put({ id: 1, revision: recordRevision.toString() });
|
|
1416
|
+
}
|
|
1417
|
+
resolve();
|
|
1418
|
+
};
|
|
1419
|
+
getRevisionRequest.onerror = (event) => {
|
|
1420
|
+
reject(
|
|
1421
|
+
new StorageError(
|
|
1422
|
+
`Failed to update sync revision: ${event.target.error.message}`
|
|
1423
|
+
)
|
|
1424
|
+
);
|
|
1425
|
+
};
|
|
1034
1426
|
};
|
|
1035
1427
|
|
|
1036
1428
|
deleteRequest.onerror = (event) => {
|
|
@@ -1220,7 +1612,7 @@ class IndexedDBStorage {
|
|
|
1220
1612
|
const outgoingStore = transaction.objectStore("sync_outgoing");
|
|
1221
1613
|
const revisionStore = transaction.objectStore("sync_revision");
|
|
1222
1614
|
|
|
1223
|
-
// Get the last revision from sync_revision
|
|
1615
|
+
// Get the last committed revision from sync_revision
|
|
1224
1616
|
const getRevisionRequest = revisionStore.get(1);
|
|
1225
1617
|
|
|
1226
1618
|
getRevisionRequest.onsuccess = () => {
|
|
@@ -1231,10 +1623,17 @@ class IndexedDBStorage {
|
|
|
1231
1623
|
const lastRevision = BigInt(revisionData.revision);
|
|
1232
1624
|
|
|
1233
1625
|
// Calculate the difference
|
|
1234
|
-
const diff = revision - lastRevision;
|
|
1626
|
+
const diff = revision > lastRevision ? revision - lastRevision : BigInt(0);
|
|
1627
|
+
|
|
1628
|
+
// Helper to update sync_revision within the same transaction so retries are idempotent
|
|
1629
|
+
const updateSyncRevision = () => {
|
|
1630
|
+
if (revision > lastRevision) {
|
|
1631
|
+
revisionStore.put({ id: 1, revision: revision.toString() });
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1235
1634
|
|
|
1236
1635
|
if (diff <= BigInt(0)) {
|
|
1237
|
-
|
|
1636
|
+
updateSyncRevision();
|
|
1238
1637
|
resolve();
|
|
1239
1638
|
return;
|
|
1240
1639
|
}
|
|
@@ -1244,13 +1643,15 @@ class IndexedDBStorage {
|
|
|
1244
1643
|
|
|
1245
1644
|
getAllRequest.onsuccess = () => {
|
|
1246
1645
|
const records = getAllRequest.result;
|
|
1247
|
-
let updatesCompleted = 0;
|
|
1248
1646
|
|
|
1249
1647
|
if (records.length === 0) {
|
|
1648
|
+
updateSyncRevision();
|
|
1250
1649
|
resolve();
|
|
1251
1650
|
return;
|
|
1252
1651
|
}
|
|
1253
1652
|
|
|
1653
|
+
let updatesCompleted = 0;
|
|
1654
|
+
|
|
1254
1655
|
for (const storeRecord of records) {
|
|
1255
1656
|
// Delete the old record
|
|
1256
1657
|
const oldKey = [
|
|
@@ -1278,6 +1679,7 @@ class IndexedDBStorage {
|
|
|
1278
1679
|
putRequest.onsuccess = () => {
|
|
1279
1680
|
updatesCompleted++;
|
|
1280
1681
|
if (updatesCompleted === records.length) {
|
|
1682
|
+
updateSyncRevision();
|
|
1281
1683
|
resolve();
|
|
1282
1684
|
}
|
|
1283
1685
|
};
|
|
@@ -1446,8 +1848,9 @@ class IndexedDBStorage {
|
|
|
1446
1848
|
}
|
|
1447
1849
|
|
|
1448
1850
|
return new Promise((resolve, reject) => {
|
|
1449
|
-
const transaction = this.db.transaction(["sync_state"], "readwrite");
|
|
1851
|
+
const transaction = this.db.transaction(["sync_state", "sync_revision"], "readwrite");
|
|
1450
1852
|
const stateStore = transaction.objectStore("sync_state");
|
|
1853
|
+
const revisionStore = transaction.objectStore("sync_revision");
|
|
1451
1854
|
|
|
1452
1855
|
const storeRecord = {
|
|
1453
1856
|
type: record.id.type,
|
|
@@ -1458,7 +1861,24 @@ class IndexedDBStorage {
|
|
|
1458
1861
|
const request = stateStore.put(storeRecord);
|
|
1459
1862
|
|
|
1460
1863
|
request.onsuccess = () => {
|
|
1461
|
-
|
|
1864
|
+
// Update sync_revision to track the highest known revision
|
|
1865
|
+
const getRevisionRequest = revisionStore.get(1);
|
|
1866
|
+
getRevisionRequest.onsuccess = () => {
|
|
1867
|
+
const current = getRevisionRequest.result || { id: 1, revision: "0" };
|
|
1868
|
+
const currentRevision = BigInt(current.revision);
|
|
1869
|
+
const incomingRevision = BigInt(record.revision);
|
|
1870
|
+
if (incomingRevision > currentRevision) {
|
|
1871
|
+
revisionStore.put({ id: 1, revision: incomingRevision.toString() });
|
|
1872
|
+
}
|
|
1873
|
+
resolve();
|
|
1874
|
+
};
|
|
1875
|
+
getRevisionRequest.onerror = (event) => {
|
|
1876
|
+
reject(
|
|
1877
|
+
new StorageError(
|
|
1878
|
+
`Failed to update sync revision: ${event.target.error.message}`
|
|
1879
|
+
)
|
|
1880
|
+
);
|
|
1881
|
+
};
|
|
1462
1882
|
};
|
|
1463
1883
|
|
|
1464
1884
|
request.onerror = (event) => {
|
|
@@ -1502,7 +1922,10 @@ class IndexedDBStorage {
|
|
|
1502
1922
|
}
|
|
1503
1923
|
|
|
1504
1924
|
// Filter by payment details
|
|
1505
|
-
if (
|
|
1925
|
+
if (
|
|
1926
|
+
request.paymentDetailsFilter &&
|
|
1927
|
+
request.paymentDetailsFilter.length > 0
|
|
1928
|
+
) {
|
|
1506
1929
|
let details = null;
|
|
1507
1930
|
|
|
1508
1931
|
// Parse details if it's a string (stored in IndexedDB)
|
|
@@ -1533,7 +1956,9 @@ class IndexedDBStorage {
|
|
|
1533
1956
|
if (
|
|
1534
1957
|
details.type !== "spark" ||
|
|
1535
1958
|
!details.htlcDetails ||
|
|
1536
|
-
!paymentDetailsFilter.htlcStatus.includes(
|
|
1959
|
+
!paymentDetailsFilter.htlcStatus.includes(
|
|
1960
|
+
details.htlcDetails.status
|
|
1961
|
+
)
|
|
1537
1962
|
) {
|
|
1538
1963
|
continue;
|
|
1539
1964
|
}
|
|
@@ -1570,11 +1995,23 @@ class IndexedDBStorage {
|
|
|
1570
1995
|
continue;
|
|
1571
1996
|
}
|
|
1572
1997
|
}
|
|
1998
|
+
// Filter by token transaction type
|
|
1999
|
+
if (
|
|
2000
|
+
paymentDetailsFilter.type === "token" &&
|
|
2001
|
+
paymentDetailsFilter.txType != null
|
|
2002
|
+
) {
|
|
2003
|
+
if (
|
|
2004
|
+
details.type !== "token" ||
|
|
2005
|
+
details.txType !== paymentDetailsFilter.txType
|
|
2006
|
+
) {
|
|
2007
|
+
continue;
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
1573
2010
|
|
|
1574
2011
|
paymentDetailsFilterMatches = true;
|
|
1575
2012
|
break;
|
|
1576
2013
|
}
|
|
1577
|
-
|
|
2014
|
+
|
|
1578
2015
|
if (!paymentDetailsFilterMatches) {
|
|
1579
2016
|
return false;
|
|
1580
2017
|
}
|
|
@@ -1706,7 +2143,11 @@ class IndexedDBStorage {
|
|
|
1706
2143
|
|
|
1707
2144
|
_fetchLnurlReceiveMetadata(payment, lnurlReceiveMetadataStore) {
|
|
1708
2145
|
// Only fetch for lightning payments with a payment hash
|
|
1709
|
-
if (
|
|
2146
|
+
if (
|
|
2147
|
+
!payment.details ||
|
|
2148
|
+
payment.details.type !== "lightning" ||
|
|
2149
|
+
!payment.details.paymentHash
|
|
2150
|
+
) {
|
|
1710
2151
|
return Promise.resolve(payment);
|
|
1711
2152
|
}
|
|
1712
2153
|
|
|
@@ -1715,11 +2156,17 @@ class IndexedDBStorage {
|
|
|
1715
2156
|
}
|
|
1716
2157
|
|
|
1717
2158
|
return new Promise((resolve, reject) => {
|
|
1718
|
-
const lnurlReceiveRequest = lnurlReceiveMetadataStore.get(
|
|
1719
|
-
|
|
2159
|
+
const lnurlReceiveRequest = lnurlReceiveMetadataStore.get(
|
|
2160
|
+
payment.details.paymentHash
|
|
2161
|
+
);
|
|
2162
|
+
|
|
1720
2163
|
lnurlReceiveRequest.onsuccess = () => {
|
|
1721
2164
|
const lnurlReceiveMetadata = lnurlReceiveRequest.result;
|
|
1722
|
-
if (
|
|
2165
|
+
if (
|
|
2166
|
+
lnurlReceiveMetadata &&
|
|
2167
|
+
(lnurlReceiveMetadata.nostrZapRequest ||
|
|
2168
|
+
lnurlReceiveMetadata.senderComment)
|
|
2169
|
+
) {
|
|
1723
2170
|
payment.details.lnurlReceiveMetadata = {
|
|
1724
2171
|
nostrZapRequest: lnurlReceiveMetadata.nostrZapRequest || null,
|
|
1725
2172
|
nostrZapReceipt: lnurlReceiveMetadata.nostrZapReceipt || null,
|
|
@@ -1728,7 +2175,7 @@ class IndexedDBStorage {
|
|
|
1728
2175
|
}
|
|
1729
2176
|
resolve(payment);
|
|
1730
2177
|
};
|
|
1731
|
-
|
|
2178
|
+
|
|
1732
2179
|
lnurlReceiveRequest.onerror = () => {
|
|
1733
2180
|
// Continue without lnurlReceiveMetadata if fetch fails
|
|
1734
2181
|
reject(new Error("Failed to fetch lnurl receive metadata"));
|