diff --git a/js/payments/zapSplit.js b/js/payments/zapSplit.js index a31ba158..62e1b43b 100644 --- a/js/payments/zapSplit.js +++ b/js/payments/zapSplit.js @@ -10,6 +10,7 @@ import { } from "./lnurl.js"; import { ensureWallet, sendPayment } from "./nwcClient.js"; import { getPlatformLightningAddress } from "./platformAddress.js"; +import { RELAY_URLS } from "../nostr/toolkit.js"; import { userLogger } from "../utils/logger.js"; import { clampPercent, @@ -271,6 +272,40 @@ function derivePointerTag(videoEvent) { return ["a", value]; } +function resolveRelayUrls(wallet) { + const normalized = []; + const seen = new Set(); + + const addCandidate = (candidate) => { + if (!candidate) { + return; + } + if (Array.isArray(candidate)) { + candidate.forEach(addCandidate); + return; + } + if (typeof candidate === "string") { + const trimmed = candidate.trim(); + if (trimmed && !seen.has(trimmed)) { + seen.add(trimmed); + normalized.push(trimmed); + } + } + }; + + if (wallet && typeof wallet === "object") { + addCandidate(wallet.relayUrls); + addCandidate(wallet.relays); + addCandidate(wallet.relayUrl); + } + + if (!normalized.length) { + addCandidate(Array.isArray(RELAY_URLS) ? RELAY_URLS : []); + } + + return normalized; +} + function buildZapRequest({ wallet, recipientPubkey, @@ -302,6 +337,11 @@ function buildZapRequest({ tags.push(["amount", String(Math.max(0, Math.round(amountSats)) * 1000)]); } + const relayUrls = resolveRelayUrls(wallet); + if (relayUrls.length) { + tags.push(["relays", ...relayUrls]); + } + const event = { kind: ZAP_KIND, content: comment || "", diff --git a/tests/zap-split.test.mjs b/tests/zap-split.test.mjs index bb389ce9..375da56f 100644 --- a/tests/zap-split.test.mjs +++ b/tests/zap-split.test.mjs @@ -80,6 +80,12 @@ const platformAddressModule = await import("../js/payments/platformAddress.js"); const { __resetPlatformAddressCache } = platformAddressModule; const { nostrClient } = await import("../js/nostr.js"); +const DEFAULT_WALLET_RELAYS = [ + "wss://wallet.primary.example", + "wss://wallet.secondary.example", + "wss://wallet.primary.example", +]; + function createDeps({ commentAllowed = 120, platformAddress = "platform@example.com", @@ -87,6 +93,7 @@ function createDeps({ maxSendable = 2_000_000, allowsNostr = true, sendPaymentImplementation, + walletRelays = DEFAULT_WALLET_RELAYS, } = {}) { let ensureWalletCalls = 0; const sendCalls = []; @@ -137,6 +144,9 @@ function createDeps({ return { clientPubkey: "c".repeat(64), secretKey: "1".repeat(64), + relayUrl: walletRelays[0] || null, + relayUrls: walletRelays.slice(), + relays: walletRelays.slice(), }; }, async sendPayment(invoice, { amountSats, zapRequest }) { @@ -202,6 +212,21 @@ async function testSplitMath() { assert.equal(parsedZap.kind, 9734); assert(parsedZap.tags.some((tag) => tag[0] === "amount" && tag[1] === "900000")); + const relaysTag = parsedZap.tags.find((tag) => Array.isArray(tag) && tag[0] === "relays"); + assert(relaysTag, "relays tag should be present in zap request"); + const expectedRelays = Array.from( + new Set( + DEFAULT_WALLET_RELAYS.map((relay) => + typeof relay === "string" ? relay.trim() : "" + ).filter((relay) => relay) + ) + ); + assert.deepEqual( + relaysTag.slice(1), + expectedRelays, + "relays tag should match wallet relay list" + ); + const platformReceipt = result.receipts[1]; assert.equal(platformReceipt.recipientType, "platform"); assert.equal(platformReceipt.status, "success");