From fddb722fa01cd885749cad9bd7d5cacc3eb51f66 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Thu, 21 May 2026 12:06:28 +0200 Subject: [PATCH] wallet handlers --- src/components/PaytoDialog/index.tsx | 121 ++++++++++---- src/data/payto-types.json | 24 ++- src/lib/payto-registry.ts | 4 + src/lib/payto-wallet-open.test.ts | 79 ++++++++- src/lib/payto-wallet-open.ts | 232 ++++++++++++++++++++++----- src/lib/payto.ts | 6 + 6 files changed, 381 insertions(+), 85 deletions(-) diff --git a/src/components/PaytoDialog/index.tsx b/src/components/PaytoDialog/index.tsx index 016fcbda..5a97981e 100644 --- a/src/components/PaytoDialog/index.tsx +++ b/src/components/PaytoDialog/index.tsx @@ -7,7 +7,15 @@ import { DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' -import { Copy, ExternalLink, Wallet, Zap } from 'lucide-react' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select' +import type { PaytoPaymentOpenHandler } from '@/lib/payto' +import { ArrowRight, Copy, Zap } from 'lucide-react' import { useTranslation } from 'react-i18next' import { closeModal } from '@getalby/bitcoin-connect-react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -16,7 +24,6 @@ import { releaseBodyScrollLocks } from '@/lib/react-remove-scroll-body-cleanup' import { filterPaytoPaymentOpenHandlersForDevice, getPaytoPaymentOpenHandlers, - getPhoenixPaymentOpenHandler, getPaytoTypeInfo } from '@/lib/payto' import { cn } from '@/lib/utils' @@ -50,10 +57,12 @@ export default function PaytoDialog({ const label = info?.label ?? type const isLightning = type.toLowerCase() === 'lightning' const [bolt11Invoice, setBolt11Invoice] = useState(null) + const [selectedOpenHandlerId, setSelectedOpenHandlerId] = useState('') useEffect(() => { if (!open) { setBolt11Invoice(null) + setSelectedOpenHandlerId('') closeModal() releaseBodyScrollLocks() } @@ -64,14 +73,42 @@ export default function PaytoDialog({ onOpenChange(false) }, [onOpenChange]) - const openHandlers = useMemo(() => { - const handlers = getPaytoPaymentOpenHandlers(type, authority) - if (isLightning && bolt11Invoice) { - const phoenix = getPhoenixPaymentOpenHandler('lightning', bolt11Invoice) - if (phoenix) handlers.push(phoenix) + const openHandlers = useMemo( + () => + filterPaytoPaymentOpenHandlersForDevice( + getPaytoPaymentOpenHandlers(type, authority, { bolt11Invoice }) + ), + [type, authority, bolt11Invoice] + ) + + useEffect(() => { + if (openHandlers.length === 0) { + setSelectedOpenHandlerId('') + return } - return filterPaytoPaymentOpenHandlersForDevice(handlers) - }, [type, authority, isLightning, bolt11Invoice]) + setSelectedOpenHandlerId((prev) => + openHandlers.some((h) => h.id === prev) ? prev : openHandlers[0].id + ) + }, [openHandlers]) + + const selectedOpenHandler = useMemo( + () => + openHandlers.find((h) => h.id === selectedOpenHandlerId) ?? + openHandlers[0] ?? + null, + [openHandlers, selectedOpenHandlerId] + ) + + const openSelectedHandler = useCallback( + (handler: PaytoPaymentOpenHandler) => { + if (handler.isHttp) { + window.open(handler.href, '_blank', 'noopener,noreferrer') + return + } + window.location.assign(handler.href) + }, + [] + ) const handleCopy = (text: string, copyLabel?: string) => { navigator.clipboard.writeText(text) @@ -166,31 +203,49 @@ export default function PaytoDialog({

{t('Open with')}

-
- {openHandlers.map((handler) => ( -
)} diff --git a/src/data/payto-types.json b/src/data/payto-types.json index 870a6d5e..5aa2fe91 100644 --- a/src/data/payto-types.json +++ b/src/data/payto-types.json @@ -3,6 +3,13 @@ "logoAssetsDir": "src/assets/payto_logos", "logoAssetPathNote": "Repo-relative path to the logo file. The app bundles these via Vite and exposes them under /assets/ at runtime (see getPaytoLogoPath)." }, + "_openWith": { + "bolt11Invoice": { + "paytoType": "lightning", + "coinScheme": "lightning", + "walletApps": ["phoenix", "zeus", "bluewallet"] + } + }, "walletApps": { "cakewallet": { "label": "Cake Wallet", @@ -13,6 +20,16 @@ "label": "Phoenix", "mobileOnly": true, "uriTemplate": "phoenix:{coinScheme}:{authority}" + }, + "zeus": { + "label": "Zeus", + "mobileOnly": true, + "uriTemplate": "zeusln:lightning:{authority}" + }, + "bluewallet": { + "label": "BlueWallet", + "mobileOnly": true, + "uriTemplate": "bluewallet:lightning:{authority}" } }, "kind0CryptocurrencyAddresses": { @@ -116,7 +133,7 @@ "walletOpen": { "scheme": "bolt12", "requirePrefix": "lno1", - "walletApps": ["phoenix"] + "walletApps": ["phoenix", "zeus"] }, "authority": { "placeholder": "lno1…", @@ -130,7 +147,7 @@ "walletOpen": { "scheme": "lightning", "requireAtSign": true, - "walletApps": ["phoenix"] + "walletApps": ["phoenix", "zeus"] }, "authority": { "placeholder": "user@example.com", @@ -179,7 +196,7 @@ "walletOpen": { "scheme": "lightning", "requireAtSign": true, - "walletApps": ["phoenix"] + "deferWalletAppsUntilBolt11": true }, "authority": { "placeholder": "user@getalby.com", @@ -361,6 +378,7 @@ "symbol": "💙", "category": "fiat", "logoAssetPath": "src/assets/payto_logos/paypal.webp", + "paymentOpen": "paypal", "profileUrlTemplate": "https://paypal.me/{authority}", "authority": { "placeholder": "username (not email)", diff --git a/src/lib/payto-registry.ts b/src/lib/payto-registry.ts index 85742bef..b64815ee 100644 --- a/src/lib/payto-registry.ts +++ b/src/lib/payto-registry.ts @@ -23,6 +23,8 @@ export type PaytoWalletOpenRow = { requireAtSign?: boolean requirePrefix?: string walletApps?: string[] + /** When true, {@link walletApps} are hidden until PaytoDialog has a BOLT11 (see catalog `_openWith.bolt11Invoice`). */ + deferWalletAppsUntilBolt11?: boolean } export type PaytoTypeRecord = { @@ -32,6 +34,8 @@ export type PaytoTypeRecord = { /** Repo-relative path, e.g. `src/assets/payto_logos/ethereum-eth-logo.svg`. */ logoAssetPath?: string profileUrlTemplate?: string + /** PaytoDialog “Open with” mode; `paypal` uses the PayPal URL resolver only. */ + paymentOpen?: 'paypal' /** Native wallet URI / app deep link (see {@link getPaytoPrimaryOpenUrl}). */ walletOpen?: PaytoWalletOpenRow authority?: PaytoAuthorityHelp diff --git a/src/lib/payto-wallet-open.test.ts b/src/lib/payto-wallet-open.test.ts index 2c96519d..64e328a7 100644 --- a/src/lib/payto-wallet-open.test.ts +++ b/src/lib/payto-wallet-open.test.ts @@ -1,9 +1,12 @@ import { describe, expect, it } from 'vitest' import { getPaytoProfileUrl } from '@/lib/payto-registry' import { + buildBlueWalletWalletHref, buildPhoenixWalletHref, + buildZeusWalletHref, filterPaytoPaymentOpenHandlersForDevice, filterWalletOpenActionsForDevice, + getLightningInvoiceWalletPaymentHandlers, getPaytoPaymentOpenHandlers, getPaytoPrimaryOpenUrl, getPaytoWalletOpenActions, @@ -86,12 +89,13 @@ describe('getPaytoPaymentOpenHandlers', () => { expect(cash[0].href).toBe('https://cash.app/%24cashtag') }) - it('builds Phoenix bolt12 deep link from offer string', () => { + it('builds Phoenix and Zeus bolt12 deep links from offer string', () => { const offer = 'lno1pg257enxv4ezqcneype82um50ynhxgrwdajx283qfwdpl28qqmc78ymlvhmxcsywdk5wrjnj36ryg488qwlrnzyjczs' const actions = getPaytoWalletOpenActions('bolt12', offer) - expect(actions).toHaveLength(1) - expect(actions[0].href).toBe(`phoenix:bolt12:${offer}`) - expect(actions[0].mobileOnly).toBe(true) + expect(actions).toHaveLength(2) + expect(actions.map((a) => a.label).sort()).toEqual(['Phoenix', 'Zeus']) + expect(actions.find((a) => a.label === 'Phoenix')?.href).toBe(`phoenix:bolt12:${offer}`) + expect(actions.find((a) => a.label === 'Zeus')?.href).toBe(`zeusln:${offer}`) }) it('builds Phoenix lightning deep link without pay?uri query', () => { @@ -104,16 +108,21 @@ describe('getPaytoPaymentOpenHandlers', () => { ) }) - it('omits Phoenix for lightning address until BOLT11 is supplied separately', () => { + it('omits mobile lightning wallets for lightning address until BOLT11 is supplied separately', () => { const handlers = getPaytoPaymentOpenHandlers('lightning', 'user@example.com') expect(handlers.some((h) => h.openTargetName === 'Phoenix')).toBe(false) + expect(handlers.some((h) => h.openTargetName === 'Zeus')).toBe(false) + expect(handlers.some((h) => h.openTargetName === 'BlueWallet')).toBe(false) }) - it('includes Phoenix on mobile only for bip353', () => { + it('includes Phoenix and Zeus on mobile only for bip353', () => { const handlers = getPaytoPaymentOpenHandlers('bip353', 'user@example.com') const phoenix = handlers.find((h) => h.openTargetName === 'Phoenix') + const zeus = handlers.find((h) => h.openTargetName === 'Zeus') expect(phoenix?.href).toBe('phoenix:lightning:user@example.com') + expect(zeus?.href).toBe('zeusln:lightning:user@example.com') expect(phoenix?.mobileOnly).toBe(true) + expect(zeus?.mobileOnly).toBe(true) const prev = navigator.userAgent Object.defineProperty(navigator, 'userAgent', { @@ -128,6 +137,64 @@ describe('getPaytoPaymentOpenHandlers', () => { Object.defineProperty(navigator, 'userAgent', { value: prev, configurable: true }) } }) + + it('builds Zeus and BlueWallet BOLT11 deep links', () => { + expect(buildZeusWalletHref('lightning', 'lnbc1p0example')).toBe('zeusln:lightning:lnbc1p0example') + expect(buildZeusWalletHref('lightning', 'lightning:lnbc1p0example')).toBe( + 'zeusln:lightning:lnbc1p0example' + ) + expect(buildBlueWalletWalletHref('lnbc1p0example')).toBe('bluewallet:lightning:lnbc1p0example') + expect(buildBlueWalletWalletHref('lightning:lnbc1p0example')).toBe( + 'bluewallet:lightning:lnbc1p0example' + ) + }) + + it('lists Phoenix, Zeus, and BlueWallet for a resolved BOLT11 invoice', () => { + const invoice = 'lnbc1p0example' + const handlers = getLightningInvoiceWalletPaymentHandlers(invoice) + expect(handlers.map((h) => h.openTargetName).sort()).toEqual(['BlueWallet', 'Phoenix', 'Zeus']) + expect(handlers.find((h) => h.openTargetName === 'Phoenix')?.href).toBe( + `phoenix:lightning:${invoice}` + ) + expect(handlers.find((h) => h.openTargetName === 'Zeus')?.href).toBe(`zeusln:lightning:${invoice}`) + expect(handlers.find((h) => h.openTargetName === 'BlueWallet')?.href).toBe( + `bluewallet:lightning:${invoice}` + ) + }) + + it('scopes lightning BOLT11 handlers to lightning payto only', () => { + const invoice = 'lnbc1p0example' + const lightning = getPaytoPaymentOpenHandlers('lightning', 'user@example.com', { + bolt11Invoice: invoice + }) + expect(lightning.map((h) => h.openTargetName).sort()).toEqual(['BlueWallet', 'Phoenix', 'Zeus']) + expect(lightning.some((h) => h.openTargetName === 'PayPal')).toBe(false) + + const moneroAddr = + '4AdUndXHHZ6cfufTMvppY6JwXNouMBzSkbLYfpAV5Usx3skxNgvYatVKtQNjUoNcknXV85jSp3wjUGpHbWfnqPm4WjwFGtW' + const moneroWithBolt11 = getPaytoPaymentOpenHandlers('monero', moneroAddr, { bolt11Invoice: invoice }) + expect(moneroWithBolt11.some((h) => h.openTargetName === 'Phoenix')).toBe(false) + expect(moneroWithBolt11.some((h) => h.openTargetName === 'Zeus')).toBe(false) + expect(moneroWithBolt11.some((h) => h.openTargetName === 'BlueWallet')).toBe(false) + expect( + getPaytoPaymentOpenHandlers('paypal', 'someuser', { bolt11Invoice: invoice }).map((h) => h.openTargetName) + ).toEqual(['PayPal']) + }) + + it('never mixes fiat web links or lightning wallets across payto types', () => { + const monero = getPaytoPaymentOpenHandlers( + 'monero', + '4AdUndXHHZ6cfufTMvppY6JwXNouMBzSkbLYfpAV5Usx3skxNgvYatVKtQNjUoNcknXV85jSp3wjUGpHbWfnqPm4WjwFGtW' + ) + expect(monero.some((h) => h.openTargetName === 'PayPal')).toBe(false) + expect(monero.some((h) => h.openTargetName === 'Phoenix')).toBe(false) + expect(monero.some((h) => h.openTargetName === 'Cake Wallet')).toBe(true) + + const cash = getPaytoPaymentOpenHandlers('cashme', '$cashtag') + expect(cash).toHaveLength(1) + expect(cash[0].openTargetName).toBe('Cash App') + expect(cash.some((h) => h.openTargetName === 'Phoenix')).toBe(false) + }) }) describe('isPaytoHttpOpenUrl', () => { diff --git a/src/lib/payto-wallet-open.ts b/src/lib/payto-wallet-open.ts index d70c3fac..87ddfce7 100644 --- a/src/lib/payto-wallet-open.ts +++ b/src/lib/payto-wallet-open.ts @@ -6,12 +6,23 @@ import paytoTypesCatalog from '@/data/payto-types.json' import type { PaytoWalletOpenRow } from '@/lib/payto-registry' import { resolvePaypalPaymentUrl } from '@/lib/payto-paypal-url' +type PaytoBolt11InvoiceOpenConfig = { + paytoType?: string + coinScheme?: string + walletApps: string[] +} + type PaytoTypeRecordWallet = { label?: string profileUrlTemplate?: string + paymentOpen?: 'paypal' walletOpen?: PaytoWalletOpenRow } +type PaytoWalletCatalogOpenWith = { + bolt11Invoice?: PaytoBolt11InvoiceOpenConfig +} + /** Labeled “open in …” action shown inside {@link PaytoDialog}. */ export type PaytoPaymentOpenHandler = { id: string @@ -38,6 +49,7 @@ type WalletAppRowJson = { } type PaytoWalletCatalogJson = { + _openWith?: PaytoWalletCatalogOpenWith aliases?: Record types: Record walletApps?: Record @@ -110,6 +122,62 @@ export function buildPhoenixWalletHref(coinScheme: string, authority: string): s return `phoenix:${scheme}:${payload}` } +/** + * Zeus {@link https://github.com/ZeusLN/zeus/blob/master/utils/AddressUtils.ts processBIP21Uri}: + * strips `zeusln:` then parses `lightning:` / bare `lno1` / `lnbc` payloads via handleAnything. + */ +export function buildZeusWalletHref(coinScheme: string, authority: string): string | null { + const auth = trimAuthority(authority) + if (!auth) return null + const scheme = coinScheme.toLowerCase().trim() + if (!scheme) return null + const payload = auth.replace(/^lightning:/i, '') + if (scheme === 'bolt12' || /^lno1/i.test(payload)) { + return `zeusln:${payload}` + } + return `zeusln:lightning:${payload}` +} + +/** + * BlueWallet wiki: wrap BOLT11 / lightning targets as `bluewallet:lightning:…`. + * @see https://github.com/BlueWallet/BlueWallet/wiki/Deeplinking + */ +export function buildBlueWalletWalletHref(authority: string): string | null { + const auth = trimAuthority(authority) + if (!auth) return null + const payload = auth.replace(/^lightning:/i, '') + if (!payload) return null + return `bluewallet:lightning:${payload}` +} + +const WALLET_APP_BUILDERS: Record< + string, + (coinScheme: string, authority: string) => string | null +> = { + phoenix: buildPhoenixWalletHref, + zeus: buildZeusWalletHref, + bluewallet: (_coinScheme, authority) => buildBlueWalletWalletHref(authority) +} + +function walletAppMeta(appId: string): WalletAppRowJson | undefined { + return walletCatalog.walletApps?.[appId] +} + +function paymentOpenHandlerFromHref( + appId: string, + coinScheme: string, + href: string +): PaytoPaymentOpenHandler { + const app = walletAppMeta(appId) + return { + id: `${appId}-${coinScheme}`, + openTargetName: app?.label ?? appId, + href, + isHttp: false, + mobileOnly: app?.mobileOnly !== false + } +} + function resolveWalletAppUri( appId: string, paytoType: string, @@ -121,8 +189,9 @@ function resolveWalletAppUri( const auth = trimAuthority(authority) if (!auth) return null const coinScheme = (row?.scheme ?? paytoType).toLowerCase() - if (appId === 'phoenix') { - return buildPhoenixWalletHref(coinScheme, auth) + const customBuild = WALLET_APP_BUILDERS[appId] + if (customBuild) { + return customBuild(coinScheme, auth) } const href = substituteAuthority( app.uriTemplate.replace(/\{coinScheme\}/g, coinScheme), @@ -138,16 +207,92 @@ export function getPhoenixPaymentOpenHandler( ): PaytoPaymentOpenHandler | null { const href = buildPhoenixWalletHref(coinScheme, authority) if (!href) return null + return paymentOpenHandlerFromHref('phoenix', coinScheme, href) +} + +export function getZeusPaymentOpenHandler( + coinScheme: string, + authority: string +): PaytoPaymentOpenHandler | null { + const href = buildZeusWalletHref(coinScheme, authority) + if (!href) return null + return paymentOpenHandlerFromHref('zeus', coinScheme, href) +} + +/** BlueWallet BOLT11 handler (invoice must already be resolved). */ +export function getBlueWalletPaymentOpenHandler( + coinScheme: string, + authority: string +): PaytoPaymentOpenHandler | null { + const href = buildBlueWalletWalletHref(authority) + if (!href) return null + return paymentOpenHandlerFromHref('bluewallet', coinScheme, href) +} + +function getBolt11InvoiceOpenConfig(): PaytoBolt11InvoiceOpenConfig | undefined { + const cfg = walletCatalog._openWith?.bolt11Invoice + if (!cfg?.walletApps?.length) return undefined + return cfg +} + +/** Build “Open with” handlers from catalog `_openWith.bolt11Invoice.walletApps`. */ +function getBolt11InvoiceOpenHandlers(invoice: string): PaytoPaymentOpenHandler[] { + const cfg = getBolt11InvoiceOpenConfig() + if (!cfg) return [] + + const paytoType = getCanonicalPaytoType(cfg.paytoType ?? 'lightning') + const row = getPaytoTypeRecord(paytoType)?.walletOpen + const auth = trimAuthority(invoice) + if (!auth) return [] + + const handlers: PaytoPaymentOpenHandler[] = [] + const seen = new Set() + for (const appId of cfg.walletApps) { + const app = walletCatalog.walletApps?.[appId] + const href = resolveWalletAppUri(appId, paytoType, auth, row) + if (!app || !href || seen.has(href)) continue + seen.add(href) + handlers.push({ + id: `${paytoType}-${appId}-bolt11`, + openTargetName: app.label, + href, + isHttp: false, + mobileOnly: app.mobileOnly !== false + }) + } + return handlers +} + +/** Handlers for a resolved BOLT11 shown after LNURL invoice creation (from {@link payto-types.json}). */ +export function getLightningInvoiceWalletPaymentHandlers( + authority: string +): PaytoPaymentOpenHandler[] { + return getBolt11InvoiceOpenHandlers(authority) +} + +export type PaytoPaymentOpenContext = { + /** LUD-16 lightning address resolved to BOLT11 in {@link PaytoDialog}. */ + bolt11Invoice?: string | null +} + +function walletActionToOpenHandler(action: PaytoWalletOpenAction): PaytoPaymentOpenHandler { return { - id: `phoenix-${coinScheme}`, - openTargetName: walletCatalog.walletApps?.phoenix?.label ?? 'Phoenix', - href, - isHttp: false, - mobileOnly: walletCatalog.walletApps?.phoenix?.mobileOnly !== false + id: action.id, + openTargetName: action.label, + href: action.href, + isHttp: isPaytoHttpOpenUrl(action.href), + mobileOnly: action.mobileOnly } } -const PAYTO_TYPES_PHOENIX_REQUIRES_BOLT11 = new Set(['lightning']) +function dedupePaymentOpenHandlers(handlers: PaytoPaymentOpenHandler[]): PaytoPaymentOpenHandler[] { + const seen = new Set() + return handlers.filter((h) => { + if (seen.has(h.href)) return false + seen.add(h.href) + return true + }) +} /** * Primary browser/OS URL for this payto target (wallet URI or https). @@ -213,56 +358,57 @@ export function filterWalletOpenActionsForDevice( } /** - * Named app/site open targets for PaytoDialog (https + walletApps only). - * Native coin schemes (monero:, bitcoin:, …) are omitted — users copy the payto URI instead. + * “Open with” targets for {@link PaytoDialog} — scoped to the active payto type only. + * Native coin schemes (monero:, bitcoin:, …) are omitted; users copy the payto URI instead. */ -export function getPaytoPaymentOpenHandlers(type: string, authority: string): PaytoPaymentOpenHandler[] { +export function getPaytoPaymentOpenHandlers( + type: string, + authority: string, + context?: PaytoPaymentOpenContext +): PaytoPaymentOpenHandler[] { const canonical = getCanonicalPaytoType(type) const record = getPaytoTypeRecord(canonical) const auth = trimAuthority(authority) if (!auth || !record) return [] const handlers: PaytoPaymentOpenHandler[] = [] - const seen = new Set() - const add = ( - id: string, - openTargetName: string, - href: string | null | undefined, - mobileOnly?: boolean - ) => { - if (!href || seen.has(href)) return - seen.add(href) - handlers.push({ - id, - openTargetName, - href, - isHttp: isPaytoHttpOpenUrl(href), - mobileOnly - }) + if (record.paymentOpen === 'paypal') { + const href = resolvePaypalPaymentUrl(auth) + if (href) { + handlers.push({ + id: 'paypal', + openTargetName: record.label ?? 'PayPal', + href, + isHttp: true, + mobileOnly: false + }) + } + return dedupePaymentOpenHandlers(handlers) } - if (canonical === 'paypal') { - add('paypal', 'PayPal', resolvePaypalPaymentUrl(auth)) - return handlers + const walletRow = record.walletOpen + if (walletRow?.deferWalletAppsUntilBolt11) { + if (context?.bolt11Invoice) { + handlers.push(...getBolt11InvoiceOpenHandlers(context.bolt11Invoice)) + } + } else if (walletRow?.walletApps?.length) { + for (const action of getPaytoWalletOpenActions(type, auth)) { + handlers.push(walletActionToOpenHandler(action)) + } } if (record.profileUrlTemplate) { - add( - `${canonical}-web`, - record.label ?? canonical, - substituteAuthority(record.profileUrlTemplate, encodeURIComponent(auth)) - ) - } - - for (const app of getPaytoWalletOpenActions(type, auth)) { - if (app.label === 'Phoenix' && PAYTO_TYPES_PHOENIX_REQUIRES_BOLT11.has(canonical)) { - continue - } - add(app.id, app.label, app.href, app.mobileOnly) + handlers.push({ + id: `${canonical}-web`, + openTargetName: record.label ?? canonical, + href: substituteAuthority(record.profileUrlTemplate, encodeURIComponent(auth)), + isHttp: true, + mobileOnly: false + }) } - return handlers + return dedupePaymentOpenHandlers(handlers) } export function filterPaytoPaymentOpenHandlersForDevice( diff --git a/src/lib/payto.ts b/src/lib/payto.ts index b50d384b..2e2e9a55 100644 --- a/src/lib/payto.ts +++ b/src/lib/payto.ts @@ -28,8 +28,13 @@ export { } from '@/lib/payto-registry' export { + buildBlueWalletWalletHref, buildPhoenixWalletHref, + buildZeusWalletHref, + getBlueWalletPaymentOpenHandler, + getLightningInvoiceWalletPaymentHandlers, getPhoenixPaymentOpenHandler, + getZeusPaymentOpenHandler, getPaytoPrimaryOpenUrl, getPaytoPaymentOpenHandlers, filterPaytoPaymentOpenHandlersForDevice, @@ -37,6 +42,7 @@ export { filterWalletOpenActionsForDevice, isPaytoHttpOpenUrl, isLikelyMobileWalletUserAgent, + type PaytoPaymentOpenContext, type PaytoPaymentOpenHandler, type PaytoWalletOpenAction } from '@/lib/payto-wallet-open'