diff --git a/lib/Ingenico/Connect/Sdk/Communicator.php b/lib/Ingenico/Connect/Sdk/Communicator.php index e6a58c7f..c2ec289e 100644 --- a/lib/Ingenico/Connect/Sdk/Communicator.php +++ b/lib/Ingenico/Connect/Sdk/Communicator.php @@ -59,6 +59,7 @@ public function disableLogging() * @param string $clientMetaInfo * @param RequestObject|null $requestParameters * @param CallContext $callContext + * @param ResponseExceptionFactory|null $responseExceptionFactory * @return DataObject * @throws Exception */ @@ -67,7 +68,8 @@ public function get( $relativeUriPath, $clientMetaInfo = '', RequestObject $requestParameters = null, - CallContext $callContext = null + CallContext $callContext = null, + ResponseExceptionFactory $responseExceptionFactory = null ) { $relativeUriPathWithRequestParameters = $this->getRelativeUriPathWithRequestParameters($relativeUriPath, $requestParameters); @@ -93,7 +95,10 @@ public function get( $this->getResponseFactory()->createResponse($connectionResponse, $responseClassMap); $httpStatusCode = $connectionResponse->getHttpStatusCode(); if ($httpStatusCode >= 400) { - throw $this->getResponseExceptionFactory()->createException($httpStatusCode, $response, $callContext); + if (is_null($responseExceptionFactory)) { + $responseExceptionFactory = $this->getResponseExceptionFactory(); + } + throw $responseExceptionFactory->createException($httpStatusCode, $response, $callContext); } return $response; } @@ -105,6 +110,7 @@ public function get( * @param RequestObject|null $requestParameters * @param callable $bodyHandler Callable accepting a response body chunk and the response headers * @param CallContext $callContext + * @param ResponseExceptionFactory|null $responseExceptionFactory * @throws Exception */ public function getWithBinaryResponse( @@ -113,7 +119,8 @@ public function getWithBinaryResponse( $clientMetaInfo = '', RequestObject $requestParameters = null, callable $bodyHandler = null, - CallContext $callContext = null + CallContext $callContext = null, + ResponseExceptionFactory $responseExceptionFactory = null ) { $relativeUriPathWithRequestParameters = $this->getRelativeUriPathWithRequestParameters($relativeUriPath, $requestParameters); @@ -143,7 +150,10 @@ public function getWithBinaryResponse( if ($httpStatusCode >= 400) { $response = $this->getResponseFactory()->createResponse($connectionResponse, $responseClassMap); - throw $this->getResponseExceptionFactory()->createException($httpStatusCode, $response, $callContext); + if (is_null($responseExceptionFactory)) { + $responseExceptionFactory = $this->getResponseExceptionFactory(); + } + throw $responseExceptionFactory->createException($httpStatusCode, $response, $callContext); } } @@ -153,6 +163,7 @@ public function getWithBinaryResponse( * @param string $clientMetaInfo * @param RequestObject|null $requestParameters * @param CallContext $callContext + * @param ResponseExceptionFactory|null $responseExceptionFactory * @return DataObject * @throws Exception */ @@ -161,7 +172,8 @@ public function delete( $relativeUriPath, $clientMetaInfo = '', RequestObject $requestParameters = null, - CallContext $callContext = null + CallContext $callContext = null, + ResponseExceptionFactory $responseExceptionFactory = null ) { $relativeUriPathWithRequestParameters = $this->getRelativeUriPathWithRequestParameters($relativeUriPath, $requestParameters); @@ -187,7 +199,10 @@ public function delete( $this->getResponseFactory()->createResponse($connectionResponse, $responseClassMap); $httpStatusCode = $connectionResponse->getHttpStatusCode(); if ($httpStatusCode >= 400) { - throw $this->getResponseExceptionFactory()->createException($httpStatusCode, $response, $callContext); + if (is_null($responseExceptionFactory)) { + $responseExceptionFactory = $this->getResponseExceptionFactory(); + } + throw $responseExceptionFactory->createException($httpStatusCode, $response, $callContext); } return $response; } @@ -199,6 +214,7 @@ public function delete( * @param RequestObject|null $requestParameters * @param callable $bodyHandler Callable accepting a response body chunk and the response headers * @param CallContext $callContext + * @param ResponseExceptionFactory|null $responseExceptionFactory * @throws Exception */ public function deleteWithBinaryResponse( @@ -207,7 +223,8 @@ public function deleteWithBinaryResponse( $clientMetaInfo = '', RequestObject $requestParameters = null, callable $bodyHandler = null, - CallContext $callContext = null + CallContext $callContext = null, + ResponseExceptionFactory $responseExceptionFactory = null ) { $relativeUriPathWithRequestParameters = $this->getRelativeUriPathWithRequestParameters($relativeUriPath, $requestParameters); @@ -237,7 +254,10 @@ public function deleteWithBinaryResponse( if ($httpStatusCode >= 400) { $response = $this->getResponseFactory()->createResponse($connectionResponse, $responseClassMap); - throw $this->getResponseExceptionFactory()->createException($httpStatusCode, $response, $callContext); + if (is_null($responseExceptionFactory)) { + $responseExceptionFactory = $this->getResponseExceptionFactory(); + } + throw $responseExceptionFactory->createException($httpStatusCode, $response, $callContext); } } @@ -248,6 +268,7 @@ public function deleteWithBinaryResponse( * @param DataObject|MultipartDataObject|null $requestBodyObject * @param RequestObject|null $requestParameters * @param CallContext $callContext + * @param ResponseExceptionFactory|null $responseExceptionFactory * @return DataObject * @throws Exception */ @@ -257,7 +278,8 @@ public function post( $clientMetaInfo = '', $requestBodyObject = null, RequestObject $requestParameters = null, - CallContext $callContext = null + CallContext $callContext = null, + ResponseExceptionFactory $responseExceptionFactory = null ) { $relativeUriPathWithRequestParameters = $this->getRelativeUriPathWithRequestParameters($relativeUriPath, $requestParameters); @@ -297,7 +319,10 @@ public function post( $this->getResponseFactory()->createResponse($connectionResponse, $responseClassMap); $httpStatusCode = $connectionResponse->getHttpStatusCode(); if ($httpStatusCode >= 400) { - throw $this->getResponseExceptionFactory()->createException($httpStatusCode, $response, $callContext); + if (is_null($responseExceptionFactory)) { + $responseExceptionFactory = $this->getResponseExceptionFactory(); + } + throw $responseExceptionFactory->createException($httpStatusCode, $response, $callContext); } return $response; } @@ -310,6 +335,7 @@ public function post( * @param RequestObject|null $requestParameters * @param callable $bodyHandler Callable accepting a response body chunk and the response headers * @param CallContext $callContext + * @param ResponseExceptionFactory|null $responseExceptionFactory * @throws Exception */ public function postWithBinaryResponse( @@ -319,7 +345,8 @@ public function postWithBinaryResponse( $requestBodyObject = null, RequestObject $requestParameters = null, callable $bodyHandler = null, - CallContext $callContext = null + CallContext $callContext = null, + ResponseExceptionFactory $responseExceptionFactory = null ) { $relativeUriPathWithRequestParameters = $this->getRelativeUriPathWithRequestParameters($relativeUriPath, $requestParameters); @@ -363,7 +390,10 @@ public function postWithBinaryResponse( if ($httpStatusCode >= 400) { $response = $this->getResponseFactory()->createResponse($connectionResponse, $responseClassMap); - throw $this->getResponseExceptionFactory()->createException($httpStatusCode, $response, $callContext); + if (is_null($responseExceptionFactory)) { + $responseExceptionFactory = $this->getResponseExceptionFactory(); + } + throw $responseExceptionFactory->createException($httpStatusCode, $response, $callContext); } } @@ -374,6 +404,7 @@ public function postWithBinaryResponse( * @param DataObject|MultipartDataObject|null $requestBodyObject * @param RequestObject|null $requestParameters * @param CallContext $callContext + * @param ResponseExceptionFactory|null $responseExceptionFactory * @return DataObject * @throws Exception */ @@ -383,7 +414,8 @@ public function put( $clientMetaInfo = '', $requestBodyObject = null, RequestObject $requestParameters = null, - CallContext $callContext = null + CallContext $callContext = null, + ResponseExceptionFactory $responseExceptionFactory = null ) { $relativeUriPathWithRequestParameters = $this->getRelativeUriPathWithRequestParameters($relativeUriPath, $requestParameters); @@ -423,7 +455,10 @@ public function put( $this->getResponseFactory()->createResponse($connectionResponse, $responseClassMap); $httpStatusCode = $connectionResponse->getHttpStatusCode(); if ($httpStatusCode >= 400) { - throw $this->getResponseExceptionFactory()->createException($httpStatusCode, $response, $callContext); + if (is_null($responseExceptionFactory)) { + $responseExceptionFactory = $this->getResponseExceptionFactory(); + } + throw $responseExceptionFactory->createException($httpStatusCode, $response, $callContext); } return $response; } @@ -436,6 +471,7 @@ public function put( * @param RequestObject|null $requestParameters * @param callable $bodyHandler Callable accepting a response body chunk and the response headers * @param CallContext $callContext + * @param ResponseExceptionFactory|null $responseExceptionFactory * @throws Exception */ public function putWithBinaryResponse( @@ -445,7 +481,8 @@ public function putWithBinaryResponse( $requestBodyObject = null, RequestObject $requestParameters = null, callable $bodyHandler = null, - CallContext $callContext = null + CallContext $callContext = null, + ResponseExceptionFactory $responseExceptionFactory = null ) { $relativeUriPathWithRequestParameters = $this->getRelativeUriPathWithRequestParameters($relativeUriPath, $requestParameters); @@ -489,7 +526,10 @@ public function putWithBinaryResponse( if ($httpStatusCode >= 400) { $response = $this->getResponseFactory()->createResponse($connectionResponse, $responseClassMap); - throw $this->getResponseExceptionFactory()->createException($httpStatusCode, $response, $callContext); + if (is_null($responseExceptionFactory)) { + $responseExceptionFactory = $this->getResponseExceptionFactory(); + } + throw $responseExceptionFactory->createException($httpStatusCode, $response, $callContext); } } diff --git a/lib/Ingenico/Connect/Sdk/RequestHeaderGenerator.php b/lib/Ingenico/Connect/Sdk/RequestHeaderGenerator.php index 1f60d281..5aa93a6d 100644 --- a/lib/Ingenico/Connect/Sdk/RequestHeaderGenerator.php +++ b/lib/Ingenico/Connect/Sdk/RequestHeaderGenerator.php @@ -11,7 +11,7 @@ */ class RequestHeaderGenerator { - const SDK_VERSION = '6.45.0'; + const SDK_VERSION = '6.46.0'; const AUTHORIZATION_ID = 'GCS'; diff --git a/lib/Ingenico/Connect/Sdk/Webhooks/SignatureValidator.php b/lib/Ingenico/Connect/Sdk/Webhooks/SignatureValidator.php new file mode 100644 index 00000000..06ea545e --- /dev/null +++ b/lib/Ingenico/Connect/Sdk/Webhooks/SignatureValidator.php @@ -0,0 +1,76 @@ +secretKeyStore = $secretKeyStore; + } + + /** + * Validates the given body using the given request headers. + * @param string $body + * @param array $requestHeaders + * @throws SignatureValidationException + */ + public function validate($body, $requestHeaders) + { + $this->validateBody($body, $requestHeaders); + } + + // utility methods + + private function validateBody($body, $requestHeaders) + { + $signature = $this->getHeaderValue($requestHeaders, 'X-GCS-Signature'); + $keyId = $this->getHeaderValue($requestHeaders, 'X-GCS-KeyId'); + $secretKey = $this->secretKeyStore->getSecretKey($keyId); + + $expectedSignature = base64_encode(hash_hmac("sha256", $body, $secretKey, true)); + + $isValid = $this->areEqualSignatures($signature, $expectedSignature); + if (!$isValid) { + throw new SignatureValidationException("failed to validate signature '$signature'"); + } + } + + private function areEqualSignatures($signature, $expectedSignature) { + if (function_exists('hash_equals')) { + return hash_equals($expectedSignature, $signature); + } else { + // copied from http://php.net/manual/en/function.hash-equals.php#115635 + if(strlen($expectedSignature) != strlen($signature)) { + return false; + } else { + $res = $expectedSignature ^ $signature; + $ret = 0; + for($i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]); + return !$ret; + } + } + } + + // general utility methods + + private function getHeaderValue($requestHeaders, $headerName) { + $lowerCaseHeaderName = strtolower($headerName); + foreach ($requestHeaders as $name => $value) { + if ($lowerCaseHeaderName === strtolower($name)) { + return $value; + } + } + throw new SignatureValidationException("could not find header '$headerName'"); + } +} diff --git a/lib/Ingenico/Connect/Sdk/Webhooks/WebhooksHelper.php b/lib/Ingenico/Connect/Sdk/Webhooks/WebhooksHelper.php index fe612c96..1de7d54a 100644 --- a/lib/Ingenico/Connect/Sdk/Webhooks/WebhooksHelper.php +++ b/lib/Ingenico/Connect/Sdk/Webhooks/WebhooksHelper.php @@ -14,9 +14,15 @@ */ class WebhooksHelper { - /** @var SecretKeyStore */ + /** + * @var SecretKeyStore + * @deprecated Will be removed in a future version + */ protected $secretKeyStore; + /** @var SignatureValidator */ + private $signatureValidator; + /** @var ResponseFactory|null */ private $responseFactory = null; @@ -26,6 +32,7 @@ class WebhooksHelper public function __construct(SecretKeyStore $secretKeyStore) { $this->secretKeyStore = $secretKeyStore; + $this->signatureValidator = new SignatureValidator($secretKeyStore); } /** @return ResponseFactory */ @@ -67,39 +74,7 @@ public function unmarshal($body, $requestHeaders) */ protected function validate($body, $requestHeaders) { - $this->validateBody($body, $requestHeaders); - } - - // validation utility methods - - private function validateBody($body, $requestHeaders) - { - $signature = $this->getHeaderValue($requestHeaders, 'X-GCS-Signature'); - $keyId = $this->getHeaderValue($requestHeaders, 'X-GCS-KeyId'); - $secretKey = $this->secretKeyStore->getSecretKey($keyId); - - $expectedSignature = base64_encode(hash_hmac("sha256", $body, $secretKey, true)); - - $isValid = $this->areEqualSignatures($signature, $expectedSignature); - if (!$isValid) { - throw new SignatureValidationException("failed to validate signature '$signature'"); - } - } - - private function areEqualSignatures($signature, $expectedSignature) { - if (function_exists('hash_equals')) { - return hash_equals($expectedSignature, $signature); - } else { - // copied from http://php.net/manual/en/function.hash-equals.php#115635 - if(strlen($expectedSignature) != strlen($signature)) { - return false; - } else { - $res = $expectedSignature ^ $signature; - $ret = 0; - for($i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]); - return !$ret; - } - } + $this->signatureValidator->validate($body, $requestHeaders); } // general utility methods @@ -110,14 +85,4 @@ private function validateApiVersion($event) throw new ApiVersionMismatchException($event->apiVersion, Client::API_VERSION); } } - - private function getHeaderValue($requestHeaders, $headerName) { - $lowerCaseHeaderName = strtolower($headerName); - foreach ($requestHeaders as $name => $value) { - if ($lowerCaseHeaderName === strtolower($name)) { - return $value; - } - } - throw new SignatureValidationException("could not find header '$headerName'"); - } } diff --git a/src/Ingenico/Connect/Sdk/Client.php b/src/Ingenico/Connect/Sdk/Client.php index ef2364ca..f7f54f71 100644 --- a/src/Ingenico/Connect/Sdk/Client.php +++ b/src/Ingenico/Connect/Sdk/Client.php @@ -1,7 +1,7 @@ acquirerPromotionCode)) { + $object->acquirerPromotionCode = $this->acquirerPromotionCode; + } if (!is_null($this->authorizationMode)) { $object->authorizationMode = $this->authorizationMode; } @@ -145,6 +153,9 @@ public function toObject() public function fromObject($object) { parent::fromObject($object); + if (property_exists($object, 'acquirerPromotionCode')) { + $this->acquirerPromotionCode = $object->acquirerPromotionCode; + } if (property_exists($object, 'authorizationMode')) { $this->authorizationMode = $object->authorizationMode; } diff --git a/src/Ingenico/Connect/Sdk/Domain/Payment/Definitions/AbstractCashPaymentMethodSpecificInput.php b/src/Ingenico/Connect/Sdk/Domain/Payment/Definitions/AbstractCashPaymentMethodSpecificInput.php index db776d2b..80fa27b3 100644 --- a/src/Ingenico/Connect/Sdk/Domain/Payment/Definitions/AbstractCashPaymentMethodSpecificInput.php +++ b/src/Ingenico/Connect/Sdk/Domain/Payment/Definitions/AbstractCashPaymentMethodSpecificInput.php @@ -1,7 +1,7 @@