null, 'secret_key' => null, 'access_key' => null, 'region' => null, 'currency_code' => null, 'sandbox' => false, 'platform_id' => null, 'cabundle_file' => null, 'application_name' => null, 'application_version' => null, 'proxy_host' => null, 'proxy_port' => -1, 'proxy_username' => null, 'proxy_password' => null, 'client_id' => null, 'handle_throttle' => true ); private $modePath = null; // Final URL to where the API parameters POST done, based off the config['region'] and respective $mwsServiceUrls private $mwsServiceUrl = null; private $mwsServiceUrls = array('eu' => 'mws-eu.amazonservices.com', 'na' => 'mws.amazonservices.com', 'jp' => 'mws.amazonservices.jp'); // Production profile end points to get the user information private $liveProfileEndpoint = array('uk' => 'https://api.amazon.co.uk', 'us' => 'https://api.amazon.com', 'de' => 'https://api.amazon.de', 'jp' => 'https://api.amazon.co.jp'); // Sandbox profile end points to get the user information private $sandboxProfileEndpoint = array('uk' => 'https://api.sandbox.amazon.co.uk', 'us' => 'https://api.sandbox.amazon.com', 'de' => 'https://api.sandbox.amazon.de', 'jp' => 'https://api.sandbox.amazon.co.jp'); private $regionMappings = array('de' => 'eu', 'uk' => 'eu', 'us' => 'na', 'jp' => 'jp'); // Boolean variable to check if the API call was a success public $success = false; /* Takes user configuration array from the user as input * Takes JSON file path with configuration information as input * Validates the user configuration array against existing config array */ public function __construct($config = null) { if (!is_null($config)) { if (is_array($config)) { $configArray = $config; } elseif (!is_array($config)) { $configArray = $this->checkIfFileExists($config); } 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'); } } else { throw new \Exception('$config cannot be null.'); } } /* checkIfFileExists - check if the JSON file exists in the path provided */ private function checkIfFileExists($config) { if(file_exists($config)) { $jsonString = file_get_contents($config); $configArray = json_decode($jsonString, true); $jsonError = json_last_error(); if ($jsonError != 0) { $errorMsg = "Error with message - content is not in json format" . $this->getErrorMessageForJsonError($jsonError) . " " . $configArray; throw new \Exception($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); } return $configArray; } /* Checks if the keys of the input configuration matches the keys in the config array * if they match the values are taken else throws exception * strict case match is not performed */ private function checkConfigKeys($config) { $config = array_change_key_case($config, CASE_LOWER); $config = $this->trimArray($config); foreach ($config as $key => $value) { 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. check the config array key names to match your key names of your config array', 1); } } } /* Convert a json error code to a descriptive error message * * @param int $jsonError message code * * @return string error message */ private function getErrorMessageForJsonError($jsonError) { switch ($jsonError) { case JSON_ERROR_DEPTH: return " - maximum stack depth exceeded."; break; case JSON_ERROR_STATE_MISMATCH: return " - invalid or malformed JSON."; break; case JSON_ERROR_CTRL_CHAR: return " - control character error."; break; case JSON_ERROR_SYNTAX: return " - syntax error."; break; default: return "."; break; } } /* Setter for sandbox * Sets the Boolean value for config['sandbox'] variable */ public function setSandbox($value) { if (is_bool($value)) { $this->config['sandbox'] = $value; } else { throw new \Exception($value . ' is of type ' . gettype($value) . ' and should be a boolean value'); } } /* Setter for config['client_id'] * Sets the value for config['client_id'] variable */ public function setClientId($value) { if (!empty($value)) { $this->config['client_id'] = $value; } else { throw new \Exception('setter value for client ID provided is empty'); } } /* Setter for Proxy * input $proxy [array] * @param $proxy['proxy_user_host'] - hostname for the proxy * @param $proxy['proxy_user_port'] - hostname for the proxy * @param $proxy['proxy_user_name'] - if your proxy required a username * @param $proxy['proxy_user_password'] - if your proxy required a password */ public function setProxy($proxy) { $proxy = $this->trimArray($proxy); if (!empty($proxy['proxy_user_host'])) $this->config['proxy_user_host'] = $proxy['proxy_user_host']; if (!empty($proxy['proxy_user_port'])) $this->config['proxy_user_port'] = $proxy['proxy_user_port']; if (!empty($proxy['proxy_user_name'])) $this->config['proxy_user_name'] = $proxy['proxy_user_name']; if (!empty($proxy['proxy_user_password'])) $this->config['proxy_user_password'] = $proxy['proxy_user_password']; } /* Setter for $mwsServiceUrl * Set the URL to which the post request has to be made for unit testing */ public function setMwsServiceUrl($url) { $this->mwsServiceUrl = $url; } /* Getter * Gets the value for the key if the key exists in config */ 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); } } /* Getter for parameters string * Gets the value for the parameters string for unit testing */ public function getParameters() { return trim($this->parameters); } /* Trim the input Array key values */ private function trimArray($array) { foreach ($array as $key => $value) { $array[$key] = trim($value); } return $array; } /* GetUserInfo convenience function - Returns user's profile information from Amazon using the access token returned by the Button widget. * * @see http://login.amazon.com/website Step 4 * @param $accessToken [String] */ public function getUserInfo($accessToken) { // Get the correct Profile Endpoint URL based off the country/region provided in the config['region'] $this->profileEndpointUrl(); if (empty($accessToken)) { throw new \InvalidArgumentException('Access Token is a required parameter and is not set'); } // To make sure double encoding doesn't occur decode first and encode again. $accessToken = urldecode($accessToken); $url = $this->profileEndpoint . '/auth/o2/tokeninfo?access_token=' . urlEncode($accessToken); $httpCurlRequest = new HttpCurl(); $response = $httpCurlRequest->httpGet($url); $data = json_decode($response); if ($data->aud != $this->config['client_id']) { // The access token does not belong to us throw new \Exception('The Access token entered is incorrect'); } // Exchange the access token for user profile $url = $this->profileEndpoint . '/user/profile'; $httpCurlRequest = new HttpCurl(); $httpCurlRequest->setAccessToken($accessToken); $httpCurlRequest->setHttpHeader(true); $response = $httpCurlRequest->httpGet($url); $userInfo = json_decode($response, true); return $userInfo; } /* setParametersAndPost - sets the parameters array with non empty values from the requestParameters array sent to API calls. * If Provider Credit Details is present, values are set by setProviderCreditDetails * If Provider Credit Reversal Details is present, values are set by setProviderCreditDetails */ private function setParametersAndPost($parameters, $fieldMappings, $requestParameters) { /* For loop to take all the non empty parameters in the $requestParameters and add it into the $parameters array, * if the keys are matched from $requestParameters array with the $fieldMappings array */ foreach ($requestParameters as $param => $value) { if(!is_array($value)) { $value = trim($value); } if (array_key_exists($param, $fieldMappings) && $value!='') { if(is_array($value)) { // If the parameter is a provider_credit_details or provider_credit_reversal_details, call the respective functions to set the values if($param === 'provider_credit_details') { $parameters = $this->setProviderCreditDetails($parameters,$value); } elseif ($param === 'provider_credit_reversal_details') { $parameters = $this->setProviderCreditReversalDetails($parameters,$value); } } else{ // For variables that are boolean values, strtolower them if($this->checkIfBool($value)) { $value = strtolower($value); } $parameters[$fieldMappings[$param]] = $value; } } } $parameters = $this->setDefaultValues($parameters, $fieldMappings, $requestParameters); $responseObject = $this->calculateSignatureAndPost($parameters); return $responseObject; } /* checkIfBool - checks if the input is a boolean */ private function checkIfBool($string) { $string = strtolower($string); return in_array($string, array('true', 'false')); } /* calculateSignatureAndPost - convert the Parameters array to string and curl POST the parameters to MWS */ private function calculateSignatureAndPost($parameters) { // Call the signature and Post function to perform the actions. Returns XML in array format $parametersString = $this->calculateSignatureAndParametersToString($parameters); // POST using curl the String converted Parameters $response = $this->invokePost($parametersString); // Send this response as args to ResponseParser class which will return the object of the class. $responseObject = new ResponseParser($response); return $responseObject; } /* If merchant_id is not set via the requestParameters array then it's taken from the config array * * Set the platform_id if set in the config['platform_id'] array * * If currency_code is set in the $requestParameters and it exists in the $fieldMappings array, strtoupper it * else take the value from config array if set */ private function setDefaultValues($parameters, $fieldMappings, $requestParameters) { if (empty($requestParameters['merchant_id'])) $parameters['SellerId'] = $this->config['merchant_id']; if (array_key_exists('platform_id', $fieldMappings)) { if (empty($requestParameters['platform_id']) && !empty($this->config['platform_id'])) $parameters[$fieldMappings['platform_id']] = $this->config['platform_id']; } if (array_key_exists('currency_code', $fieldMappings)) { if (!empty($requestParameters['currency_code'])) { $parameters[$fieldMappings['currency_code']] = strtoupper($requestParameters['currency_code']); } else { $parameters[$fieldMappings['currency_code']] = strtoupper($this->config['currency_code']); } } return $parameters; } /* setProviderCreditDetails - sets the provider credit details sent via the Capture or Authorize API calls * @param provider_id - [String] * @param credit_amount - [String] * @optional currency_code - [String] */ private function setProviderCreditDetails($parameters, $providerCreditInfo) { $providerIndex = 0; $providerString = 'ProviderCreditList.member.'; $fieldMappings = array( 'provider_id' => 'ProviderId', 'credit_amount' => 'CreditAmount.Amount', 'currency_code' => 'CreditAmount.CurrencyCode' ); foreach ($providerCreditInfo as $key => $value) { $value = array_change_key_case($value, CASE_LOWER); $providerIndex = $providerIndex + 1; foreach ($value as $param => $val) { if (array_key_exists($param, $fieldMappings) && trim($val)!='') { $parameters[$providerString.$providerIndex. '.' .$fieldMappings[$param]] = $val; } } // If currency code is not entered take it from the config array if(empty($parameters[$providerString.$providerIndex. '.' .$fieldMappings['currency_code']])) { $parameters[$providerString.$providerIndex. '.' .$fieldMappings['currency_code']] = strtoupper($this->config['currency_code']); } } return $parameters; } /* setProviderCreditReversalDetails - sets the reverse provider credit details sent via the Refund API call. * @param provider_id - [String] * @param credit_amount - [String] * @optional currency_code - [String] */ private function setProviderCreditReversalDetails($parameters, $providerCreditInfo) { $providerIndex = 0; $providerString = 'ProviderCreditReversalList.member.'; $fieldMappings = array( 'provider_id' => 'ProviderId', 'credit_reversal_amount' => 'CreditReversalAmount.Amount', 'currency_code' => 'CreditReversalAmount.CurrencyCode' ); foreach ($providerCreditInfo as $key => $value) { $value = array_change_key_case($value, CASE_LOWER); $providerIndex = $providerIndex + 1; foreach ($value as $param => $val) { if (array_key_exists($param, $fieldMappings) && trim($val)!='') { $parameters[$providerString.$providerIndex. '.' .$fieldMappings[$param]] = $val; } } // If currency code is not entered take it from the config array if(empty($parameters[$providerString.$providerIndex. '.' .$fieldMappings['currency_code']])) { $parameters[$providerString.$providerIndex. '.' .$fieldMappings['currency_code']] = strtoupper($this->config['currency_code']); } } return $parameters; } /* GetOrderReferenceDetails API call - Returns details about the Order Reference object and its current state. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_GetOrderReferenceDetails.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_order_reference_id'] - [String] * @optional requestParameters['address_consent_token'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function getOrderReferenceDetails($requestParameters = array()) { $parameters['Action'] = 'GetOrderReferenceDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_order_reference_id' => 'AmazonOrderReferenceId', 'address_consent_token' => 'AddressConsentToken', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* SetOrderReferenceDetails API call - Sets order reference details such as the order total and a description for the order. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_SetOrderReferenceDetails.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_order_reference_id'] - [String] * @param requestParameters['amount'] - [String] * @param requestParameters['currency_code'] - [String] * @optional requestParameters['platform_id'] - [String] * @optional requestParameters['seller_note'] - [String] * @optional requestParameters['seller_order_id'] - [String] * @optional requestParameters['store_name'] - [String] * @optional requestParameters['custom_information'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function setOrderReferenceDetails($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'SetOrderReferenceDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_order_reference_id' => 'AmazonOrderReferenceId', 'amount' => 'OrderReferenceAttributes.OrderTotal.Amount', 'currency_code' => 'OrderReferenceAttributes.OrderTotal.CurrencyCode', 'platform_id' => 'OrderReferenceAttributes.PlatformId', 'seller_note' => 'OrderReferenceAttributes.SellerNote', 'seller_order_id' => 'OrderReferenceAttributes.SellerOrderAttributes.SellerOrderId', 'store_name' => 'OrderReferenceAttributes.SellerOrderAttributes.StoreName', 'custom_information' => 'OrderReferenceAttributes.SellerOrderAttributes.CustomInformation', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* ConfirmOrderReferenceDetails API call - Confirms that the order reference is free of constraints and all required information has been set on the order reference. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_ConfirmOrderReference.html * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_order_reference_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function confirmOrderReference($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'ConfirmOrderReference'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_order_reference_id' => 'AmazonOrderReferenceId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* CancelOrderReferenceDetails API call - Cancels a previously confirmed order reference. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_CancelOrderReference.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_order_reference_id'] - [String] * @optional requestParameters['cancelation_reason'] [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function cancelOrderReference($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'CancelOrderReference'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_order_reference_id' => 'AmazonOrderReferenceId', 'cancelation_reason' => 'CancelationReason', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* CloseOrderReferenceDetails API call - Confirms that an order reference has been fulfilled (fully or partially) * and that you do not expect to create any new authorizations on this order reference. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_CloseOrderReference.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_order_reference_id'] - [String] * @optional requestParameters['closure_reason'] [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function closeOrderReference($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'CloseOrderReference'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_order_reference_id' => 'AmazonOrderReferenceId', 'closure_reason' => 'ClosureReason', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* CloseAuthorization API call - Closes an authorization. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_CloseOrderReference.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_authorization_id'] - [String] * @optional requestParameters['closure_reason'] [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function closeAuthorization($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'CloseAuthorization'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_authorization_id' => 'AmazonAuthorizationId', 'closure_reason' => 'ClosureReason', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* Authorize API call - Reserves a specified amount against the payment method(s) stored in the order reference. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_Authorize.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_order_reference_id'] - [String] * @param requestParameters['authorization_amount'] [String] * @param requestParameters['currency_code'] - [String] * @param requestParameters['authorization_reference_id'] [String] * @optional requestParameters['capture_now'] [String] * @optional requestParameters['provider_credit_details'] - [array (array())] * @optional requestParameters['seller_authorization_note'] [String] * @optional requestParameters['transaction_timeout'] [String] - Defaults to 1440 minutes * @optional requestParameters['soft_descriptor'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function authorize($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'Authorize'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_order_reference_id' => 'AmazonOrderReferenceId', 'authorization_amount' => 'AuthorizationAmount.Amount', 'currency_code' => 'AuthorizationAmount.CurrencyCode', 'authorization_reference_id' => 'AuthorizationReferenceId', 'capture_now' => 'CaptureNow', 'provider_credit_details' => array(), 'seller_authorization_note' => 'SellerAuthorizationNote', 'transaction_timeout' => 'TransactionTimeout', 'soft_descriptor' => 'SoftDescriptor', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* GetAuthorizationDetails API call - Returns the status of a particular authorization and the total amount captured on the authorization. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_GetAuthorizationDetails.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_authorization_id'] [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function getAuthorizationDetails($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'GetAuthorizationDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_authorization_id' => 'AmazonAuthorizationId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* Capture API call - Captures funds from an authorized payment instrument. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_Capture.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_authorization_id'] - [String] * @param requestParameters['capture_amount'] - [String] * @param requestParameters['currency_code'] - [String] * @param requestParameters['capture_reference_id'] - [String] * @optional requestParameters['provider_credit_details'] - [array (array())] * @optional requestParameters['seller_capture_note'] - [String] * @optional requestParameters['soft_descriptor'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function capture($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'Capture'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_authorization_id' => 'AmazonAuthorizationId', 'capture_amount' => 'CaptureAmount.Amount', 'currency_code' => 'CaptureAmount.CurrencyCode', 'capture_reference_id' => 'CaptureReferenceId', 'provider_credit_details' => array(), 'seller_capture_note' => 'SellerCaptureNote', 'soft_descriptor' => 'SoftDescriptor', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* GetCaptureDetails API call - Returns the status of a particular capture and the total amount refunded on the capture. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_GetCaptureDetails.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_capture_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function getCaptureDetails($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'GetCaptureDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_capture_id' => 'AmazonCaptureId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* Refund API call - Refunds a previously captured amount. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_Refund.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_capture_id'] - [String] * @param requestParameters['refund_reference_id'] - [String] * @param requestParameters['refund_amount'] - [String] * @param requestParameters['currency_code'] - [String] * @optional requestParameters['provider_credit_reversal_details'] - [array(array())] * @optional requestParameters['seller_refund_note'] [String] * @optional requestParameters['soft_descriptor'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function refund($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'Refund'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_capture_id' => 'AmazonCaptureId', 'refund_reference_id' => 'RefundReferenceId', 'refund_amount' => 'RefundAmount.Amount', 'currency_code' => 'RefundAmount.CurrencyCode', 'provider_credit_reversal_details' => array(), 'seller_refund_note' => 'SellerRefundNote', 'soft_descriptor' => 'SoftDescriptor', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* GetRefundDetails API call - Returns the status of a particular refund. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_GetRefundDetails.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_refund_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function getRefundDetails($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'GetRefundDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_refund_id' => 'AmazonRefundId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* GetServiceStatus API Call - Returns the operational status of the Off-Amazon Payments API section * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_GetServiceStatus.html * * The GetServiceStatus operation returns the operational status of the Off-Amazon Payments API * section of Amazon Marketplace Web Service (Amazon MWS). * Status values are GREEN, GREEN_I, YELLOW, and RED. * * @param requestParameters['merchant_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function getServiceStatus($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'GetServiceStatus'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* CreateOrderReferenceForId API Call - Creates an order reference for the given object * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_CreateOrderReferenceForId.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['Id'] - [String] * @optional requestParameters['inherit_shipping_address'] [Boolean] * @optional requestParameters['ConfirmNow'] - [Boolean] * @optional Amount (required when confirm_now is set to true) [String] * @optional requestParameters['currency_code'] - [String] * @optional requestParameters['seller_note'] - [String] * @optional requestParameters['seller_order_id'] - [String] * @optional requestParameters['store_name'] - [String] * @optional requestParameters['custom_information'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function createOrderReferenceForId($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'CreateOrderReferenceForId'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'id' => 'Id', 'id_type' => 'IdType', 'inherit_shipping_address' => 'InheritShippingAddress', 'confirm_now' => 'ConfirmNow', 'amount' => 'OrderReferenceAttributes.OrderTotal.Amount', 'currency_code' => 'OrderReferenceAttributes.OrderTotal.CurrencyCode', 'platform_id' => 'OrderReferenceAttributes.PlatformId', 'seller_note' => 'OrderReferenceAttributes.SellerNote', 'seller_order_id' => 'OrderReferenceAttributes.SellerOrderAttributes.SellerOrderId', 'store_name' => 'OrderReferenceAttributes.SellerOrderAttributes.StoreName', 'custom_information' => 'OrderReferenceAttributes.SellerOrderAttributes.CustomInformation', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* GetBillingAgreementDetails API Call - Returns details about the Billing Agreement object and its current state. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_GetBillingAgreementDetails.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_billing_agreement_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function getBillingAgreementDetails($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'GetBillingAgreementDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId', 'address_consent_token' => 'AddressConsentToken', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* SetBillingAgreementDetails API call - Sets Billing Agreement details such as a description of the agreement and other information about the seller. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_SetBillingAgreementDetails.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_billing_agreement_id'] - [String] * @param requestParameters['amount'] - [String] * @param requestParameters['currency_code'] - [String] * @optional requestParameters['platform_id'] - [String] * @optional requestParameters['seller_note'] - [String] * @optional requestParameters['seller_billing_agreement_id'] - [String] * @optional requestParameters['store_name'] - [String] * @optional requestParameters['custom_information'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function setBillingAgreementDetails($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'SetBillingAgreementDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId', 'platform_id' => 'BillingAgreementAttributes.PlatformId', 'seller_note' => 'BillingAgreementAttributes.SellerNote', 'seller_billing_agreement_id' => 'BillingAgreementAttributes.SellerBillingAgreementAttributes.SellerBillingAgreementId', 'custom_information' => 'BillingAgreementAttributes.SellerBillingAgreementAttributes.CustomInformation', 'store_name' => 'BillingAgreementAttributes.SellerBillingAgreementAttributes.StoreName', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* ConfirmBillingAgreement API Call - Confirms that the Billing Agreement is free of constraints and all required information has been set on the Billing Agreement. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_ConfirmBillingAgreement.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_billing_agreement_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function confirmBillingAgreement($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'ConfirmBillingAgreement'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* ValidateBillignAgreement API Call - Validates the status of the Billing Agreement object and the payment method associated with it. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_ValidateBillingAgreement.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_billing_agreement_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function validateBillingAgreement($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'ValidateBillingAgreement'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* AuthorizeOnBillingAgreement API call - Reserves a specified amount against the payment method(s) stored in the Billing Agreement. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_AuthorizeOnBillingAgreement.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_billing_agreement_id'] - [String] * @param requestParameters['authorization_reference_id'] [String] * @param requestParameters['authorization_amount'] [String] * @param requestParameters['currency_code'] - [String] * @optional requestParameters['seller_authorization_note'] [String] * @optional requestParameters['transaction_timeout'] - Defaults to 1440 minutes * @optional requestParameters['capture_now'] [String] * @optional requestParameters['soft_descriptor'] - - [String] * @optional requestParameters['seller_note'] - [String] * @optional requestParameters['platform_id'] - [String] * @optional requestParameters['custom_information'] - [String] * @optional requestParameters['seller_order_id'] - [String] * @optional requestParameters['store_name'] - [String] * @optional requestParameters['inherit_shipping_address'] [Boolean] - Defaults to true * @optional requestParameters['mws_auth_token'] - [String] */ public function authorizeOnBillingAgreement($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'AuthorizeOnBillingAgreement'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId', 'authorization_reference_id' => 'AuthorizationReferenceId', 'authorization_amount' => 'AuthorizationAmount.Amount', 'currency_code' => 'AuthorizationAmount.CurrencyCode', 'seller_authorization_note' => 'SellerAuthorizationNote', 'transaction_timeout' => 'TransactionTimeout', 'capture_now' => 'CaptureNow', 'soft_descriptor' => 'SoftDescriptor', 'seller_note' => 'SellerNote', 'platform_id' => 'PlatformId', 'custom_information' => 'SellerOrderAttributes.CustomInformation', 'seller_order_id' => 'SellerOrderAttributes.SellerOrderId', 'store_name' => 'SellerOrderAttributes.StoreName', 'inherit_shipping_address' => 'InheritShippingAddress', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* CloseBillingAgreement API Call - Returns details about the Billing Agreement object and its current state. * @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_CloseBillingAgreement.html * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_billing_agreement_id'] - [String] * @optional requestParameters['closure_reason'] [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function closeBillingAgreement($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'CloseBillingAgreement'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId', 'closure_reason' => 'ClosureReason', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* charge convenience method * Performs the API calls * 1. SetOrderReferenceDetails / SetBillingAgreementDetails * 2. ConfirmOrderReference / ConfirmBillingAgreement * 3. Authorize (with Capture) / AuthorizeOnBillingAgreeemnt (with Capture) * * @param requestParameters['merchant_id'] - [String] * * @param requestParameters['amazon_reference_id'] - [String] : Order Reference ID /Billing Agreement ID * If requestParameters['amazon_reference_id'] is empty then the following is required, * @param requestParameters['amazon_order_reference_id'] - [String] : Order Reference ID * or, * @param requestParameters['amazon_billing_agreement_id'] - [String] : Billing Agreement ID * * @param $requestParameters['charge_amount'] - [String] : Amount value to be captured * @param requestParameters['currency_code'] - [String] : Currency Code for the Amount * @param requestParameters['authorization_reference_id'] - [String]- Any unique string that needs to be passed * @optional requestParameters['charge_note'] - [String] : Seller Note sent to the buyer * @optional requestParameters['transaction_timeout'] - [String] : Defaults to 1440 minutes * @optional requestParameters['charge_order_id'] - [String] : Custom Order ID provided * @optional requestParameters['mws_auth_token'] - [String] */ public function charge($requestParameters = array()) { $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $requestParameters= $this->trimArray($requestParameters); $setParameters = $authorizeParameters = $confirmParameters = $requestParameters; $chargeType = ''; if (!empty($requestParameters['amazon_order_reference_id'])) { $chargeType = 'OrderReference'; } elseif(!empty($requestParameters['amazon_billing_agreement_id'])) { $chargeType = 'BillingAgreement'; } elseif (!empty($requestParameters['amazon_reference_id'])) { switch (substr(strtoupper($requestParameters['amazon_reference_id']), 0, 1)) { case 'P': case 'S': $chargeType = 'OrderReference'; $setParameters['amazon_order_reference_id'] = $requestParameters['amazon_reference_id']; $authorizeParameters['amazon_order_reference_id'] = $requestParameters['amazon_reference_id']; $confirmParameters['amazon_order_reference_id'] = $requestParameters['amazon_reference_id']; break; case 'B': case 'C': $chargeType = 'BillingAgreement'; $setParameters['amazon_billing_agreement_id'] = $requestParameters['amazon_reference_id']; $authorizeParameters['amazon_billing_agreement_id'] = $requestParameters['amazon_reference_id']; $confirmParameters['amazon_billing_agreement_id'] = $requestParameters['amazon_reference_id']; break; default: throw new \Exception('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'); } // Set the other parameters if the values are present $setParameters['amount'] = !empty($requestParameters['charge_amount']) ? $requestParameters['charge_amount'] : ''; $authorizeParameters['authorization_amount'] = !empty($requestParameters['charge_amount']) ? $requestParameters['charge_amount'] : ''; $setParameters['seller_note'] = !empty($requestParameters['charge_note']) ? $requestParameters['charge_note'] : ''; $authorizeParameters['seller_authorization_note'] = !empty($requestParameters['charge_note']) ? $requestParameters['charge_note'] : ''; $authorizeParameters['seller_note'] = !empty($requestParameters['charge_note']) ? $requestParameters['charge_note'] : ''; $setParameters['seller_order_id'] = !empty($requestParameters['charge_order_id']) ? $requestParameters['charge_order_id'] : ''; $setParameters['seller_billing_agreement_id'] = !empty($requestParameters['charge_order_id']) ? $requestParameters['charge_order_id'] : ''; $authorizeParameters['seller_order_id'] = !empty($requestParameters['charge_order_id']) ? $requestParameters['charge_order_id'] : ''; $authorizeParameters['capture_now'] = 'true'; $response = $this->makeChargeCalls($chargeType, $setParameters, $confirmParameters, $authorizeParameters); return $response; } /* makeChargeCalls - makes API calls based off the charge type (OrderReference or BillingAgreement) */ private function makeChargeCalls($chargeType, $setParameters, $confirmParameters, $authorizeParameters) { switch ($chargeType) { case 'OrderReference': $response = $this->setOrderReferenceDetails($setParameters); if ($this->success) { $this->confirmOrderReference($confirmParameters); } if ($this->success) { $response = $this->Authorize($authorizeParameters); } return $response; case 'BillingAgreement': // Get the Billing Agreement details and feed the response object to the ResponseParser $responseObj = $this->getBillingAgreementDetails($setParameters); // Call the function GetBillingAgreementDetailsStatus in ResponseParser.php providing it the XML response // $baStatus is an aray containing the State of the Billing Agreement $baStatus = $responseObj->getBillingAgreementDetailsStatus($responseObj->toXml()); if ($baStatus['State'] != 'Open') { $response = $this->SetBillingAgreementDetails($setParameters); if ($this->success) { $response = $this->ConfirmBillingAgreement($confirmParameters); } } // Check the Billing Agreement status again before making the Authorization. $responseObj = $this->getBillingAgreementDetails($setParameters); $baStatus = $responseObj->GetBillingAgreementDetailsStatus($responseObj->toXml()); if ($this->success && $baStatus['State'] === 'Open') { $response = $this->AuthorizeOnBillingAgreement($authorizeParameters); } return $response; } } /* GetProviderCreditDetails API Call - Get the details of the Provider Credit. * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_provider_credit_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function getProviderCreditDetails($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'GetProviderCreditDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_provider_credit_id' => 'AmazonProviderCreditId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* GetProviderCreditReversalDetails API Call - Get details of the Provider Credit Reversal. * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_provider_credit_reversal_id'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function getProviderCreditReversalDetails($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'GetProviderCreditReversalDetails'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_provider_credit_reversal_id' => 'AmazonProviderCreditReversalId', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* ReverseProviderCredit API Call - Reverse the Provider Credit. * * @param requestParameters['merchant_id'] - [String] * @param requestParameters['amazon_provider_credit_id'] - [String] * @optional requestParameters['credit_reversal_reference_id'] - [String] * @param requestParameters['credit_reversal_amount'] - [String] * @optional requestParameters['currency_code'] - [String] * @optional requestParameters['credit_reversal_note'] - [String] * @optional requestParameters['mws_auth_token'] - [String] */ public function reverseProviderCredit($requestParameters = array()) { $parameters = array(); $parameters['Action'] = 'ReverseProviderCredit'; $requestParameters = array_change_key_case($requestParameters, CASE_LOWER); $fieldMappings = array( 'merchant_id' => 'SellerId', 'amazon_provider_credit_id' => 'AmazonProviderCreditId', 'credit_reversal_reference_id' => 'CreditReversalReferenceId', 'credit_reversal_amount' => 'CreditReversalAmount.Amount', 'currency_code' => 'CreditReversalAmount.CurrencyCode', 'credit_reversal_note' => 'CreditReversalNote', 'mws_auth_token' => 'MWSAuthToken' ); $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters); return ($responseObject); } /* Create an Array of required parameters, sort them * Calculate signature and invoke the POST to the MWS Service URL * * @param AWSAccessKeyId [String] * @param Version [String] * @param SignatureMethod [String] * @param Timestamp [String] * @param Signature [String] */ private function calculateSignatureAndParametersToString($parameters = array()) { $parameters['AWSAccessKeyId'] = $this->config['access_key']; $parameters['Version'] = self::SERVICE_VERSION; $parameters['SignatureMethod'] = 'HmacSHA256'; $parameters['SignatureVersion'] = 2; $parameters['Timestamp'] = $this->getFormattedTimestamp(); uksort($parameters, 'strcmp'); $this->createServiceUrl(); $parameters['Signature'] = $this->signParameters($parameters); $parameters = $this->getParametersAsString($parameters); // Save these parameters in the parameters variable so that it can be returned for unit testing. $this->parameters = $parameters; return $parameters; } /* Computes RFC 2104-compliant HMAC signature for request parameters * Implements AWS Signature, as per following spec: * * If Signature Version is 0, it signs concatenated Action and Timestamp * * If Signature Version is 1, it performs the following: * * Sorts all parameters (including SignatureVersion and excluding Signature, * the value of which is being created), ignoring case. * * Iterate over the sorted list and append the parameter name (in original case) * and then its value. It will not URL-encode the parameter values before * constructing this string. There are no separators. * * If Signature Version is 2, string to sign is based on following: * * 1. The HTTP Request Method followed by an ASCII newline (%0A) * 2. The HTTP Host header in the form of lowercase host, followed by an ASCII newline. * 3. The URL encoded HTTP absolute path component of the URI * (up to but not including the query string parameters); * if this is empty use a forward '/'. This parameter is followed by an ASCII newline. * 4. The concatenation of all query string components (names and values) * as UTF-8 characters which are URL encoded as per RFC 3986 * (hex characters MUST be uppercase), sorted using lexicographic byte ordering. * Parameter names are separated from their values by the '=' character * (ASCII character 61), even if the value is empty. * Pairs of parameter and values are separated by the '&' character (ASCII code 38). * */ private function signParameters(array $parameters) { $signatureVersion = $parameters['SignatureVersion']; $algorithm = "HmacSHA1"; $stringToSign = null; if (2 === $signatureVersion) { $algorithm = "HmacSHA256"; $parameters['SignatureMethod'] = $algorithm; $stringToSign = $this->calculateStringToSignV2($parameters); } else { throw new \Exception("Invalid Signature Version specified"); } return $this->sign($stringToSign, $algorithm); } /* Calculate String to Sign for SignatureVersion 2 * @param array $parameters request parameters * @return String to Sign */ private function calculateStringToSignV2(array $parameters) { $data = 'POST'; $data .= "\n"; $data .= $this->mwsEndpointUrl; $data .= "\n"; $data .= $this->mwsEndpointPath; $data .= "\n"; $data .= $this->getParametersAsString($parameters); return $data; } /* Convert paremeters to Url encoded query string */ private function getParametersAsString(array $parameters) { $queryParameters = array(); foreach ($parameters as $key => $value) { $queryParameters[] = $key . '=' . $this->urlEncode($value); } return implode('&', $queryParameters); } private function urlEncode($value) { return str_replace('%7E', '~', rawurlencode($value)); } /* Computes RFC 2104-compliant HMAC signature */ private function sign($data, $algorithm) { if ($algorithm === 'HmacSHA1') { $hash = 'sha1'; } else if ($algorithm === 'HmacSHA256') { $hash = 'sha256'; } else { throw new \Exception("Non-supported signing method specified"); } return base64_encode(hash_hmac($hash, $data, $this->config['secret_key'], true)); } /* Formats date as ISO 8601 timestamp */ private function getFormattedTimestamp() { return gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time()); } /* invokePost takes the parameters and invokes the httpPost function to POST the parameters * Exponential retries on error 500 and 503 * The response from the POST is an XML which is converted to Array */ private function invokePost($parameters) { $response = array(); $statusCode = 200; $this->success = false; // Submit the request and read response body try { $shouldRetry = true; $retries = 0; do { try { $this->constructUserAgentHeader(); $httpCurlRequest = new HttpCurl($this->config); $response = $httpCurlRequest->httpPost($this->mwsServiceUrl, $this->userAgent, $parameters); // Split the API response into Response Body and the other parts of the response into other list($other, $responseBody) = explode("\r\n\r\n", $response, 2); $other = preg_split("/\r\n|\n|\r/", $other); list($protocol, $code, $text) = explode(' ', trim(array_shift($other)), 3); $response = array( 'Status' => (int) $code, 'ResponseBody' => $responseBody ); $statusCode = $response['Status']; if ($statusCode == 200) { $shouldRetry = false; $this->success = true; } elseif ($statusCode == 500 || $statusCode == 503) { $shouldRetry = true; if ($shouldRetry && strtolower($this->config['handle_throttle'])) { $this->pauseOnRetry(++$retries, $statusCode); } } else { $shouldRetry = false; } } catch (\Exception $e) { throw $e; } } while ($shouldRetry); } catch (\Exception $se) { throw $se; } return $response; } /* Exponential sleep on failed request * @param retries current retry * @throws Exception if maximum number of retries has been reached */ private function pauseOnRetry($retries, $status) { if ($retries <= self::MAX_ERROR_RETRY) { $delay = (int) (pow(4, $retries) * 100000); usleep($delay); } else { throw new \Exception('Error Code: '. $status.PHP_EOL.'Maximum number of retry attempts - '. $retries .' reached'); } } /* Create MWS service URL and the Endpoint path */ private function createServiceUrl() { $this->modePath = strtolower($this->config['sandbox']) ? 'OffAmazonPayments_Sandbox' : 'OffAmazonPayments'; if (!empty($this->config['region'])) { $region = strtolower($this->config['region']); if (array_key_exists($region, $this->regionMappings)) { $this->mwsEndpointUrl = $this->mwsServiceUrls[$this->regionMappings[$region]]; $this->mwsServiceUrl = 'https://' . $this->mwsEndpointUrl . '/' . $this->modePath . '/' . self::SERVICE_VERSION; $this->mwsEndpointPath = '/' . $this->modePath . '/' . self::SERVICE_VERSION; } else { throw new \Exception($region . ' is not a valid region'); } } else { throw new \Exception("config['region'] is a required parameter and is not set"); } } /* Based on the config['region'] and config['sandbox'] values get the user profile URL */ private function profileEndpointUrl() { if (!empty($this->config['region'])) { $region = strtolower($this->config['region']); if (array_key_exists($region, $this->sandboxProfileEndpoint) && $this->config['sandbox'] ) { $this->profileEndpoint = $this->sandboxProfileEndpoint[$region]; } elseif (array_key_exists($region, $this->liveProfileEndpoint)) { $this->profileEndpoint = $this->liveProfileEndpoint[$region]; } else{ throw new \Exception($region . ' is not a valid region'); } } else { throw new \Exception("config['region'] is a required parameter and is not set"); } } /* Create the User Agent Header sent with the POST request */ private function constructUserAgentHeader() { $this->userAgent = $this->quoteApplicationName($this->config['application_name']) . '/' . $this->quoteApplicationVersion($this->config['application_version']); $this->userAgent .= ' ('; $this->userAgent .= 'Language=PHP/' . phpversion(); $this->userAgent .= '; '; $this->userAgent .= 'Platform=' . php_uname('s') . '/' . php_uname('m') . '/' . php_uname('r'); $this->userAgent .= '; '; $this->userAgent .= 'MWSClientVersion=' . self::MWS_CLIENT_VERSION; $this->userAgent .= ')'; } /* Collapse multiple whitespace characters into a single ' ' and backslash escape '\', * and '/' characters from a string. * @param $s * @return string */ private function quoteApplicationName($s) { $quotedString = preg_replace('/ {2,}|\s/', ' ', $s); $quotedString = preg_replace('/\\\\/', '\\\\\\\\', $quotedString); $quotedString = preg_replace('/\//', '\\/', $quotedString); return $quotedString; } /* Collapse multiple whitespace characters into a single ' ' and backslash escape '\', * and '(' characters from a string. * * @param $s * @return string */ private function quoteApplicationVersion($s) { $quotedString = preg_replace('/ {2,}|\s/', ' ', $s); $quotedString = preg_replace('/\\\\/', '\\\\\\\\', $quotedString); $quotedString = preg_replace('/\\(/', '\\(', $quotedString); return $quotedString; } }