10 changed files with 177 additions and 33 deletions
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, it } from 'vitest' |
||||
import { |
||||
buildLnurlPayCallbackUrl, |
||||
parseLnurlCommentAllowed |
||||
} from './lnurl-pay' |
||||
|
||||
describe('parseLnurlCommentAllowed', () => { |
||||
it('accepts numbers and numeric strings', () => { |
||||
expect(parseLnurlCommentAllowed(255)).toBe(255) |
||||
expect(parseLnurlCommentAllowed('1024')).toBe(1024) |
||||
expect(parseLnurlCommentAllowed('0')).toBe(0) |
||||
}) |
||||
|
||||
it('returns 0 for missing or invalid values', () => { |
||||
expect(parseLnurlCommentAllowed(undefined)).toBe(0) |
||||
expect(parseLnurlCommentAllowed('')).toBe(0) |
||||
expect(parseLnurlCommentAllowed('nope')).toBe(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('buildLnurlPayCallbackUrl', () => { |
||||
it('merges params into callbacks that already have a query string', () => { |
||||
const out = buildLnurlPayCallbackUrl('https://pay.example/cb?tag=payRequest', { |
||||
amount: '21000', |
||||
comment: 'hello tip' |
||||
}) |
||||
const url = new URL(out) |
||||
expect(url.searchParams.get('tag')).toBe('payRequest') |
||||
expect(url.searchParams.get('amount')).toBe('21000') |
||||
expect(url.searchParams.get('comment')).toBe('hello tip') |
||||
}) |
||||
|
||||
it('encodes unicode in comments', () => { |
||||
const out = buildLnurlPayCallbackUrl('https://pay.example/cb', { |
||||
amount: '1000', |
||||
comment: 'café ☕' |
||||
}) |
||||
expect(out).toContain('comment=') |
||||
expect(decodeURIComponent(new URL(out).searchParams.get('comment') ?? '')).toBe('café ☕') |
||||
}) |
||||
}) |
||||
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
/** LUD-06 / LUD-12 LNURL-pay helpers (comment + callback URL). */ |
||||
|
||||
/** Default max comment length when metadata omits `commentAllowed` but we still show the field. */ |
||||
export const LNURL_PAY_FALLBACK_COMMENT_ALLOWED = 255 |
||||
|
||||
export function parseLnurlCommentAllowed(value: unknown): number { |
||||
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) { |
||||
return Math.floor(value) |
||||
} |
||||
if (typeof value === 'string') { |
||||
const trimmed = value.trim() |
||||
if (!trimmed) return 0 |
||||
const n = Number(trimmed) |
||||
if (Number.isFinite(n) && n >= 0) return Math.floor(n) |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
/** |
||||
* Append LNURL-pay GET params to `callback` (LUD-06). Uses `URL` so existing query strings are preserved. |
||||
*/ |
||||
export function buildLnurlPayCallbackUrl( |
||||
callback: string, |
||||
params: Record<string, string> |
||||
): string { |
||||
const url = new URL(callback) |
||||
for (const [key, value] of Object.entries(params)) { |
||||
url.searchParams.set(key, value) |
||||
} |
||||
return url.toString() |
||||
} |
||||
Loading…
Reference in new issue