You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

152 lines
5.2 KiB

/**
* Loads payto type metadata from {@link ../data/payto-types.json}.
* Edit that JSON to add types, editor order, hints, logos, and profile URL templates.
*/
import paytoTypesCatalog from '@/data/payto-types.json'
import { resolvePaytoLogoAssetPath } from '@/lib/payto-logos'
import { getPaytoPrimaryOpenUrl } from '@/lib/payto-wallet-open'
import { resolvePaypalPaymentUrl } from '@/lib/payto-paypal-url'
export type PaytoCategory = 'bitcoin' | 'bitcoin-layer' | 'crypto' | 'stablecoin' | 'fiat' | 'tip'
export type PaytoAuthorityHelp = {
placeholder: string
hint: string
}
export type PaytoWalletOpenRow = {
scheme?: string
style?: 'path' | 'query'
path?: string
query?: Record<string, string>
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 = {
label: string
symbol?: string
category: PaytoCategory
/** 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
}
type PaytoTypesCatalogJson = {
editorOrder: string[]
genericAuthorityHelp: PaytoAuthorityHelp
aliases: Record<string, string>
types: Record<string, PaytoTypeRecord>
}
const catalog = paytoTypesCatalog as PaytoTypesCatalogJson
export const PAYTO_EDITOR_TYPE_ORDER: readonly string[] = catalog.editorOrder
/** Select value: opens free-text payto type field (not published as this literal). */
export const PAYTO_EDITOR_OTHER_OPTION = '__other__'
const GENERIC_AUTHORITY_HELP: PaytoAuthorityHelp = catalog.genericAuthorityHelp
const PAYTO_TYPE_ALIASES: Record<string, string> = catalog.aliases
const PAYTO_TYPES: Record<string, PaytoTypeRecord> = catalog.types
/** UI summary per canonical type (label, symbol, category). */
export const PAYTO_KNOWN_TYPES: Record<
string,
{ label: string; symbol?: string; category: PaytoCategory }
> = Object.fromEntries(
Object.entries(PAYTO_TYPES).map(([id, row]) => [
id,
{ label: row.label, symbol: row.symbol, category: row.category }
])
)
export function getCanonicalPaytoType(type: string): string {
const key = type.toLowerCase().trim()
return PAYTO_TYPE_ALIASES[key] ?? key
}
export function getPaytoTypeRecord(type: string): PaytoTypeRecord | undefined {
return PAYTO_TYPES[getCanonicalPaytoType(type)]
}
export function getPaytoTypeInfo(type: string): (typeof PAYTO_KNOWN_TYPES)[string] | undefined {
return PAYTO_KNOWN_TYPES[getCanonicalPaytoType(type)]
}
export function isKnownPaytoType(type: string): boolean {
return getCanonicalPaytoType(type) in PAYTO_KNOWN_TYPES
}
export function getPaytoAuthorityFieldHelp(type: string): PaytoAuthorityHelp {
const row = getPaytoTypeRecord(type)
return row?.authority ?? GENERIC_AUTHORITY_HELP
}
export function getPaytoEditorTypeLabel(type: string): string {
return getPaytoTypeInfo(type)?.label ?? getCanonicalPaytoType(type)
}
/** True when the row uses a custom payto type (Other selected or unknown type from JSON). */
export function isPaytoEditorCustomType(type: string): boolean {
const trimmed = type.trim()
if (!trimmed || trimmed === PAYTO_EDITOR_OTHER_OPTION) return true
return !isKnownPaytoType(trimmed)
}
/** Dropdown options: catalog presets plus “Other”. */
export function paytoEditorSelectTypes(): string[] {
return [...PAYTO_EDITOR_TYPE_ORDER, PAYTO_EDITOR_OTHER_OPTION]
}
/** Bundled asset URL for `<img src>` (resolved from catalog `logoAssetPath`). */
export function getPaytoLogoPath(type: string): string | null {
return resolvePaytoLogoAssetPath(getPaytoTypeRecord(type)?.logoAssetPath)
}
/** Same as {@link getPaytoLogoPath}; alias for callers that expect a URL field name. */
export function getPaytoLogoUrl(type: string): string | null {
return getPaytoLogoPath(type)
}
export function getPaytoProfileUrl(type: string, authority: string): string | null {
if (!authority.trim()) return null
const canonical = getCanonicalPaytoType(type)
if (canonical === 'paypal') {
return resolvePaypalPaymentUrl(authority)
}
const fromWallet = getPaytoPrimaryOpenUrl(type, authority)
if (fromWallet) return fromWallet
const template = getPaytoTypeRecord(type)?.profileUrlTemplate
if (!template) return null
return template.replace('{authority}', encodeURIComponent(authority.trim()))
}
export function getPaytoIconChar(type: string): string | null {
return getPaytoTypeRecord(type)?.symbol ?? null
}
/** LUD-16 / LNURL lightning and BIP-353 DNS instructions — payment UI, not on-chain Bitcoin. */
export function isLightningPaytoType(type: string): boolean {
const canonical = getCanonicalPaytoType(type)
return canonical === 'lightning' || canonical === 'bip353'
}
/** Lightning targets that support zaps (LUD-16 / LNURL only; BIP-353 is pay/copy, not zappable). */
export function isZappableLightningPaytoType(type: string): boolean {
return getCanonicalPaytoType(type) === 'lightning'
}