✨ feat: impl accounts and channels
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user