106 lines
2.3 KiB
TypeScript
106 lines
2.3 KiB
TypeScript
export const CARD_BRANDS = [
|
|
"visa",
|
|
"mastercard",
|
|
"unionpay",
|
|
"amex",
|
|
"diners",
|
|
"discover",
|
|
"jcb",
|
|
] as const
|
|
|
|
export type CardBrand = (typeof CARD_BRANDS)[number]
|
|
|
|
export const CARD_BRAND_LABELS: Record<CardBrand, string> = {
|
|
visa: "Visa",
|
|
mastercard: "Mastercard",
|
|
unionpay: "UnionPay",
|
|
amex: "American Express",
|
|
diners: "Diners Club",
|
|
discover: "Discover",
|
|
jcb: "JCB",
|
|
}
|
|
|
|
export const CARD_BRAND_LOGOS: Record<CardBrand, string> = {
|
|
visa: "/payment-logos/visa.svg",
|
|
mastercard: "/payment-logos/mastercard.svg",
|
|
unionpay: "/payment-logos/unionpay.svg",
|
|
amex: "/payment-logos/amex.svg",
|
|
diners: "/payment-logos/diners.svg",
|
|
discover: "/payment-logos/discover.svg",
|
|
jcb: "/payment-logos/jcb.svg",
|
|
}
|
|
|
|
const CARD_BRAND_ALIASES: Record<string, CardBrand> = {
|
|
visa: "visa",
|
|
mastercard: "mastercard",
|
|
unionpay: "unionpay",
|
|
amex: "amex",
|
|
americanexpress: "amex",
|
|
diners: "diners",
|
|
dinersclub: "diners",
|
|
discover: "discover",
|
|
jcb: "jcb",
|
|
}
|
|
|
|
export function normalizeCardBrand(
|
|
input: string | null | undefined
|
|
): CardBrand | null {
|
|
if (typeof input !== "string") {
|
|
return null
|
|
}
|
|
|
|
const key = input
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/[\s_-]+/g, "")
|
|
return CARD_BRAND_ALIASES[key] ?? null
|
|
}
|
|
|
|
export function getCardBrandLogoUrl(
|
|
input: string | null | undefined
|
|
): string | null {
|
|
const brand = normalizeCardBrand(input)
|
|
return brand ? CARD_BRAND_LOGOS[brand] : null
|
|
}
|
|
|
|
type CardNumberResult = {
|
|
card: { type?: string } | null
|
|
isPotentiallyValid: boolean
|
|
}
|
|
|
|
type CardValidatorApi = {
|
|
number?: (value: string) => CardNumberResult
|
|
}
|
|
|
|
type CardValidatorModule = CardValidatorApi & {
|
|
default?: CardValidatorApi
|
|
}
|
|
|
|
export async function detectCardBrand(
|
|
cardNumber: string
|
|
): Promise<CardBrand | null> {
|
|
const digits = cardNumber.replace(/\D/g, "")
|
|
if (!digits) {
|
|
return null
|
|
}
|
|
|
|
try {
|
|
const loadedValidator =
|
|
(await import("card-validator")) as unknown as CardValidatorModule
|
|
const validator = loadedValidator.default ?? loadedValidator
|
|
|
|
if (typeof validator.number !== "function") {
|
|
return null
|
|
}
|
|
|
|
const result = validator.number(digits)
|
|
if (!result.card || !result.isPotentiallyValid) {
|
|
return null
|
|
}
|
|
|
|
return result.card.type ? normalizeCardBrand(result.card.type) : null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|