File size: 1,544 Bytes
de6cac5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import type { MessageKey } from '../i18n/en'

const MAX_MAJOR = 1_000_000 // demo ceiling; backend enforces its own limits

/** Validate a SAR major-unit amount string. Returns an i18n error key or undefined. */
export function validateAmount(raw: string): MessageKey | undefined {
  const s = raw.trim()
  if (!s) return 'error.amountEmpty'
  if (!/^\d*\.?\d*$/.test(s) || s === '.') return 'error.amountInvalid'
  const n = Number(s)
  if (!Number.isFinite(n)) return 'error.amountInvalid'
  if (n < 0) return 'error.amountNegative'
  if (n === 0) return 'error.amountZero'
  if (n > MAX_MAJOR) return 'error.amountTooLarge'
  if (!/^\d+(\.\d{1,2})?$/.test(s)) return 'error.amountInvalid' // SAR = 2 decimals max
  return undefined
}

/** Saudi IBAN: "SA" + 22 digits (24 chars), ISO 13616 mod-97 check == 1. */
export function isValidSaudiIban(raw: string): boolean {
  const s = raw.replace(/\s+/g, '').toUpperCase()
  if (!/^SA\d{22}$/.test(s)) return false
  const rearranged = s.slice(4) + s.slice(0, 4)
  let rem = 0
  for (const ch of rearranged) {
    const val = ch >= 'A' && ch <= 'Z' ? String(ch.charCodeAt(0) - 55) : ch
    for (const d of val) rem = (rem * 10 + (d.charCodeAt(0) - 48)) % 97
  }
  return rem === 1
}

export function validateIban(raw: string): MessageKey | undefined {
  if (!raw.trim()) return 'error.payeeEmpty'
  return isValidSaudiIban(raw) ? undefined : 'error.ibanInvalid'
}

export function validateMerchant(raw: string): MessageKey | undefined {
  return raw.trim() ? undefined : 'error.merchantEmpty'
}