Index: /mods/mod_amazon/PayWithAmazon/Client.php
===================================================================
--- /mods/mod_amazon/PayWithAmazon/Client.php	(revision 5485)
+++ /mods/mod_amazon/PayWithAmazon/Client.php	(revision 5485)
@@ -0,0 +1,1607 @@
+<?php
+namespace PayWithAmazon;
+
+/* 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';
+require_once 'Regions.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;
+    private $profileEndpointUrls;
+    private $regionMappings;
+
+    // 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)
+    {
+	$this->getRegionUrls();
+        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.');
+	}
+    }
+    
+    /* Get the Region specific properties from the Regions class.*/
+    
+    private function getRegionUrls()
+    {
+	$regionObject = new Regions();
+	$this->mwsServiceUrls = $regionObject->mwsServiceUrls;
+	$this->regionMappings = $regionObject->regionMappings;
+	$this->profileEndpointUrls = $regionObject->profileEndpointUrls;
+    }
+
+    /* 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)
+    {
+	if (!empty($proxy['proxy_user_host']))
+	    $this->config['proxy_host'] = $proxy['proxy_user_host'];
+
+        if (!empty($proxy['proxy_user_port']))
+            $this->config['proxy_port'] = $proxy['proxy_user_port'];
+
+        if (!empty($proxy['proxy_user_name']))
+            $this->config['proxy_username'] = $proxy['proxy_user_name'];
+
+        if (!empty($proxy['proxy_user_password']))
+            $this->config['proxy_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)
+	{
+	    if(!is_array($value) && $key!=='proxy_password')
+	    {
+		$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($this->config);
+
+        $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($this->config);
+
+        $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'] = !empty($requestParameters['capture_now']) ? $requestParameters['capture_now'] : false;
+
+	$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':
+		
+		// Get the Order Reference details and feed the response object to the ResponseParser
+                $responseObj = $this->getOrderReferenceDetails($setParameters);
+		
+		// Call the function getOrderReferenceDetailsStatus in ResponseParser.php providing it the XML response
+                // $oroStatus is an array containing the State of the Order Reference ID
+                $oroStatus = $responseObj->getOrderReferenceDetailsStatus($responseObj->toXml());
+		
+		if ($oroStatus['State'] === 'Draft') {
+		    $response = $this->setOrderReferenceDetails($setParameters);
+		    if ($this->success) {
+                    $this->confirmOrderReference($confirmParameters);
+		    }
+		}
+		
+                $responseObj = $this->getOrderReferenceDetails($setParameters);
+		
+		// Check the Order Reference Status again before making the Authorization.
+                $oroStatus = $responseObj->getOrderReferenceDetailsStatus($responseObj->toXml());
+		
+		if ($oroStatus['State'] === 'Open') {
+		    if ($this->success) {
+                    $response = $this->Authorize($authorizeParameters);
+		    }
+		}
+		if ($oroStatus['State'] != 'Open' && $oroStatus['State'] != 'Draft') {
+		    throw new \Exception('The Order Reference is in the ' . $oroStatus['State'] . " State. It should be in the Draft or Open State");
+		}
+                
+		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 array containing the State of the Billing Agreement
+                $baStatus = $responseObj->getBillingAgreementDetailsStatus($responseObj->toXml());
+                
+		if ($baStatus['State'] === 'Draft') {
+                    $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);
+                }
+		
+		if($baStatus['State'] != 'Open' && $baStatus['State'] != 'Draft') {
+		    throw new \Exception('The Billing Agreement is in the ' . $baStatus['State'] . " State. It should be in the Draft or Open State");
+		}
+		
+            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);
+                    $curlResponseInfo = $httpCurlRequest->getCurlResponseInfo();
+                    
+                    $statusCode = $curlResponseInfo["http_code"];
+                    
+                    $response = array(
+                        'Status' => $statusCode,
+                        'ResponseBody' => $response
+                    );
+
+                    $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()
+    {
+	$profileEnvt = strtolower($this->config['sandbox']) ? "api.sandbox" : "api";
+	
+        if (!empty($this->config['region'])) {
+            $region = strtolower($this->config['region']);
+
+	    if (array_key_exists($region, $this->regionMappings) ) {
+                $this->profileEndpoint = 'https://' . $profileEnvt . '.' . $this->profileEndpointUrls[$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;
+    }
+}
Index: /mods/mod_amazon/PayWithAmazon/HttpCurl.php
===================================================================
--- /mods/mod_amazon/PayWithAmazon/HttpCurl.php	(revision 5485)
+++ /mods/mod_amazon/PayWithAmazon/HttpCurl.php	(revision 5485)
@@ -0,0 +1,142 @@
+<?php
+namespace PayWithAmazon;
+
+/* Class HttpCurl
+ * Handles Curl POST function for all requests
+ */
+
+require_once 'Interface.php';
+
+class HttpCurl implements HttpCurlInterface
+{
+    private $config = array();
+    private $header = false;
+    private $accessToken = null;
+    private $curlResponseInfo = null;
+    
+    /* Takes user configuration array as input
+     * Takes configuration for API call or IPN config
+     */
+    
+    public function __construct($config = null)
+    {
+        $this->config = $config;
+    }
+    
+    /* Setter for boolean header to get the user info */
+    
+    public function setHttpHeader()
+    {
+        $this->header = true;
+    }
+    
+    /* Setter for Access token to get the user info */
+    
+    public function setAccessToken($accesstoken)
+    {
+        $this->accessToken = $accesstoken;
+    }
+
+    /* Add the common Curl Parameters to the curl handler $ch
+     * Also checks for optional parameters if provided in the config
+     * config['cabundle_file']
+     * config['proxy_port']
+     * config['proxy_host']
+     * config['proxy_username']
+     * config['proxy_password']
+     */
+    
+    private  function commonCurlParams($url,$userAgent)
+    {
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_PORT, 443);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        
+        if (!is_null($this->config['cabundle_file'])) {
+            curl_setopt($ch, CURLOPT_CAINFO, $this->config['cabundle_file']);
+        }
+        
+        if (!empty($userAgent))
+            curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
+        
+        if ($this->config['proxy_host'] != null && $this->config['proxy_port'] != -1) {
+            curl_setopt($ch, CURLOPT_PROXY, $this->config['proxy_host'] . ':' . $this->config['proxy_port']);
+        }
+        
+        if ($this->config['proxy_username'] != null && $this->config['proxy_password'] != null) {
+            curl_setopt($ch, CURLOPT_PROXYUSERPWD, $this->config['proxy_username'] . ':' . $this->config['proxy_password']);
+        }
+        
+        return $ch;
+    }
+    
+    /* POST using curl for the following situations
+     * 1. API calls
+     * 2. IPN certificate retrieval
+     * 3. Get User Info
+     */
+    
+    public function httpPost($url, $userAgent = null, $parameters = null)
+    {
+        $ch = $this->commonCurlParams($url,$userAgent);
+        
+        curl_setopt($ch, CURLOPT_POST, true);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $parameters);
+        curl_setopt($ch, CURLOPT_HEADER, false);
+        
+        $response = $this->execute($ch);
+        return $response;
+    }
+    
+    /* GET using curl for the following situations
+     * 1. IPN certificate retrieval
+     * 2. Get User Info
+     */
+    
+    public function httpGet($url, $userAgent = null)
+    {
+        $ch = $this->commonCurlParams($url,$userAgent);
+        
+        // Setting the HTTP header with the Access Token only for Getting user info
+        if ($this->header) {
+            curl_setopt($ch, CURLOPT_HTTPHEADER, array(
+                'Authorization: bearer ' . $this->accessToken
+            ));
+        }
+        
+        $response = $this->execute($ch);
+        return $response;
+    }
+    
+    /* Execute Curl request */
+    
+    private function execute($ch)
+	{
+		
+		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
+		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
+		
+        $response = '';
+        $response = curl_exec($ch);
+        if (!$response = curl_exec($ch)) {
+            $error_msg = "Unable to post request, underlying exception of " . curl_error($ch);
+            curl_close($ch);
+            throw new \Exception($error_msg);
+        }
+        else{
+            $this->curlResponseInfo = curl_getinfo($ch);
+        }
+        curl_close($ch);
+        return $response;
+    }
+    
+    /* Get the output of Curl Getinfo */
+    
+    public function getCurlResponseInfo()
+    {
+        return $this->curlResponseInfo;
+    }
+}
Index: /mods/mod_amazon/PayWithAmazon/Interface.php
===================================================================
--- /mods/mod_amazon/PayWithAmazon/Interface.php	(revision 5485)
+++ /mods/mod_amazon/PayWithAmazon/Interface.php	(revision 5485)
@@ -0,0 +1,479 @@
+<?php
+namespace PayWithAmazon;
+
+/* Interface class to showcase the public API methods for Pay With Amazon */
+
+interface ClientInterface
+{
+    /* Takes user configuration array from the user as input
+     * Takes JSON file path with configuration information as input
+     * Validates the user configuation array against existing config array
+     */
+    
+    public function __construct($config = null);
+    
+    /* Setter for sandbox
+     * Sets the boolean value for config['sandbox'] variable
+     */
+    
+    public function setSandbox($value);
+    
+    /* Setter for config['client_id']
+     * Sets the  value for config['client_id'] variable
+     */
+    
+    public function setClientId($value);
+    
+    /* 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 passowrd
+     */
+    
+    public function setProxy($proxy);
+    
+    /* Setter for $_mwsServiceUrl
+     * Set the URL to which the post request has to be made for unit testing 
+     */
+    
+    public function setMwsServiceUrl($url);
+    
+    /* Getter
+     * Gets the value for the key if the key exists in config
+     */
+    
+    public function __get($name);
+    
+    /* Getter for parameters string
+     * Gets the value for the parameters string for unit testing
+     */
+    
+    public function getParameters();
+    
+    /* GetUserInfo convenience funtion - Returns user's profile information from Amazon using the access token returned by the Button widget.
+     *
+     * @see http://docs.developer.amazonservices.com/en_US/apa_guide/APAGuide_ObtainProfile.html
+     * @param $access_token [String]
+     */
+    
+    public function getUserInfo($access_token);
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* ValidateBillingAgreement 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_ValidateBillignAgreement.html
+     *
+     * @param requestParameters['merchant_id'] - [String]
+     * @param requestParameters['amazon_billing_agreement_id'] - [String]
+     * @optional requestParameters['mws_auth_token'] - [String]
+     */
+    
+    public function validateBillingAgreement($requestParameters = array());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+    
+    /* 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());
+}
+
+/* Interface for IpnHandler.php */
+
+interface IpnHandlerInterface
+{
+   /* Takes headers and body of the IPN message as input in the constructor
+    * verifies that the IPN is from the right resource and has the valid data
+    */
+   
+    public function __construct($headers, $body, $ipnConfig = null);
+    
+    /* returnMessage() - JSON decode the raw [Message] portion of the IPN */
+    
+    public function returnMessage();
+
+    /* toJson() - Converts IPN [Message] field to JSON
+     *
+     * Has child elements
+     * ['NotificationData'] [XML] - API call XML notification data
+     * @param remainingFields - consists of remaining IPN array fields that are merged
+     * Type - Notification
+     * MessageId -  ID of the Notification
+     * Topic ARN - Topic of the IPN
+     * @return response in JSON format
+     */
+    
+    public function toJson();
+
+    /* toArray() - Converts IPN [Message] field to associative array
+     * @return response in array format
+     */
+    
+    public function toArray();
+}
+
+/* Interface for HttpCurl.php */
+
+interface HttpCurlInterface
+{
+    /* Takes user configuration array as input
+     * Takes configuration for API call or IPN config
+     */
+    
+    public function __construct($config = null);
+    
+    /* Set Http header for Access token for the GetUserInfo call */
+    
+    public function setHttpHeader();
+    
+    /* Setter for  Access token to get the user info */
+    
+    public function setAccessToken($accesstoken);
+    
+    /* POST using curl for the following situations
+     * 1. API calls
+     * 2. IPN certificate retrieval
+     * 3. Get User Info
+     */
+    
+    public function httpPost($url, $userAgent = null, $parameters = null);
+    
+    /* GET using curl for the following situations
+     * 1. IPN certificate retrieval
+     * 3. Get User Info
+     */
+    
+    public function httpGet($url, $userAgent = null);
+}
+
+/* Interface for ResponseParser.php */
+
+interface ResponseInterface
+{
+    /* Takes response from the API call */
+    
+    public function __construct($response = null);
+    
+    /* Returns the XML portion of the response */
+    
+    public function toXml();
+    
+    /* toJson  - converts XML into Json
+     * @param $response [XML]
+     */
+    
+    public function toJson();
+    
+    /* toArray  - converts XML into associative array
+     * @param $this->_response [XML]
+     */
+    
+    public function toArray();
+    
+    /* Get the status of the BillingAgreement */
+    
+    public function getBillingAgreementDetailsStatus($response);
+}
Index: /mods/mod_amazon/PayWithAmazon/IpnHandler.php
===================================================================
--- /mods/mod_amazon/PayWithAmazon/IpnHandler.php	(revision 5485)
+++ /mods/mod_amazon/PayWithAmazon/IpnHandler.php	(revision 5485)
@@ -0,0 +1,416 @@
+<?php
+namespace PayWithAmazon;
+
+/* Class IPN_Handler
+ * Takes headers and body of the IPN message as input in the constructor
+ * verifies that the IPN is from the right resource and has the valid data
+ */
+
+require_once 'HttpCurl.php';
+require_once 'Interface.php';
+class IpnHandler implements IpnHandlerInterface
+{
+
+    private $headers = null;
+    private $body = null;
+    private $snsMessage = null;
+    private $fields = array();
+    private $signatureFields = array();
+    private $certificate = null;
+    private $expectedCnName = 'sns.amazonaws.com';
+
+    private $ipnConfig = array('cabundle_file'  => null,
+			       'proxy_host' 	=> null,
+                               'proxy_port' 	=> -1,
+                               'proxy_username' => null,
+			       'proxy_password' => null);
+
+
+    public function __construct($headers, $body, $ipnConfig = null)
+    {
+        $this->headers = array_change_key_case($headers, CASE_LOWER);
+        $this->body = $body;
+
+        if ($ipnConfig != null) {
+            $this->checkConfigKeys($ipnConfig);
+        }
+
+        // Get the list of fields that we are interested in
+        $this->fields = array(
+            "Timestamp" => true,
+            "Message" => true,
+            "MessageId" => true,
+            "Subject" => false,
+            "TopicArn" => true,
+            "Type" => true
+        );
+
+        // Validate the IPN message header [x-amz-sns-message-type]
+        $this->validateHeaders();
+
+        // Converts the IPN [Message] to Notification object
+        $this->getMessage();
+
+        // Checks if the notification [Type] is Notification and constructs the signature fields
+        $this->checkForCorrectMessageType();
+
+        // Verifies the signature against the provided pem file in the IPN
+        $this->constructAndVerifySignature();
+    }
+
+    private function checkConfigKeys($ipnConfig)
+    {
+        $ipnConfig = array_change_key_case($ipnConfig, CASE_LOWER);
+	$ipnConfig = $this->trimArray($ipnConfig);
+
+        foreach ($ipnConfig as $key => $value) {
+            if (array_key_exists($key, $this->ipnConfig)) {
+                $this->ipnConfig[$key] = $value;
+            } else {
+                throw new \Exception('Key ' . $key . ' is either not part of the configuration or has incorrect Key name.
+				check the ipnConfig array key names to match your key names of your config array ', 1);
+            }
+        }
+    }
+
+    /* Setter function
+     * Sets the value for the key if the key exists in ipnConfig
+     */
+    
+    public function __set($name, $value)
+    {
+        if (array_key_exists(strtolower($name), $this->ipnConfig)) {
+            $this->ipnConfig[$name] = $value;
+        } else {
+            throw new \Exception("Key " . $name . " is not part of the configuration", 1);
+        }
+    }
+
+    /* Getter function
+     * Returns the value for the key if the key exists in ipnConfig
+     */
+    
+    public function __get($name)
+    {
+        if (array_key_exists(strtolower($name), $this->ipnConfig)) {
+            return $this->ipnConfig[$name];
+        } else {
+            throw new \Exception("Key " . $name . " was not found in the configuration", 1);
+        }
+    }
+
+    /* Trim the input Array key values */
+    
+    private function trimArray($array)
+    {
+	foreach ($array as $key => $value)
+	{
+	    $array[$key] = trim($value);
+	}
+	return $array;
+    }
+    
+    private function validateHeaders()
+    {
+        // Quickly check that this is a sns message
+        if (!array_key_exists('x-amz-sns-message-type', $this->headers)) {
+            throw new \Exception("Error with message - header " . "does not contain x-amz-sns-message-type header");
+        }
+
+        if ($this->headers['x-amz-sns-message-type'] !== 'Notification') {
+            throw new \Exception("Error with message - header x-amz-sns-message-type is not " . "Notification, is " . $this->headers['x-amz-sns-message-type']);
+        }
+    }
+
+    private function getMessage()
+    {
+        $this->snsMessage = json_decode($this->body, true);
+
+        $json_error = json_last_error();
+
+        if ($json_error != 0) {
+            $errorMsg = "Error with message - content is not in json format" . $this->getErrorMessageForJsonError($json_error) . " " . $this->snsMessage;
+            throw new \Exception($errorMsg);
+        }
+    }
+
+    /* Convert a json error code to a descriptive error message
+     *
+     * @param int $json_error message code
+     *
+     * @return string error message
+     */
+    
+    private function getErrorMessageForJsonError($json_error)
+    {
+        switch ($json_error) {
+            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;
+        }
+    }
+
+    /* checkForCorrectMessageType()
+     *
+     * Checks if the Field [Type] is set to ['Notification']
+     * Gets the value for the fields marked true in the fields array
+     * Constructs the signature string
+     */
+
+    private function checkForCorrectMessageType()
+    {
+        $type = $this->getMandatoryField("Type");
+        if (strcasecmp($type, "Notification") != 0) {
+            throw new \Exception("Error with SNS Notification - unexpected message with Type of " . $type);
+        }
+
+        if (strcmp($this->getMandatoryField("Type"), "Notification") != 0) {
+            throw new \Exception("Error with signature verification - unable to verify " . $this->getMandatoryField("Type") . " message");
+        } else {
+
+            // Sort the fields into byte order based on the key name(A-Za-z)
+            ksort($this->fields);
+
+            // Extract the key value pairs and sort in byte order
+            $signatureFields = array();
+            foreach ($this->fields as $fieldName => $mandatoryField) {
+                if ($mandatoryField) {
+                    $value = $this->getMandatoryField($fieldName);
+                } else {
+                    $value = $this->getField($fieldName);
+                }
+
+                if (!is_null($value)) {
+                    array_push($signatureFields, $fieldName);
+                    array_push($signatureFields, $value);
+                }
+            }
+
+            /* Create the signature string - key / value in byte order
+             * delimited by newline character + ending with a new line character
+             */
+            $this->signatureFields = implode("\n", $signatureFields) . "\n";
+
+        }
+    }
+
+    /* Verify that the signature is correct for the given data and
+     * public key
+     *
+     * @param string $data            data to validate
+     * @param string $signature       decoded signature to compare against
+     * @param string $certificatePath path to certificate, can be file or url
+     *
+     * @throws Exception if there is an error with the call
+     *
+     * @return bool true if valid
+     */
+    
+    private function constructAndVerifySignature()
+    {
+	$signature       = base64_decode($this->getMandatoryField("Signature"));
+        $certificatePath = $this->getMandatoryField("SigningCertURL");
+
+        $this->certificate = $this->getCertificate($certificatePath);
+
+        $result = $this->verifySignatureIsCorrectFromCertificate($signature);
+        if (!$result) {
+            throw new \Exception("Unable to match signature from remote server: signature of " . $this->getCertificate($certificatePath) . " , SigningCertURL of " . $this->getMandatoryField("SigningCertURL") . " , SignatureOf " . $this->getMandatoryField("Signature"));
+        }
+    }
+
+    /* getCertificate($certificatePath)
+     *
+     * gets the certificate from the $certificatePath using Curl
+     */
+    
+    private function getCertificate($certificatePath)
+    {
+        $httpCurlRequest  = new HttpCurl($this->ipnConfig);
+
+	$response = $httpCurlRequest->httpGet($certificatePath);
+
+        return $response;
+    }
+
+    /* Verify that the signature is correct for the given data and public key
+     *
+     * @param string $data            data to validate
+     * @param string $signature       decoded signature to compare against
+     * @param string $certificate     certificate object defined in Certificate.php
+     */
+
+    public function verifySignatureIsCorrectFromCertificate($signature)
+    {
+        $certKey = openssl_get_publickey($this->certificate);
+
+        if ($certKey === False) {
+            throw new \Exception("Unable to extract public key from cert");
+        }
+
+        try {
+            $certInfo    = openssl_x509_parse($this->certificate, true);
+            $certSubject = $certInfo["subject"];
+
+            if (is_null($certSubject)) {
+                throw new \Exception("Error with certificate - subject cannot be found");
+            }
+        } catch (\Exception $ex) {
+            throw new \Exception("Unable to verify certificate - error with the certificate subject", null, $ex);
+        }
+
+        if (strcmp($certSubject["CN"], $this->expectedCnName)) {
+            throw new \Exception("Unable to verify certificate issued by Amazon - error with certificate subject");
+        }
+
+        $result = -1;
+        try {
+            $result = openssl_verify($this->signatureFields, $signature, $certKey, OPENSSL_ALGO_SHA1);
+        } catch (\Exception $ex) {
+            throw new \Exception("Unable to verify signature - error with the verification algorithm", null, $ex);
+        }
+
+        return ($result > 0);
+    }
+
+
+    /* Extract the mandatory field from the message and return the contents
+     *
+     * @param string $fieldName name of the field to extract
+     *
+     * @throws Exception if not found
+     *
+     * @return string field contents if found
+     */
+    
+    private function getMandatoryField($fieldName)
+    {
+        $value = $this->getField($fieldName);
+        if (is_null($value)) {
+            throw new \Exception("Error with json message - mandatory field " . $fieldName . " cannot be found");
+        }
+        return $value;
+    }
+
+    /* Extract the field if present, return null if not defined
+     *
+     * @param string $fieldName name of the field to extract
+     *
+     * @return string field contents if found, null otherwise
+     */
+    
+    private function getField($fieldName)
+    {
+        if (array_key_exists($fieldName, $this->snsMessage)) {
+            return $this->snsMessage[$fieldName];
+        } else {
+            return null;
+        }
+    }
+
+    /* returnMessage() - JSON decode the raw [Message] portion of the IPN */
+    
+    public function returnMessage()
+    {
+        return json_decode($this->snsMessage['Message'], true);
+    }
+
+    /* toJson() - Converts IPN [Message] field to JSON
+     *
+     * Has child elements
+     * ['NotificationData'] [XML] - API call XML notification data
+     * @param remainingFields - consists of remaining IPN array fields that are merged
+     * Type - Notification
+     * MessageId -  ID of the Notification
+     * Topic ARN - Topic of the IPN
+     * @return response in JSON format
+     */
+    
+    public function toJson()
+    {
+        $response = $this->simpleXmlObject();
+
+        // Merging the remaining fields with the response
+        $remainingFields = $this->getRemainingIpnFields();
+        $responseArray = array_merge($remainingFields,(array)$response);
+
+        // Converting to JSON format
+        $response = json_encode($responseArray);
+
+        return $response;
+    }
+
+    /* toArray() - Converts IPN [Message] field to associative array
+     * @return response in array format
+     */
+    
+    public function toArray()
+    {
+        $response = $this->simpleXmlObject();
+
+        // Converting the SimpleXMLElement Object to array()
+        $response = json_encode($response);
+        $response = json_decode($response, true);
+
+        // Merging the remaining fields with the response array
+        $remainingFields = $this->getRemainingIpnFields();
+        $response = array_merge($remainingFields,$response);
+
+        return $response;
+    }
+
+    /* addRemainingFields() - Add remaining fields to the datatype
+     *
+     * Has child elements
+     * ['NotificationData'] [XML] - API call XML response data
+     * Convert to SimpleXML element object
+     * Type - Notification
+     * MessageId -  ID of the Notification
+     * Topic ARN - Topic of the IPN
+     * @return response in array format
+     */
+
+    private function simpleXmlObject()
+    {
+        $ipnMessage = $this->returnMessage();
+
+        // Getting the Simple XML element object of the IPN XML Response Body
+        $response = simplexml_load_string((string) $ipnMessage['NotificationData']);
+
+        // Adding the Type, MessageId, TopicArn details of the IPN to the Simple XML element Object
+        $response->addChild('Type', $this->snsMessage['Type']);
+        $response->addChild('MessageId', $this->snsMessage['MessageId']);
+        $response->addChild('TopicArn', $this->snsMessage['TopicArn']);
+
+        return $response;
+    }
+
+    /* getRemainingIpnFields()
+     * Gets the remaining fields of the IPN to be later appended to the return message
+     */
+    
+    private function getRemainingIpnFields()
+    {
+        $ipnMessage = $this->returnMessage();
+
+        $remainingFields = array(
+                            'NotificationReferenceId' =>$ipnMessage['NotificationReferenceId'],
+                            'NotificationType' =>$ipnMessage['NotificationType'],
+                            'SellerId' =>$ipnMessage['SellerId'],
+                            'ReleaseEnvironment' =>$ipnMessage['ReleaseEnvironment'] );
+
+        return $remainingFields;
+    }
+}
Index: /mods/mod_amazon/PayWithAmazon/Regions.php
===================================================================
--- /mods/mod_amazon/PayWithAmazon/Regions.php	(revision 5485)
+++ /mods/mod_amazon/PayWithAmazon/Regions.php	(revision 5485)
@@ -0,0 +1,23 @@
+<?php
+namespace PayWithAmazon;
+
+/* Class Regions
+ * Defines all Region specific properties
+ */
+
+class Regions
+{
+    public $mwsServiceUrls = array('eu' => 'mws-eu.amazonservices.com',
+				   'na' => 'mws.amazonservices.com',
+				   'jp' => 'mws.amazonservices.jp');
+    
+    public $profileEndpointUrls = array('uk' => 'amazon.co.uk',
+					'us' => 'amazon.com',
+					'de' => 'amazon.de',
+					'jp' => 'amazon.co.jp');
+    
+    public $regionMappings = array('de' => 'eu',
+				   'uk' => 'eu',
+				   'us' => 'na',
+				   'jp' => 'jp');
+}
Index: /mods/mod_amazon/PayWithAmazon/ResponseParser.php
===================================================================
--- /mods/mod_amazon/PayWithAmazon/ResponseParser.php	(revision 5485)
+++ /mods/mod_amazon/PayWithAmazon/ResponseParser.php	(revision 5485)
@@ -0,0 +1,100 @@
+<?php
+namespace PayWithAmazon;
+
+/* ResponseParser
+ * Methods provided to convert the Response from the POST to XML, Array or JSON
+ */
+
+require_once 'Interface.php';
+
+class ResponseParser implements ResponseInterface
+{
+    public $response = null;
+    
+    public function __construct($response=null)
+    {
+        $this->response = $response;
+    }
+   
+    /* Returns the XML portion of the response */
+    
+    public function toXml()
+    {
+        return $this->response['ResponseBody'];
+    }
+    
+    /* toJson - converts XML into Json
+     * @param $response [XML]
+     */
+    
+    public function toJson()
+    {
+        $response = $this->simpleXmlObject();
+        
+        return (json_encode($response));
+    }
+    
+    /* toArray - converts XML into associative array
+     * @param $this->response [XML]
+     */
+    
+    public function toArray()
+    {
+        $response = $this->simpleXmlObject();
+        
+        // Converting the SimpleXMLElement Object to array()
+        $response = json_encode($response);
+        
+        return (json_decode($response, true));
+    }
+    
+    private function simpleXmlObject()
+    {
+        $response = $this->response;
+        
+        // Getting the HttpResponse Status code to the output as a string
+        $status = strval($response['Status']);
+        
+        // Getting the Simple XML element object of the XML Response Body
+        $response = simplexml_load_string((string) $response['ResponseBody']);
+        
+        // Adding the HttpResponse Status code to the output as a string
+        $response->addChild('ResponseStatus', $status);
+        
+        return $response;
+    }
+    
+    /* Get the status of the Order Reference ID */
+    
+    public function getOrderReferenceDetailsStatus($response)
+    {
+       $data= new \SimpleXMLElement($response);
+        $namespaces = $data->getNamespaces(true);
+        foreach($namespaces as $key=>$value){
+            $namespace = $value;
+        }
+        $data->registerXPathNamespace('GetORO', $namespace);
+        foreach ($data->xpath('//GetORO:OrderReferenceStatus') as $value) {
+            $oroStatus = json_decode(json_encode((array)$value), TRUE);
+        }
+        
+        return $oroStatus ;
+    }
+    
+    /* Get the status of the BillingAgreement */
+    
+    public function getBillingAgreementDetailsStatus($response)
+    {
+       $data= new \SimpleXMLElement($response);
+        $namespaces = $data->getNamespaces(true);
+        foreach($namespaces as $key=>$value){
+            $namespace = $value;
+        }
+        $data->registerXPathNamespace('GetBA', $namespace);
+        foreach ($data->xpath('//GetBA:BillingAgreementStatus') as $value) {
+            $baStatus = json_decode(json_encode((array)$value), TRUE);
+        }
+        
+        return $baStatus ;
+    }
+}
Index: /mods/wpsg_mod_amazon.class.php
===================================================================
--- /mods/wpsg_mod_amazon.class.php	(revision 5485)
+++ /mods/wpsg_mod_amazon.class.php	(revision 5485)
@@ -0,0 +1,775 @@
+<?php
+
+	/**
+	 * Modul fÃŒr "Bezahlen mit Amazon"
+	 * @author Daschmi (12.03.2016)
+	 */
+	class wpsg_mod_amazon extends wpsg_mod_basic
+	{
+		
+		var $lizenz = 1;
+		var $id = 3140;
+		var $hilfeURL = 'http://wpshopgermany.de/?p=3462587';
+		var $version = "9.9.9";
+		var $free = false;
+		
+		const ORDER_DONE_START = 0; // Zahlung starten lassen mit Pay with Amazon
+		const ORDER_DONE_GO = 1; // Zahlung gestartet, Wallet aneigen
+		const ORDER_DONE_PAST = 9; // Zahlung bereits durchgefÃŒhrt
+		const ORDER_DONE = 10; // Zahlung wurde gerade durchgefÃŒhrt
+		
+		/**
+		 * Kostruktor
+		 */
+		public function __construct()
+		{
+				
+			parent::__construct();
+				
+			$this->name = __('AmazonPayment', 'wpsg');
+			$this->group = __('Zahlungsarten', 'wpsg');
+			$this->desc = __('ErmÃ¶glicht die Anmeldung / Zahlung mit Amazon.', 'wpsg');
+		
+		} // public function __construct()
+		
+		public function install()
+		{
+			 
+			$this->shop->checkDefault('wpsg_mod_amazon_aktiv', '1');
+			$this->shop->checkDefault('wpsg_mod_amazon_basket', '1');
+			$this->shop->checkDefault('wpsg_mod_amazon_name', __('AmazonPayment', 'wpsg'), false, true);
+			$this->shop->checkDefault('wpsg_mod_amazon_hint', __('Bezahlen Sie ihre Bestellung einfach mit ihrem Amazon Konto.', 'wpsg'), false, true);
+			$this->shop->checkDefault('wpsg_mod_amazon_subject', __('O%order_id% - K%kunde_id%', 'wpsg'), false, true);
+						
+			
+		} // public function install()
+		
+		public function settings_edit()
+		{
+			
+			$this->shop->view['ipnURL'] = $this->shop->getUrl(wpsg_ShopController::URL_BASKET, 'wpsg_mod_amazon', 'ipn');
+			$this->shop->view['jsOrigin'] = preg_replace('/^http:\/\//i', 'https://', $this->shop->get_option('siteurl')); 
+			
+			$this->shop->render(WPSG_PATH_VIEW.'/mods/mod_amazon/settings_edit.phtml');
+			
+		} // public function settings_edit()
+		
+		public function settings_save()
+		{
+			
+			$this->shop->update_option('wpsg_mod_amazon_seller_id', $_REQUEST['wpsg_mod_amazon_seller_id']);
+			$this->shop->update_option('wpsg_mod_amazon_mws_access_key_id', $_REQUEST['wpsg_mod_amazon_mws_access_key_id']);
+			$this->shop->update_option('wpsg_mod_amazon_mws_access_key', $_REQUEST['wpsg_mod_amazon_mws_access_key']);
+			$this->shop->update_option('wpsg_mod_amazon_lwa_client_id', $_REQUEST['wpsg_mod_amazon_lwa_client_id']);
+			$this->shop->update_option('wpsg_mod_amazon_lwa_client_secret', $_REQUEST['wpsg_mod_amazon_lwa_client_secret']);
+			$this->shop->update_option('wpsg_mod_amazon_sandbox', $_REQUEST['wpsg_mod_amazon_sandbox']);
+			
+			$this->shop->update_option('wpsg_mod_amazon_aktiv', $_REQUEST['wpsg_mod_amazon_aktiv']);
+			$this->shop->update_option('wpsg_mod_amazon_name', $_REQUEST['wpsg_mod_amazon_name'], false, true);
+			$this->shop->update_option('wpsg_mod_amazon_subject', $_REQUEST['wpsg_mod_amazon_subject'], false, true);
+			$this->shop->update_option('wpsg_mod_amazon_hint', $_REQUEST['wpsg_mod_amazon_hint'], false, true);
+			$this->shop->update_option('wpsg_mod_amazon_basket', $_REQUEST['wpsg_mod_amazon_basket']);
+						
+			$this->shop->update_option('wpsg_mod_amazon_gebuehr', $_REQUEST['wpsg_mod_amazon_gebuehr']);
+			$this->shop->update_option('wpsg_mod_amazon_mwst', $_REQUEST['wpsg_mod_amazon_mwst']);
+			$this->shop->update_option('wpsg_mod_amazon_mwstland', $_REQUEST['wpsg_mod_amazon_mwstland']);
+						
+		} // public function settings_save()
+		
+		public function basket_inner_prebutton(&$basket_view) 
+		{ 
+			
+			// Amazon ÃŒberhaupt mÃ¶glich
+			if (!array_key_exists($this->id, $this->shop->arPayment)) return;
+			
+			if (!wpsg_isSizedInt($this->shop->get_option('wpsg_mod_amazon_aktiv')) || !wpsg_isSizedInt($this->shop->get_option('wpsg_mod_amazon_basket'))) return;
+			
+			$oOrder = wpsg_order::getInstance($_SESSION['wpsg']['order_id']);
+			
+			$this->shop->view['wpsg_mod_amazon']['redirectURL'] = $this->shop->getUrl(wpsg_ShopController::URL_BASKET, 'wpsg_mod_amazon', 'return');
+			$this->shop->view['wpsg_mod_amazon']['logoutURL'] = $this->shop->getUrl(wpsg_ShopController::URL_BASKET, 'wpsg_mod_amazon', 'logout');
+			$this->shop->view['wpsg_mod_amazon']['amazon_last_authorization_reference_id'] = $oOrder->getMeta('amazon_last_authorization_reference_id');
+		
+			$this->shop->render(WPSG_PATH_VIEW.'/mods/mod_amazon/basket_inner_prebutton.phtml');
+			
+		} // public function basket_inner_prebutton(&$basket_view)
+		
+		public function setOrderStatus($order_id, $status_id, $inform)
+		{
+		 
+			$oOrder = wpsg_order::getInstance($order_id);
+				
+			if ($oOrder->getPaymentID() == $this->id && in_array($status_id, array(wpsg_ShopController::STATUS_STORNIERT, wpsg_ShopController::STATUS_ZURUECKGEZAHLT)))
+			{
+				
+				$capture_id = $oOrder->getMeta('amazon_capture_id');
+		
+				if (wpsg_isSizedString($capture_id))
+				{
+					
+					$arReturn = $this->api_refund($oOrder->id);
+					
+					if (wpsg_isSizedString($arReturn['RefundResult']['RefundDetails']['AmazonRefundId']))
+					{
+						
+						$this->shop->addBackendMessage(__('Der Betrag wurde erfolgreich zurÃŒckerstattet.', 'wpsg'));
+						
+					}
+					else
+					{
+						
+						$this->shop->addBackendError(__('Es gab technische Probleme beim zurÃŒckerstatten des Betrages.', 'wpsg'));
+						
+						$oOrder->log(__('Fehler beim zurÃŒckerstatten des Bestellbetrages', 'wpsg'), print_r($arReturn, 1));
+						
+					}
+					
+				}
+				else
+				{
+					
+					$this->shop->addBackendError(__('RÃŒckzahlung nicht mÃ¶glich, da keine Capture/Authorize ID gesetzt wurde', 'wpsg'));
+					
+				}
+				
+			} 
+		
+		} // public function setOrderStatus($order_id, $status_id, $inform)
+		
+		public function wp_head()
+		{
+			
+			if (!wpsg_isSizedInt($this->shop->get_option('wpsg_mod_amazon_aktiv'))) return;
+			
+			echo '<script type="text/javascript"> window.onAmazonLoginReady = function() { amazon.Login.setClientId("'.$this->shop->get_option('wpsg_mod_amazon_lwa_client_id').'"); }; </script>';
+			
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_mod_amazon_sandbox')))
+			{
+			
+				echo '<script type="text/javascript" src="https://static-eu.payments-amazon.com/OffAmazonPayments/de/sandbox/lpa/js/Widgets.js"></script>';
+				
+			}
+			else
+			{
+				
+				echo '<script type="text/javascript" src="https://static-eu.payments-amazon.com/OffAmazonPayments/de/lpa/js/Widgets.js"></script>';
+				
+			}
+			
+		} // public function wp_head()
+		
+		public function order_done($order_id, $done_view)
+		{
+						
+			$oOrder = wpsg_order::getInstance($order_id);
+			
+			if ($oOrder->getAmount() <= 0 || $oOrder->getPaymentID() != $this->id) return;
+					 
+			if (wpsg_isSizedString($oOrder->getMeta('amazon_order_reference_id')) && !wpsg_isSizedString($oOrder->getMeta('amazon_order_done')))
+			{
+	 
+				// Zahlung durchfÃŒhren				 
+				$this->api_setPayment($order_id);
+				$this->api_finishPayment($order_id);
+							
+				$capture_return = $this->api_capturePayment($order_id);
+				
+				$this->api_closePayment($order_id);
+				
+				if (wpsg_isSizedString($capture_return['AuthorizeResult']['AuthorizationDetails']['AuthorizationAmount']['Amount']))
+				{
+				
+					$amount_captured = wpsg_tf($capture_return['AuthorizeResult']['AuthorizationDetails']['AuthorizationAmount']['Amount']);
+					
+					if ($this->shop->setPayMent($oOrder->id, $amount_captured))
+					{
+							
+						//$this->shop->setOrderStatus($oOrder->id, 100, true);
+						$oOrder->setMeta('amazon_order_done', serialize($capture_return));
+											
+						$this->shop->view['wpsg_mod_amazon']['state'] = self::ORDER_DONE;
+						return $this->shop->render(WPSG_PATH_VIEW.'/mods/mod_amazon/order_done.phtml');
+						
+					}
+					
+				}
+			 
+			}
+			else if (wpsg_isSizedString($oOrder->getMeta('amazon_order_done')))
+			{
+				 
+				$this->shop->view['wpsg_mod_amazon']['state'] = self::ORDER_DONE_PAST;
+				return $this->shop->render(WPSG_PATH_VIEW.'/mods/mod_amazon/order_done.phtml');
+				
+			}
+		 
+			if (wpsg_isSizedString($oOrder->getMeta('amazon_order_reference_id')))
+			{
+						
+				$oOrder->setMeta('amazon_order_reference_id', null);
+				$oOrder->setMeta('amazon_last_authorization_reference_id', null);
+			
+			}
+			
+			if (wpsg_isSizedString($_REQUEST['access_token']))
+			{
+				
+				$this->shop->view['wpsg_mod_amazon']['state'] = self::ORDER_DONE_GO;
+				
+			}
+			else
+			{
+			
+				$this->shop->view['wpsg_mod_amazon']['state'] = self::ORDER_DONE_START;
+								
+			}
+			
+			$this->shop->view['wpsg_mod_amazon']['redirectURL'] = $this->shop->getDoneURL($order_id);
+			
+			return $this->shop->render(WPSG_PATH_VIEW.'/mods/mod_amazon/order_done.phtml');
+			
+		} // public function order_done($order_id, $done_view)
+		
+		public function basket_checkoutAction(&$basketController)
+		{
+			
+			if ($_SESSION['wpsg']['checkout']['payment'] != $this->id || !wpsg_isSizedInt($this->shop->get_option('wpsg_mod_amazon_basket'))) return;
+			
+			if (wpsg_isSizedString($_REQUEST['amazon']['OrderReference']))
+			{
+									
+				$oOrder = wpsg_order::getInstance($_SESSION['wpsg']['order_id']);
+								
+				$oOrder->setMeta('amazon_order_reference_id', $_REQUEST['amazon']['OrderReference']);
+												
+				$res = $this->api_getPayment($_SESSION['wpsg']['order_id']);
+				 						 
+				$res = $res['GetOrderReferenceDetailsResult']['OrderReferenceDetails'];
+				
+				list($vname, $name) = wpsg_explodeName($res['Destination']['PhysicalDestination']['Name']);
+				 					
+				if (wpsg_isSizedString($res['Buyer']['Email']) && !wpsg_isSizedString($_SESSION['wpsg']['checkout']['email']))
+				{
+					
+					$_SESSION['wpsg']['checkout']['email'] = $res['Buyer']['Email'];
+					$_SESSION['wpsg']['checkout']['email2'] = $res['Buyer']['Email'];
+					
+				}
+				
+				if (wpsg_isSizedString($res['Buyer']['Phone'])) $_SESSION['wpsg']['checkout']['tel'] = $res['Buyer']['Phone'];
+				 
+				// Vereinfacht auf die Ãbernahme der Lieferadresse
+				if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']))
+				{
+				
+					if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['vname'])) $_SESSION['wpsg']['checkout']['vname'] = $vname;
+					if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['name'])) $_SESSION['wpsg']['checkout']['name'] = $name;
+				
+					if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']['AddressLine1'])) $res['Destination']['PhysicalDestination']['AddressLine1'] = implode(' ', $res['Destination']['PhysicalDestination']['AddressLine1']);
+					if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']['AddressLine2'])) $res['Destination']['PhysicalDestination']['AddressLine1'] = implode(' ', $res['Destination']['PhysicalDestination']['AddressLine2']);
+						
+					if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['strasse'])) $_SESSION['wpsg']['checkout']['strasse'] = ltrim(implode(', ', array(
+							wpsg_getStr($res['Destination']['PhysicalDestination']['AddressLine1']),
+							wpsg_getStr($res['Destination']['PhysicalDestination']['AddressLine2']),
+					)), ', ');
+					if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['plz'])) $_SESSION['wpsg']['checkout']['plz'] = $res['Destination']['PhysicalDestination']['PostalCode'];
+					if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['ort'])) $_SESSION['wpsg']['checkout']['ort'] = $res['Destination']['PhysicalDestination']['City'];
+						
+					// Land wenn gefunden
+					$country_id = wpsg_country::getCountryIDFromCode($res['Destination']['PhysicalDestination']['CountryCode']);
+					if (!wpsg_isSizedInt($_SESSION['wpsg']['checkout']['land']) && wpsg_isSizedString($country_id)) $_SESSION['wpsg']['checkout']['land'] = $country_id;
+						
+				}
+				
+				/*
+				if ($this->shop->hasMod('wpsg_mod_shippingadress'))
+				{
+					
+					// Lieferadresse ist aktiv
+					// Lieferadresse aus Amazon -> Lieferadresse
+					// Recnungsadresse aus Amazon -> Rechnungsadresse
+					// Eventuell schauen ob gleich ist :?
+				
+					if (wpsg_isSizedArray($res['BillingAddress']['PhysicalAddress']))
+					{
+						
+						if (wpsg_isSizedArray($res['BillingAddress']['PhysicalAddress']['AddressLine1'])) $res['BillingAddress']['PhysicalAddress']['AddressLine1'] = implode(' ', $res['Destination']['PhysicalDestination']['AddressLine1']);
+						if (wpsg_isSizedArray($res['BillingAddress']['PhysicalAddress']['AddressLine2'])) $res['BillingAddress']['PhysicalAddress']['AddressLine1'] = implode(' ', $res['Destination']['PhysicalDestination']['AddressLine2']);
+					
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['shipping_vname'])) $_SESSION['wpsg']['checkout']['shipping_vname'] = $vname;
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['shipping_name'])) $_SESSION['wpsg']['checkout']['shipping_name'] = $name;
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['strasse'])) $_SESSION['wpsg']['checkout']['strasse'] = ltrim(implode(', ', array(
+								wpsg_getStr($res['BillingAddress']['PhysicalAddress']['AddressLine1']),
+								wpsg_getStr($res['BillingAddress']['PhysicalAddress']['AddressLine2']),
+						)), ', ');
+						
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['plz'])) $_SESSION['wpsg']['checkout']['plz'] = $res['BillingAddress']['PhysicalAddress']['PostalCode'];
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['ort'])) $_SESSION['wpsg']['checkout']['ort'] = $res['BillingAddress']['PhysicalAddress']['City'];
+						
+						// Land wenn gefunden
+						$country_id = wpsg_country::getCountryIDFromCode($res['BillingAddress']['PhysicalAddress']['CountryCode']);
+						if (!wpsg_isSizedInt($_SESSION['wpsg']['checkout']['land']) && wpsg_isSizedString($country_id)) $_SESSION['wpsg']['checkout']['land'] = $country_id;
+						
+					}
+					
+					if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']))
+					{
+						 						
+						if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']['AddressLine1'])) $res['Destination']['PhysicalDestination']['AddressLine1'] = implode(' ', $res['Destination']['PhysicalDestination']['AddressLine1']);
+						if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']['AddressLine2'])) $res['Destination']['PhysicalDestination']['AddressLine1'] = implode(' ', $res['Destination']['PhysicalDestination']['AddressLine2']);
+						
+						if (!isset($_SESSION['wpsg']['checkout']['diff_shippingadress'])) $_SESSION['wpsg']['checkout']['diff_shippingadress'] = '1';
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['shipping_vname'])) $_SESSION['wpsg']['checkout']['shipping_vname'] = $vname;
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['shipping_name'])) $_SESSION['wpsg']['checkout']['shipping_name'] = $name;
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['shipping_strasse'])) $_SESSION['wpsg']['checkout']['shipping_strasse'] = ltrim(implode(', ', array(
+							wpsg_getStr($res['Destination']['PhysicalDestination']['AddressLine1']),
+							wpsg_getStr($res['Destination']['PhysicalDestination']['AddressLine2']),
+						)), ', ');
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['shipping_plz'])) $_SESSION['wpsg']['checkout']['shipping_plz'] = $res['Destination']['PhysicalDestination']['PostalCode'];
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['shipping_ort'])) $_SESSION['wpsg']['checkout']['shipping_ort'] = $res['Destination']['PhysicalDestination']['City'];
+						
+						// Land wenn gefunden
+						$country_id = wpsg_country::getCountryIDFromCode($res['Destination']['PhysicalDestination']['CountryCode']);
+						if (!wpsg_isSizedInt($_SESSION['wpsg']['checkout']['shipping_land']) && wpsg_isSizedString($country_id)) $_SESSION['wpsg']['checkout']['shipping_land'] = $country_id;
+											
+					}
+					
+				}
+				else
+				{
+				
+					// Modul "Lieferanschrift" ist deaktiviert
+					// Ich nehme hier als Liefer- und Rechnungsanschrift die Daten aus Amazon Lieferanschrift
+					if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']))
+					{
+										
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['vname'])) $_SESSION['wpsg']['checkout']['vname'] = $vname;
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['name'])) $_SESSION['wpsg']['checkout']['name'] = $name;
+						
+						if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']['AddressLine1'])) $res['Destination']['PhysicalDestination']['AddressLine1'] = implode(' ', $res['Destination']['PhysicalDestination']['AddressLine1']);
+						if (wpsg_isSizedArray($res['Destination']['PhysicalDestination']['AddressLine2'])) $res['Destination']['PhysicalDestination']['AddressLine1'] = implode(' ', $res['Destination']['PhysicalDestination']['AddressLine2']);
+					
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['strasse'])) $_SESSION['wpsg']['checkout']['strasse'] = ltrim(implode(', ', array(
+								wpsg_getStr($res['Destination']['PhysicalDestination']['AddressLine1']),
+								wpsg_getStr($res['Destination']['PhysicalDestination']['AddressLine2']),
+						)), ', ');
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['plz'])) $_SESSION['wpsg']['checkout']['plz'] = $res['Destination']['PhysicalDestination']['PostalCode'];
+						if (!wpsg_isSizedString($_SESSION['wpsg']['checkout']['ort'])) $_SESSION['wpsg']['checkout']['ort'] = $res['Destination']['PhysicalDestination']['City'];
+					
+						// Land wenn gefunden
+						$country_id = wpsg_country::getCountryIDFromCode($res['Destination']['PhysicalDestination']['CountryCode']);
+						if (!wpsg_isSizedInt($_SESSION['wpsg']['checkout']['land']) && wpsg_isSizedString($country_id)) $_SESSION['wpsg']['checkout']['land'] = $country_id;
+							
+					}
+					
+				}
+				*/
+						
+				$this->shop->basket->initFromSession(true);
+				$check1 = $this->shop->basket->checkCheckout(1);
+										
+				if ($check1 === true)
+				{
+				
+					$check2 = $this->shop->basket->checkCheckout(2);
+					
+					if ($check2 === true)
+					{
+						
+						$this->shop->redirect($this->shop->getUrl(wpsg_ShopController::URL_OVERVIEW));
+						
+					}
+					else
+					{
+						
+						$this->shop->redirect($this->shop->getUrl(wpsg_ShopController::URL_CHECKOUT2));
+						
+					}
+					
+				}
+				else
+				{
+					
+					$this->shop->redirect($this->shop->getUrl(wpsg_ShopController::URL_CHECKOUT));
+					
+				}
+				 
+			}
+			
+			if ($bError) return -2;
+			
+		} // public function basket_checkoutAction(&$basketController)
+		
+		public function addPayment(&$arPayment)
+		{
+		
+			if (is_admin() || $this->shop->get_option('wpsg_mod_amazon_aktiv') == '1')
+			{
+					
+				$mwst_value = $this->getTaxValue($this->shop->get_option('wpsg_mod_amazon_mwst'), $this->shop->getDefaultCountry());
+					
+				$arPayment[$this->id] = array(
+					'id' => $this->id,
+					'name' => __($this->shop->get_option('wpsg_mod_amazon_name'), 'wpsg'),
+					'preis' => $this->shop->get_option('wpsg_mod_amazon_gebuehr'),
+					'mwst' => $this->shop->get_option('wpsg_mod_amazon_mwst'),
+					'mwst_value' => $mwst_value,
+					'mwst_null' => $this->shop->get_option('wpsg_mod_amazon_mwstland'),
+					'hint' => __($this->shop->get_option('wpsg_mod_amazon_hint'), 'wpsg'),
+					//'logo' => $this->shop->getRessourceURL('gfx/amazon.png')
+				);
+					
+				if (isset($_REQUEST['wpsg_checkout2']) || ($this->shop->hasMod('wpsg_mod_onepagecheckout') && isset($_REQUEST['wpsg_checkout'])))
+				{
+						
+					$arBasket = $this->shop->basket->toArray();
+						
+					if ($this->shop->get_option('wpsg_preisangaben') == WPSG_NETTO || $arBasket['noMwSt'])
+					{
+							
+						$payment_netto = $this->getPreis($this->shop->get_option('wpsg_mod_amazon_gebuehr'), $arBasket['sum']['preis']);
+						$payment_brutto = wpsg_calculatePreis($payment_netto, WPSG_BRUTTO, $mwst_value);
+							
+					}
+					else
+					{
+							
+						$payment_brutto = $this->getPreis($this->shop->get_option('wpsg_mod_amazon_gebuehr'), $arBasket['sum']['preis']);
+						$payment_netto = wpsg_calculatePreis($payment_brutto, WPSG_NETTO, $mwst_value);
+							
+					}
+						
+					// Entsprechenden Preis anzeigen
+					if ($this->shop->getFrontendTaxview() == WPSG_NETTO)
+					{
+							
+						$arPayment[$this->id]['price'] = $payment_netto;
+							
+					}
+					else
+					{
+							
+						$arPayment[$this->id]['price'] = $payment_brutto;
+							
+					}
+						
+				}
+					
+			}
+				
+		} // public function addPayment(&$arPayment)
+				
+		public function calcPayment(&$arBasket)
+		{
+		
+			if ($this->id == $arBasket['checkout']['payment'])
+			{
+		
+				$payment_price = $this->getPreis($this->shop->get_option('wpsg_mod_amazon_gebuehr'), $arBasket['sum']['preis']);
+		
+				if ($this->shop->get_option('wpsg_mod_amazon_mwstland') == '1' && $arBasket['noMwSt'])
+				{
+		
+					// Keine MwSt. berechnen !
+					$arBasket['sum']['preis_payment'] = $payment_price;
+					$arBasket['sum']['preis_payment_brutto'] = $payment_price;
+					$arBasket['sum']['preis_payment_netto'] = $payment_price;
+		
+					$arBasket['payment']['mwst'] = 0;
+					$this->shop->arPayment[$arBasket['checkout']['payment']]['mwst_value'] = 0;
+		
+					$arBasket['payment']['tax_rata'] = false;
+		
+				}
+				else
+				{
+		
+					// MwSt berechnen (Angaben im Backend sind immer Brutto)
+					$this->setPaymentKosten($arBasket, $payment_price, $this->shop->get_option('wpsg_mod_amazon_mwstland'));
+		
+				}
+		
+			}
+		
+		} // public function calcPayment(&$arBasket)
+		
+		/* Redirect */
+		 
+		public function ipnRedirect()
+		{
+			 
+			require_once WPSG_PATH_MOD.'mod_amazon/PayWithAmazon/IpnHandler.php';
+			
+			$headers = getallheaders();
+			$body = file_get_contents('php://input');
+			
+			$ipnHandler = new \PayWithAmazon\IpnHandler($headers, $body);
+			$ipnData = $ipnHandler->toArray();
+			
+			if (wpsg_isSizedArray($ipnData) && wpsg_isSizedString($ipnData['NotificationType'], 'PaymentCapture'))
+			{
+				
+				$order_id = $ipnData['CaptureDetails']['CaptureReferenceId'];
+				$oOrder = wpsg_order::getInstance($order_id); 
+								
+				if (wpsg_isSizedInt($this->shop->get_option('wpsg_debugModus'))) $oOrder->log('Amazon:notification:PaymentCapture', print_r($ipnData, 1));
+				
+				if ($this->shop->setPayMent($order_id, $ipnData['CaptureDetails']['CaptureAmount']['Amount']) && wpsg_isSizedString($ipnData['CaptureDetails']['CaptureStatus']['State'], 'Completed'))
+				{
+				
+					$oOrder->setMeta('amazon_capture_id', $ipnData['CaptureDetails']['AmazonCaptureId']);
+					
+					$this->shop->setOrderStatus($order_id, 100, true);
+						
+				}
+				
+			}
+			  			
+		} // public function ipnRedirect()
+		
+		public function logoutRedirect()
+		{
+			
+			$this->shop->redirect($this->shop->getUrl(wpsg_ShopController::URL_BASKET));
+			
+		} // public function logoutRedirect()
+		 
+		public function returnRedirect()
+		{
+			  
+			if (wpsg_isSizedInt($_REQUEST['order_id']) && wpsg_isSizedString($_REQUEST['wpsg_done']))
+			{
+			
+				if (wpsg_isSizedString($_REQUEST['amazon']['OrderReference']))
+				{
+				
+					$hash = preg_replace('/(\+)|(\=)/', 'A', base64_encode(md5('wpShopGermany'.$_REQUEST['order_id'], $this->shop->get_option("wpsg_salt"))));
+						
+					if ($hash != $_REQUEST['wpsg_done']) die(__('Aufrufsfehler!!', 'wpsg'));
+					
+					$oOrder = wpsg_order::getInstance($_REQUEST['order_id']);
+					$oOrder->setMeta('amazon_order_reference_id', $_REQUEST['amazon']['OrderReference']);
+					
+				}
+				
+				$this->shop->redirect($this->shop->getDoneURL($_REQUEST['order_id']));
+				
+			}
+			
+			$oOrder = wpsg_order::getInstance($_SESSION['wpsg']['order_id']);
+			$oOrder->setMeta('amazon_last_authorization_reference_id', $_REQUEST['access_token']);
+						
+			$_SESSION['wpsg']['checkout']['payment'] = $this->id;
+					
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_mod_amazon_sandbox'))) $c = curl_init('https://api.sandbox.amazon.com/auth/o2/tokeninfo?access_token='. urlencode($_REQUEST['access_token']));
+			else $c = curl_init('https://api.amazon.com/auth/o2/tokeninfo?access_token='. urlencode($_REQUEST['access_token']));
+			
+			curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
+			curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
+			
+			$r = curl_exec($c); curl_close($c); $d = json_decode($r);
+			
+			if ($d->aud != $this->shop->get_option('wpsg_mod_amazon_lwa_client_id')) 
+			{
+				
+				die("Amazon API Fehler!");
+				exit;
+				
+			}
+
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_mod_amazon_sandbox'))) $c = curl_init('https://api.sandbox.amazon.com/user/profile');
+			else $c = curl_init('https://api.amazon.com/user/profile');
+			
+			curl_setopt($c, CURLOPT_HTTPHEADER, array('Authorization: bearer '.$_REQUEST['access_token']));
+			
+			curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
+			curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
+			
+			$r = curl_exec($c); curl_close($c); $d = json_decode($r);
+			
+			$_SESSION['wpsg']['checkout']['email'] = $d->email;
+			$_SESSION['wpsg']['checkout']['email2'] = $d->email;
+			
+			$this->shop->redirect($this->shop->getUrl(wpsg_ShopController::URL_BASKET, false, false, array(), false, 'LoginWithAmazon'));
+						
+		} // public function returnRedirect()
+		
+		// Modulfunktionen 
+		 
+		private function api_finishPayment($order_id)
+		{
+			
+			require_once WPSG_PATH_MOD.'mod_amazon/PayWithAmazon/Client.php';
+			
+			$oOrder = wpsg_order::getInstance($order_id);
+			$client = new PayWithAmazon\Client($this->getAPIConfig());
+			
+			$oResponse = $client->confirmOrderReference(array(
+				'amazon_order_reference_id' => $oOrder->getMeta('amazon_order_reference_id'),
+				'mws_auth_token' => $oOrder->getMeta('amazon_last_authorization_reference_id')
+			));
+			
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_debugModus'))) $oOrder->log('Amazon:confirmOrderReference', print_r($oResponse->toArray(), 1));
+			
+			return $oResponse->toArray();
+						
+		} // private function api_finishPayment($order_id)
+		
+		private function api_getPayment($order_id)
+		{
+			
+			require_once WPSG_PATH_MOD.'mod_amazon/PayWithAmazon/Client.php';
+			
+			$oOrder = wpsg_order::getInstance($order_id);
+			$client = new PayWithAmazon\Client($this->getAPIConfig());
+			
+			$oResponse = $client->getOrderReferenceDetails(array(
+				'amazon_order_reference_id' => $oOrder->getMeta('amazon_order_reference_id'),
+				'address_consent_token' => $oOrder->getMeta('amazon_last_authorization_reference_id')
+			));
+			
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_debugModus'))) $oOrder->log('Amazon:getOrderReferenceDetails', print_r($oResponse->toArray(), 1));
+		
+			return $oResponse->toArray();
+			
+		} // private function api_getPayment($order_id)
+		
+		private function api_setPayment($order_id)
+		{
+			
+			require_once WPSG_PATH_MOD.'mod_amazon/PayWithAmazon/Client.php';
+			
+			$oOrder = wpsg_order::getInstance($order_id);			
+			$client = new PayWithAmazon\Client($this->getAPIConfig());
+			
+			$oResponse = $client->setOrderReferenceDetails(array(
+				'amazon_order_reference_id' => $oOrder->getMeta('amazon_order_reference_id'),
+				'amount' => $oOrder->getAmount(),
+				'seller_order_id' => $oOrder->id,
+				'mws_auth_token' => $oOrder->getMeta('amazon_last_authorization_reference_id'),
+				'seller_note' => $this->shop->replaceUniversalPlatzhalter(__($this->shop->get_option('wpsg_mod_amazon_subject'), 'wpsg'), $order_id),
+				'currency_code' => 'EUR'
+			));
+			
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_debugModus'))) $oOrder->log('Amazon:setOrderReferenceDetails', print_r($oResponse->toArray(), 1));
+			
+			return $oResponse->toArray();
+			 						
+		} // private function api_setPayment($order_id)
+		 
+		private function api_capturePayment($order_id)
+		{
+			
+			require_once WPSG_PATH_MOD.'mod_amazon/PayWithAmazon/Client.php';
+			
+			$oOrder = wpsg_order::getInstance($order_id);
+			$client = new PayWithAmazon\Client($this->getAPIConfig());
+			
+			$oResponse = $client->authorize(array(
+				'amazon_order_reference_id' => $oOrder->getMeta('amazon_order_reference_id'),
+				'authorization_amount' => $oOrder->getAmount(),
+				'authorization_reference_id' => $order_id,
+				'soft_descriptor' => $this->shop->replaceUniversalPlatzhalter(__($this->shop->get_option('wpsg_mod_amazon_subject'), 'wpsg'), $order_id),
+				'seller_authorization_note' => $this->shop->replaceUniversalPlatzhalter(__($this->shop->get_option('wpsg_mod_amazon_subject'), 'wpsg'), $order_id),
+				'currency_code' => 'EUR',
+				'capture_now' => true
+			));
+			
+			$arReturn = $oResponse->toArray();
+			
+			if (wpsg_isSizedString($arReturn['AuthorizeResult']['AuthorizationDetails']['AmazonAuthorizationId'])) $oOrder->setMeta('amazon_capture_id', $arReturn['AuthorizeResult']['AuthorizationDetails']['AmazonAuthorizationId']);
+			
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_debugModus'))) $oOrder->log('Amazon:authorize', print_r($arReturn, 1));
+			
+			return $arReturn;
+						
+		} // private function api_capturePayment($order_id)
+		
+		private function api_closePayment($order_id)
+		{
+			
+			require_once WPSG_PATH_MOD.'mod_amazon/PayWithAmazon/Client.php';
+				
+			$oOrder = wpsg_order::getInstance($order_id);
+			$client = new PayWithAmazon\Client($this->getAPIConfig());
+				
+			$oResponse = $client->closeOrderReference(array(
+				'amazon_order_reference_id' => $oOrder->getMeta('amazon_order_reference_id'),
+				'closure_reason' => '',
+				'mws_auth_token' => $oOrder->getMeta('amazon_last_authorization_reference_id')
+			));
+			
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_debugModus'))) $oOrder->log('Amazon:closeOrderReference', print_r($oResponse->toArray(), 1));
+				
+			return $oResponse->toArray();
+			
+		} // private function api_closePayment($order_id)
+		
+		private function api_cancelPayment($order_id)
+		{
+				
+			require_once WPSG_PATH_MOD.'mod_amazon/PayWithAmazon/Client.php';
+		
+			$oOrder = wpsg_order::getInstance($order_id);
+			$client = new PayWithAmazon\Client($this->getAPIConfig());
+		
+			$oResponse = $client->cancelOrderReference(array(
+				'amazon_order_reference_id' => $oOrder->getMeta('amazon_order_reference_id')				
+			));
+				
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_debugModus'))) $oOrder->log('Amazon:cancelOrderReference', print_r($oResponse->toArray(), 1));
+		
+			return $oResponse->toArray();
+				
+		} // private function api_cancelPayment($order_id)
+	
+		private function api_refund($order_id)
+		{
+			
+			require_once WPSG_PATH_MOD.'mod_amazon/PayWithAmazon/Client.php';
+									
+			$oOrder = wpsg_order::getInstance($order_id);
+			$client = new PayWithAmazon\Client($this->getAPIConfig());
+			
+			$oResponse = $client->refund(array(
+				'amazon_capture_id' => $oOrder->getMeta('amazon_capture_id'),
+				'refund_reference_id' => 'R'.$oOrder->id,
+				'refund_amount' => $oOrder->getAmount(),
+				'currency_code' => 'EUR',
+				'seller_refund_note' => 'STORNO'
+			));
+			
+			$arReturn = $oResponse->toArray();
+			
+			if (wpsg_isSizedString($arReturn['RefundResult']['RefundDetails']['AmazonRefundId']))
+			{
+				
+				$oOrder->setMeta('amazon_refund_id', $arReturn['RefundResult']['RefundDetails']['AmazonRefundId']);
+				$oOrder->setMeta('amazon_capture_id', null);
+				
+			}
+			
+			if (wpsg_isSizedInt($this->shop->get_option('wpsg_debugModus'))) $oOrder->log('Amazon:refund', print_r($arReturn, 1));
+			
+			return $arReturn;
+			
+		} // private function api_refund($order_id)
+		
+		private function getAPIConfig()
+		{
+			
+			$config = array(
+				'merchant_id' => $this->shop->get_option('wpsg_mod_amazon_seller_id'),
+				'access_key' => $this->shop->get_option('wpsg_mod_amazon_mws_access_key_id'),
+				'secret_key' => $this->shop->get_option('wpsg_mod_amazon_mws_access_key'),
+				'region' => 'de',
+				'sandbox' => ((wpsg_isSizedInt($this->shop->get_option('wpsg_mod_amazon_sandbox')))?true:false)
+			);
+			
+			return $config;				
+			
+		} // private function getAPIConfig()
+		
+	} // class wpsg_mod_amazon extends wpsg_mod_basic
+
+?>
Index: /mods/wpsg_mod_basic.class.php
===================================================================
--- /mods/wpsg_mod_basic.class.php	(revision 5472)
+++ /mods/wpsg_mod_basic.class.php	(revision 5485)
@@ -88,5 +88,6 @@
 			3110    => 'wpsg_mod_customernr', /* Anpassung fÃŒr LÃ€nderspezifische Kundennummern */
 			3120    => 'wpsg_mod_flexipay', /* Flexipay */			
-			3130	=> 'wpsg_mod_packagetracking', /* Paketverfolugn */
+			3130	=> 'wpsg_mod_packagetracking', /* Paketverfolgung */
+			3140	=> 'wpsg_mod_amazon', /* Amazon Payment */
 			4000	=> 'wpsg_mod_crefopay', /* Sollte die letzte Zahlungsart bleiben, da sie die anderen ausblendet */
 			3150	=> 'wpsg_mod_request',
Index: /system/conf.php
===================================================================
--- /system/conf.php	(revision 5472)
+++ /system/conf.php	(revision 5485)
@@ -7,4 +7,5 @@
 		"mod_keygen", "wpsg_mod_keygen.class.php",
 		"mod_paypalapi", "wpsg_mod_paypalapi.class.php",
+		"mod_amazon", "wpsg_mod_amazon.class.php", "wpsg_mod_amazon",	
 		"mod_productfilter", "wpsg_mod_productfilter.class.php", "wpsg_mod_productfilter",
 		"mod_securepay", "wpsg_mod_securepay.class.php", "wpsg_mod_securepay",
Index: /system/module.php
===================================================================
--- /system/module.php	(revision 5472)
+++ /system/module.php	(revision 5485)
@@ -51,5 +51,17 @@
 		),
 		*/
-			
+		'wpsg_mod_amazon' => array(
+				'shop_product_id' => 34,
+				'modulname' => 'wpsg_mod_amazon',
+				'files' => array(
+						'mods/mod_amazon',
+						'mods/wpsg_mod_amazon.class.php',
+						'views/mods/mod_amazon'
+				),
+				'url' => 'https://shop.maennchen1.de/produkte/wpshopgermany-modul-amazon-payment/',
+				'version' => '3.3.0',
+				'hide' => true,
+				'free' => false
+		),
 		'wpsg_mod_paypalapi' => array(
 			'shop_product_id' => 33,
Index: /views/mods/mod_amazon/basket_inner_prebutton.phtml
===================================================================
--- /views/mods/mod_amazon/basket_inner_prebutton.phtml	(revision 5485)
+++ /views/mods/mod_amazon/basket_inner_prebutton.phtml	(revision 5485)
@@ -0,0 +1,123 @@
+<?php 
+
+	/**
+	 * Template fÃŒr die Integration von "Bezahlen mit Amazon" in den Warenkorb
+	 */
+
+?>
+ 
+<div id="LoginWithAmazon"></div>
+
+<div class="wpsg_amazonhandler" id="addressBookWidgetDiv"></div>
+<div class="wpsg_amazonhandler" id="walletWidgetDiv"></div>
+
+<input type="hidden" name="amazon[OrderReference]" id="wpsg_mod_amazon_OrderReference" value="" />
+
+<div class="wpsg_clear"></div>
+ 
+<script type="text/javascript">/* <![CDATA[ */
+ 
+	var authRequest; 
+
+	OffAmazonPayments.Button('LoginWithAmazon', '<?php echo $this->get_option('wpsg_mod_amazon_seller_id'); ?>', {
+		
+		type:  "PwA", 
+		color: "Gold", 
+		size:  "small", 
+		language: "de-DE",
+
+		authorization: function() { 
+
+			loginOptions = {
+				scope: "profile payments:widget payments:shipping_address payments:billing_address", 
+				popup: true
+			}; 
+			
+			authRequest = amazon.Login.authorize(loginOptions, "<?php echo $this->view['wpsg_mod_amazon']['redirectURL']; ?>");
+				 
+		},
+
+		onError: function(error) { 
+		    
+			//console.log("Login: Es ist folgender Fehler aufgetreten: " + error.getErrorCode() + ' - ' + error.getErrorMessage());
+			alert("Login: Es ist folgender Fehler aufgetreten: " + error.getErrorCode() + ' - ' + error.getErrorMessage());
+				
+		} 
+	        
+	} );
+
+	<?php if (wpsg_isSizedString($this->view['wpsg_mod_amazon']['amazon_last_authorization_reference_id']) && $this->view['basket']['checkout']['payment'] == 3140) { ?>
+
+	jQuery('.wpsg_checkoutbutton').hide();
+	jQuery('.wpsg_amazonhandler').show();
+	
+	new OffAmazonPayments.Widgets.AddressBook( {
+		
+	    sellerId: '<?php echo $this->get_option('wpsg_mod_amazon_seller_id'); ?>',
+
+	    onOrderReferenceCreate: function(orderReference) {
+
+	    	var oID = orderReference.getAmazonOrderReferenceId();
+	    	
+			jQuery('#wpsg_mod_amazon_OrderReference').val(oID);
+			jQuery('.wpsg_checkoutbutton').show();
+	    			     
+	    },
+	    
+	    onAddressSelect: function(orderReference) {
+ 
+		    // Replace the following code with the action that you want to perform
+		    // after the address is selected.
+		    // The amazonOrderReferenceId can be used to retrieve
+	    	// the address details by calling the GetOrderReferenceDetails
+		    // operation. If rendering the AddressBook and Wallet widgets on the
+		    // same page, you should wait for this event before you render the
+	    	// Wallet widget for the first time.
+		    // The Wallet widget will re-render itself on all subsequent
+		    // onAddressSelect events, without any action from you. It is not
+	    	// recommended that you explicitly refresh it.
+
+			new OffAmazonPayments.Widgets.Wallet({
+
+	    	    sellerId: '<?php echo $this->get_option('wpsg_mod_amazon_seller_id'); ?>',
+
+	    	    onPaymentSelect: function(orderReference) {
+
+	    			// Replace this code with the action that you want to perform
+	    	      	// after the payment method is selected.
+	    		      
+	    	    },
+	    	    
+	    	    design: {
+		    	    
+	    	      	designMode: 'responsive'
+		    	      	
+	    	    },
+	    	    
+	    	    onError: function(error) {
+
+	    	    	alert("Wallet: Es ist folgender Fehler aufgetreten: " + error.getErrorCode() + ' - ' + error.getErrorMessage());
+		    	     
+	    	    }
+	    	    
+	    	}).bind("walletWidgetDiv");
+	    	  
+	    },
+	    
+	    design: {
+		    
+	      designMode: 'responsive'
+		      
+	    },
+	    
+	    onError: function(error) {
+
+	    	alert("Payment: Es ist folgender Fehler aufgetreten: " + error.getErrorCode() + ' - ' + error.getErrorMessage());
+		       
+	    }
+	    
+	} ).bind("addressBookWidgetDiv");
+
+	<?php } ?>
+	
+/* ]]> */</script>
Index: /views/mods/mod_amazon/order_done.phtml
===================================================================
--- /views/mods/mod_amazon/order_done.phtml	(revision 5485)
+++ /views/mods/mod_amazon/order_done.phtml	(revision 5485)
@@ -0,0 +1,119 @@
+<?php
+
+	/**
+	 * Integration der Zahlung ÃŒber Amazon in den BestellabschluÃ
+	 */
+
+?>
+
+<?php if ($this->view['wpsg_mod_amazon']['state'] === wpsg_mod_amazon::ORDER_DONE) { ?>
+
+<p><?php echo __('Zahlung wurde erfolgreich mit Amazon Payment abgewickelt.', 'wpsg'); ?></p>
+
+<?php } else if ($this->view['wpsg_mod_amazon']['state'] === wpsg_mod_amazon::ORDER_DONE_PAST) { ?>
+
+<p><?php echo __('Zahlung wurde bereits erfolgreich mit Amazon Payment abgewickelt.', 'wpsg'); ?></p>
+
+<?php } else if ($this->view['wpsg_mod_amazon']['state'] === wpsg_mod_amazon::ORDER_DONE_START || $this->view['wpsg_mod_amazon']['state'] === wpsg_mod_amazon::ORDER_DONE_GO) { ?>
+
+<div id="PayWithAmazon" style="<?php echo (($this->view['wpsg_mod_amazon']['state'] === wpsg_mod_amazon::ORDER_DONE_GO)?'display:none':''); ?>"></div>
+
+<script type="text/javascript">/* <![CDATA[ */
+ 
+	var authRequest; 
+
+	OffAmazonPayments.Button('PayWithAmazon', '<?php echo $this->get_option('wpsg_mod_amazon_seller_id'); ?>', {
+		
+		type:  "PwA", 
+		color: "Gold", 
+		size:  "small", 
+		language: "de-DE",
+
+		authorization: function() { 
+			
+			loginOptions = {
+				scope: "profile payments:widget payments:shipping_address payments:billing_address", 
+				popup: true 
+			}; 
+			
+			authRequest = amazon.Login.authorize(loginOptions, "<?php echo $this->view['wpsg_mod_amazon']['redirectURL']; ?>");
+							 
+		},
+
+		onError: function(error) { 
+		    
+			console.log("Login: Es ist folgender Fehler aufgetreten: " + error.getErrorCode() + ' - ' + error.getErrorMessage());
+				
+		} 
+	        
+	} );
+
+	var autoRun = setInterval(function(){ 
+
+		if (jQuery('#PayWithAmazon img').length > 0)
+		{
+
+			//setTimeout(function(){ jQuery('#PayWithAmazon img').click(); }, 1000);
+			clearInterval(autoRun);
+		
+		}
+
+	}, 100);
+
+/* ]]> */</script>
+	
+<?php } ?>
+
+<?php if ($this->view['wpsg_mod_amazon']['state'] === wpsg_mod_amazon::ORDER_DONE_GO) { ?>
+
+<form id="wpsg_mod_amazon_paymentForm" method="POST" action="<?php echo $this->getUrl(wpsg_ShopController::URL_BASKET, 'wpsg_mod_amazon', 'return'); ?>">
+	
+	<input type="hidden" name="order_id" value="<?php echo $_REQUEST['order_id']; ?>" />
+	<input type="hidden" name="wpsg_done" value="<?php echo $_REQUEST['wpsg_done']; ?>" />	
+	<input type="hidden" name="amazon[OrderReference]" id="wpsg_mod_amazon_OrderReference" value="" />
+	
+	<div id="walletWidgetDiv"></div>
+	
+	<input type="submit" class="wpsg_button" name="submit" value="<?php echo __('Zahlung durchfÃŒhren', 'wpsg'); ?>" id="wpsg_mod_amazon_doPayment" style="display:none;" />
+	<div class="wpsg_clear"></div>
+	
+</form>
+
+<script type="text/javascript">/* <![CDATA[ */
+                                            
+	new OffAmazonPayments.Widgets.Wallet( {
+
+		sellerId: '<?php echo $this->get_option('wpsg_mod_amazon_seller_id'); ?>',
+
+		onOrderReferenceCreate: function(orderReference) {
+
+			jQuery('#wpsg_mod_amazon_OrderReference').val(orderReference.getAmazonOrderReferenceId());
+			jQuery('#wpsg_mod_amazon_doPayment').show();
+
+		},
+		
+		onPaymentSelect: function(orderReference) {
+ 
+			// Replace this code with the action that you want to perform
+			// after the payment method is selected.
+	    		      
+		},
+	    	    
+		design: {
+		    	    
+			designMode: 'responsive'
+		    	      	
+		},
+	    	    
+		onError: function(error) {
+
+			jQuery('#wpsg_mod_amazon_paymentForm').hide();
+			jQuery('#PayWithAmazon').show();
+			
+	   	     
+		}
+	    	    
+	} ).bind("walletWidgetDiv");
+	    	
+/* ]]> */</script>
+<?php } ?>
Index: /views/mods/mod_amazon/settings_edit.phtml
===================================================================
--- /views/mods/mod_amazon/settings_edit.phtml	(revision 5485)
+++ /views/mods/mod_amazon/settings_edit.phtml	(revision 5485)
@@ -0,0 +1,33 @@
+<?php
+
+	/**
+	 * Einstellungen des Moduls "AmazonPayments"
+	 */
+
+?>
+
+<?php echo wpsg_drawForm_Input('wpsg_mod_amazon_seller_id', __('Seller ID / VerkÃ€ufer-ID', 'wpsg'), $this->get_option('wpsg_mod_amazon_seller_id'), array('help' => 'wpsg_mod_amazon_seller_id')); ?>
+<?php echo wpsg_drawForm_Input('wpsg_mod_amazon_mws_access_key_id', __('Access Key ID', 'wpsg'), $this->get_option('wpsg_mod_amazon_mws_access_key_id'), array('help' => 'wpsg_mod_amazon_mws_access_key_id')); ?>
+<?php echo wpsg_drawForm_Input('wpsg_mod_amazon_mws_access_key', __('Secret Access Key', 'wpsg'), $this->get_option('wpsg_mod_amazon_mws_access_key'), array('help' => 'wpsg_mod_amazon_mws_access_key')); ?>
+<?php echo wpsg_drawForm_Input('wpsg_mod_amazon_lwa_client_id', __('Login with Amazon Client ID', 'wpsg'), $this->get_option('wpsg_mod_amazon_lwa_client_id'), array('help' => 'wpsg_mod_amazon_lwa_client_id')); ?>
+<?php echo wpsg_drawForm_Input('wpsg_mod_amazon_lwa_client_secret', __('Login with Amazon Client Secret', 'wpsg'), $this->get_option('wpsg_mod_amazon_lwa_client_secret'), array('help' => 'wpsg_mod_amazon_lwa_client_secret')); ?>
+<?php echo wpsg_drawForm_Checkbox('wpsg_mod_amazon_sandbox', __('Sandbox Modus', 'wpsg'), $this->get_option('wpsg_mod_amazon_sandbox'), array('help' => 'wpsg_mod_amazon_sandbox')); ?>
+
+<br />
+
+<?php echo wpsg_drawForm_Checkbox('wpsg_mod_amazon_aktiv', __('Zahlungsart fÃŒr neue Bestellungen aktiv', 'wpsg'), $this->get_option('wpsg_mod_amazon_aktiv')); ?>
+<?php echo wpsg_drawForm_Input('wpsg_mod_amazon_name', __('Name der Zahlungsart', 'wpsg'), $this->get_option('wpsg_mod_amazon_name'), array('help' => 'wpsg_mod_amazon_name')); ?>
+<?php echo wpsg_drawForm_Textarea('wpsg_mod_amazon_hint', __('Hinweistext', 'wpsg'), $this->get_option('wpsg_mod_amazon_hint'), array('help' => 'wpsg_shippay_hint')); ?>
+<?php echo wpsg_drawForm_Input('wpsg_mod_amazon_subject', __('Betreff (Auf Abrechnungen fÃŒr VerkÃ€ufer sichtbar)', 'wpsg'), $this->get_option('wpsg_mod_amazon_subject'), array('help' => 'wpsg_mod_amazon_subject')); ?>
+<?php echo wpsg_drawForm_Checkbox('wpsg_mod_amazon_basket', __('Button in Warenkorb integrieren', 'wpsg'), $this->get_option('wpsg_mod_amazon_basket'), array('help' => 'wpsg_mod_amazon_basket')); ?>
+
+<br />
+
+<?php echo wpsg_drawForm_Input('wpsg_mod_amazon_gebuehr', __('GebÃŒhr/Rabatt', 'wpsg'), wpsg_ff($this->get_option('wpsg_mod_amazon_gebuehr'), true), array('unit' => $this->get_option('wpsg_currency').' / %', 'help' => 'wpsg_shippay_gebuehr')); ?>
+<?php echo wpsg_drawForm_Select('wpsg_mod_amazon_mwst', __('Mehrwertsteuersatz', 'wpsg'), wpsg_tax_groups(), $this->get_option('wpsg_mod_amazon_mwst'), array('help' => 'wpsg_mod_amazon_mwst')); ?>
+<?php echo wpsg_drawForm_Checkbox('wpsg_mod_amazon_mwstland', __('Keine MwSt. wenn Land steuerfrei', 'wpsg'), $this->get_option('wpsg_mod_amazon_mwstland'), array('help' => 'wpsg_mod_amazon_mwstland')); ?>
+
+<br />
+
+<?php echo wpsg_drawForm_Input('', __('HÃ€ndler URL', 'wpsg'), $this->view['ipnURL'], array('readonly' => true)); ?>
+<?php echo wpsg_drawForm_Input('', __('JavaScript Origin', 'wpsg'), $this->view['jsOrigin'], array('readonly' => true)); ?>
Index: /views/mods/mod_keygen/settings_edit.phtml
===================================================================
--- /views/mods/mod_keygen/settings_edit.phtml	(revision 5472)
+++ /views/mods/mod_keygen/settings_edit.phtml	(revision 5485)
@@ -88,4 +88,8 @@
 			</label>
 			<div class="wpsg_clear"></div>
+			<label style="line-height:25px;">
+				<input class="checkbox" type="checkbox" name="wpsg_mod_keygen[mods][]" <?php echo ((@$_SESSION['wpsg_mod_keygen']['mods']['wpsg_mod_amazon'] == '1')?'checked="checked"':''); ?> value="wpsg_mod_amazon" />&nbsp;<?php echo __('AmazonPayment', 'wpsg'); ?>
+			</label>
+			<div class="wpsg_clear"></div>
 		</div>	
 	</div>
