diff --git a/README.md b/README.md index 83c4df2..a56c93a 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ Receive incoming parameters from gateway and verifying signature. ```php getSignaturePayment()->verify('signature_from_post_params', ['all_parameters_from_post'])) { +if (! $uniteller->verifyCallbackRequest(['all_parameters_from_post_with_signature'])) { return 'invalid_signature'; } ``` diff --git a/README_RU.md b/README_RU.md index fbc8d38..0bfae99 100644 --- a/README_RU.md +++ b/README_RU.md @@ -172,11 +172,11 @@ var_dump($results); ### Callback -Приём данных от шлюза и проверка сигнатуры. +Проверка сигнатуры при приёме данных от шлюза. ```php getSignaturePayment()->verify('signature_from_post_params', ['all_parameters_from_post'])) { +if (! $uniteller->verifyCallbackRequest(['all_parameters_from_post_with_signature'])) { return 'invalid_signature'; } ``` diff --git a/src/Client.php b/src/Client.php index 209ab6e..a4f0742 100644 --- a/src/Client.php +++ b/src/Client.php @@ -17,6 +17,7 @@ use Tmconsulting\Uniteller\Recurrent\RecurrentRequest; use Tmconsulting\Uniteller\Request\RequestInterface; use Tmconsulting\Uniteller\Results\ResultsRequest; +use Tmconsulting\Uniteller\Signature\SignatureCallback; use Tmconsulting\Uniteller\Signature\SignatureInterface; use Tmconsulting\Uniteller\Signature\SignaturePayment; use Tmconsulting\Uniteller\Signature\SignatureRecurrent; @@ -50,6 +51,11 @@ class Client implements ClientInterface */ protected $signatureRecurrent; + /** + * @var SignatureInterface + */ + protected $signatureCallback; + /** * @var RequestInterface */ @@ -81,6 +87,7 @@ public function __construct() $this->registerRecurrentRequest(new RecurrentRequest()); $this->registerSignaturePayment(new SignaturePayment()); $this->registerSignatureRecurrent(new SignatureRecurrent()); + $this->registerSignatureCallback(new SignatureCallback()); } /** @@ -215,6 +222,17 @@ public function registerSignatureRecurrent(SignatureInterface $signature) return $this; } + /** + * @param \Tmconsulting\Uniteller\Signature\SignatureInterface $signature + * @return $this + */ + public function registerSignatureCallback(SignatureInterface $signature) + { + $this->signatureCallback = $signature; + + return $this; + } + /** * @return array */ @@ -317,6 +335,14 @@ public function getSignatureRecurrent() return $this->signatureRecurrent; } + /** + * @return \Tmconsulting\Uniteller\Signature\SignatureInterface + */ + public function getSignatureCallback() + { + return $this->signatureCallback; + } + /** * @return \Tmconsulting\Uniteller\Http\HttpManagerInterface */ @@ -458,4 +484,20 @@ private function getParameters($parameters) return $parameters; } + + /** + * Verify signature when Client will be send callback request. + * + * @param array $params + * @return bool + */ + public function verifyCallbackRequest(array $params) + { + return $this->signatureCallback + ->setOrderId(array_get($params, 'Order_ID')) + ->setStatus(array_get($params, 'Status')) + ->setFields(array_except($params, ['Order_ID', 'Status', 'Signature'])) + ->setPassword($this->getPassword()) + ->verify(array_get($params, 'Signature')); + } } \ No newline at end of file diff --git a/src/ClientInterface.php b/src/ClientInterface.php index a7f66df..288897d 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -52,4 +52,12 @@ public function confirm($parameters); * @return mixed */ public function card($parameters); -} \ No newline at end of file + + /** + * Verify signature when Client will be send callback request. + * + * @param array $params + * @return bool + */ + public function verifyCallbackRequest(array $params); +} diff --git a/src/Signature/AbstractSignature.php b/src/Signature/AbstractSignature.php index 43f5cec..4fcc294 100644 --- a/src/Signature/AbstractSignature.php +++ b/src/Signature/AbstractSignature.php @@ -19,7 +19,7 @@ abstract class AbstractSignature implements SignatureInterface, ArraybleInterfac { /** - * Create signature for send payment request. + * Create signature * * @return string */ @@ -33,14 +33,13 @@ public function create() } /** - * Verify signature when Client will be send callback request. + * Verify signature * - * @param $signature - * @param array $params + * @param string $signature * @return bool */ - public function verify($signature, array $params) + public function verify($signature) { - return strtoupper(md5(join('', $params))) === $signature; + return $this->create() === $signature; } } \ No newline at end of file diff --git a/src/Signature/SignatureCallback.php b/src/Signature/SignatureCallback.php new file mode 100644 index 0000000..b16a506 --- /dev/null +++ b/src/Signature/SignatureCallback.php @@ -0,0 +1,137 @@ +orderId = $orderId; + + return $this; + } + + /** + * @param $status + * @return SignatureCallback + */ + public function setStatus($status) + { + $this->status = $status; + + return $this; + } + + /** + * @param array $fields + * @return SignatureCallback + */ + public function setFields($fields) + { + $this->fields = $fields; + + return $this; + } + + /** + * @param string $password + * @return SignatureCallback + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * @return string + */ + public function getOrderId() + { + return $this->orderId; + } + + /** + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * @return array + */ + public function getFields() + { + return $this->fields; + } + + /** + * @return string + */ + public function getPassword() + { + return $this->password; + } + + /** + * @return array + */ + public function toArray() + { + $array = []; + $array['Order_ID'] = $this->getOrderId(); + $array['Status'] = $this->getStatus(); + $array = array_merge($array, $this->getFields()); + $array['Password'] = $this->getPassword(); + + return $array; + } + + /** + * Create signature + * + * @return string + */ + public function create() + { + return strtoupper(md5(join('', $this->toArray()))); + } +} \ No newline at end of file diff --git a/src/Signature/SignatureInterface.php b/src/Signature/SignatureInterface.php index ed949b0..3f96abd 100644 --- a/src/Signature/SignatureInterface.php +++ b/src/Signature/SignatureInterface.php @@ -15,18 +15,24 @@ interface SignatureInterface { /** - * Create signature for send payment request. + * Create signature * * @return string */ public function create(); /** - * Verify signature when Client will be send callback request. + * Array params signature * - * @param $signature - * @param array $params + * @return array + */ + public function toArray(); + + /** + * Verify signature + * + * @param string $signature * @return bool */ - public function verify($signature, array $params); + public function verify($signature); } diff --git a/src/helpers.php b/src/helpers.php index ad2a671..a8bba27 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -64,4 +64,18 @@ function csv_to_array($string, $isFlat = false) } return $data; +} + +/** + * @param $array + * @param array $excludeKeys + * @return mixed + */ +function array_except($array, array $excludeKeys) +{ + foreach ($excludeKeys as $key) { + unset($array[$key]); + } + + return $array; } \ No newline at end of file diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 844d5b9..0c20715 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -241,6 +241,33 @@ public function testShouldBeActionsAcceptClassesWhichImplementArraybleInterface( $client->{$methodName}($arrayble); } + public function testCallbackSignatureVerifying() + { + $params = [ + 'Order_ID' => 'FOO', + 'Status' => 'paid', + 'Signature' => '3F728AA479E50F5B10EE6C20258BFF88', + ]; + $client = new Client(); + $client->setPassword('LONG-PWD'); + $this->assertTrue($client->verifyCallbackRequest($params)); + } + + public function testCallbackSignatureVerifyingWithFields() + { + $params = [ + 'Order_ID' => 'FOO', + 'Status' => 'paid', + 'AcquirerID' => 'fOO', + 'ApprovalCode' => 'BaR', + 'BillNumber' => 'baz', + 'Signature' => '1F4E3B63AE408D0BE1E33965E6697236', + ]; + $client = new Client(); + $client->setPassword('LONG-PWD'); + $this->assertTrue($client->verifyCallbackRequest($params)); + } + } class HttpManagerStub implements HttpManagerInterface { diff --git a/tests/Signature/SignatureTest.php b/tests/Signature/SignatureTest.php index 8e550e4..1a318f1 100644 --- a/tests/Signature/SignatureTest.php +++ b/tests/Signature/SignatureTest.php @@ -7,6 +7,7 @@ namespace Tmconsulting\Uniteller\Tests\Signature; +use Tmconsulting\Uniteller\Signature\SignatureCallback; use Tmconsulting\Uniteller\Signature\SignaturePayment; use Tmconsulting\Uniteller\Signature\SignatureRecurrent; use Tmconsulting\Uniteller\Tests\TestCase; @@ -27,7 +28,6 @@ public function testPaymentSignatureCreation() $this->assertSame('3D1D6F830384886A81AD672F66392B03', $sig); } - public function testRecurrentSignatureCreation() { $sig = (new SignatureRecurrent()) @@ -41,16 +41,80 @@ public function testRecurrentSignatureCreation() $this->assertSame('A5FE1C95A2819EBACFC2145EE83742F6', $sig); } - public function testSignatureVerifying() + public function testCallbackSignatureCreation() + { + $sig = (new SignatureCallback()) + ->setOrderId('FOO') + ->setStatus('paid') + ->setPassword('LONG-PWD') + ->create(); + + $this->assertSame('3F728AA479E50F5B10EE6C20258BFF88', $sig); + } + + public function testCallbackSignatureCreationWithFields() + { + $sig = (new SignatureCallback()) + ->setOrderId('FOO') + ->setStatus('paid') + ->setFields([ + 'AcquirerID' => 'fOO', + 'ApprovalCode' => 'BaR', + 'BillNumber' => 'baz', + ]) + ->setPassword('LONG-PWD') + ->create(); + + $this->assertSame('1F4E3B63AE408D0BE1E33965E6697236', $sig); + } + + public function testPaymentSignatureVerifying() + { + $sig = (new SignaturePayment()) + ->setShopIdp('ACME') + ->setOrderIdp('FOO') + ->setSubtotalP(100) + ->setLifeTime(300) + ->setCustomerIdp('short_shop_string') + ->setPassword('LONG-PWD'); + + $this->assertTrue($sig->verify('3D1D6F830384886A81AD672F66392B03')); + } + + public function testRecurrentSignatureVerifying() { - $sig = new SignaturePayment; + $sig = (new SignatureRecurrent()) + ->setShopIdp('ACME') + ->setOrderIdp('FOO') + ->setSubtotalP(100) + ->setParentOrderIdp('BAR') + ->setPassword('LONG-PWD'); + + $this->assertTrue($sig->verify('A5FE1C95A2819EBACFC2145EE83742F6')); + } + + public function testCallbackSignatureVerifying() + { + $sig = (new SignatureCallback()) + ->setOrderId('FOO') + ->setStatus('paid') + ->setPassword('LONG-PWD'); + + $this->assertTrue($sig->verify('3F728AA479E50F5B10EE6C20258BFF88')); + } - $results = $sig->verify('3F728AA479E50F5B10EE6C20258BFF88', [ - 'Order_ID' => 'FOO', - 'Status' => 'paid', - 'Password' => 'LONG-PWD', - ]); + public function testCallbackSignatureVerifyingWithFields() + { + $sig = (new SignatureCallback()) + ->setOrderId('FOO') + ->setStatus('paid') + ->setFields([ + 'AcquirerID' => 'fOO', + 'ApprovalCode' => 'BaR', + 'BillNumber' => 'baz', + ]) + ->setPassword('LONG-PWD'); - $this->assertTrue($results); + $this->assertTrue($sig->verify('1F4E3B63AE408D0BE1E33965E6697236')); } } \ No newline at end of file