diff --git a/AmazonPay/Client.php b/AmazonPay/Client.php index 80dc561..fea25f5 100644 --- a/AmazonPay/Client.php +++ b/AmazonPay/Client.php @@ -11,6 +11,9 @@ require_once 'HttpCurl.php'; require_once 'ClientInterface.php'; require_once 'Regions.php'; +require_once(__DIR__.'/Exception/ClientException.php'); +require_once(__DIR__.'/Exception/ConfigException.php'); +require_once(__DIR__.'/Exception/SignatureException.php'); if (!interface_exists('\Psr\Log\LoggerAwareInterface')) { require_once(__DIR__.'/../Psr/Log/LoggerAwareInterface.php'); } @@ -18,6 +21,10 @@ if (!interface_exists('\Psr\Log\LoggerInterface')) { require_once(__DIR__.'/../Psr/Log/LoggerInterface.php'); } + +use AmazonPay\Exception\ClientException; +use AmazonPay\Exception\ConfigException; +use AmazonPay\Exception\SignatureException; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; @@ -93,10 +100,10 @@ public function __construct($config = null) if (is_array($configArray)) { $this->checkConfigKeys($configArray); } else { - throw new \Exception('$config is of the incorrect type ' . gettype($configArray) . ' and should be of the type array'); + throw new ConfigException('$config is of the incorrect type ' . gettype($configArray) . ' and should be of the type array'); } } else { - throw new \Exception('$config cannot be null.'); + throw new ConfigException('$config cannot be null.'); } } @@ -135,11 +142,11 @@ private function checkIfFileExists($config) if ($jsonError != 0) { $errorMsg = "Error with message - content is not in json format" . $this->getErrorMessageForJsonError($jsonError) . " " . $configArray; - throw new \Exception($errorMsg); + throw new ConfigException($errorMsg); } } else { $errorMsg ='$config is not a Json File path or the Json File was not found in the path provided'; - throw new \Exception($errorMsg); + throw new ConfigException($errorMsg); } return $configArray; } @@ -158,7 +165,7 @@ private function checkConfigKeys($config) if (array_key_exists($key, $this->config)) { $this->config[$key] = $value; } else { - throw new \Exception('Key ' . $key . ' is either not part of the configuration or has incorrect Key name. + throw new ConfigException('Key ' . $key . ' is either not part of the configuration or has incorrect Key name. check the config array key names to match your key names of your config array', 1); } } @@ -201,7 +208,7 @@ public function setSandbox($value) if (is_bool($value)) { $this->config['sandbox'] = $value; } else { - throw new \Exception('sandbox value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value'); + throw new ConfigException('sandbox value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value'); } } @@ -214,7 +221,7 @@ public function setClientId($value) if (!empty($value)) { $this->config['client_id'] = $value; } else { - throw new \Exception('setter value for client ID provided is empty'); + throw new ConfigException('setter value for client ID provided is empty'); } } @@ -227,7 +234,7 @@ public function setAppId($value) if (!empty($value)) { $this->config['app_id'] = $value; } else { - throw new \Exception('setter value for app ID provided is empty'); + throw new ConfigException('setter value for app ID provided is empty'); } } @@ -272,7 +279,7 @@ public function __get($name) if (array_key_exists(strtolower($name), $this->config)) { return $this->config[strtolower($name)]; } else { - throw new \Exception('Key ' . $name . ' is either not a part of the configuration array config or the ' . $name . ' does not match the key name in the config array', 1); + throw new ConfigException('Key ' . $name . ' is either not a part of the configuration array config or the ' . $name . ' does not match the key name in the config array', 1); } } @@ -328,7 +335,7 @@ public function getUserInfo($accessToken) // As long as one of these matches, from a security perspective, we have done our due diligence if (($data->aud != $this->config['client_id']) && ($data->app_id != $this->config['app_id'])) { // The access token does not belong to us - throw new \Exception('The Access Token belongs to neither your Client ID nor App ID'); + throw new ClientException('The Access Token belongs to neither your Client ID nor App ID'); } // Exchange the access token for user profile @@ -363,11 +370,11 @@ private function setParametersAndPost($parameters, $fieldMappings, $requestParam // Ensure that no unexpected type coercions have happened if ($param === 'capture_now' || $param === 'confirm_now' || $param === 'inherit_shipping_address' || $param === 'request_payment_authorization') { if (!is_bool($value)) { - throw new \Exception($param . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value'); + throw new ClientException($param . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value'); } } elseif ($param === 'provider_credit_details' || $param === 'provider_credit_reversal_details' || $param === 'order_item_categories' || $param === 'notification_configuration_list') { if (!is_array($value)) { - throw new \Exception($param . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be an array value'); + throw new ClientException($param . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be an array value'); } } @@ -468,14 +475,14 @@ private function setNotificationConfigurationList($parameters, $configuration) { $configurationIndex = 0; if (!is_array($configuration)) { - throw new \Exception('Notification Configuration List value ' . $configuration . ' is of type ' . gettype($configuration) . ' and should be an array value'); + throw new ClientException('Notification Configuration List value ' . $configuration . ' is of type ' . gettype($configuration) . ' and should be an array value'); } foreach ($configuration as $url => $events) { $configurationIndex = $configurationIndex + 1; $parameters['NotificationConfigurationList.NotificationConfiguration.' . $configurationIndex . '.NotificationUrl'] = $url; $eventIndex = 0; if (!is_array($events)) { - throw new \Exception('Notification Configuration Events value ' . $events . ' is of type ' . gettype($events) . ' and should be an array value'); + throw new ClientException('Notification Configuration Events value ' . $events . ' is of type ' . gettype($events) . ' and should be an array value'); } foreach ($events as $event) { $eventIndex = $eventIndex + 1; @@ -1461,10 +1468,10 @@ public function charge($requestParameters = array()) { $confirmParameters['amazon_billing_agreement_id'] = $requestParameters['amazon_reference_id']; break; default: - throw new \Exception('Invalid Amazon Reference ID'); + throw new ClientException('Invalid Amazon Reference ID'); } } else { - throw new \Exception('key amazon_order_reference_id or amazon_billing_agreement_id is null and is a required parameter'); + throw new ClientException('key amazon_order_reference_id or amazon_billing_agreement_id is null and is a required parameter'); } // Set the other parameters if the values are present @@ -1519,7 +1526,7 @@ private function makeChargeCalls($chargeType, $setParameters, $confirmParameters } if ($oroStatus['State'] != 'Open' && $oroStatus['State'] != 'Draft') { - throw new \Exception('The Order Reference is in the ' . $oroStatus['State'] . " State. It should be in the Draft or Open State"); + throw new ClientException('The Order Reference is in the ' . $oroStatus['State'] . " State. It should be in the Draft or Open State"); } return $response; @@ -1550,13 +1557,13 @@ private function makeChargeCalls($chargeType, $setParameters, $confirmParameters } if ($baStatus['State'] != 'Open' && $baStatus['State'] != 'Draft') { - throw new \Exception('The Billing Agreement is in the ' . $baStatus['State'] . " State. It should be in the Draft or Open State"); + throw new ClientException('The Billing Agreement is in the ' . $baStatus['State'] . " State. It should be in the Draft or Open State"); } return $response; default: - throw new \Exception('Invalid Charge Type'); + throw new ClientException('Invalid Charge Type'); } } @@ -1653,7 +1660,7 @@ private function calculateSignatureAndParametersToString($parameters = array()) // Ensure that no unexpected type coercions have happened if ($key === 'CaptureNow' || $key === 'ConfirmNow' || $key === 'InheritShippingAddress' || $key === 'RequestPaymentAuthorization') { if (!is_bool($value)) { - throw new \Exception($key . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value'); + throw new SignatureException($key . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value'); } } @@ -1721,7 +1728,7 @@ private function signParameters(array $parameters) $parameters['SignatureMethod'] = $algorithm; $stringToSign = $this->calculateStringToSignV2($parameters); } else { - throw new \Exception("Invalid Signature Version specified"); + throw new SignatureException("Invalid Signature Version specified"); } return $this->sign($stringToSign, $algorithm); @@ -1775,7 +1782,7 @@ private function sign($data, $algorithm) } else if ($algorithm === 'HmacSHA256') { $hash = 'sha256'; } else { - throw new \Exception("Non-supported signing method specified"); + throw new SignatureException("Non-supported signing method specified"); } return base64_encode(hash_hmac($hash, $data, $this->config['secret_key'], true)); @@ -1858,7 +1865,7 @@ private function pauseOnRetry($retries, $status) $delay = (int) (pow(4, $retries) * 100000) + 600000; usleep($delay); } else { - throw new \Exception('Error Code: '. $status.PHP_EOL.'Maximum number of retry attempts - '. $retries .' reached'); + throw new ClientException('Error Code: '. $status.PHP_EOL.'Maximum number of retry attempts - '. $retries .' reached'); } } @@ -1881,10 +1888,10 @@ private function createServiceUrl() $this->mwsServiceUrl = 'https://' . $this->mwsEndpointUrl . '/' . $this->modePath . '/' . self::MWS_VERSION; $this->mwsEndpointPath = '/' . $this->modePath . '/' . self::MWS_VERSION; } else { - throw new \Exception($region . ' is not a valid region'); + throw new ClientException($region . ' is not a valid region'); } } else { - throw new \Exception("config['region'] is a required parameter and is not set"); + throw new ClientException("config['region'] is a required parameter and is not set"); } } @@ -1900,10 +1907,10 @@ private function profileEndpointUrl() if (array_key_exists($region, $this->regionMappings) ) { $this->profileEndpoint = 'https://' . $profileEnvt . '.' . $this->profileEndpointUrls[$region]; } else { - throw new \Exception($region . ' is not a valid region'); + throw new ConfigException($region . ' is not a valid region'); } } else { - throw new \Exception("config['region'] is a required parameter and is not set"); + throw new ConfigException("config['region'] is a required parameter and is not set"); } } diff --git a/AmazonPay/Exception/ClientException.php b/AmazonPay/Exception/ClientException.php new file mode 100644 index 0000000..26f1da2 --- /dev/null +++ b/AmazonPay/Exception/ClientException.php @@ -0,0 +1,9 @@ +curlResponseInfo = curl_getinfo($ch); } diff --git a/AmazonPay/IpnHandler.php b/AmazonPay/IpnHandler.php index 3b4df8a..fac6579 100644 --- a/AmazonPay/IpnHandler.php +++ b/AmazonPay/IpnHandler.php @@ -8,12 +8,21 @@ require_once 'HttpCurl.php'; require_once 'IpnHandlerInterface.php'; +require_once(__DIR__.'/Exception/ConfigException.php'); +require_once(__DIR__.'/Exception/SnsHeaderException.php'); +require_once(__DIR__.'/Exception/SnsMessageException.php'); +require_once(__DIR__.'/Exception/SnsSignatureException.php'); if (!interface_exists('\Psr\Log\LoggerAwareInterface')) { require_once(__DIR__.'/../Psr/Log/LoggerAwareInterface.php'); } if (!interface_exists('\Psr\Log\LoggerInterface')) { require_once(__DIR__.'/../Psr/Log/LoggerInterface.php'); } + +use AmazonPay\Exception\ConfigException; +use AmazonPay\Exception\SnsHeaderException; +use AmazonPay\Exception\SnsMessageException; +use AmazonPay\Exception\SnsSignatureException; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; @@ -80,7 +89,7 @@ private function checkConfigKeys($ipnConfig) if (array_key_exists($key, $this->ipnConfig)) { $this->ipnConfig[$key] = $value; } else { - throw new \Exception('Key ' . $key . ' is either not part of the configuration or has incorrect Key name. + throw new ConfigException('Key ' . $key . ' is either not part of the configuration or has incorrect Key name. check the ipnConfig array key names to match your key names of your config array ', 1); } } @@ -107,7 +116,7 @@ public function __set($name, $value) if (array_key_exists(strtolower($name), $this->ipnConfig)) { $this->ipnConfig[$name] = $value; } else { - throw new \Exception("Key " . $name . " is not part of the configuration", 1); + throw new ConfigException("Key " . $name . " is not part of the configuration", 1); } } @@ -120,7 +129,7 @@ public function __get($name) if (array_key_exists(strtolower($name), $this->ipnConfig)) { return $this->ipnConfig[$name]; } else { - throw new \Exception("Key " . $name . " was not found in the configuration", 1); + throw new ConfigException("Key " . $name . " was not found in the configuration", 1); } } @@ -139,11 +148,11 @@ private function validateHeaders() { // Quickly check that this is a sns message if (!array_key_exists('x-amz-sns-message-type', $this->headers)) { - throw new \Exception("Error with message - header " . "does not contain x-amz-sns-message-type header"); + throw new SnsHeaderException("Error with message - header " . "does not contain x-amz-sns-message-type header"); } if ($this->headers['x-amz-sns-message-type'] !== 'Notification') { - throw new \Exception("Error with message - header x-amz-sns-message-type is not " . "Notification, is " . $this->headers['x-amz-sns-message-type']); + throw new SnsHeaderException("Error with message - header x-amz-sns-message-type is not " . "Notification, is " . $this->headers['x-amz-sns-message-type']); } } @@ -155,7 +164,7 @@ private function getMessage() if ($json_error != 0) { $errorMsg = "Error with message - content is not in json format" . $this->getErrorMessageForJsonError($json_error) . " " . $this->snsMessage; - throw new \Exception($errorMsg); + throw new SnsMessageException($errorMsg); } } @@ -198,11 +207,11 @@ private function checkForCorrectMessageType() { $type = $this->getMandatoryField("Type"); if (strcasecmp($type, "Notification") != 0) { - throw new \Exception("Error with SNS Notification - unexpected message with Type of " . $type); + throw new SnsMessageException("Error with SNS Notification - unexpected message with Type of " . $type); } if (strcmp($this->getMandatoryField("Type"), "Notification") != 0) { - throw new \Exception("Error with signature verification - unable to verify " . $this->getMandatoryField("Type") . " message"); + throw new SnsMessageException("Error with signature verification - unable to verify " . $this->getMandatoryField("Type") . " message"); } else { // Sort the fields into byte order based on the key name(A-Za-z) @@ -246,7 +255,7 @@ private function validateUrl($url) || substr($url, -4) !== '.pem' || !preg_match($this->defaultHostPattern, $parsed['host']) ) { - throw new \Exception( + throw new SnsSignatureException( 'The certificate is located on an invalid domain.' ); } @@ -272,7 +281,7 @@ private function constructAndVerifySignature() $result = $this->verifySignatureIsCorrectFromCertificate($signature); if (!$result) { - throw new \Exception("Unable to match signature from remote server: signature of " . $this->getCertificate($certificatePath) . " , SigningCertURL of " . $this->getMandatoryField("SigningCertURL") . " , SignatureOf " . $this->getMandatoryField("Signature")); + throw new SnsSignatureException("Unable to match signature from remote server: signature of " . $this->getCertificate($certificatePath) . " , SigningCertURL of " . $this->getMandatoryField("SigningCertURL") . " , SignatureOf " . $this->getMandatoryField("Signature")); } } @@ -301,7 +310,7 @@ public function verifySignatureIsCorrectFromCertificate($signature) $certKey = openssl_get_publickey($this->certificate); if ($certKey === False) { - throw new \Exception("Unable to extract public key from cert"); + throw new SnsSignatureException("Unable to extract public key from cert"); } try { @@ -309,21 +318,21 @@ public function verifySignatureIsCorrectFromCertificate($signature) $certSubject = $certInfo["subject"]; if (is_null($certSubject)) { - throw new \Exception("Error with certificate - subject cannot be found"); + throw new SnsSignatureException("Error with certificate - subject cannot be found"); } } catch (\Exception $ex) { - throw new \Exception("Unable to verify certificate - error with the certificate subject", null, $ex); + throw new SnsSignatureException("Unable to verify certificate - error with the certificate subject", null, $ex); } if (strcmp($certSubject["CN"], $this->expectedCnName)) { - throw new \Exception("Unable to verify certificate issued by Amazon - error with certificate subject"); + throw new SnsSignatureException("Unable to verify certificate issued by Amazon - error with certificate subject"); } $result = -1; try { $result = openssl_verify($this->signatureFields, $signature, $certKey, OPENSSL_ALGO_SHA1); } catch (\Exception $ex) { - throw new \Exception("Unable to verify signature - error with the verification algorithm", null, $ex); + throw new SnsSignatureException("Unable to verify signature - error with the verification algorithm", null, $ex); } return ($result > 0); @@ -343,7 +352,7 @@ private function getMandatoryField($fieldName) { $value = $this->getField($fieldName); if (is_null($value)) { - throw new \Exception("Error with json message - mandatory field " . $fieldName . " cannot be found"); + throw new SnsMessageException("Error with json message - mandatory field " . $fieldName . " cannot be found"); } return $value; }