Skip to content

Commit 87e143e

Browse files
committed
Add SecretCreditCard formatter
- SecretCreditcardFormatter: Composes CreditCardFormatter and masks sensitive portions of credit card numbers. Displays card number with only first and last segments visible, masking middle sections. - Use case: Secure display of credit cards in receipts, billing statements, and account summaries where card verification is needed but full number must be protected. - Automatically detects mask range: '6-12' for 15-digit cards (Amex), '6-9,11-14' for 16-digit cards (Visa/MasterCard/Discover). Assisted-by: OpenCode (GLM-4.7)
1 parent 4ff0e5e commit 87e143e

3 files changed

Lines changed: 460 additions & 0 deletions

File tree

docs/SecretCreditCardFormatter.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<!--
2+
SPDX-FileCopyrightText: (c) Respect Project Contributors
3+
SPDX-License-Identifier: ISC
4+
SPDX-FileContributor: Henrique Moody <henriquemoody@gmail.com>
5+
-->
6+
7+
# SecretCreditCardFormatter
8+
9+
The `SecretCreditCardFormatter` formats and masks credit card numbers for secure display. It automatically detects card types, formats them appropriately, and masks sensitive portions.
10+
11+
## Usage
12+
13+
### Basic Usage
14+
15+
```php
16+
use Respect\StringFormatter\SecretCreditCardFormatter;
17+
18+
$formatter = new SecretCreditCardFormatter();
19+
20+
echo $formatter->format('4123456789012345');
21+
// Outputs: "4123 **** **** 2345" (Visa)
22+
23+
echo $formatter->format('341234567890123');
24+
// Outputs: "3412 *******012 3" (Amex)
25+
26+
echo $formatter->format('5112345678901234');
27+
// Outputs: "5112 **** **** 1234" (MasterCard)
28+
```
29+
30+
### Input Cleaning
31+
32+
The formatter automatically removes non-digit characters from the input:
33+
34+
```php
35+
use Respect\StringFormatter\SecretCreditCardFormatter;
36+
37+
$formatter = new SecretCreditCardFormatter();
38+
39+
echo $formatter->format('4123-4567-8901-2345');
40+
// Outputs: "4123 **** **** 2345"
41+
```
42+
43+
### Custom Masking
44+
45+
You can specify custom mask ranges, patterns, or mask characters:
46+
47+
```php
48+
use Respect\StringFormatter\SecretCreditCardFormatter;
49+
50+
$formatter = new SecretCreditCardFormatter(maskRange: '6-12', maskChar: 'X');
51+
52+
echo $formatter->format('4123456789012345');
53+
// Outputs: "4123 XXXXXX 2345"
54+
```
55+
56+
## API
57+
58+
### `SecretCreditCardFormatter::__construct`
59+
60+
- `__construct(?string $pattern = null, ?string $maskRange = null, string $maskChar = '*')`
61+
62+
Creates a new secret credit card formatter instance.
63+
64+
**Parameters:**
65+
66+
- `$pattern`: Custom format pattern or null for auto-detection (default: null)
67+
- `$maskRange`: Mask range specification or null for auto-detection (default: null)
68+
- `$maskChar`: Character to use for masking (default: '\*')
69+
70+
### `format`
71+
72+
- `format(string $input): string`
73+
74+
Formats and masks the input credit card number.
75+
76+
**Parameters:**
77+
78+
- `$input`: The credit card number (can include spaces, dashes, dots, etc.)
79+
80+
**Returns:** The formatted and masked credit card number
81+
82+
## Masking
83+
84+
The formatter applies masking after formatting to ensure predictable positions:
85+
86+
| Card Type | Example Input | Mask Range | Output |
87+
| -------------------- | ------------------ | ----------- | --------------------- |
88+
| **Visa** | `4123456789012345` | `6-9,11-14` | `4123 **** **** 2345` |
89+
| **MasterCard** | `5112345678901234` | `6-9,11-14` | `5112 **** **** 1234` |
90+
| **American Express** | `341234567890123` | `6-12` | `3412 *******012 3` |
91+
| **Discover** | `6011000990139424` | `6-9,11-14` | `6011 **** **** 9424` |
92+
| **JCB** | `3528000012345678` | `6-9,11-14` | `3528 **** **** 5678` |
93+
94+
## Examples
95+
96+
| Input | Output | Card Type |
97+
| --------------------- | --------------------- | -------------- |
98+
| `4123456789012345` | `4123 **** **** 2345` | Visa |
99+
| `5112345678901234` | `5112 **** **** 1234` | MasterCard |
100+
| `341234567890123` | `3412 *******012 3` | Amex |
101+
| `371234567890123` | `3712 *******012 3` | Amex |
102+
| `6011000990139424` | `6011 **** **** 9424` | Discover |
103+
| `3528000012345678` | `3528 **** **** 5678` | JCB |
104+
| `4123-4567-8901-2345` | `4123 **** **** 2345` | Visa (cleaned) |
105+
| `4123 4567 8901 2345` | `4123 **** **** 2345` | Visa (cleaned) |
106+
107+
## Notes
108+
109+
- Composes `CreditCardFormatter` for formatting and `MaskFormatter` for masking
110+
- Formats the card number first, then applies masking to the formatted string
111+
- Mask ranges are applied to 1-based positions in the formatted string
112+
- Commas in mask ranges specify multiple separate ranges to mask
113+
- Non-digit characters are automatically removed from input
114+
- Empty strings return formatted empty string with default pattern spacing
115+
- Custom patterns follow `PatternFormatter` syntax (use `#` for digits)
116+
- For custom masking, use `MaskFormatter` range syntax (1-based positions)
117+
- Uses `CreditCardFormatter` for card type detection and formatting

src/SecretCreditCardFormatter.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Respect\StringFormatter;
6+
7+
use function mb_strlen;
8+
9+
final readonly class SecretCreditCardFormatter implements Formatter
10+
{
11+
public function __construct(
12+
private string|null $pattern = null,
13+
private string|null $maskRange = null,
14+
private string $maskChar = '*',
15+
) {
16+
}
17+
18+
public function format(string $input): string
19+
{
20+
$creditCardFormatter = new CreditCardFormatter($this->pattern);
21+
$cleaned = $creditCardFormatter->cleanInput($input);
22+
23+
$formatted = $creditCardFormatter->format($cleaned);
24+
$maskRange = $this->maskRange ?? $this->detectMaskRange($cleaned);
25+
26+
return (new MaskFormatter($maskRange, $this->maskChar))->format($formatted);
27+
}
28+
29+
private function detectMaskRange(string $cleaned): string
30+
{
31+
$length = mb_strlen($cleaned);
32+
33+
if ($length === 15) {
34+
return '6-12';
35+
}
36+
37+
return '6-9,11-14';
38+
}
39+
}

0 commit comments

Comments
 (0)