create-amazon-pay-phar.php 0000666 00000000727 13436752777 0011561 0 ustar 00 startBuffering();
$p->setStub('');
$p->buildFromDirectory('.', '$(.*)\.php$');
$p->stopBuffering();
echo "Phar created: amazon-pay.phar\n";
?>
README.md 0000666 00000047262 13436752777 0006067 0 ustar 00 # Amazon Pay SDK (PHP)
Amazon Pay API Integration
## Requirements
* Amazon Pay account:
* [US - Registration](https://pay.amazon.com/us/signup)
* [UK - Registration](https://pay.amazon.com/uk/signup)
* [DE - Registration](https://pay.amazon.com/de/signup)
* [JP - Registration](https://pay.amazon.com/jp/contactsales)
* PHP 5.5 or higher
* Curl 7.18 or higher
Support for PHP 5.3 and 5.4 is being deprecated. The SDK will work in these older environments, but future versions may not. We encourage merchants to move to a newer version of PHP at their earliest convenience.
## Documentation
Integration steps can be found below:
* [US](https://pay.amazon.com/us/developer/documentation)
* [UK](https://pay.amazon.com/uk/developer/documentation)
* [DE](https://pay.amazon.com/de/developer/documentation)
* [JP](https://pay.amazon.com/jp/developer/documentation)
## Sample
* View the [Amazon Pay SDK samples](https://amzn.github.io/amazon-pay-sdk-samples/)
## Quick Start
The client takes in parameters in the following format:
1. Associative array
2. Path to the JSON file containing configuration information.
## Installing using Composer
```
composer require amzn/amazon-pay-sdk-php
```
## Directory Tree
```
.
├── composer.json - Configuration for composer
├── LICENSE.txt
├── NOTICE.txt
├── AmazonPay
│ ├── Client.php - Main class with the API calls
│ ├── ClientInterface.php - Shows the public function definitions in Client.php
│ ├── HttpCurl.php - Client class uses this file to execute the GET/POST
│ ├── HttpCurlInterface.php - Shows the public function definitions in the HttpCurl.php
│ ├── IpnHandler.php - Class handles verification of the IPN
│ ├── IpnHandlerInterface.php - Shows the public function definitions in the IpnHandler.php
│ ├── Regions.php - Defines the regions that is supported
│ ├── ResponseParser.php - Parses the API call response
│ └── ResponseInterface.php - Shows the public function definitions in the ResponseParser.php
├── README.md
└── UnitTests
├── ClientTest.php
├── config.json
├── coverage.txt
├── IpnHandlerTest.php
└── Signature.php
```
## Parameters List
#### Mandatory Parameters
| Parameter | variable name | Values |
|--------------|---------------|------------------------------------------------|
| Merchant Id | `merchant_id` | Default : `null` |
| Access Key | `access_key` | Default : `null` |
| Secret Key | `secret_key` | Default : `null` |
| Region | `region` | Default : `null`
Other: `us`,`de`,`uk`,`jp` |
#### Optional Parameters
| Parameter | Variable name | Values |
|---------------------|-----------------------|----------------------------------------------------|
| Currency Code | `currency_code` | Default : `null`
Other: `USD`,`EUR`,`GBP`,`JPY` |
| Environment | `sandbox` | Default : `false`
Other: `true` |
| Platform ID | `platform_id` | Default : `null` |
| CA Bundle File | `cabundle_file` | Default : `null` |
| Application Name | `application_name` | Default : `null` |
| Application Version | `application_version` | Default : `null` |
| Proxy Host | `proxy_host` | Default : `null` |
| Proxy Port | `proxy_port` | Default : `-1` |
| Proxy Username | `proxy_username` | Default : `null` |
| Proxy Password | `proxy_password` | Default : `null` |
| LWA Client ID | `client_id` | Default : `null` |
| Handle Throttle | `handle_throttle` | Default : `true`
Other: `false` |
## Setting Configuration
Your Amazon Pay keys are available in your Seller Central account
Setting configuration while instantiating the Client object
```php
'YOUR_MERCHANT_ID',
'access_key' => 'YOUR_ACCESS_KEY',
'secret_key' => 'YOUR_SECRET_KEY',
'client_id' => 'YOUR_LOGIN_WITH_AMAZON_CLIENT_ID',
'region' => 'REGION');
// or, instead of setting the array in the code, you can
// initialze the Client by specifying a JSON file
// $config = 'PATH_TO_JSON_FILE';
// Instantiate the client class with the config type
$client = new Client($config);
```
### Testing in Sandbox Mode
The sandbox parameter is defaulted to false if not specified:
```php
'YOUR_MERCHANT_ID',
'access_key' => 'YOUR_ACCESS_KEY',
'secret_key' => 'YOUR_SECRET_KEY',
'client_id' => 'YOUR_LOGIN_WITH_AMAZON_CLIENT_ID',
'region' => 'REGION',
'sandbox' => true);
$client = new Client($config);
// Also you can set the sandbox variable in the config() array of the Client class by
$client->setSandbox(true);
```
### Setting Proxy values
Proxy parameters can be set after Instantiating the Client Object with the following setter
```php
$proxy = array();
$proxy['proxy_user_host'] // Hostname for the proxy
$proxy['proxy_user_port'] // Hostname for the proxy
$proxy['proxy_user_name'] // If your proxy requires a username
$proxy['proxy_user_password'] // If your proxy requires a password
$client->setProxy($proxy);
```
### Making an API Call
Below is an example on how to make the GetOrderReferenceDetails API call:
```php
getOrderReferenceDetails($requestParameters);
```
See the [API Response](https://github.com/amzn/amazon-pay-sdk-php#api-response) section for information on parsing the API response.
Below is an example on how to make the GetMerchantAccountStatus API call:
```php
$requestParameters = array();
// Optional Parameter
$requestParameters['mws_auth_token'] = 'MWS_AUTH_TOKEN';
$response = $client->getMerchantAccountStatus($requestParameters);
echo $response->toXml() . "\n";
// Sample Response
ACTIVE
b0a141f7-712a-4830-8014-2aa0c446b04e
```
See the [API Response](https://github.com/amzn/amazon-pay-sdk-php#api-response) section for information on parsing the API response.
Below is an example on how to make the ListOrderReference API call:
```php
$requestParameters = array();
// Required Parameter
$configArray['query_id'] = 'SELLER_ORDER_ID';
$configArray['query_id_type'] = 'SellerOrderId';
// Optional Parameter
$requestParameters['mws_auth_token'] = 'MWS_AUTH_TOKEN';
$configArray['page_size'] = "1";
$response = $client->listOrderReference($requestParameters);
echo $response->toXml() . "\n";
// Sample Response
Sandbox
2018-08-06T22:45:37.314Z
Open
S01-6649662-0708590
2018-08-06T22:45:28.203Z
PHP SDK Test goGetOrderReferenceDetails
PHP SDK Custom Information Testing
PHP SDK ID# 12345
USD
0.01
eyJuZXh0UGFn...=
5749768d-307b-493b-90b0-8b5b9f2ea436
```
See the [API Response](https://github.com/amzn/amazon-pay-sdk-php#api-response) section for information on parsing the API response.
Below is an example on how to make the ListOrderReferenceByNextToken API call:
```php
$requestParameters = array();
// Required Parameter
$configArray['next_page_token'] = "NEXT_PAGE_TOKEN";
$response = $client->listOrderReferenceByNextToken($requestParameters);
echo $response->toXml() . "\n";
// Sample Response
Sandbox
2018-08-06T22:42:50.191Z
Open
S01-1662310-7599388
2018-08-06T22:42:35.904Z
PHP SDK Test goGetOrderReferenceDetails
PHP SDK Custom Information Testing
PHP SDK ID# 12345
USD
0.01
eyJuZXh0UGFnZVRva2VuIjoiQUFBQUFBQUFBQ...
8e06c852-4072-4cfb-99a3-060ec1ef7be8
```
See the [API Response](https://github.com/amzn/amazon-pay-sdk-php#api-response) section for information on parsing the API response.
### IPN Handling
1. To receive IPN's successfully you will need an valid SSL on your domain.
2. You can set up your Notification endpoints in Seller Central by accessing the Integration Settings page in the Settings tab.
3. IpnHandler.php class handles verification of the source and the data of the IPN
Add the below code into any file and set the URL to the file location in Merchant/Integrator URL by accessing Integration Settings page in the Settings tab.
```php
**Capture Now** can be set to `true` for digital goods . For Physical goods it's highly recommended to set the Capture Now to `false`
and the amount captured by making the `capture` API call after the shipment is complete.
| Parameter | Variable Name | Mandatory | Values |
|----------------------------|------------------------------|-----------|-----------------------------------------------------------------------------------------------------------|
| Amazon Reference ID | `amazon_reference_id` | yes | OrderReference ID (`starts with P01 or S01`) or
Billing Agreement ID (`starts with B01 or C01`) |
| Amazon OrderReference ID | `amazon_order_reference_id` | no | OrderReference ID (`starts with P01 or S01`) if no Amazon Reference ID is provided |
| Amazon Billing Agreement ID| `amazon_billing_agreement_id`| no | Billing Agreement ID (`starts with B01 or C01`) if no Amazon Reference ID is provided |
| Merchant ID | `merchant_id` | no | Value taken from config array in Client.php |
| Charge Amount | `charge_amount` | yes | Amount that needs to be captured.
Maps to API call variables `amount` , `authorization_amount` |
| Currency code | `currency_code` | no | If no value is provided, value is taken from the config array in Client.php |
| Authorization Reference ID | `authorization_reference_id` | yes | Unique string to be passed |
| Transaction Timeout | `transaction_timeout` | no | Timeout for Authorization - Defaults to 1440 minutes |
| Capture Now | `capture_now` | no | Will capture the payment automatically when set to `true`. Defaults to `false` |
| Charge Note | `charge_note` | no | Note that is sent to the buyer.
Maps to API call variables `seller_note` , `seller_authorization_note`|
| Charge Order ID | `charge_order_id` | no | Custom order ID provided
Maps to API call variables `seller_order_id` , `seller_billing_agreement_id` |
| Store Name | `store_name` | no | Name of the store |
| Platform ID | `platform_id` | no | Platform ID of the Solution provider |
| Custom Information | `custom_information` | no | Any custom string |
| MWS Auth Token | `mws_auth_token` | no | MWS Auth Token required if API call is made on behalf of the seller |
```php
// Create an array that will contain the parameters for the charge API call
$requestParameters = array();
// Adding the parameters values to the respective keys in the array
$requestParameters['amazon_reference_id'] = 'AMAZON_REFERENCE_ID';
// Or
// If $requestParameters['amazon_reference_id'] is not provided,
// either one of the following ID input is needed
$requestParameters['amazon_order_reference_id'] = 'AMAZON_ORDER_REFERENCE_ID';
$requestParameters['amazon_billing_agreement_id'] = 'AMAZON_BILLING_AGREEMENT_ID';
$requestParameters['seller_id'] = null;
$requestParameters['charge_amount'] = '100.50';
$requestParameters['currency_code'] = 'USD';
$requestParameters['authorization_reference_id'] = 'UNIQUE STRING';
$requestParameters['transaction_timeout'] = 0;
$requestParameters['capture_now'] = false; //true for Digital goods
$requestParameters['charge_note'] = 'Example item note';
$requestParameters['charge_order_id'] = '1234-Example-Order';
$requestParameters['store_name'] = 'Example Store';
$requestParameters['platform_Id'] = null;
$requestParameters['custom_information'] = 'Any_Custom_String';
$requestParameters['mws_auth_token'] = null;
// Get the Authorization response from the charge method
$response = $client->charge($requestParameters);
```
See the [API Response](https://github.com/amzn/amazon-pay-sdk-php#api-response) section for information on parsing the API response.
#### Obtain profile information (getUserInfo method)
1. obtains the user's profile information from Amazon using the access token returned by the Button widget.
2. An access token is granted by the authorization server when a user logs in to a site.
3. An access token is specific to a client, a user, and an access scope. A client must use an access token to retrieve customer profile data.
| Parameter | Variable Name | Mandatory | Values |
|---------------------|-----------------------|-----------|------------------------------------------------------------------------------------------|
| Access Token | `access_token` | yes | Retrieved as GET parameter from the URL |
| Region | `region` | yes | Default :`null`
Other:`us`,`de`,`uk`,`jp`
Value is set in config['region'] array |
| LWA Client ID | `client_id` | yes | Default: null
Value should be set in config array |
```php
'YOUR_LWA_CLIENT_ID',
'region' => 'REGION');
$client = new Client($config);
// Client ID can also be set using the setter function setClientId($client_id)
$client->setClientId(‘YOUR_LWA_CLIENT_ID’);
// Get the Access Token from the URL
$access_token = 'ACCESS_TOKEN';
// Calling the function getUserInfo with the access token parameter returns object
$userInfo = $client->getUserInfo($access_token);
// Buyer name
$userInfo['name'];
// Buyer Email
$userInfo['email'];
// Buyer User Id
$userInfo['user_id'];
```
### Response Parsing
Responses are provided in 3 formats
1. XML/Raw response
2. Associative array
3. JSON format
#### API Response
```php
// Returns an object($response) of the class ResponseParser.php
$response = $client->getOrderReferenceDetails($requestParameters);
// XML response
$response->toXml();
// Associative array response
$response->toArray();
// JSON response
$response->toJson();
```
#### IPN Response
```php
$ipnHandler = new IpnHandler($headers, $body);
// Raw message response
$ipnHandler->returnMessage();
// Associative array response
$ipnHandler->toArray();
// JSON response
$ipnHandler->toJson();
```
### Logging
SDK logging of sanitized requests and responses can work with any PSR-3 compliant logger such as Monolog.
#### API Response
```php
namespace AmazonPay;
require 'vendor/autoload.php';
include 'amazon-pay.phar';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
date_default_timezone_set('America/Los_Angeles');
$log = new Logger('TestSDK');
$log->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));
$client = new Client('us.config');
$client->setLogger($log);
$response = $client->getServiceStatus();
```
CHANGES.txt 0000666 00000015722 13436752777 0006415 0 ustar 00 3.4.0 - January 2019
- Added new supplementary_data attribute for AuthorizeOnBillingAgreement and CreateOrderReferenceForId.
- Added additional attributes (success_url, failure_url, authorization_amount, currency_code) to ConfirmOrderReference. Amazon Pay Strong Customer Authentication (SCA) Upgrade Integration Guide
3.3.2 - August 2018
- Added method for listOrderReference API call
- Added method for listOrderReferenceByNextToken API call
- Added new supplementary_data attribute for SetOrderAttributes and SetOrderReferenceDetails.
3.3.1 - April 2018
- Add call to GetMerchantAccountStatus API call
3.2.0 - November 2017
- Add support for new SetOrderAttributes API call (available for use in 2018)
3.1.0 - May 2017
- Fix getUserInfo call (bearer token) issue impacted by 3.0.0's Curl fix
- app_id can be passed in to Client constructor as optional configuration parameter
Needed if your Mobile SDK application needs to call the PHP SDK getUserInfo call on the backend
- access_token can be specified as a parameter to the getOrderReferenceDetails functions
This provides access to the payment descriptor response needed for Mobile SDK implementations
Your account must be whitelisted for Mobile SDK access to retrieve payment descriptor details
- SDK was not consistently handling boolean input parameters:
'false' was sometimes treated as true because it was a non-empty string, 'true' was sometimes getting converted to '1' instead of true
An Exception will be thrown if sandbox, capture_now, confirm_now, inherit_shipping_address attributes are not specified as booleans
- Fix relative paths in Psr\Log interface files
3.0.0 - March 2017
- Pay with Amazon to Amazon Pay rebranding
- PHP Archive (amazon-pay.phar) now bundled with release for convenience
- User-Agent header modified to adhere to standards
- Retry timing adjusted (1 second, 2 seconds, 7 seconds)
- Disable Curl "Expect: 100-Continue" header
2.1.0 - October 2016
- Contains PSR logging feature
2.0.4 - October 2016
- Fixing Curl implementation
- PHP 7 compatability
2.0.3 - June 2016
- Response parser fixed and added signature utility
2.0.2 - May 2016
- PSR-4 compliance changes
2.0.1 - January 2016
- Added verification for signing cert URL attribute of the IPN to ensure certificate is coming from an AWS SNS URL
2.0.0
- Rewrite of the 1.x SDK with much easier to use calling convention
1.0.16
- Added additional validation for IPN parsing.
1.0.15
- Added Soft Decline feature for Authorization Response
1.0.14 - May 2015
- Updated sample code and web server based examples for Order Reference object and Billing Agreement object
containing orderLanguage parameter.
- Updated library and added orderLanguage as an additional parameter in GetOrderReferenceDetails and
GetBillingAgreementDetails response objects.
1.0.13 - March 2015
- Fix regression that prevented usage on PHP 5.3 and 5.4
1.0.12 - January 2015
- Fix for incorrect comparison operators in OffAmazonService/Client.php
- Fix for incorrect exception variable in OpenSslVerifySignature.php
- Add support for proxy usage in API requests
- Modified Exception.php to check for parameter existence before assigning fields
- Additional verification checks in place for IPN signature certificate validation
- Change to OffAmazonPaymentsService.config.inc.php - new mandatory property cnName
- Add support for MWSAuthToken field on all request objects
1.0.11 - September 2014
- Added 'Login & Pay with Amazon' flow for EU.
- Unified US and EU code samples
1.0.10 -May 2014
- Updated sample code and webserver based examples for using the Fast Authorization option. (No Library change needed)
- Updated library and added
ProviderCreditList as an additional parameter in AuthorizationRequest and CaptureRequest.
ProviderCreditReversalList as an additional parameter in RefundRequest.
ProviderCreditSummaryList as an additional parameter in CaptureResponse and CaptureNotification.
ProviderCreditReversalSummaryList as an additional parameter in RefundResponse and RefundNotification.
- Updated library and added support for Solution Provider related operations (ReverseProviderCredit, GetProviderCreditDetails, GetProviderCreditReversalDetails) and notifications (ProviderCreditNotification, ProviderCreditReversalNotification)
- Added sample code and webserver based examples for ProviderCheckout, ProviderRefund and ReverseProviderCredit.
- Added support for SolutionProviderMerchantNotification.
1.0.8 - April 2014
- Updated library and added Billing Address as a whitelisted parameter in OrderReference details API
- Updated library and Added AddressVerificationCode as an additional parameter in the Authorize Notification IPN.
- Billing Address and AddressVerificationCode are available only to sellers pre-approved by Amazon. Contact Amazon Payments Support or your Account manager.
1.0.7 - March 2014
- Updated library and Added AddressVerificationCode as an additional parameter in the AuthorizationDetails object
- Added IdList as an additional parameter to the OrderReferenceDetails object.
- Added ParentDetails as an additional parameter to the OrderReferenceDetails object
- Updated sample code and webserver based examples for Billing Address use case in Billing agreement details API
1.0.6 - March 2014
- Updated library and added support for Automatic Payments related operations
- Added sample code and webserver based examples for billing agreement notifications
- Fixed typos and bugs related to undefined variable notices in library package
1.0.5 - November 2013
- Added addressConsentToken field to getOrderReferenceDetailsRequest object to
support Pay with Amazon use cases for US
- Changed sample code for US to use the new Pay with Amazon widgets
- Added new sample code for US to show usage of the address consent token
- Updated product name strings for US & EU
1.0.4 - November 2013
- Add AuthorizationBillingAddress field to AuthorizationResponse service model
object to support VAT invoicing in applicable countries (DE, UK).
- Removed EU region, added DE & UK to support future configuration options.
- Added US region in place of NA and updated sample docs to use new option - NA
is deprecated but will still function to support existing merchants.
- Modified SimpleCheckout example to show call to getAuthorizationDetails after
receiving an authorization IPN.
- Added additional property to setup cert folder so that additional certificates
can be used during SSL verification check
- Modified sample code for verifying refund is completed to accept a RefundDetails object
in place of a GetRefundDetailsResponse object, so that it can be used from either the RefundResponse
or getRefundDetailsResponse object references.
1.0.3 - October 2013
- Fixed issue with signature verification for windows platforms.
1.0.2 - October 2013
- Added EU endpoints to service code
- Added platformID field to setOrderReferenceDetails object
1.0.1 - May 2013
- Added payment notification model objects
- Added sample code and webserver based examples for payment notifications
1.0.0 - April 2013
- Initial release
tst/unit/IpnHandlerTest.php 0000666 00000011137 13436752777 0011766 0 ustar 00 null,
'proxy_host' => null,
'proxy_port' => -1,
'proxy_username' => null,
'proxy_Password' => null
);
public function testConstructor()
{
try {
$headers = array();
$headers = array('ab'=>'abc');
$body = 'abctest';
$ipnHandler = new IpnHandler($headers,$body,$this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/Error with message - header./i', strval($expected));
}
try {
$headers['x-amz-sns-message-type'] = 'Notification';
$body = 'abctest';
$ipnHandler = new IpnHandler($headers,$body,$this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/Error with message - content is not in json format./i', strval($expected));
}
try {
$ConfigParams = array(
'a' => 'A',
'b' => 'B'
);
$ipnHandler = new IpnHandler(array(),null,$ConfigParams);
} catch (\Exception $expected) {
$this->assertRegExp('/is either not part of the configuration or has incorrect Key name./i', strval($expected));
}
}
public function testValidateUrl()
{
$headers = array('x-amz-sns-message-type' => 'Notification');
try {
$body = '{"Type":"Notification", "Message":"Test", "MessageId":"Test", "Timestamp":"Test", "Subject":"Test", "TopicArn":"Test", "Signature":"Test", "SigningCertURL":"http://sns.us-east-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem"}';
$ipnHandler = new IpnHandler($headers, $body, $this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/The certificate is located on an invalid domain./i', strval($expected));
}
try {
$body = '{"Type":"Notification", "Message":"Test", "MessageId":"Test", "Timestamp":"Test", "Subject":"Test", "TopicArn":"Test", "Signature":"Test", "SigningCertURL":"https://sns.us-east-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.exe"}';
$ipnHandler = new IpnHandler($headers, $body, $this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/The certificate is located on an invalid domain./i', strval($expected));
}
try {
$body = '{"Type":"Notification", "Message":"Test", "MessageId":"Test", "Timestamp":"Test", "Subject":"Test", "TopicArn":"Test", "Signature":"Test", "SigningCertURL":"https://sns.us-east-1.example.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem"}';
$ipnHandler = new IpnHandler($headers, $body, $this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/The certificate is located on an invalid domain./i', strval($expected));
}
try {
$body = '{"Type":"Notification", "Message":"Test", "MessageId":"Test", "Timestamp":"Test", "Subject":"Test", "TopicArn":"Test", "Signature":"Test", "SigningCertURL":"https://sni.us-east-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem"}';
$ipnHandler = new IpnHandler($headers, $body, $this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/The certificate is located on an invalid domain./i', strval($expected));
}
try {
$body = '{"Type":"Notification", "Message":"Test", "MessageId":"Test", "Timestamp":"Test", "Subject":"Test", "TopicArn":"Test", "Signature":"Test", "SigningCertURL":"https://sns.us.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem"}';
$ipnHandler = new IpnHandler($headers, $body, $this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/The certificate is located on an invalid domain./i', strval($expected));
}
try {
$body = '{"Type":"Notification", "Message":"Test", "MessageId":"Test", "Timestamp":"Test", "Subject":"Test", "TopicArn":"Test", "Signature":"Test", "SigningCertURL":"https://sns.us-east-1.amazonaws.com.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem"}';
$ipnHandler = new IpnHandler($headers, $body, $this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/The certificate is located on an invalid domain./i', strval($expected));
}
}
}
tst/unit/Signature.php 0000666 00000014056 13436752777 0011046 0 ustar 00 'mws-eu.amazonservices.com',
'na' => 'mws.amazonservices.com',
'jp' => 'mws.amazonservices.jp');
private $regionMappings = array('de' => 'eu',
'uk' => 'eu',
'us' => 'na',
'jp' => 'jp');
public function __construct($config = array(),$parameters = array())
{
$config = array_change_key_case($config, CASE_LOWER);
$this->config = $config;
$this->signature = $this->calculateSignature($parameters);
}
public function getSignature()
{
return trim($this->signature);
}
/* Create an Array of required parameters, sort them
* Calculate signature and invoke the POST them to the MWS Service URL
*
* @param AWSAccessKeyId [String]
* @param Version [String]
* @param SignatureMethod [String]
* @param Timestamp [String]
* @param Signature [String]
*/
private function calculateSignature($parameters)
{
$this->createServiceUrl();
$signature = $this->signParameters($parameters);
return $signature;
}
/* 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());
}
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::MWS_VERSION;
$this->mwsEndpointPath = '/' . $this->modePath . '/' . self::MWS_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");
}
}
}
tst/unit/config/sandbox_none.json 0000666 00000000552 13436752777 0013205 0 ustar 00 {
"merchant_id": "test_merchant_id",
"access_key": "test_access_key",
"secret_key": "test_secret_key",
"currency_code": "USD",
"client_id": "test_client_id",
"region": "us",
"application_name": "sdk testing",
"application_version": "1.0",
"proxy_host": null,
"proxy_port": -1,
"proxy_username": null,
"proxy_password": null
}
tst/unit/config/sandbox_false_bool.json 0000666 00000000577 13436752777 0014362 0 ustar 00 {
"merchant_id": "test_merchant_id",
"access_key": "test_access_key",
"secret_key": "test_secret_key",
"currency_code": "USD",
"client_id": "test_client_id",
"region": "us",
"sandbox": false,
"application_name": "sdk testing",
"application_version": "1.0",
"proxy_host": null,
"proxy_port": -1,
"proxy_username": null,
"proxy_password": null
}
tst/unit/config/sandbox_false_string.json 0000666 00000000601 13436752777 0014721 0 ustar 00 {
"merchant_id": "test_merchant_id",
"access_key": "test_access_key",
"secret_key": "test_secret_key",
"currency_code": "USD",
"client_id": "test_client_id",
"region": "us",
"sandbox": "false",
"application_name": "sdk testing",
"application_version": "1.0",
"proxy_host": null,
"proxy_port": -1,
"proxy_username": null,
"proxy_password": null
}
tst/unit/config/sandbox_true_bool.json 0000666 00000000576 13436752777 0014246 0 ustar 00 {
"merchant_id": "test_merchant_id",
"access_key": "test_access_key",
"secret_key": "test_secret_key",
"currency_code": "USD",
"client_id": "test_client_id",
"region": "us",
"sandbox": true,
"application_name": "sdk testing",
"application_version": "1.0",
"proxy_host": null,
"proxy_port": -1,
"proxy_username": null,
"proxy_password": null
}
tst/unit/config/sandbox_true_string.json 0000666 00000000600 13436752777 0014605 0 ustar 00 {
"merchant_id": "test_merchant_id",
"access_key": "test_access_key",
"secret_key": "test_secret_key",
"currency_code": "USD",
"client_id": "test_client_id",
"region": "us",
"sandbox": "true",
"application_name": "sdk testing",
"application_version": "1.0",
"proxy_host": null,
"proxy_port": -1,
"proxy_username": null,
"proxy_password": null
}
tst/unit/ClientTest.php 0000666 00000130550 13436752777 0011161 0 ustar 00 'MERCHANT1234567',
'access_key' => 'ABCDEFGHI1JKLMN2O7',
'secret_key' => "abc123Def456gHi789jKLmpQ987rstu6vWxyz",
'currency_code' => 'usd',
'client_id' => 'amzn1.application-oa2-client.45789c45a8f34927830be1d9e029f480',
'region' => 'us',
'sandbox' => true,
'platform_id' => 'test',
'application_name' => 'sdk testing',
'application_version' => '1.0',
'proxy_host' => null,
'proxy_port' => -1,
'proxy_username' => null,
'proxy_Password' => null
);
public function testConfigArray()
{
// Test that trimmimg isn't converting the Boolean to a string
$client = new Client($this->configParams);
$this->assertTrue((bool)$client->__get('sandbox'));
// Test four cases in which sandbox is in constructor with an array
$client = new Client(array('sandbox' => false));
$this->assertFalse((bool)$client->__get('sandbox'));
try {
$client = new Client(array('sandbox' => 'false'));
} catch (\Exception $expected) {
$this->assertRegExp('/should be a boolean value/i', strval($expected));
}
$client = new Client(array('sandbox' => true));
$this->assertTrue((bool)$client->__get('sandbox'));
try {
$client = new Client(array('sandbox' => 'true'));
} catch (\Exception $expected) {
$this->assertRegExp('/should be a boolean value/i', strval($expected));
}
// Test that string trimming is working as intended
$client = new Client(array(
'region' => 'us ', // two spaces at end
'currency_code' => ' usd', // two spaces at beginning
'client_id' => ' A113 ' // two spaces and beginning and end
));
$this->assertEquals('us', $client->__get('region'));
$this->assertEquals('usd', $client->__get('currency_code'));
$this->assertEquals('A113', $client->__get('client_id'));
$this->assertFalse((bool)$client->__get('sandbox'));
// Unclear what is is actually doing, exception doesn't get thrown, consider removing
try {
$client = new Client($this->configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/is not a Json File or the Json File./i', strval($expected));
}
// Test passing in invalid keys to constructor
try {
$configParams = array(
'a' => 'A',
'b' => 'B'
);
$client = new Client($configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/is either not part of the configuration or has incorrect Key name./i', strval($expected));
}
// Test passing in override service URL for MWS API endpoint
$client = new Client(array('override_service_url' => 'https://over.ride'));
$this->assertEquals('https://over.ride', $client->__get('override_service_url'));
// Test passing in an empty array to construtor
try {
$configParams = array();
$client = new Client($configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/$config cannot be null./i', strval($expected));
}
}
public function testJsonFile()
{
$configParams = "tst/unit/config/sandbox_true_bool.json";
$client = new Client($configParams);
$this->assertTrue((bool)$client->__get('sandbox'));
$this->assertEquals('test_merchant_id', $client->__get('merchant_id'));
$this->assertEquals('test_access_key', $client->__get('access_key'));
$this->assertEquals('test_secret_key', $client->__get('secret_key'));
$this->assertEquals('USD', $client->__get('currency_code'));
$this->assertEquals('test_client_id', $client->__get('client_id'));
$this->assertEquals('us', $client->__get('region'));
$this->assertEquals('sdk testing', $client->__get('application_name'));
$this->assertEquals('1.0', $client->__get('application_version'));
try {
$configParams = "tst/unit/config/sandbox_true_string.json";
$client = new Client($configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/should be a boolean value/i', strval($expected));
}
$configParams = "tst/unit/config/sandbox_false_bool.json";
$client = new Client($configParams);
$this->assertFalse((bool)$client->__get('sandbox'));
$configParams = "tst/unit/config/sandbox_none.json";
$client = new Client($configParams);
$this->assertFalse((bool)$client->__get('sandbox'));
try {
$configParams = "tst/unit/config/sandbox_false_string.json";
$client = new Client($configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/should be a boolean value/i', strval($expected));
}
try {
$configParams = "abc.json";
$client = new Client($configParams);
} catch (\Exception $expected) {
$this->assertRegExp('/is not a Json File path or the Json File./i', strval($expected));
}
}
public function testSandboxSetter()
{
$client = new Client($this->configParams);
try {
$client->setSandbox(true);
} catch (\Exception $expected) {
$this->assertRegExp('/and should be a boolean value./i', strval($expected));
}
try {
$client->setSandbox('string value');
} catch (\Exception $expected) {
$this->assertRegExp('/and should be a boolean value./i', strval($expected));
}
}
public function testGetOrderReferenceDetails()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_order_reference_id' => 'AmazonOrderReferenceId',
'address_consent_token' => 'AddressConsentToken',
'access_token' => 'AccessToken',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'GetOrderReferenceDetails';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->getOrderReferenceDetails($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testListOrderReference()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'mws_auth_token' => 'MWSAuthToken',
'query_id' => 'QueryId',
'query_id_type' => 'QueryIdType',
'page_size' => 'PageSize',
'created_start_time' => 'CreatedTimeRange.StartTime',
'created_end_time' => 'CreatedTimeRange.EndTime',
'sort_order' => 'SortOrder',
'order_status_list' => array()
);
$action = 'ListOrderReference';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$expectedParameters['OrderReferenceStatusListFilter.OrderReferenceStatus.1'] = 'Open';
$expectedParameters['OrderReferenceStatusListFilter.OrderReferenceStatus.2'] = 'Closed';
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->listOrderReference($apiCallParams);
$apiParametersString = $client->getParameters();
// Hack to remove mismatched Signature (due to param mismatch), then remove Signature from both to eliminate mismatch
$apiParametersString = preg_replace("/&PaymentDomain=[^&]*/", "", $apiParametersString);
$apiParametersString = preg_replace("/&Signature=[^&]*/", "", $apiParametersString);
$expectedStringParams = preg_replace("/&Signature=[^&]*/", "", $expectedStringParams);
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testListOrderReferenceByNextToken()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'mws_auth_token' => 'MWSAuthToken',
'next_page_token' => 'NextPageToken'
);
$action = 'ListOrderReferenceByNextToken';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->listOrderReferenceByNextToken($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testSetOrderReferenceDetails()
{
$client = new Client($this->configParams);
$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',
'supplementary_data' => 'OrderReferenceAttributes.SellerOrderAttributes.SupplementaryData',
'request_payment_authorization' => 'OrderReferenceAttributes.RequestPaymentAuthorization',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'SetOrderReferenceDetails';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->setOrderReferenceDetails($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testSetOrderAttributesBeforeConfirm()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_order_reference_id' => 'AmazonOrderReferenceId',
'amount' => 'OrderAttributes.OrderTotal.Amount',
'currency_code' => 'OrderAttributes.OrderTotal.CurrencyCode',
'platform_id' => 'OrderAttributes.PlatformId',
'seller_note' => 'OrderAttributes.SellerNote',
'seller_order_id' => 'OrderAttributes.SellerOrderAttributes.SellerOrderId',
'store_name' => 'OrderAttributes.SellerOrderAttributes.StoreName',
'custom_information' => 'OrderAttributes.SellerOrderAttributes.CustomInformation',
'supplementary_data' => 'OrderAttributes.SellerOrderAttributes.SupplementaryData',
'request_payment_authorization' => 'OrderAttributes.RequestPaymentAuthorization',
'payment_service_provider_id' => 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderId',
'payment_service_provider_order_id' => 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderOrderId',
'order_item_categories' => array(),
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'SetOrderAttributes';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$expectedParameters['OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.1'] = 'Antiques';
$expectedParameters['OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.2'] = 'Electronics';
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->setOrderAttributes($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
/* Call is same as BeforeConfirm call except the amount and currency_code fields are omitted */
public function testSetOrderAttributesAfterConfirm()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_order_reference_id' => 'AmazonOrderReferenceId',
'platform_id' => 'OrderAttributes.PlatformId',
'seller_note' => 'OrderAttributes.SellerNote',
'seller_order_id' => 'OrderAttributes.SellerOrderAttributes.SellerOrderId',
'store_name' => 'OrderAttributes.SellerOrderAttributes.StoreName',
'custom_information' => 'OrderAttributes.SellerOrderAttributes.CustomInformation',
'request_payment_authorization' => 'OrderAttributes.RequestPaymentAuthorization',
'payment_service_provider_id' => 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderId',
'payment_service_provider_order_id' => 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderOrderId',
'order_item_categories' => array(),
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'SetOrderAttributes';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$expectedParameters['OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.1'] = 'Antiques';
$expectedParameters['OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.2'] = 'Electronics';
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->setOrderAttributes($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testConfirmOrderReference()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_order_reference_id' => 'AmazonOrderReferenceId',
'mws_auth_token' => 'MWSAuthToken',
'success_url' => 'SuccessUrl',
'failure_url' => 'FailureUrl',
'authorization_amount' => 'AuthorizationAmount.Amount',
'currency_code' => 'AuthorizationAmount.CurrencyCode'
);
$action = 'ConfirmOrderReference';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->confirmOrderReference($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testCancelOrderReference()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_order_reference_id' => 'AmazonOrderReferenceId',
'cancelation_reason' => 'CancelationReason',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'CancelOrderReference';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->cancelOrderReference($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testCloseOrderReference()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_order_reference_id' => 'AmazonOrderReferenceId',
'closure_reason' => 'ClosureReason',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'CloseOrderReference';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->closeOrderReference($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testCloseAuthorization()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_authorization_id' => 'AmazonAuthorizationId',
'closure_reason' => 'ClosureReason',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'CloseAuthorization';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->closeAuthorization($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testAuthorize()
{
$client = new Client($this->configParams);
$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',
'seller_authorization_note' => 'SellerAuthorizationNote',
'transaction_timeout' => 'TransactionTimeout',
'soft_descriptor' => 'SoftDescriptor',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'Authorize';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->authorize($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testGetAuthorizationDetails()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_authorization_id' => 'AmazonAuthorizationId',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'GetAuthorizationDetails';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->getAuthorizationDetails($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testCapture()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_authorization_id' => 'AmazonAuthorizationId',
'capture_amount' => 'CaptureAmount.Amount',
'currency_code' => 'CaptureAmount.CurrencyCode',
'capture_reference_id' => 'CaptureReferenceId',
'seller_capture_note' => 'SellerCaptureNote',
'soft_descriptor' => 'SoftDescriptor',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'Capture';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->capture($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testGetCaptureDetails()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_capture_id' => 'AmazonCaptureId',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'GetCaptureDetails';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->getCaptureDetails($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testRefund()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_capture_id' => 'AmazonCaptureId',
'refund_reference_id' => 'RefundReferenceId',
'refund_amount' => 'RefundAmount.Amount',
'currency_code' => 'RefundAmount.CurrencyCode',
'seller_refund_note' => 'SellerRefundNote',
'soft_descriptor' => 'SoftDescriptor',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'Refund';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->refund($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testGetRefundDetails()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_refund_id' => 'AmazonRefundId',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'GetRefundDetails';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->getRefundDetails($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testGetMerchantAccountStatus()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'GetMerchantAccountStatus';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->getMerchantAccountStatus($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testGetServiceStatus()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'GetServiceStatus';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->getServiceStatus($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testCreateOrderReferenceForId()
{
$client = new Client($this->configParams);
$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'
);
$action = 'CreateOrderReferenceForId';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->createOrderReferenceForId($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testGetBillingAgreementDetails()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
'address_consent_token' => 'AddressConsentToken',
'access_token' => 'AccessToken',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'GetBillingAgreementDetails';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->getBillingAgreementDetails($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testSetBillingAgreementDetails()
{
$client = new Client($this->configParams);
$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'
);
$action = 'SetBillingAgreementDetails';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->setBillingAgreementDetails($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testConfirmBillingAgreement()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'ConfirmBillingAgreement';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->confirmBillingAgreement($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testValidateBillingAgreement()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'ValidateBillingAgreement';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->validateBillingAgreement($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testAuthorizeOnBillingAgreement()
{
$client = new Client($this->configParams);
$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'
);
$action = 'AuthorizeOnBillingAgreement';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->authorizeOnBillingAgreement($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testCloseBillingAgreement()
{
$client = new Client($this->configParams);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
'closure_reason' => 'ClosureReason',
'mws_auth_token' => 'MWSAuthToken'
);
$action = 'CloseBillingAgreement';
$parameters = $this->setParametersAndPost($fieldMappings, $action);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedStringParams = $this->callPrivateMethod($client, 'calculateSignatureAndParametersToString', $expectedParameters);
$response = $client->closeBillingAgreement($apiCallParams);
$apiParametersString = $client->getParameters();
$this->assertEquals($apiParametersString, $expectedStringParams);
}
public function testCharge()
{
$client = new Client($this->configParams);
$apiCallParams = array('amazon_reference_id' => 'S01-TEST');
try {
$client = new Client($this->configParams);
$apiCallParams = array('amazon_reference_id' => '');
$client->charge($apiCallParams);
} catch (\Exception $expected) {
$this->assertRegExp('/key amazon_order_reference_id or amazon_billing_agreement_id is null and is a required parameter./i', strval($expected));
}
try {
$client = new Client($this->configParams);
$apiCallParams = array('amazon_reference_id' => 'T01');
$client->charge($apiCallParams);
} catch (\Exception $expected) {
$this->assertRegExp('/Invalid Amazon Reference ID./i', strval($expected));
}
}
public function testGetUserInfo()
{
try {
$this->configParams['region'] = '';
$client = new Client($this->configParams);
$client->getUserInfo('Atza');
} catch (\Exception $expected) {
$this->assertRegExp('/is a required parameter./i', strval($expected));
}
try {
$this->configParams['region'] = 'us';
$client = new Client($this->configParams);
$client->getUserInfo(null);
} catch (\Exception $expected) {
$this->assertRegExp('/Access Token is a required parameter and is not set./i', strval($expected));
}
}
public function testSignature()
{
$client = new Client($this->configParams);
$parameters['SellerId'] = $this->configParams['merchant_id'];
$parameters['AWSAccessKeyId'] = $this->configParams['access_key'];
$parameters['Version'] = 'test';
$parameters['SignatureMethod'] = 'HmacSHA256';
$parameters['SignatureVersion'] = 2;
$parameters['Timestamp'] = $this->getFormattedTimestamp();
uksort($parameters, 'strcmp');
$signatureObj = new Signature($this->configParams,$parameters);
$expectedSignature = $signatureObj->getSignature();
$this->callPrivateMethod($client,'createServiceUrl', null);
$signature = $this->callPrivateMethod($client,'signParameters', $parameters);
$this->assertEquals($signature, $expectedSignature);
}
public function test500or503()
{
try {
$client = new Client($this->configParams);
$url = 'https://www.amazon.com/OffAmazonPayments_Sandbox/2013-01-01';
$client->setMwsServiceUrl($url);
$this->callPrivateMethod($client, 'invokePost', null);
} catch (\Exception $expected) {
$this->assertRegExp('/Maximum number of retry attempts./i', strval($expected));
}
}
public function testXmlResponse()
{
$response = array();
$response['ResponseBody'] =
'
S01-5806490-2147504
2015-09-27T02:18:33.408Z
This is testing API call
';
$responseObj = new ResponseParser($response);
$xmlResponse = $responseObj->toXml();
$this->assertEquals($xmlResponse, $response['ResponseBody']);
}
public function testJsonResponse()
{
$response = array('Status' => '200');
$response['ResponseBody'] =
'
S01-5806490-2147504
2015-09-27T02:18:33.408Z
This is testing API call
';
$json =
'{"AmazonOrderReferenceId":"S01-5806490-2147504","ExpirationTimestamp":"2015-09-27T02:18:33.408Z","SellerNote":"This is testing API call","ResponseStatus":"200"}';
$responseObj = new ResponseParser($response);
$jsonResponse = $responseObj->toJson();
$this->assertEquals($json, $jsonResponse);
}
public function testArrayResponse()
{
$response = array('Status' => '200');
$response['ResponseBody'] =
'
S01-5806490-2147504
2015-09-27T02:18:33.408Z
This is testing API call
';
$array = array('AmazonOrderReferenceId' => 'S01-5806490-2147504',
'ExpirationTimestamp' => '2015-09-27T02:18:33.408Z',
'SellerNote' => 'This is testing API call',
'ResponseStatus' => '200');
$responseObj = new ResponseParser($response);
$arrayResponse = $responseObj->toArray();
$this->assertEquals($array, $arrayResponse);
}
private function setParametersAndPost($fieldMappings, $action)
{
$expectedParameters = array();
$apiCallParams = array();
$parameters = $this->setDefaultValues($fieldMappings);
$expectedParameters = $parameters['expectedParameters'];
$apiCallParams = $parameters['apiCallParams'];
$expectedParameters['Action'] = $action;
foreach ($fieldMappings as $parm => $value) {
if ($parm === 'capture_now' || $parm === 'confirm_now' || $parm === 'inherit_shipping_address' || $parm === 'request_payment_authorization') {
$expectedParameters[$value] = true;
$apiCallParams[$parm] = true;
} elseif ($parm === 'order_item_categories') {
$apiCallParams[$parm] = array('Antiques', 'Electronics');
} elseif ($parm === 'order_status_list') {
$apiCallParams[$parm] = array('Open', 'Closed');
} elseif (!isset($expectedParameters[$value])) {
$unique_id = uniqid();
$expectedParameters[$value] = $unique_id;
$apiCallParams[$parm] = $unique_id;
}
}
return array('expectedParameters' => $expectedParameters,
'apiCallParams' => $apiCallParams);
}
private function setDefaultValues($fieldMappings)
{
$expectedParameters = array();
$apiCallParams = array();
if (array_key_exists('platform_id', $fieldMappings)) {
$expectedParameters[$fieldMappings['platform_id']] = $this->configParams['platform_id'];
$apiCallParams['platform_id'] = $this->configParams['platform_id'];
}
if (array_key_exists('currency_code', $fieldMappings)) {
$expectedParameters[$fieldMappings['currency_code']] = 'TEST';
$apiCallParams['currency_code'] = 'TEST';
}
return array('expectedParameters' => $expectedParameters,
'apiCallParams' => $apiCallParams);
}
/* Formats date as ISO 8601 timestamp */
private function getFormattedTimestamp()
{
return gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
}
private function callPrivateMethod($client, $methodName, $parameters)
{
$reflectionClass = new \ReflectionClass("AmazonPay\Client");
$reflectionMethod = $reflectionClass->getMethod($methodName);
$reflectionMethod->setAccessible(true);
$expectedStringParams = $reflectionMethod->invoke($client, $parameters);
return $expectedStringParams;
}
}
.github/PULL_REQUEST_TEMPLATE.md 0000666 00000000251 13436752777 0011734 0 ustar 00 *Issue #, if available:*
*Description of changes:*
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
CODE_OF_CONDUCT.md 0000666 00000000467 13436752777 0007403 0 ustar 00 ## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
opensource-codeofconduct@amazon.com with any additional questions or comments.
AmazonPay/IpnHandler.php 0000666 00000040271 13436752777 0011235 0 ustar 00 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);
}
}
}
public function setLogger(LoggerInterface $logger = null) {
$this->logger = $logger;
}
/* Helper function to log data within the Client */
private function logMessage($message) {
if ($this->logger) {
$this->logger->debug($message);
}
}
/* 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";
}
}
/* Ensures that the URL of the certificate is one belonging to AWS.
*
* @param string $url Certificate URL
*
* @throws InvalidSnsMessageException if the cert url is invalid.
*/
private function validateUrl($url)
{
$parsed = parse_url($url);
if (empty($parsed['scheme'])
|| empty($parsed['host'])
|| $parsed['scheme'] !== 'https'
|| substr($url, -4) !== '.pem'
|| !preg_match($this->defaultHostPattern, $parsed['host'])
) {
throw new \Exception(
'The certificate is located on an invalid domain.'
);
}
}
/* 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->validateUrl($certificatePath);
$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();
$this->logMessage(sprintf('IPN received for merchant account: %s', $this->sanitizeResponseData($ipnMessage['SellerId'])));
$this->logMessage($this->sanitizeResponseData($ipnMessage['NotificationData']));
// 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;
}
private function sanitizeResponseData($input)
{
$patterns = array();
$patterns[0] = '/()(.+)(<\/SellerNote>)/ms';
$patterns[1] = '/()(.+)(<\/AuthorizationBillingAddress>)/ms';
$patterns[2] = '/()(.+)(<\/SellerAuthorizationNote>)/ms';
$patterns[3] = '/()(.+)(<\/SellerCaptureNote>)/ms';
$patterns[4] = '/()(.+)(<\/SellerRefundNote>)/ms';
$replacements = array();
$replacements[0] = '$1 REMOVED $3';
$replacements[1] = '$1 REMOVED $3';
$replacements[2] = '$1 REMOVED $3';
$replacements[3] = '$1 REMOVED $3';
$replacements[4] = '$1 REMOVED $3';
return preg_replace($patterns, $replacements, $input);
}
}
AmazonPay/Regions.php 0000666 00000001026 13436752777 0010612 0 ustar 00 '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');
}
AmazonPay/ResponseParser.php 0000666 00000005132 13436752777 0012161 0 ustar 00 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)
{
$oroStatus = $this->getStatus('GetORO', '//GetORO:OrderReferenceStatus', $response);
return $oroStatus;
}
/* Get the status of the BillingAgreement */
public function getBillingAgreementDetailsStatus($response)
{
$baStatus = $this->getStatus('GetBA', '//GetBA:BillingAgreementStatus', $response);
return $baStatus;
}
private function getStatus($type, $path, $response)
{
$data= new \SimpleXMLElement($response);
$namespaces = $data->getNamespaces(true);
foreach($namespaces as $key=>$value){
$namespace = $value;
}
$data->registerXPathNamespace($type, $namespace);
foreach ($data->xpath($path) as $value) {
$status = json_decode(json_encode((array)$value), TRUE);
}
return $status;
}
}
AmazonPay/ResponseInterface.php 0000666 00000001255 13436752777 0012627 0 ustar 00 _response [XML]
*/
public function toArray();
/* Get the status of the BillingAgreement */
public function getBillingAgreementDetailsStatus($response);
/* Get the status of the OrderReference */
public function getOrderReferenceDetailsStatus($response);
}
AmazonPay/ClientInterface.php 0000666 00000046436 13436752777 0012261 0 ustar 00 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']
*/
protected 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) {
$this->headerArray[] = 'Authorization: bearer ' . $this->accessToken;
}
$response = $this->execute($ch);
return $response;
}
/* Execute Curl request */
/* Protected because will be used by PSP module */
protected function execute($ch)
{
$response = '';
// Ensure we never send the "Expect: 100-continue" header
$this->headerArray[] = 'Expect:';
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headerArray);
$response = curl_exec($ch);
if ($response === false) {
$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;
}
}
AmazonPay/Client.php 0000666 00000250102 13436752777 0010423 0 ustar 00 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,
'app_id' => null,
'handle_throttle' => true,
'override_service_url' => null
);
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;
// Implement a logging library that utilizes the PSR 3 logger interface
private $logger = null;
// 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);
}
// Invoke sandbox setter to throw exception if not Boolean datatype
if (!empty($configArray['sandbox'])) {
$this->setSandbox($configArray['sandbox']);
}
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.');
}
}
public function setLogger(LoggerInterface $logger = null) {
$this->logger = $logger;
}
/* Helper function to log data within the Client */
private function logMessage($message) {
if ($this->logger) {
$this->logger->debug($message);
}
}
/* 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('sandbox value ' . $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 config['app_id']
* Sets the value for config['app_id'] variable
*/
public function setAppId($value)
{
if (!empty($value)) {
$this->config['app_id'] = $value;
} else {
throw new \Exception('setter value for app 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) {
// Do not attemp to trim array variables, boolean variables, or the proxy password
// Trimming a boolean value (as a string) may not produce the expected output, so pass it through as-is
if (!is_array($value) && !is_bool($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=' . $this->urlEncode($accessToken);
$httpCurlRequest = new HttpCurl($this->config);
$response = $httpCurlRequest->httpGet($url);
$data = json_decode($response);
// Ensure that the Access Token matches either the supplied Client ID *or* the supplied App ID
// Web apps and Mobile apps will have different Client ID's but App ID should be the same
// As long as one of these matches, from a security perspective, we have done our due diligence
if (($data->aud != $this->config['client_id']) && ($data->app_id != $this->config['app_id'])) {
// The access token does not belong to us
throw new \Exception('The Access Token belongs to neither your Client ID nor App ID');
}
// Exchange the access token for user profile
$url = $this->profileEndpoint . '/user/profile';
$httpCurlRequest = new HttpCurl($this->config);
$httpCurlRequest->setAccessToken($accessToken);
$httpCurlRequest->setHttpHeader();
$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) {
// Do not use trim on boolean values, or it will convert them to '0' or '1'
if (!is_array($value) && !is_bool($value)) {
$value = trim($value);
}
// Ensure that no unexpected type coercions have happened
if ($param === 'capture_now' || $param === 'confirm_now' || $param === 'inherit_shipping_address' || $param === 'request_payment_authorization') {
if (!is_bool($value)) {
throw new \Exception($param . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value');
}
} elseif ($param === 'provider_credit_details' || $param === 'provider_credit_reversal_details' || $param === 'order_item_categories') {
if (!is_array($value)) {
throw new \Exception($param . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be an array value');
}
}
// When checking for non-empty values, consider any boolean as non-empty
if (array_key_exists($param, $fieldMappings) && (is_bool($value) || $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);
} elseif ($param === 'order_item_categories') {
$parameters = $this->setOrderItemCategories($parameters, $value);
}
} else {
$parameters[$fieldMappings[$param]] = $value;
}
}
}
$parameters = $this->setDefaultValues($parameters, $fieldMappings, $requestParameters);
$responseObject = $this->calculateSignatureAndPost($parameters);
return $responseObject;
}
/* 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 if (!(array_key_exists('Action', $parameters) && ( $parameters['Action'] === 'SetOrderAttributes' || $parameters['Action'] === 'ConfirmOrderReference'))) {
// Only supply a default CurrencyCode parameter if not using SetOrderAttributes API
$parameters[$fieldMappings['currency_code']] = strtoupper($this->config['currency_code']);
}
}
return $parameters;
}
/* setOrderItemCategories - helper function used by SetOrderAttributes API to set
* one or more Order Item Categories
*/
private function setOrderItemCategories($parameters, $categories)
{
$categoryIndex = 0;
$categoryString = 'OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.';
foreach ($categories as $value) {
$categoryIndex = $categoryIndex + 1;
$parameters[$categoryString . $categoryIndex] = $value;
}
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;
}
/* GetMerchantAccountStatus API call - Returns the status of the Merchant Account.
* @see TODO
* @param requestParameters['merchant_id'] - [String]
* @optional requestParameters['mws_auth_token'] - [String]
*/
public function getMerchantAccountStatus($requestParameters = array())
{
$parameters = array();
$parameters['Action'] = 'GetMerchantAccountStatus';
$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);
}
/* GetOrderReferenceDetails API call - Returns details about the Order Reference object and its current state.
* @see https://pay.amazon.com/developer/documentation/apireference/201751970
*
* @param requestParameters['merchant_id'] - [String]
* @param requestParameters['amazon_order_reference_id'] - [String]
* @optional requestParameters['address_consent_token'] - [String]
* @optional requestParameters['access_token'] - [String]
* @optional requestParameters['mws_auth_token'] - [String]
*
* You cannot pass both address_consent_token and access_token in
* the same call or you will encounter a 400/"AmbiguousToken" error
*/
public function getOrderReferenceDetails($requestParameters = array())
{
$parameters = 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',
'access_token' => 'AccessToken',
'mws_auth_token' => 'MWSAuthToken'
);
$responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
return ($responseObject);
}
/* ListOrderReference API call - Returns details about the Order Reference object and its current state from the sellers.
* @see https://pay.amazon.com/developer/documentation/apireference/201751970
*
* @param requestParameters['merchant_id'] - [String]
* @param requestParameters['query_id'] - [String]
* @param requestParameters['query_id_type'] - [String] (SellerOrderId)
* @optional requestParameters['page_size'] - [Int]
* @optional requestParameters['created_start_time'] - [String] (Date/Time ISO8601)
* @optional requestParameters['created_end_time'] - [String] (Date/Time ISO8601) Limited to 31 days
* @optional requestParameters['sort_order'] - [String] (Ascending/Descending)
* @optional requestParameters['mws_auth_token'] - [String]
* @optional requestParameters['status_list'] - [Array]
*/
public function listOrderReference($requestParameters = array())
{
$parameters = array();
$parameters['Action'] = 'ListOrderReference';
$requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
$payment_domains = array(
"us" => "NA_USD",
"jp" => "FE_JPY",
"de" => "EU_EUR",
"uk" => "EU_GBP"
);
$requestParameters['payment_domain'] = $payment_domains[strtolower($this->config['region'])];
$fieldMappings = array(
'merchant_id' => 'SellerId',
'mws_auth_token' => 'MWSAuthToken',
'query_id' => 'QueryId',
'query_id_type' => 'QueryIdType',
'page_size' => 'PageSize',
'created_start_time' => 'CreatedTimeRange.StartTime',
'created_end_time' => 'CreatedTimeRange.EndTime',
'sort_order' => 'SortOrder',
'payment_domain' => 'PaymentDomain'
);
if( $requestParameters['order_status_list'] ){
$status_index = 0;
foreach ($requestParameters['order_status_list'] as $status) {
$status_index++;
$requestParameters['order_status_list_'.$status_index] = $status;
$fieldMappings['order_status_list_'.$status_index] = 'OrderReferenceStatusListFilter.OrderReferenceStatus.'.$status_index;
}
}
$responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
return ($responseObject);
}
/* ListOrderReferenceByNextToken API call - Returns details about the Order Reference object and its current
* state from the sellers.
* @see https://pay.amazon.com/developer/documentation/apireference/201751970
*
* @param requestParameters['merchant_id'] - [String]
* @param requestParameters['next_token'] - [String]
* @optional requestParameters['mws_auth_token'] - [String]
*/
public function listOrderReferenceByNextToken($requestParameters = array())
{
$parameters = array();
$parameters['Action'] = 'ListOrderReferenceByNextToken';
$requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'mws_auth_token' => 'MWSAuthToken',
'next_page_token' => 'NextPageToken'
);
$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 https://pay.amazon.com/developer/documentation/apireference/201751960
*
* @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['supplementary_data'] - [String]
* @optional requestParameters['request_payment_authorization'] - [Boolean]
* @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',
'supplementary_data' => 'OrderReferenceAttributes.SellerOrderAttributes.SupplementaryData',
'request_payment_authorization' => 'OrderReferenceAttributes.RequestPaymentAuthorization',
'mws_auth_token' => 'MWSAuthToken'
);
$responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
return ($responseObject);
}
/* SetOrderAttributes API call - Sets order reference details such as the order total and a description for the order.
*
* @param requestParameters['merchant_id'] - [String]
* @param requestParameters['amazon_order_reference_id'] - [String]
* @optional requestParameters['amount'] - [String]
* @optional 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['supplementary_data'] - [String]
* @optional requestParameters['request_payment_authorization'] - [Boolean]
* @optional requestParameters['payment_service_provider_id'] - [String]
* @optional requestParameters['payment_service_provider_order_id'] - [String]
* @optional requestParameters['order_item_categories'] - [array()]
* @optional requestParameters['mws_auth_token'] - [String]
*/
public function setOrderAttributes($requestParameters = array())
{
$parameters = array();
$parameters['Action'] = 'SetOrderAttributes';
$requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
$fieldMappings = array(
'merchant_id' => 'SellerId',
'amazon_order_reference_id' => 'AmazonOrderReferenceId',
'amount' => 'OrderAttributes.OrderTotal.Amount',
'currency_code' => 'OrderAttributes.OrderTotal.CurrencyCode',
'platform_id' => 'OrderAttributes.PlatformId',
'seller_note' => 'OrderAttributes.SellerNote',
'seller_order_id' => 'OrderAttributes.SellerOrderAttributes.SellerOrderId',
'store_name' => 'OrderAttributes.SellerOrderAttributes.StoreName',
'custom_information' => 'OrderAttributes.SellerOrderAttributes.CustomInformation',
'supplementary_data' => 'OrderAttributes.SellerOrderAttributes.SupplementaryData',
'request_payment_authorization' => 'OrderAttributes.RequestPaymentAuthorization',
'payment_service_provider_id' => 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderId',
'payment_service_provider_order_id' => 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderOrderId',
'order_item_categories' => array(),
'mws_auth_token' => 'MWSAuthToken'
);
$responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
return ($responseObject);
}
/* ConfirmOrderReference API call - Confirms that the order reference is free of constraints and all required information has been set on the order reference.
* @see https://pay.amazon.com/developer/documentation/apireference/201751980
* @param requestParameters['merchant_id'] - [String]
* @param requestParameters['amazon_order_reference_id'] - [String]
* @optional requestParameters['success_url'] - [String]'
* @optional requestParameters['failure_url'] - [String]
* @optional requestParameters['authorization_amount'] - [String]
* @optional requestParameters['currency_code'] - [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',
'success_url' => 'SuccessUrl',
'failure_url' => 'FailureUrl',
'authorization_amount' => 'AuthorizationAmount.Amount',
'currency_code' => 'AuthorizationAmount.CurrencyCode',
'mws_auth_token' => 'MWSAuthToken'
);
if ($requestParameters['authorization_amount'] && !$requestParameters['currency_code']) {
$requestParameters['currency_code'] = strtoupper($this->config['currency_code']);
}
$responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
return ($responseObject);
}
/* CancelOrderReference API call - Cancels a previously confirmed order reference.
* @see https://pay.amazon.com/developer/documentation/apireference/201751990
*
* @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);
}
/* CloseOrderReference 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 https://pay.amazon.com/developer/documentation/apireference/201752000
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201752070
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201752010
*
* @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'] [Boolean]
* @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 https://pay.amazon.com/developer/documentation/apireference/201752030
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201752040
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201752060
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201752080
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201752100
*
* @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 OffAmazonPayments API section
* @see https://pay.amazon.com/developer/documentation/apireference/201752110
*
* The GetServiceStatus operation returns the operational status of the OffAmazonPayments 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 https://pay.amazon.com/developer/documentation/apireference/201751670
*
* @param requestParameters['merchant_id'] - [String]
* @param requestParameters['id'] - [String]
* @optional requestParameters['inherit_shipping_address'] [Boolean]
* @optional requestParameters['confirm_now'] - [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['supplementary_data'] - [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',
'supplementary_data' => 'OrderReferenceAttributes.SupplementaryData',
'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 https://pay.amazon.com/developer/documentation/apireference/201751690
*
* @param requestParameters['merchant_id'] - [String]
* @param requestParameters['amazon_billing_agreement_id'] - [String]
* @optional requestParameters['address_consent_token'] - [String]
* @optional requestParameters['access_token'] - [String]
* @optional requestParameters['mws_auth_token'] - [String]
*
* You cannot pass both address_consent_token and access_token in
* the same call or you will encounter a 400/"AmbiguousToken" error
*/
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',
'access_token' => 'AccessToken',
'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 https://pay.amazon.com/developer/documentation/apireference/201751700
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201751710
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201751720
*
* @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 https://pay.amazon.com/developer/documentation/apireference/201751940
*
* @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'] [Boolean]
* @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['supplementary_data'] - [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',
'supplementary_data' => 'SellerOrderAttributes.SupplementaryData',
'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 https://pay.amazon.com/developer/documentation/apireference/201751950
*
* @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;
default:
throw new \Exception('Invalid Charge Type');
}
}
/* 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())
{
foreach ($parameters as $key => $value) {
// Ensure that no unexpected type coercions have happened
if ($key === 'CaptureNow' || $key === 'ConfirmNow' || $key === 'InheritShippingAddress' || $key === 'RequestPaymentAuthorization') {
if (!is_bool($value)) {
throw new \Exception($key . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value');
}
}
// Ensure boolean values are outputed as 'true' or 'false'
if (is_bool($value)) {
$parameters[$key] = json_encode($value);
}
}
$parameters['AWSAccessKeyId'] = $this->config['access_key'];
$parameters['Version'] = self::MWS_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);
$this->logMessage($this->sanitizeRequestData($data));
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"];
$this->logMessage($this->userAgent);
$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;
}
$this->logMessage($this->sanitizeResponseData($response['ResponseBody']));
return $response;
}
/* Exponential sleep on failed request
* Up to three retries will occur if first reqest fails
* after 1.0 second, 2.2 seconds, and finally 7.0 seconds
* @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) {
// PHP delays are in microseconds (1 million microsecond = 1 sec)
// 1st delay is (4^1) * 100000 + 600000 = 0.4 + 0.6 second = 1.0 sec
// 2nd delay is (4^2) * 100000 + 600000 = 1.6 + 0.6 second = 2.2 sec
// 3rd delay is (4^3) * 100000 + 600000 = 6.4 + 0.6 second = 7.0 sec
$delay = (int) (pow(4, $retries) * 100000) + 600000;
usleep($delay);
} else {
throw new \Exception('Error Code: '. $status.PHP_EOL.'Maximum number of retry attempts - '. $retries .' reached');
}
}
/* 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)) {
if (!is_null($this->config['override_service_url'])) {
$this->mwsEndpointUrl = preg_replace("(https?://)", "", $this->config['override_service_url']);
} else {
$this->mwsEndpointUrl = $this->mwsServiceUrls[$this->regionMappings[$region]];
}
$this->mwsServiceUrl = 'https://' . $this->mwsEndpointUrl . '/' . $this->modePath . '/' . self::MWS_VERSION;
$this->mwsEndpointPath = '/' . $this->modePath . '/' . self::MWS_VERSION;
} else {
throw new \Exception($region . ' is not a valid region');
}
} 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 */
/* Protected because of PSP module usaged */
protected function constructUserAgentHeader()
{
$this->userAgent = 'amazon-pay-sdk-php/' . self::SDK_VERSION . ' (';
if (($this->config['application_name']) || ($this->config['application_version'])) {
if ($this->config['application_name']) {
$this->userAgent .= $this->quoteApplicationName($this->config['application_name']);
if ($this->config['application_version']) {
$this->userAgent .= '/';
}
}
if ($this->config['application_version']) {
$this->userAgent .= $this->quoteApplicationVersion($this->config['application_version']);
}
$this->userAgent .= '; ';
}
$this->userAgent .= 'PHP/' . phpversion() . '; ';
$this->userAgent .= php_uname('s') . '/' . php_uname('m') . '/' . php_uname('r');
$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;
}
private function sanitizeRequestData($input)
{
$patterns = array();
$patterns[0] = '/(SellerNote=)(.+)(&)/ms';
$patterns[1] = '/(SellerAuthorizationNote=)(.+)(&)/ms';
$patterns[2] = '/(SellerCaptureNote=)(.+)(&)/ms';
$patterns[3] = '/(SellerRefundNote=)(.+)(&)/ms';
$replacements = array();
$replacements[0] = '$1REMOVED$3';
$replacements[1] = '$1REMOVED$3';
$replacements[2] = '$1REMOVED$3';
$replacements[3] = '$1REMOVED$3';
return preg_replace($patterns, $replacements, $input);
}
private function sanitizeResponseData($input)
{
$patterns = array();
$patterns[0] = '/()(.+)(<\/Buyer>)/ms';
$patterns[1] = '/()(.+)(<\/PhysicalDestination>)/ms';
$patterns[2] = '/()(.+)(<\/BillingAddress>)/ms';
$patterns[3] = '/()(.+)(<\/SellerNote>)/ms';
$patterns[4] = '/()(.+)(<\/AuthorizationBillingAddress>)/ms';
$patterns[5] = '/()(.+)(<\/SellerAuthorizationNote>)/ms';
$patterns[6] = '/()(.+)(<\/SellerCaptureNote>)/ms';
$patterns[7] = '/()(.+)(<\/SellerRefundNote>)/ms';
$replacements = array();
$replacements[0] = '$1 REMOVED $3';
$replacements[1] = '$1 REMOVED $3';
$replacements[2] = '$1 REMOVED $3';
$replacements[3] = '$1 REMOVED $3';
$replacements[4] = '$1 REMOVED $3';
$replacements[5] = '$1 REMOVED $3';
$replacements[6] = '$1 REMOVED $3';
$replacements[7] = '$1 REMOVED $3';
return preg_replace($patterns, $replacements, $input);
}
/* Computes RFC 2104-compliant HMAC signature */
public static function getSignature($stringToSign, $secretKey)
{
return base64_encode(hash_hmac('sha256', $stringToSign, $secretKey, true));
}
}
AmazonPay/IpnHandlerInterface.php 0000666 00000001445 13436752777 0013056 0 ustar 00 =5.5.0"
},
"require-dev": {
"phpunit/phpunit": "^4"
}
}
NOTICE.txt 0000666 00000001134 13436752777 0006316 0 ustar 00 *-*-**-***-*****-********-*************
Amazon Pay SDK (PHP)
Copyright 2013-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
*-*-**-***-*****-********-*************
You may not use this file except in compliance with the License. You may obtain a copy of the License at:
http://aws.amazon.com/apache2.0
This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
CONTRIBUTING.md 0000666 00000007003 13436752777 0007026 0 ustar 00 # Contributing Guidelines
Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
documentation, we greatly value feedback and contributions from our community.
Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
information to effectively respond to your bug report or contribution.
## Reporting Bugs/Feature Requests
We welcome you to use the GitHub issue tracker to report bugs or suggest features.
When filing an issue, please check [existing open](https://github.com/amzn/amazon-pay-sdk-php/issues), or [recently closed](https://github.com/amzn/amazon-pay-sdk-php/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
* A reproducible test case or series of steps
* The version of our code being used
* Any modifications you've made relevant to the bug
* Anything unusual about your environment or deployment
## Contributing via Pull Requests
Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
1. You are working against the latest source on the *master* branch.
2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
To send us a pull request, please:
1. Fork the repository.
2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
3. Ensure local tests pass.
4. Commit to your fork using clear commit messages.
5. Send us a pull request, answering any default questions in the pull request interface.
6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
## Finding contributions to work on
Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/amzn/amazon-pay-sdk-php/labels/help%20wanted) issues is a great place to start.
## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
opensource-codeofconduct@amazon.com with any additional questions or comments.
## Security issue notifications
If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
## Licensing
See the [LICENSE](https://github.com/amzn/amazon-pay-sdk-php/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
LICENSE.txt 0000666 00000026136 13436752777 0006430 0 ustar 00
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.gitignore 0000666 00000000026 13436752777 0006563 0 ustar 00 vendor
composer.lock
Psr/Log/LoggerTrait.php 0000666 00000006437 13436752777 0011030 0 ustar 00 log(LogLevel::EMERGENCY, $message, $context);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function alert($message, array $context = array())
{
$this->log(LogLevel::ALERT, $message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function critical($message, array $context = array())
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function error($message, array $context = array())
{
$this->log(LogLevel::ERROR, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function warning($message, array $context = array())
{
$this->log(LogLevel::WARNING, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function notice($message, array $context = array())
{
$this->log(LogLevel::NOTICE, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function info($message, array $context = array())
{
$this->log(LogLevel::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function debug($message, array $context = array())
{
$this->log(LogLevel::DEBUG, $message, $context);
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return null
*/
abstract public function log($level, $message, array $context = array());
}
Psr/Log/NullLogger.php 0000666 00000001213 13436752777 0010642 0 ustar 00 logger) { }`
* blocks.
*/
class NullLogger extends AbstractLogger
{
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return null
*/
public function log($level, $message, array $context = array())
{
// noop
}
}
Psr/Log/Test/LoggerInterfaceTest.php 0000666 00000010422 13436752777 0013411 0 ustar 00 ".
*
* Example ->error('Foo') would yield "error Foo".
*
* @return string[]
*/
abstract public function getLogs();
public function testImplements()
{
$this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
}
/**
* @dataProvider provideLevelsAndMessages
*/
public function testLogsAtAllLevels($level, $message)
{
$logger = $this->getLogger();
$logger->{$level}($message, array('user' => 'Bob'));
$logger->log($level, $message, array('user' => 'Bob'));
$expected = array(
$level.' message of level '.$level.' with context: Bob',
$level.' message of level '.$level.' with context: Bob',
);
$this->assertEquals($expected, $this->getLogs());
}
public function provideLevelsAndMessages()
{
return array(
LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
);
}
/**
* @expectedException \Psr\Log\InvalidArgumentException
*/
public function testThrowsOnInvalidLevel()
{
$logger = $this->getLogger();
$logger->log('invalid level', 'Foo');
}
public function testContextReplacement()
{
$logger = $this->getLogger();
$logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
$expected = array('info {Message {nothing} Bob Bar a}');
$this->assertEquals($expected, $this->getLogs());
}
public function testObjectCastToString()
{
$dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString'));
$dummy->expects($this->once())
->method('__toString')
->will($this->returnValue('DUMMY'));
$this->getLogger()->warning($dummy);
$expected = array('warning DUMMY');
$this->assertEquals($expected, $this->getLogs());
}
public function testContextCanContainAnything()
{
$context = array(
'bool' => true,
'null' => null,
'string' => 'Foo',
'int' => 0,
'float' => 0.5,
'nested' => array('with object' => new DummyTest),
'object' => new \DateTime,
'resource' => fopen('php://memory', 'r'),
);
$this->getLogger()->warning('Crazy context data', $context);
$expected = array('warning Crazy context data');
$this->assertEquals($expected, $this->getLogs());
}
public function testContextExceptionKeyCanBeExceptionOrOtherValues()
{
$logger = $this->getLogger();
$logger->warning('Random message', array('exception' => 'oops'));
$logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));
$expected = array(
'warning Random message',
'critical Uncaught Exception!'
);
$this->assertEquals($expected, $this->getLogs());
}
}
class DummyTest
{
}
Psr/Log/LoggerAwareTrait.php 0000666 00000000615 13436752777 0012000 0 ustar 00 logger = $logger;
}
}
Psr/Log/LoggerAwareInterface.php 0000666 00000000451 13436752777 0012613 0 ustar 00 log(LogLevel::EMERGENCY, $message, $context);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function alert($message, array $context = array())
{
$this->log(LogLevel::ALERT, $message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function critical($message, array $context = array())
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function error($message, array $context = array())
{
$this->log(LogLevel::ERROR, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function warning($message, array $context = array())
{
$this->log(LogLevel::WARNING, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function notice($message, array $context = array())
{
$this->log(LogLevel::NOTICE, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function info($message, array $context = array())
{
$this->log(LogLevel::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return null
*/
public function debug($message, array $context = array())
{
$this->log(LogLevel::DEBUG, $message, $context);
}
}