CreditCard::isRequireCvv()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Larium\CreditCard;
6
7
use function array_intersect_key;
8
use function array_replace;
9
use function strlen;
10
use function strtoupper;
11
use function substr;
12
13
/**
14
 * CreditCard class acts as value object.
15
 */
16
final class CreditCard
17
{
18
    public const VISA               = 'visa';
19
    public const MASTER             = 'master';
20
    public const DISCOVER           = 'discover';
21
    public const AMEX               = 'american_express';
22
    public const DINERS_CLUB        = 'diners_club';
23
    public const JCB                = 'jcb';
24
    public const SWITCH_BRAND       = 'switch';
25
    public const SOLO               = 'solo';
26
    public const DANKORT            = 'dankort';
27
    public const MAESTRO            = 'maestro';
28
    public const FORBRUGSFORENINGEN = 'forbrugsforeningen';
29
    public const LASER              = 'laser';
30
    public const UNIONPAY           = 'unionpay';
31
    public const MIR                = 'mir';
32
33
    /**
34
     * Card holder name.
35
     * Should be in upper case.
36
     *
37
     * @var string
38
     */
39
    private ?string $holderName = '';
40
41
    /**
42
     * Expire date of card as value object
43
     *
44
     * @var ExpiryDate
45
     */
46
    private ExpiryDate $expiryDate;
47
48
    /**
49
     * The brand of card.
50
     *
51
     * @var string
52
     */
53
    private string $brand = '';
54
55
    /**
56
     * The number of card.
57
     *
58
     * @var string
59
     */
60
    private string $number = '';
61
62
    /**
63
     * The verification value of card (cvv).
64
     * 3 or 4 digits.
65
     *
66
     * @var string
67
     */
68
    private string $cvv;
69
70
    /**
71
     * Whether card is require verification value to be present.
72
     *
73
     * @var bool
74
     */
75
    private bool $requireCvv = true;
76
77
    /**
78
     * Token stored from a real credit card and can be used for purchases.
79
     *
80
     * @var Token
81
     */
82
    private ?Token $token;
83
84
    /**
85
     * @var string
86
     */
87
    private string $bin = '';
88
89
    /**
90
     * @var string
91
     */
92
    private string $issuingBank = '';
93
94
    /**
95
     * The iso alpha 3 country code.
96
     * @var string
97
     */
98
    private string $country = '';
99
100 26
    public function __construct(array $options = [])
101
    {
102 26
        $default = [
103 26
            'holderName' => '',
104 26
            'month'      => '1',
105 26
            'year'       => '1970',
106 26
            'brand'      => '',
107 26
            'number'     => '',
108 26
            'cvv'        => '',
109 26
            'requireCvv' => true,
110 26
            'token'      => null
111 26
        ];
112
113 26
        $options = array_intersect_key($options, $default);
114 26
        $options = array_replace($default, $options);
115
116 26
        $month = strval($options['month'] ?? 1);
117 26
        $year  = strval($options['year'] ?? 1970);
118 26
        $token = $options['token'];
119
120 26
        unset($options['month'], $options['year'], $options['brand'], $options['token']);
121
122 26
        $this->setProperties($month, $year, $token, $options);
123
    }
124
125 26
    private function setProperties(
126
        string $month,
127
        string $year,
128
        string|Token|null $token = null,
129
        array $options = []
130
    ) {
131 26
        foreach ($options as $prop => $value) {
132 26
            $this->$prop = $value;
133
        }
134
135 26
        if (!empty($this->holderName)) {
136 14
            $this->holderName = strtoupper($this->holderName);
137
        }
138 26
        $this->expiryDate = new ExpiryDate($month, $year);
139
140 26
        $this->detectBrand();
141
142 26
        $this->token($token);
143
144 26
        $this->bin = substr($this->number, 0, 6);
145
    }
146
147 26
    private function token(string|Token|null $token): void
148
    {
149 26
        if ($token instanceof Token || $token === null) {
150 23
            $this->token = $token;
151
152 23
            return;
153
        }
154
155 3
        $this->token = new Token($token);
156
    }
157
158
    /**
159
     * @return void
160
     */
161 26
    private function detectBrand(): void
162
    {
163 26
        $detector = new CreditCardDetector();
164
165 26
        $this->brand = $detector->detect($this->number);
166
    }
167
168
    /**
169
     * @param string $prop
170
     * @param mixed $value
171
     * @return CreditCard
172
     */
173 5
    private function with(string $prop, mixed $value): self
174
    {
175 5
        $card = clone $this;
176
177 5
        $card->$prop = $value;
178
179 5
        return $card;
180
    }
181
182
    /**
183
     * Gets the number of card.
184
     *
185
     * @return string
186
     */
187 18
    public function getNumber(): string
188
    {
189 18
        return $this->number;
190
    }
191
192
    /**
193
     * Sets card number.
194
     *
195
     * @param  string $number
196
     * @return CreditCard
197
     */
198 3
    public function withNumber(string $number): self
199
    {
200 3
        $card = $this->with('number', $number);
201 3
        $card->detectBrand();
202 3
        $card->token = null;
203 3
        $card->bin = substr($number, 0, 6);
204
205 3
        return $card;
206
    }
207
208
    /**
209
     * Gets card holder name.
210
     *
211
     * @return string|null
212
     */
213 17
    public function getHolderName(): ?string
214
    {
215 17
        return $this->holderName;
216
    }
217
218
    /**
219
     * Sets card holder name.
220
     *
221
     * @param  string $holderName
222
     * @return CreditCard
223
     */
224 1
    public function withHolderName(string $holderName): self
225
    {
226 1
        $holderName = strtoupper($holderName);
227
228 1
        return $this->with('holderName', $holderName);
229
    }
230
231
    /**
232
     * Gets expiry date card.
233
     *
234
     * @return ExpiryDate
235
     */
236 17
    public function getExpiryDate(): ExpiryDate
237
    {
238 17
        return $this->expiryDate;
239
    }
240
241
    /**
242
     * Sets expiry month of card.
243
     *
244
     * @param  ExpiryDate $expiryDate
245
     * @return CreditCard
246
     */
247 1
    public function withExpiryDate(ExpiryDate $expiryDate): self
248
    {
249 1
        return $this->with('expiryDate', $expiryDate);
250
    }
251
252
    /**
253
     * Gets the brand of card.
254
     *
255
     * @return string
256
     */
257 17
    public function getBrand(): string
258
    {
259 17
        return $this->brand;
260
    }
261
262
    /**
263
     * Sets the brand of card.
264
     *
265
     * @param  string $brand
266
     * @return CreditCard
267
     */
268 1
    public function withBrand(string $brand): self
269
    {
270 1
        return $this->with('brand', $brand);
271
    }
272
273
    /**
274
     * Gets card verification value (cvv).
275
     *
276
     * @return string
277
     */
278 15
    public function getCvv(): string
279
    {
280 15
        return $this->cvv;
281
    }
282
283
    /**
284
     * Sets card verification value.
285
     *
286
     * @param  string $cvv
287
     * @return CreditCard
288
     */
289 1
    public function withCvv(string $cvv): self
290
    {
291 1
        return $this->with('cvv', $cvv);
292
    }
293
294
    /**
295
     * Check if cvv is required for credit card validation.
296
     *
297
     * @return bool
298
     */
299 15
    public function isRequireCvv(): bool
300
    {
301 15
        return $this->requireCvv;
302
    }
303
304
    /**
305
     * Gets referenece token of a credit card.
306
     *
307
     * @return Token|null
308
     */
309 6
    public function getToken(): ?Token
310
    {
311 6
        return $this->token;
312
    }
313
314
    /**
315
     * Sets token value.
316
     *
317
     * @param  Token $token
318
     * @return CreditCard
319
     */
320 2
    public function withToken(Token $token): self
321
    {
322 2
        $card = $this->with('token', $token);
323
324 2
        if (null !== $card->number) {
325 2
            $lastDigits = strlen($card->number) <= 4
326 1
                ? $card->number :
327 1
                substr($card->number, -4);
328 2
            $card->number = "XXXX-XXXX-XXXX-" . $lastDigits;
329
        }
330
331 2
        $card->cvv = '';
332
333 2
        return $card;
334
    }
335
336
    /**
337
     * Checks whether credit card has stored a Token reference or not.
338
     *
339
     * @return bool
340
     */
341 2
    public function hasToken(): bool
342
    {
343 2
        return null !== $this->token;
344
    }
345
346 6
    public function __clone(): void
347
    {
348 6
        if ($this->expiryDate) {
349 6
            $this->expiryDate = clone $this->expiryDate;
350
        }
351
    }
352
353 2
    public function getBin(): string
354
    {
355 2
        return $this->bin;
356
    }
357
358 1
    public function withIssuingBank(string $issuingBank): self
359
    {
360 1
        return $this->with('issuingBank', $issuingBank);
361
    }
362
363 1
    public function getIssuingBank(): string
364
    {
365 1
        return $this->issuingBank;
366
    }
367
368 1
    public function withCountry(string $country): self
369
    {
370 1
        return $this->with('country', $country);
371
    }
372
373 1
    public function getCountry(): string
374
    {
375 1
        return $this->country;
376
    }
377
}
378