1 import type { FormikErrors } from 'formik';
2 import { c } from 'ttag';
4 import { validateItemErrors } from './item';
6 export type CreditCardItemFormValues = {
9 cardholderName: string;
11 expirationDate: string;
12 verificationNumber: string;
17 /** Returns a tuple `[MM, YY|YYYY]` from a raw expiration date string.
18 * Supported date formats for extraction :
23 * - MM[seperator]YYYY */
24 const extractExpirationDateParts = (rawDate: string): [string, string] => {
25 /* account for YYYY-MM format */
26 if (/^(\d{4})-(\d{2})$/.test(rawDate)) return [rawDate.slice(-2), rawDate.slice(0, 4)];
28 const date = rawDate.replaceAll(/\/|-|\.|,|\s/g, '');
29 if (date.length === 4) return [date.slice(0, 2), date.slice(-2)];
30 if (date.length === 6) return [date.slice(0, 2), date.slice(-4)];
34 const isValidMonth = (maybeMonth: string): boolean => {
35 const month = parseInt(maybeMonth, 10);
36 return !isNaN(month) && month >= 1 && month <= 12;
38 const isValidYear = (maybeYear: string): boolean => {
39 const year = parseInt(maybeYear, 10);
40 return !isNaN(year) && year >= 0 && year <= 9999;
43 const isValidExpirationDate = (date: string): boolean => {
44 const [month, year] = extractExpirationDateParts(date);
45 return isValidMonth(month) && isValidYear(year);
48 export const formatExpirationDateMMYY = (date: string): string => {
49 const [month, year] = extractExpirationDateParts(date);
50 return isValidMonth(month) && isValidYear(year) ? `${month}${year.slice(-2)}` : '';
53 export const formatExpirationDateMMYYYY = (date: string): string => {
54 const [month, year] = extractExpirationDateParts(date);
56 if (isValidMonth(month) && isValidYear(year)) {
57 if (year.length === 4) return `${month}${year}`;
58 const yearBase = new Date().getFullYear().toString().slice(0, 2);
59 return `${month}${yearBase}${year}`;
65 /** Formats the provided date string as `YYYY-MM`. */
66 export const formatExpirationDateYYYYMM = (date: string): string => {
67 const [month, year] = extractExpirationDateParts(date);
69 if (isValidMonth(month) && isValidYear(year)) {
70 if (year.length === 4) return `${year}-${month}`;
71 const yearBase = new Date().getFullYear().toString().slice(0, 2);
72 return `${yearBase}${year}-${month}`;
78 export const validateCreditCardForm = (values: CreditCardItemFormValues): FormikErrors<CreditCardItemFormValues> => {
79 const errors: FormikErrors<CreditCardItemFormValues> = validateItemErrors(values);
81 if (values.expirationDate.length && !isValidExpirationDate(values.expirationDate)) {
82 errors.expirationDate = c('Warning').t`Expiration Date is not in the format MM/YY`;