10 changed files with 177 additions and 33 deletions
@ -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 @@ |
|||||||
|
/** 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