From 7a8a1c8f022bcc6ec4aa6ceac32715ad432928eb Mon Sep 17 00:00:00 2001 From: Serhii Kolisnyk Date: Fri, 31 Oct 2025 10:04:06 +0200 Subject: [PATCH 1/2] add new field --- src/Envelopes/Builder.php | 31 ++++- src/Envelopes/ValidatorV1.php | 73 +++++++++++- tests/Covery/BuildPayoutEventTest.php | 77 ++++++++++++- tests/Covery/BuildTransactionEventTest.php | 126 ++++++++++++++++++++- 4 files changed, 296 insertions(+), 11 deletions(-) diff --git a/src/Envelopes/Builder.php b/src/Envelopes/Builder.php index 044091f..0008d9f 100644 --- a/src/Envelopes/Builder.php +++ b/src/Envelopes/Builder.php @@ -272,6 +272,7 @@ public static function registrationEvent( * @param string|null $groupId * @param string|null $linksToDocuments * @param array|null $documentId + * @param int|null $cardPan * * @return Builder */ @@ -299,7 +300,8 @@ public static function payoutEvent( $cardExpirationYear = null, $groupId = null, $linksToDocuments = null, - $documentId = null + $documentId = null, + $cardPan = null ) { $builder = new self('payout', $sequenceId); if ($payoutTimestamp === null) { @@ -325,7 +327,8 @@ public static function payoutEvent( ->addShortUserData($email, $userId, $phone, $firstName, $lastName, $country) ->addGroupId($groupId) ->addLinksToDocuments($linksToDocuments) - ->addDocumentData($documentId); + ->addDocumentData($documentId) + ->addCardPan($cardPan); } /** @@ -379,6 +382,7 @@ public static function payoutEvent( * @param string|null $groupId * @param string|null $linksToDocuments * @param array|null $documentId + * @param int|null $cardPan * * @return Builder */ @@ -430,7 +434,8 @@ public static function transactionEvent( $acquirerMerchantId = null, $groupId = null, $linksToDocuments = null, - $documentId = null + $documentId = null, + $cardPan = null ) { $builder = new self('transaction', $sequenceId); if ($transactionTimestamp === null) { @@ -482,8 +487,8 @@ public static function transactionEvent( ->addIpData(null, null, $merchantIp) ->addGroupId($groupId) ->addLinksToDocuments($linksToDocuments) - ->addDocumentData($documentId); - + ->addDocumentData($documentId) + ->addCardPan($cardPan); } /** @@ -4038,6 +4043,22 @@ public function addDocumentData($documentId = null) return $this; } + /** + * Provides card pan value to envelope + * + * @param int|null $cardPan + * @return $this + */ + public function addCardPan($cardPan = null) + { + if ($cardPan !== null && !is_int($cardPan)) { + throw new \InvalidArgumentException('Card Pan must be int'); + } + $this->replace('card_pan', $cardPan); + + return $this; + } + /** * Add links to documents * diff --git a/src/Envelopes/ValidatorV1.php b/src/Envelopes/ValidatorV1.php index cf4bb77..222cb11 100644 --- a/src/Envelopes/ValidatorV1.php +++ b/src/Envelopes/ValidatorV1.php @@ -271,7 +271,8 @@ class ValidatorV1 'text_language_details' => 'array_string', 'translated_extracted_text' => 'string(225)', 'translated_from' => 'string(225)', - 'translated_to' => 'string(225)' + 'translated_to' => 'string(225)', + 'card_pan' => 'int' ); private static $fieldWithZeroAllowed = array( @@ -323,6 +324,8 @@ class ValidatorV1 'client_resolution' ); + private static $cardPanKey = 'card_pan'; + private static $types = array( 'confirmation' => array( 'mandatory' => array('confirmation_timestamp', 'user_merchant_id'), @@ -415,6 +418,7 @@ class ValidatorV1 'group_id', 'links_to_documents', 'document_id', + 'card_pan', ) ), 'payout' => array( @@ -444,6 +448,7 @@ class ValidatorV1 'group_id', 'links_to_documents', 'document_id', + 'card_pan', ) ), 'install' => array( @@ -1212,6 +1217,69 @@ public function analyzeFieldTypes(EnvelopeInterface $envelope) return array(); } + /** + * Analyzes card pan + * + * @param int|null $cardPan + * @return array + */ + public function analyzeCardPan(int $cardPan = null) + { + $details = array(); + if (!empty($cardPan)) { + $numberStr = (string)$cardPan; + + $length = strlen($numberStr); + if ($length < 14) { + $details[] = sprintf( + 'Field "%s" must contain at least 14 digits, but only %d provided (%s)', + self::$cardPanKey, + $length, + $numberStr + ); + } + + if ($length > 19) { + $details[] = sprintf( + 'Field "%s" must not exceed 19 digits, but %d provided (%s)', + self::$cardPanKey, + $length, + $numberStr + ); + } + + if (!empty($details)) { + return $details; + } + + $sum = 0; + $alt = false; + + for ($i = $length - 1; $i >= 0; $i--) { + $n = (int)$numberStr[$i]; + if ($alt) { + $n *= 2; + if ($n > 9) { + $n -= 9; + } + } + $sum += $n; + $alt = !$alt; + } + + if ($sum % 10 !== 0) { + $details[] = sprintf( + 'Field "%s" failed Luhn validation (sum mod 10 = %d)', + self::$cardPanKey, + $sum % 10 + ); + } + return $details; + } + + return array(); + } + /** * Checks envelope validity and throws an exception on error * @@ -1230,7 +1298,8 @@ public function validate(EnvelopeInterface $envelope) $this->analyzeSequenceId($envelope->getSequenceId()), $this->analyzeIdentities($envelope->getIdentities()), $this->analyzeTypeAndMandatoryFields($envelope), - $this->analyzeFieldTypes($envelope) + $this->analyzeFieldTypes($envelope), + $this->analyzeCardPan(!empty($envelope[self::$cardPanKey]) ? $envelope[self::$cardPanKey] : null) ); } diff --git a/tests/Covery/BuildPayoutEventTest.php b/tests/Covery/BuildPayoutEventTest.php index 4cf94b2..ce23c7d 100644 --- a/tests/Covery/BuildPayoutEventTest.php +++ b/tests/Covery/BuildPayoutEventTest.php @@ -33,13 +33,14 @@ public function testBuild() 22, "group id value", 'links to documents', - [1, 2] + [1, 2], + 5555555555554444 )->addBrowserData('88889', 'Test curl')->addIdentity(new \Covery\Client\Identities\Stub())->build(); self::assertSame('payout', $result->getType()); self::assertCount(1, $result->getIdentities()); self::assertSame('someSequenceId', $result->getSequenceId()); - self::assertCount(25, $result); + self::assertCount(26, $result); self::assertSame('fooUserId', $result['user_merchant_id']); self::assertSame(5566, $result['payout_timestamp']); self::assertSame('payoutLargeId', $result['payout_id']); @@ -157,4 +158,76 @@ public function testEventExpectInvalidArgumentExceptionForNegativeAmountConverte -2 )->build(); } + + public function testAnalyzeCardPanThrowsForTooShortCardPan() + { + // Card Pan to short + $validator = new \Covery\Client\Envelopes\ValidatorV1(); + $this->expectException(\Covery\Client\EnvelopeValidationException::class); + $this->expectExceptionMessage("Field \"card_pan\" must contain at least 14 digits, but only 8 provided (55555555)"); + $result = \Covery\Client\Envelopes\Builder::payoutEvent( + 'someSequenceId', + 'fooUserId', + 'payoutLargeId', + 'GBP', + 0.12, + 5566, + 'someCard0001', + 'someAccountId', + 'mtd', + 'sts', + 'midnight', + 23, + 'tony', + 'hawk', + 'zimbabwe', + 'jjj@xx.zzz', + '+323423234', + 123456, + '4445', + 11, + 22, + "group id value", + 'links to documents', + [1, 2], + 55555555 + )->build(); + $validator->validate($result); + } + + public function testAnalyzeCardPanThrowsForBadChecksum() + { + // Card Pan bad checksum + $validator = new \Covery\Client\Envelopes\ValidatorV1(); + $this->expectException(\Covery\Client\EnvelopeValidationException::class); + $this->expectExceptionMessage("Field \"card_pan\" failed Luhn validation (sum mod 10 = 9)"); + $result = \Covery\Client\Envelopes\Builder::payoutEvent( + 'someSequenceId', + 'fooUserId', + 'payoutLargeId', + 'GBP', + 0.12, + 5566, + 'someCard0001', + 'someAccountId', + 'mtd', + 'sts', + 'midnight', + 23, + 'tony', + 'hawk', + 'zimbabwe', + 'jjj@xx.zzz', + '+323423234', + 123456, + '4445', + 11, + 22, + "group id value", + 'links to documents', + [1, 2], + 5555555555554443 + )->build(); + $validator->validate($result); + } } diff --git a/tests/Covery/BuildTransactionEventTest.php b/tests/Covery/BuildTransactionEventTest.php index 30288dd..006a5aa 100644 --- a/tests/Covery/BuildTransactionEventTest.php +++ b/tests/Covery/BuildTransactionEventTest.php @@ -57,7 +57,8 @@ public function testBuild() "acquirer merchant id value", "group id value", 'links to documents', - [1, 2] + [1, 2], + 5555555555554444 ) ->addBrowserData('88889', 'Test curl') ->addIdentity(new \Covery\Client\Identities\Stub()) @@ -67,7 +68,7 @@ public function testBuild() self::assertSame('transaction', $result->getType()); self::assertCount(1, $result->getIdentities()); self::assertSame('someSequenceId', $result->getSequenceId()); - self::assertCount(50, $result); + self::assertCount(51, $result); self::assertSame('fooUserId', $result['user_merchant_id']); self::assertSame('transactionId', $result['transaction_id']); self::assertSame(0.12, $result['transaction_amount']); @@ -116,6 +117,7 @@ public function testBuild() self::assertSame('group id value', $result['group_id']); self::assertSame('links to documents', $result['links_to_documents']); self::assertSame([1, 2], $result['document_id']); + self::assertSame(5555555555554444, $result['card_pan']); $validator->validate($result); @@ -223,4 +225,124 @@ public function testEventExpectInvalidArgumentExceptionForNegativeAmountConverte -2.3 )->build(); } + + public function testAnalyzeCardPanThrowsForTooShortCardPan() + { + // Card Pan to short + $validator = new \Covery\Client\Envelopes\ValidatorV1(); + $this->expectException(\Covery\Client\EnvelopeValidationException::class); + $this->expectExceptionMessage("Field \"card_pan\" must contain at least 14 digits, but only 8 provided (55555555)"); + $result = \Covery\Client\Envelopes\Builder::transactionEvent( + 'someSequenceId', + 'fooUserId', + 'transactionId', + 0.12, + 'GBP', + 123456, + 'mode', + 'type', + 444444, + 'qwef53f12e1s121sd34f', + '1234', + 12, + 2017, + 21, + 'ukr', + 'test@test.com', + 'male', + 'John', + 'Snow', + '380501234567', + 'Lord of north', + 'z1234fcdfd23', + 'method', + 'mid', + 'system', + 0.22, + 'source', + 'castle', + 'Winterfell', + 'Westeros', + 'John', + 'Targarien', + 'John Targarien', + 'north', + '123', + 'Rusted swords', + 'Sword', + 1000, + 'http://example.com', + '127.0.0.1', + 'affiliateId', + 'email campaign', + "merchant country value", + "mcc value", + "acquirer merchant id value", + "group id value", + 'links to documents', + [1, 2], + 55555555 + )->build(); + $validator->validate($result); + } + + public function testAnalyzeCardPanThrowsForBadChecksum() + { + // Card Pan bad checksum + $validator = new \Covery\Client\Envelopes\ValidatorV1(); + $this->expectException(\Covery\Client\EnvelopeValidationException::class); + $this->expectExceptionMessage("Field \"card_pan\" failed Luhn validation (sum mod 10 = 9)"); + $result = \Covery\Client\Envelopes\Builder::transactionEvent( + 'someSequenceId', + 'fooUserId', + 'transactionId', + 0.12, + 'GBP', + 123456, + 'mode', + 'type', + 444444, + 'qwef53f12e1s121sd34f', + '1234', + 12, + 2017, + 21, + 'ukr', + 'test@test.com', + 'male', + 'John', + 'Snow', + '380501234567', + 'Lord of north', + 'z1234fcdfd23', + 'method', + 'mid', + 'system', + 0.22, + 'source', + 'castle', + 'Winterfell', + 'Westeros', + 'John', + 'Targarien', + 'John Targarien', + 'north', + '123', + 'Rusted swords', + 'Sword', + 1000, + 'http://example.com', + '127.0.0.1', + 'affiliateId', + 'email campaign', + "merchant country value", + "mcc value", + "acquirer merchant id value", + "group id value", + 'links to documents', + [1, 2], + 5555555555554443 + )->build(); + $validator->validate($result); + } } From c345b55b8ecac0834d5e2450063e464c6f4a9223 Mon Sep 17 00:00:00 2001 From: Serhii Kolisnyk Date: Wed, 12 Nov 2025 12:50:46 +0200 Subject: [PATCH 2/2] fix readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ae4cb97..6ec10aa 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,6 @@ You may provide the following as envelopes: ## Changelog * `1.5.4` * Added optional `card_pan` field for transaction, payout events -* `1.5.2` * `1.5.3` * Added optional `deepfake` and `deepfake_confidence` fields for document event * `1.5.2`