|
| 1 | +/** |
| 2 | + * Payment Link Utility Functions (Client-safe) |
| 3 | + * |
| 4 | + * These functions don't require database access and can be used on the client. |
| 5 | + */ |
| 6 | + |
| 7 | +// Format amount for display |
| 8 | +export function formatAmount(amount: string | null, asset: string = 'XLM'): string { |
| 9 | + if (!amount) return `Any amount (${asset})`; |
| 10 | + const num = parseFloat(amount); |
| 11 | + if (isNaN(num)) return amount; |
| 12 | + return `${num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })} ${asset}`; |
| 13 | +} |
| 14 | + |
| 15 | +// Check if link is expired |
| 16 | +export function isLinkExpired(expiresAt: string | null): boolean { |
| 17 | + if (!expiresAt) return false; |
| 18 | + return new Date(expiresAt) < new Date(); |
| 19 | +} |
| 20 | + |
| 21 | +// Get time until expiration |
| 22 | +export function getTimeUntilExpiration(expiresAt: string | null): string | null { |
| 23 | + if (!expiresAt) return null; |
| 24 | + |
| 25 | + const now = new Date(); |
| 26 | + const expiry = new Date(expiresAt); |
| 27 | + const diff = expiry.getTime() - now.getTime(); |
| 28 | + |
| 29 | + if (diff <= 0) return 'Expired'; |
| 30 | + |
| 31 | + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); |
| 32 | + const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); |
| 33 | + const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); |
| 34 | + |
| 35 | + if (days > 0) return `${days}d ${hours}h`; |
| 36 | + if (hours > 0) return `${hours}h ${minutes}m`; |
| 37 | + return `${minutes}m`; |
| 38 | +} |
| 39 | + |
| 40 | +// Validate Stellar address format |
| 41 | +export function isValidStellarAddress(address: string): boolean { |
| 42 | + if (!address || address.length !== 56) return false; |
| 43 | + if (!address.startsWith('G')) return false; |
| 44 | + return /^G[A-Z2-7]{55}$/.test(address); |
| 45 | +} |
| 46 | + |
| 47 | +// Validate amount |
| 48 | +export function isValidAmount(amount: string): boolean { |
| 49 | + const num = parseFloat(amount); |
| 50 | + return !isNaN(num) && num > 0 && num <= 100000000000; |
| 51 | +} |
| 52 | + |
| 53 | +// Generate payment link URL |
| 54 | +export function getPaymentLinkUrl(id: string, baseUrl: string): string { |
| 55 | + return `${baseUrl}/pay/${id}`; |
| 56 | +} |
| 57 | + |
| 58 | +// Generate Stellar URI for wallet apps |
| 59 | +export function getStellarPayUri( |
| 60 | + recipientAddress: string, |
| 61 | + amount?: string | null, |
| 62 | + memo?: string | null, |
| 63 | + asset?: string |
| 64 | +): string { |
| 65 | + let uri = `web+stellar:pay?destination=${recipientAddress}`; |
| 66 | + |
| 67 | + if (amount) { |
| 68 | + uri += `&amount=${amount}`; |
| 69 | + } |
| 70 | + |
| 71 | + if (memo) { |
| 72 | + uri += `&memo=${encodeURIComponent(memo)}&memo_type=MEMO_TEXT`; |
| 73 | + } |
| 74 | + |
| 75 | + if (asset && asset !== 'XLM') { |
| 76 | + // For non-XLM assets, would need issuer |
| 77 | + // uri += `&asset_code=${asset}&asset_issuer=...`; |
| 78 | + } |
| 79 | + |
| 80 | + return uri; |
| 81 | +} |
0 commit comments