Skip to content

Commit 4d2fc35

Browse files
committed
refactor: Extract payment link utilities to separate module
- Create paymentLinkUtils.ts with client-safe utility functions - Move formatAmount, getTimeUntilExpiration, and validation functions - Update payment link page to use new utility module - Separate client-side utils from server-side database functions
1 parent e478258 commit 4d2fc35

2 files changed

Lines changed: 82 additions & 1 deletion

File tree

demo/src/app/pay/[id]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
} from 'lucide-react';
2525
import { QRCodeSVG } from 'qrcode.react';
2626
import { useZkWallet } from '@/hooks/useZkWallet';
27-
import { formatAmount, getTimeUntilExpiration } from '@/lib/paymentLinks';
27+
import { formatAmount, getTimeUntilExpiration } from '@/lib/paymentLinkUtils';
2828

2929
interface PaymentLinkData {
3030
id: string;

demo/src/lib/paymentLinkUtils.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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

Comments
 (0)