laipower/wp-content/plugins/easy-digital-downloads/includes/gateways/libs/amazon/Client.php

1583 lines
58 KiB
PHP

<?php
namespace PayWithAmazon;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/* Class Client
* Takes configuration information
* Makes API calls to MWS for Pay With Amazon
* returns Response Object
*/
require_once 'ResponseParser.php';
require_once 'HttpCurl.php';
require_once 'Interface.php';
class Client implements ClientInterface
{
const MWS_CLIENT_VERSION = '1.0.0';
const SERVICE_VERSION = '2013-01-01';
const MAX_ERROR_RETRY = 3;
// Construct User agent string based off of the application_name, application_version, PHP platform
private $userAgent = null;
private $parameters = null;
private $mwsEndpointPath = null;
private $mwsEndpointUrl = null;
private $profileEndpoint = null;
private $config = array('merchant_id' => 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;
}
}