import { Pipe, PipeTransform } from "@angular/core"; interface CardRuleEntry { cardLength: number; blocks: number[]; } // See https://baymard.com/checkout-usability/credit-card-patterns for // all possible credit card spacing patterns. For now, we just handle // the below. const numberFormats: Record = { Visa: [{ cardLength: 16, blocks: [4, 4, 4, 4] }], Mastercard: [{ cardLength: 16, blocks: [4, 4, 4, 4] }], Maestro: [ { cardLength: 16, blocks: [4, 4, 4, 4] }, { cardLength: 13, blocks: [4, 4, 5] }, { cardLength: 15, blocks: [4, 6, 5] }, { cardLength: 19, blocks: [4, 4, 4, 4, 3] }, ], Discover: [{ cardLength: 16, blocks: [4, 4, 4, 4] }], "Diners Club": [{ cardLength: 14, blocks: [4, 6, 4] }], JCB: [{ cardLength: 16, blocks: [4, 4, 4, 4] }], UnionPay: [ { cardLength: 16, blocks: [4, 4, 4, 4] }, { cardLength: 19, blocks: [6, 13] }, ], Amex: [{ cardLength: 15, blocks: [4, 6, 5] }], Other: [{ cardLength: 16, blocks: [4, 4, 4, 4] }], }; @Pipe({ name: "creditCardNumber" }) export class CreditCardNumberPipe implements PipeTransform { transform(creditCardNumber: string, brand: string): string { let rules = numberFormats[brand]; if (rules == null) { rules = numberFormats["Other"]; } const cardLength = creditCardNumber.length; let matchingRule = rules.find((r) => r.cardLength == cardLength); if (matchingRule == null) { matchingRule = rules[0]; } const blocks = matchingRule.blocks; const chunks: string[] = []; let total = 0; blocks.forEach((c) => { chunks.push(creditCardNumber.slice(total, total + c)); total += c; }); // Append the remaining part if (cardLength > total) { chunks.push(creditCardNumber.slice(total)); } return chunks.join(" "); } }