131 lines
3.7 KiB
TypeScript
131 lines
3.7 KiB
TypeScript
import { TransactionLogger } from './TransactionLogger'
|
|
import { getPaymentMode } from '../lib/paymentMode'
|
|
|
|
export interface CustomerData {
|
|
first_name?: string
|
|
last_name?: string
|
|
name?: string
|
|
email?: string
|
|
phone?: string
|
|
address?: string
|
|
city?: string
|
|
postal_code?: string
|
|
country_code?: string
|
|
}
|
|
|
|
export interface SanitizedCustomerData {
|
|
name: string
|
|
email: string
|
|
phone?: string
|
|
address?: string
|
|
}
|
|
|
|
export class CustomerDataHandler {
|
|
static sanitizeCustomerData(customer: CustomerData): SanitizedCustomerData {
|
|
const mode = getPaymentMode()
|
|
|
|
try {
|
|
// Combine first_name and last_name if available, otherwise use name
|
|
const name = customer.name ||
|
|
(customer.first_name && customer.last_name
|
|
? `${customer.first_name} ${customer.last_name}`
|
|
: customer.first_name || customer.last_name || 'Unknown')
|
|
|
|
// Basic email validation
|
|
const email = customer.email?.toLowerCase().trim()
|
|
if (!email || !this.isValidEmail(email)) {
|
|
TransactionLogger.log(mode, 'customer.data.warning', {
|
|
field: 'email',
|
|
reason: 'invalid_or_missing'
|
|
})
|
|
}
|
|
|
|
// Sanitize phone number (remove non-numeric characters except +)
|
|
const phone = customer.phone?.replace(/[^\d+]/g, '')
|
|
|
|
// Create sanitized address if available
|
|
const address = this.buildAddressString(customer)
|
|
|
|
const sanitized: SanitizedCustomerData = {
|
|
name: name.trim(),
|
|
email: email || '',
|
|
phone,
|
|
address
|
|
}
|
|
|
|
TransactionLogger.log(mode, 'customer.data.sanitized', {
|
|
hasName: !!sanitized.name,
|
|
hasEmail: !!sanitized.email,
|
|
hasPhone: !!sanitized.phone,
|
|
hasAddress: !!sanitized.address
|
|
})
|
|
|
|
return sanitized
|
|
|
|
} catch (error) {
|
|
TransactionLogger.log(mode, 'customer.data.sanitization.error', {
|
|
error: error instanceof Error ? error.message : String(error)
|
|
})
|
|
// Return minimal safe data on error
|
|
return {
|
|
name: 'Unknown Customer',
|
|
email: '',
|
|
phone: customer.phone,
|
|
address: customer.address
|
|
}
|
|
}
|
|
}
|
|
|
|
static validateCustomerData(customer: CustomerData): { isValid: boolean; errors: string[] } {
|
|
const errors: string[] = []
|
|
|
|
if (!customer.name && !customer.first_name && !customer.last_name) {
|
|
errors.push('Name is required')
|
|
}
|
|
|
|
if (!customer.email) {
|
|
errors.push('Email is required')
|
|
} else if (!this.isValidEmail(customer.email)) {
|
|
errors.push('Invalid email format')
|
|
}
|
|
|
|
if (!customer.phone) {
|
|
errors.push('Phone number is required')
|
|
}
|
|
|
|
return {
|
|
isValid: errors.length === 0,
|
|
errors
|
|
}
|
|
}
|
|
|
|
static buildAddressString(customer: CustomerData): string | undefined {
|
|
const parts: string[] = []
|
|
|
|
if (customer.address) parts.push(customer.address)
|
|
if (customer.city) parts.push(customer.city)
|
|
if (customer.postal_code) parts.push(customer.postal_code)
|
|
if (customer.country_code) parts.push(customer.country_code)
|
|
|
|
return parts.length > 0 ? parts.join(', ') : undefined
|
|
}
|
|
|
|
private static isValidEmail(email: string): boolean {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
return emailRegex.test(email)
|
|
}
|
|
|
|
static formatForMidtrans(customer: SanitizedCustomerData) {
|
|
// Format customer data for Midtrans API (both Core and Snap)
|
|
return {
|
|
first_name: customer.name.split(' ')[0] || '',
|
|
last_name: customer.name.split(' ').slice(1).join(' ') || '',
|
|
email: customer.email,
|
|
phone: customer.phone || '',
|
|
address: customer.address || '',
|
|
city: '',
|
|
postal_code: '',
|
|
country_code: 'IDN'
|
|
}
|
|
}
|
|
} |