zips-react-native-sdk-test
Version:
Lightweight ZIPS Payment Gateway SDK for React Native - Complete payment solution with card payments, wallet payments (AfrMoney & ZApp), netbanking, and native UI design
193 lines (163 loc) • 4.13 kB
text/typescript
/**
* Default currency for the ZIPS payment system
*/
export const DEFAULT_CURRENCY = 'GMD';
/**
* Get default currency
*/
export const getDefaultCurrency = (): string => {
return DEFAULT_CURRENCY;
};
/**
* Format amount for display
*/
export const formatAmount = (
amount: number,
currency: string = DEFAULT_CURRENCY
): string => {
return `${currency} ${(amount / 100).toFixed(2)}`;
};
/**
* Format date for display
*/
export const formatDate = (dateString: string): string => {
const date = new Date(dateString);
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
};
/**
* Validate account number
*/
export const validateAccountNumber = (account: string): boolean => {
const cleanAccount = account.replace(/\s/g, '');
return cleanAccount.length >= 10 && /^\d+$/.test(cleanAccount);
};
/**
* Validate card number using Luhn algorithm
*/
export const validateCardNumber = (cardNumber: string): boolean => {
const num = cardNumber.replace(/\D/g, '');
if (num.length < 13 || num.length > 19) {
return false;
}
let sum = 0;
let shouldDouble = false;
for (let i = num.length - 1; i >= 0; i--) {
let digit = parseInt(num.charAt(i), 10);
if (shouldDouble) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
shouldDouble = !shouldDouble;
}
return sum % 10 === 0;
};
/**
* Format card number with spaces
*/
export const formatCardNumber = (text: string): string => {
const cleaned = text.replace(/\D/g, '');
const formatted = cleaned.replace(/(\d{4})(?=\d)/g, '$1 ');
return formatted;
};
/**
* Format account number with spaces
*/
export const formatAccountNumber = (text: string): string => {
const cleaned = text.replace(/\D/g, '');
const formatted = cleaned.replace(/(\d{4})(?=\d)/g, '$1 ');
return formatted;
};
/**
* Validate phone number
*/
export const validatePhoneNumber = (phone: string): boolean => {
const cleanPhone = phone.replace(/\D/g, '');
return cleanPhone.length >= 7 && cleanPhone.length <= 15;
};
/**
* Validate email address
*/
export const validateEmail = (email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
/**
* Generate UUID v4
*/
export const generateUUID = (): string => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
};
/**
* Delay function for retries
*/
export const delay = (ms: number): Promise<void> => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
/**
* Retry function with exponential backoff
*/
export const retryWithBackoff = async <T>(
fn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> => {
let lastError: Error;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (i === maxRetries - 1) {
throw lastError;
}
const delayTime = baseDelay * Math.pow(2, i);
await delay(delayTime);
}
}
throw lastError!;
};
/**
* Check if error is network related
*/
export const isNetworkError = (error: any): boolean => {
return (
error.code === 'NETWORK_ERROR' ||
error.message?.includes('Network Error') ||
error.message?.includes('Failed to fetch')
);
};
/**
* Sanitize input for security
*/
export const sanitizeInput = (input: string): string => {
return input.trim().replace(/[<>\"']/g, '');
};
/**
* Deep clone object
*/
export const deepClone = <T>(obj: T): T => {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime()) as T;
}
if (obj instanceof Array) {
return obj.map((item) => deepClone(item)) as T;
}
if (typeof obj === 'object') {
const cloned = {} as T;
Object.keys(obj).forEach((key) => {
(cloned as any)[key] = deepClone((obj as any)[key]);
});
return cloned;
}
return obj;
};