Client.php 88 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016
  1. <?php
  2. namespace AmazonPay;
  3. /* Class Client
  4. * Takes configuration information
  5. * Makes API calls to MWS for Amazon Pay
  6. * returns Response Object
  7. */
  8. require_once 'ResponseParser.php';
  9. require_once 'HttpCurl.php';
  10. require_once 'ClientInterface.php';
  11. require_once 'Regions.php';
  12. if (!interface_exists('\Psr\Log\LoggerAwareInterface')) {
  13. require_once(__DIR__.'/../Psr/Log/LoggerAwareInterface.php');
  14. }
  15. if (!interface_exists('\Psr\Log\LoggerInterface')) {
  16. require_once(__DIR__.'/../Psr/Log/LoggerInterface.php');
  17. }
  18. use Psr\Log\LoggerAwareInterface;
  19. use Psr\Log\LoggerInterface;
  20. class Client implements ClientInterface, LoggerAwareInterface
  21. {
  22. const SDK_VERSION = '3.6.0';
  23. const MWS_VERSION = '2013-01-01';
  24. const MAX_ERROR_RETRY = 3;
  25. // Construct User agent string based off of the application_name, application_version, PHP platform
  26. private $userAgent = null;
  27. private $parameters = null;
  28. private $mwsEndpointPath = null;
  29. private $mwsEndpointUrl = null;
  30. private $profileEndpoint = null;
  31. private $config = array(
  32. 'merchant_id' => null,
  33. 'secret_key' => null,
  34. 'access_key' => null,
  35. 'region' => null,
  36. 'currency_code' => null,
  37. 'sandbox' => false,
  38. 'platform_id' => null,
  39. 'cabundle_file' => null,
  40. 'application_name' => null,
  41. 'application_version' => null,
  42. 'proxy_host' => null,
  43. 'proxy_port' => -1,
  44. 'proxy_username' => null,
  45. 'proxy_password' => null,
  46. 'client_id' => null,
  47. 'app_id' => null,
  48. 'handle_throttle' => true,
  49. 'override_service_url' => null
  50. );
  51. private $modePath = null;
  52. // Final URL to where the API parameters POST done, based off the config['region'] and respective $mwsServiceUrls
  53. private $mwsServiceUrl = null;
  54. private $mwsServiceUrls;
  55. private $profileEndpointUrls;
  56. private $regionMappings;
  57. // Implement a logging library that utilizes the PSR 3 logger interface
  58. private $logger = null;
  59. // Boolean variable to check if the API call was a success
  60. public $success = false;
  61. /* Takes user configuration array from the user as input
  62. * Takes JSON file path with configuration information as input
  63. * Validates the user configuration array against existing config array
  64. */
  65. public function __construct($config = null)
  66. {
  67. $this->getRegionUrls();
  68. if (!is_null($config)) {
  69. if (is_array($config)) {
  70. $configArray = $config;
  71. } elseif (!is_array($config)) {
  72. $configArray = $this->checkIfFileExists($config);
  73. }
  74. // Invoke sandbox setter to throw exception if not Boolean datatype
  75. if (!empty($configArray['sandbox'])) {
  76. $this->setSandbox($configArray['sandbox']);
  77. }
  78. if (is_array($configArray)) {
  79. $this->checkConfigKeys($configArray);
  80. } else {
  81. throw new \Exception('$config is of the incorrect type ' . gettype($configArray) . ' and should be of the type array');
  82. }
  83. } else {
  84. throw new \Exception('$config cannot be null.');
  85. }
  86. }
  87. public function setLogger(LoggerInterface $logger = null) {
  88. $this->logger = $logger;
  89. }
  90. /* Helper function to log data within the Client */
  91. private function logMessage($message) {
  92. if ($this->logger) {
  93. $this->logger->debug($message);
  94. }
  95. }
  96. /* Get the Region specific properties from the Regions class.*/
  97. private function getRegionUrls()
  98. {
  99. $regionObject = new Regions();
  100. $this->mwsServiceUrls = $regionObject->mwsServiceUrls;
  101. $this->regionMappings = $regionObject->regionMappings;
  102. $this->profileEndpointUrls = $regionObject->profileEndpointUrls;
  103. }
  104. /* checkIfFileExists - check if the JSON file exists in the path provided */
  105. private function checkIfFileExists($config)
  106. {
  107. if (file_exists($config)) {
  108. $jsonString = file_get_contents($config);
  109. $configArray = json_decode($jsonString, true);
  110. $jsonError = json_last_error();
  111. if ($jsonError != 0) {
  112. $errorMsg = "Error with message - content is not in json format" . $this->getErrorMessageForJsonError($jsonError) . " " . $configArray;
  113. throw new \Exception($errorMsg);
  114. }
  115. } else {
  116. $errorMsg ='$config is not a Json File path or the Json File was not found in the path provided';
  117. throw new \Exception($errorMsg);
  118. }
  119. return $configArray;
  120. }
  121. /* Checks if the keys of the input configuration matches the keys in the config array
  122. * if they match the values are taken else throws exception
  123. * strict case match is not performed
  124. */
  125. private function checkConfigKeys($config)
  126. {
  127. $config = array_change_key_case($config, CASE_LOWER);
  128. $config = $this->trimArray($config);
  129. foreach ($config as $key => $value) {
  130. if (array_key_exists($key, $this->config)) {
  131. $this->config[$key] = $value;
  132. } else {
  133. throw new \Exception('Key ' . $key . ' is either not part of the configuration or has incorrect Key name.
  134. check the config array key names to match your key names of your config array', 1);
  135. }
  136. }
  137. }
  138. /* Convert a json error code to a descriptive error message
  139. *
  140. * @param int $jsonError message code
  141. *
  142. * @return string error message
  143. */
  144. private function getErrorMessageForJsonError($jsonError)
  145. {
  146. switch ($jsonError) {
  147. case JSON_ERROR_DEPTH:
  148. return " - maximum stack depth exceeded.";
  149. break;
  150. case JSON_ERROR_STATE_MISMATCH:
  151. return " - invalid or malformed JSON.";
  152. break;
  153. case JSON_ERROR_CTRL_CHAR:
  154. return " - control character error.";
  155. break;
  156. case JSON_ERROR_SYNTAX:
  157. return " - syntax error.";
  158. break;
  159. default:
  160. return ".";
  161. break;
  162. }
  163. }
  164. /* Setter for sandbox
  165. * Sets the Boolean value for config['sandbox'] variable
  166. */
  167. public function setSandbox($value)
  168. {
  169. if (is_bool($value)) {
  170. $this->config['sandbox'] = $value;
  171. } else {
  172. throw new \Exception('sandbox value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value');
  173. }
  174. }
  175. /* Setter for config['client_id']
  176. * Sets the value for config['client_id'] variable
  177. */
  178. public function setClientId($value)
  179. {
  180. if (!empty($value)) {
  181. $this->config['client_id'] = $value;
  182. } else {
  183. throw new \Exception('setter value for client ID provided is empty');
  184. }
  185. }
  186. /* Setter for config['app_id']
  187. * Sets the value for config['app_id'] variable
  188. */
  189. public function setAppId($value)
  190. {
  191. if (!empty($value)) {
  192. $this->config['app_id'] = $value;
  193. } else {
  194. throw new \Exception('setter value for app ID provided is empty');
  195. }
  196. }
  197. /* Setter for Proxy
  198. * input $proxy [array]
  199. * @param $proxy['proxy_user_host'] - hostname for the proxy
  200. * @param $proxy['proxy_user_port'] - hostname for the proxy
  201. * @param $proxy['proxy_user_name'] - if your proxy required a username
  202. * @param $proxy['proxy_user_password'] - if your proxy required a password
  203. */
  204. public function setProxy($proxy)
  205. {
  206. if (!empty($proxy['proxy_user_host']))
  207. $this->config['proxy_host'] = $proxy['proxy_user_host'];
  208. if (!empty($proxy['proxy_user_port']))
  209. $this->config['proxy_port'] = $proxy['proxy_user_port'];
  210. if (!empty($proxy['proxy_user_name']))
  211. $this->config['proxy_username'] = $proxy['proxy_user_name'];
  212. if (!empty($proxy['proxy_user_password']))
  213. $this->config['proxy_password'] = $proxy['proxy_user_password'];
  214. }
  215. /* Setter for $mwsServiceUrl
  216. * Set the URL to which the post request has to be made for unit testing
  217. */
  218. public function setMwsServiceUrl($url)
  219. {
  220. $this->mwsServiceUrl = $url;
  221. }
  222. /* Getter
  223. * Gets the value for the key if the key exists in config
  224. */
  225. public function __get($name)
  226. {
  227. if (array_key_exists(strtolower($name), $this->config)) {
  228. return $this->config[strtolower($name)];
  229. } else {
  230. 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);
  231. }
  232. }
  233. /* Getter for parameters string
  234. * Gets the value for the parameters string for unit testing
  235. */
  236. public function getParameters()
  237. {
  238. return trim($this->parameters);
  239. }
  240. /* Trim the input Array key values */
  241. private function trimArray($array)
  242. {
  243. foreach ($array as $key => $value) {
  244. // Do not attemp to trim array variables, boolean variables, or the proxy password
  245. // Trimming a boolean value (as a string) may not produce the expected output, so pass it through as-is
  246. if (!is_array($value) && !is_bool($value) && $key !== 'proxy_password') {
  247. $array[$key] = trim($value);
  248. }
  249. }
  250. return $array;
  251. }
  252. /* GetUserInfo convenience function - Returns user's profile information from Amazon using the access token returned by the Button widget.
  253. *
  254. * @see http://login.amazon.com/website Step 4
  255. * @param $accessToken [String]
  256. */
  257. public function getUserInfo($accessToken)
  258. {
  259. // Get the correct Profile Endpoint URL based off the country/region provided in the config['region']
  260. $this->profileEndpointUrl();
  261. if (empty($accessToken)) {
  262. throw new \InvalidArgumentException('Access Token is a required parameter and is not set');
  263. }
  264. // To make sure double encoding doesn't occur decode first and encode again.
  265. $accessToken = urldecode($accessToken);
  266. $url = $this->profileEndpoint . '/auth/o2/tokeninfo?access_token=' . $this->urlEncode($accessToken);
  267. $httpCurlRequest = new HttpCurl($this->config);
  268. $response = $httpCurlRequest->httpGet($url);
  269. $data = json_decode($response);
  270. // Ensure that the Access Token matches either the supplied Client ID *or* the supplied App ID
  271. // Web apps and Mobile apps will have different Client ID's but App ID should be the same
  272. // As long as one of these matches, from a security perspective, we have done our due diligence
  273. if (($data->aud != $this->config['client_id']) && ($data->app_id != $this->config['app_id'])) {
  274. // The access token does not belong to us
  275. throw new \Exception('The Access Token belongs to neither your Client ID nor App ID');
  276. }
  277. // Exchange the access token for user profile
  278. $url = $this->profileEndpoint . '/user/profile';
  279. $httpCurlRequest = new HttpCurl($this->config);
  280. $httpCurlRequest->setAccessToken($accessToken);
  281. $httpCurlRequest->setHttpHeader();
  282. $response = $httpCurlRequest->httpGet($url);
  283. $userInfo = json_decode($response, true);
  284. return $userInfo;
  285. }
  286. /* setParametersAndPost - sets the parameters array with non empty values from the requestParameters array sent to API calls.
  287. * If Provider Credit Details is present, values are set by setProviderCreditDetails
  288. * If Provider Credit Reversal Details is present, values are set by setProviderCreditDetails
  289. */
  290. private function setParametersAndPost($parameters, $fieldMappings, $requestParameters)
  291. {
  292. /* For loop to take all the non empty parameters in the $requestParameters and add it into the $parameters array,
  293. * if the keys are matched from $requestParameters array with the $fieldMappings array
  294. */
  295. foreach ($requestParameters as $param => $value) {
  296. // Do not use trim on boolean values, or it will convert them to '0' or '1'
  297. if (!is_array($value) && !is_bool($value)) {
  298. $value = trim($value);
  299. }
  300. // Ensure that no unexpected type coercions have happened
  301. if ($param === 'capture_now' || $param === 'confirm_now' || $param === 'inherit_shipping_address' || $param === 'request_payment_authorization') {
  302. if (!is_bool($value)) {
  303. throw new \Exception($param . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value');
  304. }
  305. } elseif ($param === 'provider_credit_details' || $param === 'provider_credit_reversal_details' || $param === 'order_item_categories' || $param === 'notification_configuration_list') {
  306. if (!is_array($value)) {
  307. throw new \Exception($param . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be an array value');
  308. }
  309. }
  310. // When checking for non-empty values, consider any boolean as non-empty
  311. if (array_key_exists($param, $fieldMappings) && (is_bool($value) || $value!='')) {
  312. if (is_array($value)) {
  313. // If the parameter is a provider_credit_details or provider_credit_reversal_details, call the respective functions to set the values
  314. if ($param === 'provider_credit_details') {
  315. $parameters = $this->setProviderCreditDetails($parameters, $value);
  316. } elseif ($param === 'provider_credit_reversal_details') {
  317. $parameters = $this->setProviderCreditReversalDetails($parameters, $value);
  318. } elseif ($param === 'order_item_categories') {
  319. $parameters = $this->setOrderItemCategories($parameters, $value);
  320. } elseif ($param == 'notification_configuration_list') {
  321. $parameters = $this->setNotificationConfigurationList($parameters, $value);
  322. }
  323. } else {
  324. $parameters[$fieldMappings[$param]] = $value;
  325. }
  326. }
  327. }
  328. $parameters = $this->setDefaultValues($parameters, $fieldMappings, $requestParameters);
  329. $responseObject = $this->calculateSignatureAndPost($parameters);
  330. return $responseObject;
  331. }
  332. /* calculateSignatureAndPost - convert the Parameters array to string and curl POST the parameters to MWS */
  333. private function calculateSignatureAndPost($parameters)
  334. {
  335. // Call the signature and Post function to perform the actions. Returns XML in array format
  336. $parametersString = $this->calculateSignatureAndParametersToString($parameters);
  337. // POST using curl the String converted Parameters
  338. $response = $this->invokePost($parametersString);
  339. // Send this response as args to ResponseParser class which will return the object of the class.
  340. $responseObject = new ResponseParser($response);
  341. return $responseObject;
  342. }
  343. /* If merchant_id is not set via the requestParameters array then it's taken from the config array
  344. *
  345. * Set the platform_id if set in the config['platform_id'] array
  346. *
  347. * If currency_code is set in the $requestParameters and it exists in the $fieldMappings array, strtoupper it
  348. * else take the value from config array if set
  349. */
  350. private function setDefaultValues($parameters, $fieldMappings, $requestParameters)
  351. {
  352. if (empty($requestParameters['merchant_id']))
  353. $parameters['SellerId'] = $this->config['merchant_id'];
  354. if (array_key_exists('platform_id', $fieldMappings)) {
  355. if (empty($requestParameters['platform_id']) && !empty($this->config['platform_id']))
  356. $parameters[$fieldMappings['platform_id']] = $this->config['platform_id'];
  357. }
  358. if (array_key_exists('currency_code', $fieldMappings)) {
  359. if (!empty($requestParameters['currency_code'])) {
  360. $parameters[$fieldMappings['currency_code']] = strtoupper($requestParameters['currency_code']);
  361. } else if (!(array_key_exists('Action', $parameters) &&
  362. ($parameters['Action'] === 'SetOrderAttributes' || $parameters['Action'] === 'ConfirmOrderReference' || $parameters['Action'] === 'SetBillingAgreementDetails'))) {
  363. // Only supply a default CurrencyCode parameter if not using SetOrderAttributes, ConfirmOrderReference, or SetBillingAgreementDetails
  364. $parameters[$fieldMappings['currency_code']] = strtoupper($this->config['currency_code']);
  365. }
  366. }
  367. return $parameters;
  368. }
  369. /* setOrderItemCategories - helper function used by SetOrderAttributes API to set
  370. * one or more Order Item Categories
  371. */
  372. private function setOrderItemCategories($parameters, $categories)
  373. {
  374. $categoryIndex = 0;
  375. $categoryString = 'OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.';
  376. foreach ($categories as $value) {
  377. $categoryIndex = $categoryIndex + 1;
  378. $parameters[$categoryString . $categoryIndex] = $value;
  379. }
  380. return $parameters;
  381. }
  382. /* setMerchantNotificationUrls - helper function used by SetMerchantNotificationConfiguration API to set
  383. * one or more Notification Configurations
  384. */
  385. private function setNotificationConfigurationList($parameters, $configuration)
  386. {
  387. $configurationIndex = 0;
  388. if (!is_array($configuration)) {
  389. throw new \Exception('Notification Configuration List value ' . $configuration . ' is of type ' . gettype($configuration) . ' and should be an array value');
  390. }
  391. foreach ($configuration as $url => $events) {
  392. $configurationIndex = $configurationIndex + 1;
  393. $parameters['NotificationConfigurationList.NotificationConfiguration.' . $configurationIndex . '.NotificationUrl'] = $url;
  394. $eventIndex = 0;
  395. if (!is_array($events)) {
  396. throw new \Exception('Notification Configuration Events value ' . $events . ' is of type ' . gettype($events) . ' and should be an array value');
  397. }
  398. foreach ($events as $event) {
  399. $eventIndex = $eventIndex + 1;
  400. $parameters['NotificationConfigurationList.NotificationConfiguration.' . $configurationIndex . '.EventTypes.EventTypeList.' . $eventIndex] = $event;
  401. }
  402. }
  403. return $parameters;
  404. }
  405. /* setProviderCreditDetails - sets the provider credit details sent via the Capture or Authorize API calls
  406. * @param provider_id - [String]
  407. * @param credit_amount - [String]
  408. * @optional currency_code - [String]
  409. */
  410. private function setProviderCreditDetails($parameters, $providerCreditInfo)
  411. {
  412. $providerIndex = 0;
  413. $providerString = 'ProviderCreditList.member.';
  414. $fieldMappings = array(
  415. 'provider_id' => 'ProviderId',
  416. 'credit_amount' => 'CreditAmount.Amount',
  417. 'currency_code' => 'CreditAmount.CurrencyCode'
  418. );
  419. foreach ($providerCreditInfo as $key => $value) {
  420. $value = array_change_key_case($value, CASE_LOWER);
  421. $providerIndex = $providerIndex + 1;
  422. foreach ($value as $param => $val) {
  423. if (array_key_exists($param, $fieldMappings) && trim($val)!='') {
  424. $parameters[$providerString.$providerIndex. '.' .$fieldMappings[$param]] = $val;
  425. }
  426. }
  427. // If currency code is not entered take it from the config array
  428. if (empty($parameters[$providerString.$providerIndex. '.' .$fieldMappings['currency_code']])) {
  429. $parameters[$providerString.$providerIndex. '.' .$fieldMappings['currency_code']] = strtoupper($this->config['currency_code']);
  430. }
  431. }
  432. return $parameters;
  433. }
  434. /* setProviderCreditReversalDetails - sets the reverse provider credit details sent via the Refund API call.
  435. * @param provider_id - [String]
  436. * @param credit_amount - [String]
  437. * @optional currency_code - [String]
  438. */
  439. private function setProviderCreditReversalDetails($parameters, $providerCreditInfo)
  440. {
  441. $providerIndex = 0;
  442. $providerString = 'ProviderCreditReversalList.member.';
  443. $fieldMappings = array(
  444. 'provider_id' => 'ProviderId',
  445. 'credit_reversal_amount' => 'CreditReversalAmount.Amount',
  446. 'currency_code' => 'CreditReversalAmount.CurrencyCode'
  447. );
  448. foreach ($providerCreditInfo as $key => $value) {
  449. $value = array_change_key_case($value, CASE_LOWER);
  450. $providerIndex = $providerIndex + 1;
  451. foreach ($value as $param => $val) {
  452. if (array_key_exists($param, $fieldMappings) && trim($val)!='') {
  453. $parameters[$providerString.$providerIndex. '.' .$fieldMappings[$param]] = $val;
  454. }
  455. }
  456. // If currency code is not entered take it from the config array
  457. if (empty($parameters[$providerString.$providerIndex. '.' .$fieldMappings['currency_code']])) {
  458. $parameters[$providerString.$providerIndex. '.' .$fieldMappings['currency_code']] = strtoupper($this->config['currency_code']);
  459. }
  460. }
  461. return $parameters;
  462. }
  463. /* GetMerchantAccountStatus API call - Returns the status of the Merchant Account.
  464. * @see TODO
  465. * @param requestParameters['merchant_id'] - [String]
  466. * @optional requestParameters['mws_auth_token'] - [String]
  467. */
  468. public function getMerchantAccountStatus($requestParameters = array())
  469. {
  470. $parameters = array();
  471. $parameters['Action'] = 'GetMerchantAccountStatus';
  472. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  473. $fieldMappings = array(
  474. 'merchant_id' => 'SellerId',
  475. 'mws_auth_token' => 'MWSAuthToken'
  476. );
  477. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  478. return ($responseObject);
  479. }
  480. /* GetOrderReferenceDetails API call - Returns details about the Order Reference object and its current state.
  481. * @see https://pay.amazon.com/developer/documentation/apireference/201751970
  482. *
  483. * @param requestParameters['merchant_id'] - [String]
  484. * @param requestParameters['amazon_order_reference_id'] - [String]
  485. * @optional requestParameters['address_consent_token'] - [String]
  486. * @optional requestParameters['access_token'] - [String]
  487. * @optional requestParameters['mws_auth_token'] - [String]
  488. *
  489. * You cannot pass both address_consent_token and access_token in
  490. * the same call or you will encounter a 400/"AmbiguousToken" error
  491. */
  492. public function getOrderReferenceDetails($requestParameters = array())
  493. {
  494. $parameters = array();
  495. $parameters['Action'] = 'GetOrderReferenceDetails';
  496. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  497. $fieldMappings = array(
  498. 'merchant_id' => 'SellerId',
  499. 'amazon_order_reference_id' => 'AmazonOrderReferenceId',
  500. 'address_consent_token' => 'AddressConsentToken',
  501. 'access_token' => 'AccessToken',
  502. 'mws_auth_token' => 'MWSAuthToken'
  503. );
  504. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  505. return ($responseObject);
  506. }
  507. /* ListOrderReference API call - Returns details about the Order Reference object and its current state from the sellers.
  508. * @see https://pay.amazon.com/developer/documentation/apireference/201751970
  509. *
  510. * @param requestParameters['merchant_id'] - [String]
  511. * @param requestParameters['query_id'] - [String]
  512. * @param requestParameters['query_id_type'] - [String] (SellerOrderId)
  513. * @optional requestParameters['page_size'] - [Int]
  514. * @optional requestParameters['created_start_time'] - [String] (Date/Time ISO8601)
  515. * @optional requestParameters['created_end_time'] - [String] (Date/Time ISO8601) Limited to 31 days
  516. * @optional requestParameters['sort_order'] - [String] (Ascending/Descending)
  517. * @optional requestParameters['mws_auth_token'] - [String]
  518. * @optional requestParameters['status_list'] - [Array]
  519. */
  520. public function listOrderReference($requestParameters = array())
  521. {
  522. $parameters = array();
  523. $parameters['Action'] = 'ListOrderReference';
  524. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  525. $payment_domains = array(
  526. "us" => "NA_USD",
  527. "jp" => "FE_JPY",
  528. "de" => "EU_EUR",
  529. "uk" => "EU_GBP"
  530. );
  531. $requestParameters['payment_domain'] = $payment_domains[strtolower($this->config['region'])];
  532. $fieldMappings = array(
  533. 'merchant_id' => 'SellerId',
  534. 'mws_auth_token' => 'MWSAuthToken',
  535. 'query_id' => 'QueryId',
  536. 'query_id_type' => 'QueryIdType',
  537. 'page_size' => 'PageSize',
  538. 'created_start_time' => 'CreatedTimeRange.StartTime',
  539. 'created_end_time' => 'CreatedTimeRange.EndTime',
  540. 'sort_order' => 'SortOrder',
  541. 'payment_domain' => 'PaymentDomain'
  542. );
  543. if( $requestParameters['order_status_list'] ){
  544. $status_index = 0;
  545. foreach ($requestParameters['order_status_list'] as $status) {
  546. $status_index++;
  547. $requestParameters['order_status_list_'.$status_index] = $status;
  548. $fieldMappings['order_status_list_'.$status_index] = 'OrderReferenceStatusListFilter.OrderReferenceStatus.'.$status_index;
  549. }
  550. }
  551. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  552. return ($responseObject);
  553. }
  554. /* ListOrderReferenceByNextToken API call - Returns details about the Order Reference object and its current
  555. * state from the sellers.
  556. * @see https://pay.amazon.com/developer/documentation/apireference/201751970
  557. *
  558. * @param requestParameters['merchant_id'] - [String]
  559. * @param requestParameters['next_token'] - [String]
  560. * @optional requestParameters['mws_auth_token'] - [String]
  561. */
  562. public function listOrderReferenceByNextToken($requestParameters = array())
  563. {
  564. $parameters = array();
  565. $parameters['Action'] = 'ListOrderReferenceByNextToken';
  566. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  567. $fieldMappings = array(
  568. 'merchant_id' => 'SellerId',
  569. 'mws_auth_token' => 'MWSAuthToken',
  570. 'next_page_token' => 'NextPageToken'
  571. );
  572. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  573. return ($responseObject);
  574. }
  575. /* SetOrderReferenceDetails API call - Sets order reference details such as the order total and a description for the order.
  576. * @see https://pay.amazon.com/developer/documentation/apireference/201751960
  577. *
  578. * @param requestParameters['merchant_id'] - [String]
  579. * @param requestParameters['amazon_order_reference_id'] - [String]
  580. * @param requestParameters['amount'] - [String]
  581. * @param requestParameters['currency_code'] - [String]
  582. * @optional requestParameters['platform_id'] - [String]
  583. * @optional requestParameters['seller_note'] - [String]
  584. * @optional requestParameters['seller_order_id'] - [String]
  585. * @optional requestParameters['store_name'] - [String]
  586. * @optional requestParameters['custom_information'] - [String]
  587. * @optional requestParameters['supplementary_data'] - [String]
  588. * @optional requestParameters['request_payment_authorization'] - [Boolean]
  589. * @optional requestParameters['mws_auth_token'] - [String]
  590. */
  591. public function setOrderReferenceDetails($requestParameters = array())
  592. {
  593. $parameters = array();
  594. $parameters['Action'] = 'SetOrderReferenceDetails';
  595. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  596. $fieldMappings = array(
  597. 'merchant_id' => 'SellerId',
  598. 'amazon_order_reference_id' => 'AmazonOrderReferenceId',
  599. 'amount' => 'OrderReferenceAttributes.OrderTotal.Amount',
  600. 'currency_code' => 'OrderReferenceAttributes.OrderTotal.CurrencyCode',
  601. 'platform_id' => 'OrderReferenceAttributes.PlatformId',
  602. 'seller_note' => 'OrderReferenceAttributes.SellerNote',
  603. 'seller_order_id' => 'OrderReferenceAttributes.SellerOrderAttributes.SellerOrderId',
  604. 'store_name' => 'OrderReferenceAttributes.SellerOrderAttributes.StoreName',
  605. 'custom_information' => 'OrderReferenceAttributes.SellerOrderAttributes.CustomInformation',
  606. 'supplementary_data' => 'OrderReferenceAttributes.SellerOrderAttributes.SupplementaryData',
  607. 'request_payment_authorization' => 'OrderReferenceAttributes.RequestPaymentAuthorization',
  608. 'mws_auth_token' => 'MWSAuthToken'
  609. );
  610. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  611. return ($responseObject);
  612. }
  613. /* SetOrderAttributes API call - Sets order reference details such as the order total and a description for the order.
  614. *
  615. * @param requestParameters['merchant_id'] - [String]
  616. * @param requestParameters['amazon_order_reference_id'] - [String]
  617. * @optional requestParameters['amount'] - [String]
  618. * @optional requestParameters['currency_code'] - [String]
  619. * @optional requestParameters['platform_id'] - [String]
  620. * @optional requestParameters['seller_note'] - [String]
  621. * @optional requestParameters['seller_order_id'] - [String]
  622. * @optional requestParameters['store_name'] - [String]
  623. * @optional requestParameters['custom_information'] - [String]
  624. * @optional requestParameters['supplementary_data'] - [String]
  625. * @optional requestParameters['request_payment_authorization'] - [Boolean]
  626. * @optional requestParameters['payment_service_provider_id'] - [String]
  627. * @optional requestParameters['payment_service_provider_order_id'] - [String]
  628. * @optional requestParameters['order_item_categories'] - [array()]
  629. * @optional requestParameters['mws_auth_token'] - [String]
  630. */
  631. public function setOrderAttributes($requestParameters = array())
  632. {
  633. $parameters = array();
  634. $parameters['Action'] = 'SetOrderAttributes';
  635. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  636. $fieldMappings = array(
  637. 'merchant_id' => 'SellerId',
  638. 'amazon_order_reference_id' => 'AmazonOrderReferenceId',
  639. 'amount' => 'OrderAttributes.OrderTotal.Amount',
  640. 'currency_code' => 'OrderAttributes.OrderTotal.CurrencyCode',
  641. 'platform_id' => 'OrderAttributes.PlatformId',
  642. 'seller_note' => 'OrderAttributes.SellerNote',
  643. 'seller_order_id' => 'OrderAttributes.SellerOrderAttributes.SellerOrderId',
  644. 'store_name' => 'OrderAttributes.SellerOrderAttributes.StoreName',
  645. 'custom_information' => 'OrderAttributes.SellerOrderAttributes.CustomInformation',
  646. 'supplementary_data' => 'OrderAttributes.SellerOrderAttributes.SupplementaryData',
  647. 'request_payment_authorization' => 'OrderAttributes.RequestPaymentAuthorization',
  648. 'payment_service_provider_id' => 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderId',
  649. 'payment_service_provider_order_id' => 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderOrderId',
  650. 'order_item_categories' => array(),
  651. 'mws_auth_token' => 'MWSAuthToken'
  652. );
  653. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  654. return ($responseObject);
  655. }
  656. /* ConfirmOrderReference API call - Confirms that the order reference is free of constraints and all required information has been set on the order reference.
  657. * @see https://pay.amazon.com/developer/documentation/apireference/201751980
  658. *
  659. * @param requestParameters['merchant_id'] - [String]
  660. * @param requestParameters['amazon_order_reference_id'] - [String]
  661. * @optional requestParameters['success_url'] - [String]
  662. * @optional requestParameters['failure_url'] - [String]
  663. * @optional requestParameters['authorization_amount'] - [String]
  664. * @optional requestParameters['currency_code'] - [String]
  665. * @optional requestParameters['mws_auth_token'] - [String]
  666. */
  667. public function confirmOrderReference($requestParameters = array())
  668. {
  669. $parameters = array();
  670. $parameters['Action'] = 'ConfirmOrderReference';
  671. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  672. $fieldMappings = array(
  673. 'merchant_id' => 'SellerId',
  674. 'amazon_order_reference_id' => 'AmazonOrderReferenceId',
  675. 'success_url' => 'SuccessUrl',
  676. 'failure_url' => 'FailureUrl',
  677. 'authorization_amount' => 'AuthorizationAmount.Amount',
  678. 'currency_code' => 'AuthorizationAmount.CurrencyCode',
  679. 'mws_auth_token' => 'MWSAuthToken'
  680. );
  681. if (isset($requestParameters['authorization_amount']) && !isset($requestParameters['currency_code'])) {
  682. $requestParameters['currency_code'] = strtoupper($this->config['currency_code']);
  683. }
  684. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  685. return ($responseObject);
  686. }
  687. /* CancelOrderReference API call - Cancels a previously confirmed order reference.
  688. * @see https://pay.amazon.com/developer/documentation/apireference/201751990
  689. *
  690. * @param requestParameters['merchant_id'] - [String]
  691. * @param requestParameters['amazon_order_reference_id'] - [String]
  692. * @optional requestParameters['cancelation_reason'] [String]
  693. * @optional requestParameters['mws_auth_token'] - [String]
  694. */
  695. public function cancelOrderReference($requestParameters = array())
  696. {
  697. $parameters = array();
  698. $parameters['Action'] = 'CancelOrderReference';
  699. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  700. $fieldMappings = array(
  701. 'merchant_id' => 'SellerId',
  702. 'amazon_order_reference_id' => 'AmazonOrderReferenceId',
  703. 'cancelation_reason' => 'CancelationReason',
  704. 'mws_auth_token' => 'MWSAuthToken'
  705. );
  706. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  707. return ($responseObject);
  708. }
  709. /* CloseOrderReference API call - Confirms that an order reference has been fulfilled (fully or partially)
  710. * and that you do not expect to create any new authorizations on this order reference.
  711. * @see https://pay.amazon.com/developer/documentation/apireference/201752000
  712. *
  713. * @param requestParameters['merchant_id'] - [String]
  714. * @param requestParameters['amazon_order_reference_id'] - [String]
  715. * @optional requestParameters['closure_reason'] [String]
  716. * @optional requestParameters['mws_auth_token'] - [String]
  717. */
  718. public function closeOrderReference($requestParameters = array())
  719. {
  720. $parameters = array();
  721. $parameters['Action'] = 'CloseOrderReference';
  722. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  723. $fieldMappings = array(
  724. 'merchant_id' => 'SellerId',
  725. 'amazon_order_reference_id' => 'AmazonOrderReferenceId',
  726. 'closure_reason' => 'ClosureReason',
  727. 'mws_auth_token' => 'MWSAuthToken'
  728. );
  729. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  730. return ($responseObject);
  731. }
  732. /* CloseAuthorization API call - Closes an authorization.
  733. * @see https://pay.amazon.com/developer/documentation/apireference/201752070
  734. *
  735. * @param requestParameters['merchant_id'] - [String]
  736. * @param requestParameters['amazon_authorization_id'] - [String]
  737. * @optional requestParameters['closure_reason'] [String]
  738. * @optional requestParameters['mws_auth_token'] - [String]
  739. */
  740. public function closeAuthorization($requestParameters = array())
  741. {
  742. $parameters = array();
  743. $parameters['Action'] = 'CloseAuthorization';
  744. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  745. $fieldMappings = array(
  746. 'merchant_id' => 'SellerId',
  747. 'amazon_authorization_id' => 'AmazonAuthorizationId',
  748. 'closure_reason' => 'ClosureReason',
  749. 'mws_auth_token' => 'MWSAuthToken'
  750. );
  751. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  752. return ($responseObject);
  753. }
  754. /* Authorize API call - Reserves a specified amount against the payment method(s) stored in the order reference.
  755. * @see https://pay.amazon.com/developer/documentation/apireference/201752010
  756. *
  757. * @param requestParameters['merchant_id'] - [String]
  758. * @param requestParameters['amazon_order_reference_id'] - [String]
  759. * @param requestParameters['authorization_amount'] [String]
  760. * @param requestParameters['currency_code'] - [String]
  761. * @param requestParameters['authorization_reference_id'] [String]
  762. * @optional requestParameters['capture_now'] [Boolean]
  763. * @optional requestParameters['provider_credit_details'] - [array (array())]
  764. * @optional requestParameters['seller_authorization_note'] [String]
  765. * @optional requestParameters['transaction_timeout'] [String] - Defaults to 1440 minutes
  766. * @optional requestParameters['soft_descriptor'] - [String]
  767. * @optional requestParameters['mws_auth_token'] - [String]
  768. */
  769. public function authorize($requestParameters = array())
  770. {
  771. $parameters = array();
  772. $parameters['Action'] = 'Authorize';
  773. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  774. $fieldMappings = array(
  775. 'merchant_id' => 'SellerId',
  776. 'amazon_order_reference_id' => 'AmazonOrderReferenceId',
  777. 'authorization_amount' => 'AuthorizationAmount.Amount',
  778. 'currency_code' => 'AuthorizationAmount.CurrencyCode',
  779. 'authorization_reference_id' => 'AuthorizationReferenceId',
  780. 'capture_now' => 'CaptureNow',
  781. 'provider_credit_details' => array(),
  782. 'seller_authorization_note' => 'SellerAuthorizationNote',
  783. 'transaction_timeout' => 'TransactionTimeout',
  784. 'soft_descriptor' => 'SoftDescriptor',
  785. 'mws_auth_token' => 'MWSAuthToken'
  786. );
  787. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  788. return ($responseObject);
  789. }
  790. /* GetAuthorizationDetails API call - Returns the status of a particular authorization and the total amount captured on the authorization.
  791. * @see https://pay.amazon.com/developer/documentation/apireference/201752030
  792. *
  793. * @param requestParameters['merchant_id'] - [String]
  794. * @param requestParameters['amazon_authorization_id'] [String]
  795. * @optional requestParameters['mws_auth_token'] - [String]
  796. */
  797. public function getAuthorizationDetails($requestParameters = array())
  798. {
  799. $parameters = array();
  800. $parameters['Action'] = 'GetAuthorizationDetails';
  801. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  802. $fieldMappings = array(
  803. 'merchant_id' => 'SellerId',
  804. 'amazon_authorization_id' => 'AmazonAuthorizationId',
  805. 'mws_auth_token' => 'MWSAuthToken'
  806. );
  807. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  808. return ($responseObject);
  809. }
  810. /* Capture API call - Captures funds from an authorized payment instrument.
  811. * @see https://pay.amazon.com/developer/documentation/apireference/201752040
  812. *
  813. * @param requestParameters['merchant_id'] - [String]
  814. * @param requestParameters['amazon_authorization_id'] - [String]
  815. * @param requestParameters['capture_amount'] - [String]
  816. * @param requestParameters['currency_code'] - [String]
  817. * @param requestParameters['capture_reference_id'] - [String]
  818. * @optional requestParameters['provider_credit_details'] - [array (array())]
  819. * @optional requestParameters['seller_capture_note'] - [String]
  820. * @optional requestParameters['soft_descriptor'] - [String]
  821. * @optional requestParameters['mws_auth_token'] - [String]
  822. */
  823. public function capture($requestParameters = array())
  824. {
  825. $parameters = array();
  826. $parameters['Action'] = 'Capture';
  827. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  828. $fieldMappings = array(
  829. 'merchant_id' => 'SellerId',
  830. 'amazon_authorization_id' => 'AmazonAuthorizationId',
  831. 'capture_amount' => 'CaptureAmount.Amount',
  832. 'currency_code' => 'CaptureAmount.CurrencyCode',
  833. 'capture_reference_id' => 'CaptureReferenceId',
  834. 'provider_credit_details' => array(),
  835. 'seller_capture_note' => 'SellerCaptureNote',
  836. 'soft_descriptor' => 'SoftDescriptor',
  837. 'mws_auth_token' => 'MWSAuthToken'
  838. );
  839. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  840. return ($responseObject);
  841. }
  842. /* GetCaptureDetails API call - Returns the status of a particular capture and the total amount refunded on the capture.
  843. * @see https://pay.amazon.com/developer/documentation/apireference/201752060
  844. *
  845. * @param requestParameters['merchant_id'] - [String]
  846. * @param requestParameters['amazon_capture_id'] - [String]
  847. * @optional requestParameters['mws_auth_token'] - [String]
  848. */
  849. public function getCaptureDetails($requestParameters = array())
  850. {
  851. $parameters = array();
  852. $parameters['Action'] = 'GetCaptureDetails';
  853. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  854. $fieldMappings = array(
  855. 'merchant_id' => 'SellerId',
  856. 'amazon_capture_id' => 'AmazonCaptureId',
  857. 'mws_auth_token' => 'MWSAuthToken'
  858. );
  859. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  860. return ($responseObject);
  861. }
  862. /* Refund API call - Refunds a previously captured amount.
  863. * @see https://pay.amazon.com/developer/documentation/apireference/201752080
  864. *
  865. * @param requestParameters['merchant_id'] - [String]
  866. * @param requestParameters['amazon_capture_id'] - [String]
  867. * @param requestParameters['refund_reference_id'] - [String]
  868. * @param requestParameters['refund_amount'] - [String]
  869. * @param requestParameters['currency_code'] - [String]
  870. * @optional requestParameters['provider_credit_reversal_details'] - [array(array())]
  871. * @optional requestParameters['seller_refund_note'] [String]
  872. * @optional requestParameters['soft_descriptor'] - [String]
  873. * @optional requestParameters['mws_auth_token'] - [String]
  874. */
  875. public function refund($requestParameters = array())
  876. {
  877. $parameters = array();
  878. $parameters['Action'] = 'Refund';
  879. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  880. $fieldMappings = array(
  881. 'merchant_id' => 'SellerId',
  882. 'amazon_capture_id' => 'AmazonCaptureId',
  883. 'refund_reference_id' => 'RefundReferenceId',
  884. 'refund_amount' => 'RefundAmount.Amount',
  885. 'currency_code' => 'RefundAmount.CurrencyCode',
  886. 'provider_credit_reversal_details' => array(),
  887. 'seller_refund_note' => 'SellerRefundNote',
  888. 'soft_descriptor' => 'SoftDescriptor',
  889. 'mws_auth_token' => 'MWSAuthToken'
  890. );
  891. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  892. return ($responseObject);
  893. }
  894. /* GetRefundDetails API call - Returns the status of a particular refund.
  895. * @see https://pay.amazon.com/developer/documentation/apireference/201752100
  896. *
  897. * @param requestParameters['merchant_id'] - [String]
  898. * @param requestParameters['amazon_refund_id'] - [String]
  899. * @optional requestParameters['mws_auth_token'] - [String]
  900. */
  901. public function getRefundDetails($requestParameters = array())
  902. {
  903. $parameters = array();
  904. $parameters['Action'] = 'GetRefundDetails';
  905. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  906. $fieldMappings = array(
  907. 'merchant_id' => 'SellerId',
  908. 'amazon_refund_id' => 'AmazonRefundId',
  909. 'mws_auth_token' => 'MWSAuthToken'
  910. );
  911. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  912. return ($responseObject);
  913. }
  914. /* GetServiceStatus API Call - Returns the operational status of the OffAmazonPayments API section
  915. * @see https://pay.amazon.com/developer/documentation/apireference/201752110
  916. *
  917. * The GetServiceStatus operation returns the operational status of the OffAmazonPayments API
  918. * section of Amazon Marketplace Web Service (Amazon MWS).
  919. * Status values are GREEN, GREEN_I, YELLOW, and RED.
  920. *
  921. * @param requestParameters['merchant_id'] - [String]
  922. * @optional requestParameters['mws_auth_token'] - [String]
  923. */
  924. public function getServiceStatus($requestParameters = array())
  925. {
  926. $parameters = array();
  927. $parameters['Action'] = 'GetServiceStatus';
  928. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  929. $fieldMappings = array(
  930. 'merchant_id' => 'SellerId',
  931. 'mws_auth_token' => 'MWSAuthToken'
  932. );
  933. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  934. return ($responseObject);
  935. }
  936. /* CreateOrderReferenceForId API Call - Creates an order reference for the given object
  937. * @see https://pay.amazon.com/developer/documentation/apireference/201751670
  938. *
  939. * @param requestParameters['merchant_id'] - [String]
  940. * @param requestParameters['id'] - [String]
  941. * @optional requestParameters['inherit_shipping_address'] [Boolean]
  942. * @optional requestParameters['confirm_now'] - [Boolean]
  943. * @optional Amount (required when confirm_now is set to true) [String]
  944. * @optional requestParameters['currency_code'] - [String]
  945. * @optional requestParameters['seller_note'] - [String]
  946. * @optional requestParameters['seller_order_id'] - [String]
  947. * @optional requestParameters['store_name'] - [String]
  948. * @optional requestParameters['supplementary_data'] - [String]
  949. * @optional requestParameters['custom_information'] - [String]
  950. * @optional requestParameters['mws_auth_token'] - [String]
  951. */
  952. public function createOrderReferenceForId($requestParameters = array())
  953. {
  954. $parameters = array();
  955. $parameters['Action'] = 'CreateOrderReferenceForId';
  956. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  957. $fieldMappings = array(
  958. 'merchant_id' => 'SellerId',
  959. 'id' => 'Id',
  960. 'id_type' => 'IdType',
  961. 'inherit_shipping_address' => 'InheritShippingAddress',
  962. 'confirm_now' => 'ConfirmNow',
  963. 'amount' => 'OrderReferenceAttributes.OrderTotal.Amount',
  964. 'currency_code' => 'OrderReferenceAttributes.OrderTotal.CurrencyCode',
  965. 'platform_id' => 'OrderReferenceAttributes.PlatformId',
  966. 'seller_note' => 'OrderReferenceAttributes.SellerNote',
  967. 'seller_order_id' => 'OrderReferenceAttributes.SellerOrderAttributes.SellerOrderId',
  968. 'store_name' => 'OrderReferenceAttributes.SellerOrderAttributes.StoreName',
  969. 'supplementary_data' => 'OrderReferenceAttributes.SupplementaryData',
  970. 'custom_information' => 'OrderReferenceAttributes.SellerOrderAttributes.CustomInformation',
  971. 'mws_auth_token' => 'MWSAuthToken'
  972. );
  973. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  974. return ($responseObject);
  975. }
  976. /* GetBillingAgreementDetails API Call - Returns details about the Billing Agreement object and its current state.
  977. * @see https://pay.amazon.com/developer/documentation/apireference/201751690
  978. *
  979. * @param requestParameters['merchant_id'] - [String]
  980. * @param requestParameters['amazon_billing_agreement_id'] - [String]
  981. * @optional requestParameters['address_consent_token'] - [String]
  982. * @optional requestParameters['access_token'] - [String]
  983. * @optional requestParameters['mws_auth_token'] - [String]
  984. *
  985. * You cannot pass both address_consent_token and access_token in
  986. * the same call or you will encounter a 400/"AmbiguousToken" error
  987. */
  988. public function getBillingAgreementDetails($requestParameters = array())
  989. {
  990. $parameters = array();
  991. $parameters['Action'] = 'GetBillingAgreementDetails';
  992. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  993. $fieldMappings = array(
  994. 'merchant_id' => 'SellerId',
  995. 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
  996. 'address_consent_token' => 'AddressConsentToken',
  997. 'access_token' => 'AccessToken',
  998. 'mws_auth_token' => 'MWSAuthToken'
  999. );
  1000. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1001. return ($responseObject);
  1002. }
  1003. /* SetBillingAgreementDetails API call - Sets Billing Agreement details such as a description of the agreement and other information about the seller.
  1004. * @see https://pay.amazon.com/developer/documentation/apireference/201751700
  1005. *
  1006. * @param requestParameters['merchant_id'] - [String]
  1007. * @param requestParameters['amazon_billing_agreement_id'] - [String]
  1008. * @param requestParameters['amount'] - [String]
  1009. * @param requestParameters['currency_code'] - [String]
  1010. * @optional requestParameters['platform_id'] - [String]
  1011. * @optional requestParameters['seller_note'] - [String]
  1012. * @optional requestParameters['seller_billing_agreement_id'] - [String]
  1013. * @optional requestParameters['store_name'] - [String]
  1014. * @optional requestParameters['custom_information'] - [String]
  1015. * @optional requestParameters['billing_agreement_type'] - [String] either 'CustomerInitiatedTransaction' or 'MerchantInitiatedTransaction'
  1016. * @optional requestParameters['subscription_amount'] - [String]
  1017. * @optional requestParameters['currency_code'] - [String]
  1018. * @optional requestParameters['mws_auth_token'] - [String]
  1019. */
  1020. public function setBillingAgreementDetails($requestParameters = array())
  1021. {
  1022. $parameters = array();
  1023. $parameters['Action'] = 'SetBillingAgreementDetails';
  1024. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1025. $fieldMappings = array(
  1026. 'merchant_id' => 'SellerId',
  1027. 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
  1028. 'platform_id' => 'BillingAgreementAttributes.PlatformId',
  1029. 'seller_note' => 'BillingAgreementAttributes.SellerNote',
  1030. 'seller_billing_agreement_id' => 'BillingAgreementAttributes.SellerBillingAgreementAttributes.SellerBillingAgreementId',
  1031. 'custom_information' => 'BillingAgreementAttributes.SellerBillingAgreementAttributes.CustomInformation',
  1032. 'store_name' => 'BillingAgreementAttributes.SellerBillingAgreementAttributes.StoreName',
  1033. 'billing_agreement_type' => 'BillingAgreementAttributes.BillingAgreementType',
  1034. 'subscription_amount' => 'BillingAgreementAttributes.SubscriptionAmount.Amount',
  1035. 'currency_code' => 'BillingAgreementAttributes.SubscriptionAmount.CurrencyCode',
  1036. 'mws_auth_token' => 'MWSAuthToken'
  1037. );
  1038. if (isset($requestParameters['subscription_amount']) && !isset($requestParameters['currency_code'])) {
  1039. $requestParameters['currency_code'] = strtoupper($this->config['currency_code']);
  1040. }
  1041. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1042. return ($responseObject);
  1043. }
  1044. /* ConfirmBillingAgreement API Call - Confirms that the Billing Agreement is free of constraints and all required information has been set on the Billing Agreement.
  1045. * @see https://pay.amazon.com/developer/documentation/apireference/201751710
  1046. *
  1047. * @param requestParameters['merchant_id'] - [String]
  1048. * @param requestParameters['amazon_billing_agreement_id'] - [String]
  1049. * @optional requestParameters['success_url'] - [String]
  1050. * @optional requestParameters['failure_url'] - [String]
  1051. * @optional requestParameters['mws_auth_token'] - [String]
  1052. */
  1053. public function confirmBillingAgreement($requestParameters = array())
  1054. {
  1055. $parameters = array();
  1056. $parameters['Action'] = 'ConfirmBillingAgreement';
  1057. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1058. $fieldMappings = array(
  1059. 'merchant_id' => 'SellerId',
  1060. 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
  1061. 'success_url' => 'SuccessUrl',
  1062. 'failure_url' => 'FailureUrl',
  1063. 'mws_auth_token' => 'MWSAuthToken'
  1064. );
  1065. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1066. return ($responseObject);
  1067. }
  1068. /* ValidateBillignAgreement API Call - Validates the status of the Billing Agreement object and the payment method associated with it.
  1069. * @see https://pay.amazon.com/developer/documentation/apireference/201751720
  1070. *
  1071. * @param requestParameters['merchant_id'] - [String]
  1072. * @param requestParameters['amazon_billing_agreement_id'] - [String]
  1073. * @optional requestParameters['mws_auth_token'] - [String]
  1074. */
  1075. public function validateBillingAgreement($requestParameters = array())
  1076. {
  1077. $parameters = array();
  1078. $parameters['Action'] = 'ValidateBillingAgreement';
  1079. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1080. $fieldMappings = array(
  1081. 'merchant_id' => 'SellerId',
  1082. 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
  1083. 'mws_auth_token' => 'MWSAuthToken'
  1084. );
  1085. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1086. return ($responseObject);
  1087. }
  1088. /* AuthorizeOnBillingAgreement API call - Reserves a specified amount against the payment method(s) stored in the Billing Agreement.
  1089. * @see https://pay.amazon.com/developer/documentation/apireference/201751940
  1090. *
  1091. * @param requestParameters['merchant_id'] - [String]
  1092. * @param requestParameters['amazon_billing_agreement_id'] - [String]
  1093. * @param requestParameters['authorization_reference_id'] - [String]
  1094. * @param requestParameters['authorization_amount'] - [String]
  1095. * @param requestParameters['currency_code'] - [String]
  1096. * @optional requestParameters['seller_authorization_note'] [String]
  1097. * @optional requestParameters['transaction_timeout'] - Defaults to 1440 minutes
  1098. * @optional requestParameters['capture_now'] [Boolean]
  1099. * @optional requestParameters['soft_descriptor'] - - [String]
  1100. * @optional requestParameters['seller_note'] - [String]
  1101. * @optional requestParameters['platform_id'] - [String]
  1102. * @optional requestParameters['custom_information'] - [String]
  1103. * @optional requestParameters['seller_order_id'] - [String]
  1104. * @optional requestParameters['store_name'] - [String]
  1105. * @optional requestParameters['supplementary_data'] - [String]
  1106. * @optional requestParameters['inherit_shipping_address'] [Boolean] - Defaults to true
  1107. * @optional requestParameters['mws_auth_token'] - [String]
  1108. */
  1109. public function authorizeOnBillingAgreement($requestParameters = array())
  1110. {
  1111. $parameters = array();
  1112. $parameters['Action'] = 'AuthorizeOnBillingAgreement';
  1113. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1114. $fieldMappings = array(
  1115. 'merchant_id' => 'SellerId',
  1116. 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
  1117. 'authorization_reference_id' => 'AuthorizationReferenceId',
  1118. 'authorization_amount' => 'AuthorizationAmount.Amount',
  1119. 'currency_code' => 'AuthorizationAmount.CurrencyCode',
  1120. 'seller_authorization_note' => 'SellerAuthorizationNote',
  1121. 'transaction_timeout' => 'TransactionTimeout',
  1122. 'capture_now' => 'CaptureNow',
  1123. 'soft_descriptor' => 'SoftDescriptor',
  1124. 'seller_note' => 'SellerNote',
  1125. 'platform_id' => 'PlatformId',
  1126. 'custom_information' => 'SellerOrderAttributes.CustomInformation',
  1127. 'seller_order_id' => 'SellerOrderAttributes.SellerOrderId',
  1128. 'store_name' => 'SellerOrderAttributes.StoreName',
  1129. 'supplementary_data' => 'SellerOrderAttributes.SupplementaryData',
  1130. 'inherit_shipping_address' => 'InheritShippingAddress',
  1131. 'mws_auth_token' => 'MWSAuthToken'
  1132. );
  1133. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1134. return ($responseObject);
  1135. }
  1136. /* CloseBillingAgreement API Call - Returns details about the Billing Agreement object and its current state.
  1137. * @see https://pay.amazon.com/developer/documentation/apireference/201751950
  1138. *
  1139. * @param requestParameters['merchant_id'] - [String]
  1140. * @param requestParameters['amazon_billing_agreement_id'] - [String]
  1141. * @optional requestParameters['closure_reason'] [String]
  1142. * @optional requestParameters['mws_auth_token'] - [String]
  1143. */
  1144. public function closeBillingAgreement($requestParameters = array())
  1145. {
  1146. $parameters = array();
  1147. $parameters['Action'] = 'CloseBillingAgreement';
  1148. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1149. $fieldMappings = array(
  1150. 'merchant_id' => 'SellerId',
  1151. 'amazon_billing_agreement_id' => 'AmazonBillingAgreementId',
  1152. 'closure_reason' => 'ClosureReason',
  1153. 'mws_auth_token' => 'MWSAuthToken'
  1154. );
  1155. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1156. return ($responseObject);
  1157. }
  1158. /* GetMerchantNotificationConfiguration API Call - Returns details about the defined IPN endpoints
  1159. *
  1160. * @param requestParameters['merchant_id'] - [String]
  1161. * @optional requestParameters['mws_auth_token'] - [String]
  1162. */
  1163. public function getMerchantNotificationConfiguration($requestParameters = array())
  1164. {
  1165. $parameters = array();
  1166. $parameters['Action'] = 'GetMerchantNotificationConfiguration';
  1167. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1168. $fieldMappings = array(
  1169. 'merchant_id' => 'SellerId',
  1170. 'mws_auth_token' => 'MWSAuthToken'
  1171. );
  1172. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1173. return ($responseObject);
  1174. }
  1175. /* SetMerchantNotificationConfiguration API Call - Set IPN endpoints
  1176. *
  1177. * @param requestParameters['merchant_id'] - [String]
  1178. * @param requestParameters['notification_configuration_list'] - [Array]
  1179. * @optional requestParameters['mws_auth_token'] - [String]
  1180. */
  1181. public function setMerchantNotificationConfiguration($requestParameters = array())
  1182. {
  1183. $parameters = array();
  1184. $parameters['Action'] = 'SetMerchantNotificationConfiguration';
  1185. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1186. $fieldMappings = array(
  1187. 'merchant_id' => 'SellerId',
  1188. 'notification_configuration_list' => array(),
  1189. 'mws_auth_token' => 'MWSAuthToken'
  1190. );
  1191. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1192. return ($responseObject);
  1193. }
  1194. /* charge convenience method
  1195. * Performs the API calls
  1196. * 1. SetOrderReferenceDetails / SetBillingAgreementDetails
  1197. * 2. ConfirmOrderReference / ConfirmBillingAgreement
  1198. * 3. Authorize (with Capture) / AuthorizeOnBillingAgreeemnt (with Capture)
  1199. *
  1200. * @param requestParameters['merchant_id'] - [String]
  1201. *
  1202. * @param requestParameters['amazon_reference_id'] - [String] : Order Reference ID /Billing Agreement ID
  1203. * If requestParameters['amazon_reference_id'] is empty then the following is required,
  1204. * @param requestParameters['amazon_order_reference_id'] - [String] : Order Reference ID
  1205. * or,
  1206. * @param requestParameters['amazon_billing_agreement_id'] - [String] : Billing Agreement ID
  1207. *
  1208. * @param $requestParameters['charge_amount'] - [String] : Amount value to be captured
  1209. * @param requestParameters['currency_code'] - [String] : Currency Code for the Amount
  1210. * @param requestParameters['authorization_reference_id'] - [String]- Any unique string that needs to be passed
  1211. * @optional requestParameters['charge_note'] - [String] : Seller Note sent to the buyer
  1212. * @optional requestParameters['transaction_timeout'] - [String] : Defaults to 1440 minutes
  1213. * @optional requestParameters['charge_order_id'] - [String] : Custom Order ID provided
  1214. * @optional requestParameters['mws_auth_token'] - [String]
  1215. */
  1216. public function charge($requestParameters = array()) {
  1217. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1218. $requestParameters = $this->trimArray($requestParameters);
  1219. $setParameters = $authorizeParameters = $confirmParameters = $requestParameters;
  1220. $chargeType = '';
  1221. if (!empty($requestParameters['amazon_order_reference_id'])) {
  1222. $chargeType = 'OrderReference';
  1223. } elseif (!empty($requestParameters['amazon_billing_agreement_id'])) {
  1224. $chargeType = 'BillingAgreement';
  1225. } elseif (!empty($requestParameters['amazon_reference_id'])) {
  1226. switch (substr(strtoupper($requestParameters['amazon_reference_id']), 0, 1)) {
  1227. case 'P':
  1228. case 'S':
  1229. $chargeType = 'OrderReference';
  1230. $setParameters['amazon_order_reference_id'] = $requestParameters['amazon_reference_id'];
  1231. $authorizeParameters['amazon_order_reference_id'] = $requestParameters['amazon_reference_id'];
  1232. $confirmParameters['amazon_order_reference_id'] = $requestParameters['amazon_reference_id'];
  1233. break;
  1234. case 'B':
  1235. case 'C':
  1236. $chargeType = 'BillingAgreement';
  1237. $setParameters['amazon_billing_agreement_id'] = $requestParameters['amazon_reference_id'];
  1238. $authorizeParameters['amazon_billing_agreement_id'] = $requestParameters['amazon_reference_id'];
  1239. $confirmParameters['amazon_billing_agreement_id'] = $requestParameters['amazon_reference_id'];
  1240. break;
  1241. default:
  1242. throw new \Exception('Invalid Amazon Reference ID');
  1243. }
  1244. } else {
  1245. throw new \Exception('key amazon_order_reference_id or amazon_billing_agreement_id is null and is a required parameter');
  1246. }
  1247. // Set the other parameters if the values are present
  1248. $setParameters['amount'] = !empty($requestParameters['charge_amount']) ? $requestParameters['charge_amount'] : '';
  1249. $authorizeParameters['authorization_amount'] = !empty($requestParameters['charge_amount']) ? $requestParameters['charge_amount'] : '';
  1250. $setParameters['seller_note'] = !empty($requestParameters['charge_note']) ? $requestParameters['charge_note'] : '';
  1251. $authorizeParameters['seller_authorization_note'] = !empty($requestParameters['charge_note']) ? $requestParameters['charge_note'] : '';
  1252. $authorizeParameters['seller_note'] = !empty($requestParameters['charge_note']) ? $requestParameters['charge_note'] : '';
  1253. $setParameters['seller_order_id'] = !empty($requestParameters['charge_order_id']) ? $requestParameters['charge_order_id'] : '';
  1254. $setParameters['seller_billing_agreement_id'] = !empty($requestParameters['charge_order_id']) ? $requestParameters['charge_order_id'] : '';
  1255. $authorizeParameters['seller_order_id'] = !empty($requestParameters['charge_order_id']) ? $requestParameters['charge_order_id'] : '';
  1256. $authorizeParameters['capture_now'] = !empty($requestParameters['capture_now']) ? $requestParameters['capture_now'] : false;
  1257. $response = $this->makeChargeCalls($chargeType, $setParameters, $confirmParameters, $authorizeParameters);
  1258. return $response;
  1259. }
  1260. /* makeChargeCalls - makes API calls based off the charge type (OrderReference or BillingAgreement) */
  1261. private function makeChargeCalls($chargeType, $setParameters, $confirmParameters, $authorizeParameters)
  1262. {
  1263. switch ($chargeType) {
  1264. case 'OrderReference':
  1265. // Get the Order Reference details and feed the response object to the ResponseParser
  1266. $responseObj = $this->getOrderReferenceDetails($setParameters);
  1267. // Call the function getOrderReferenceDetailsStatus in ResponseParser.php providing it the XML response
  1268. // $oroStatus is an array containing the State of the Order Reference ID
  1269. $oroStatus = $responseObj->getOrderReferenceDetailsStatus($responseObj->toXml());
  1270. if ($oroStatus['State'] === 'Draft') {
  1271. $response = $this->setOrderReferenceDetails($setParameters);
  1272. if ($this->success) {
  1273. $this->confirmOrderReference($confirmParameters);
  1274. }
  1275. }
  1276. $responseObj = $this->getOrderReferenceDetails($setParameters);
  1277. // Check the Order Reference Status again before making the Authorization.
  1278. $oroStatus = $responseObj->getOrderReferenceDetailsStatus($responseObj->toXml());
  1279. if ($oroStatus['State'] === 'Open') {
  1280. if ($this->success) {
  1281. $response = $this->authorize($authorizeParameters);
  1282. }
  1283. }
  1284. if ($oroStatus['State'] != 'Open' && $oroStatus['State'] != 'Draft') {
  1285. throw new \Exception('The Order Reference is in the ' . $oroStatus['State'] . " State. It should be in the Draft or Open State");
  1286. }
  1287. return $response;
  1288. case 'BillingAgreement':
  1289. // Get the Billing Agreement details and feed the response object to the ResponseParser
  1290. $responseObj = $this->getBillingAgreementDetails($setParameters);
  1291. // Call the function getBillingAgreementDetailsStatus in ResponseParser.php providing it the XML response
  1292. // $baStatus is an array containing the State of the Billing Agreement
  1293. $baStatus = $responseObj->getBillingAgreementDetailsStatus($responseObj->toXml());
  1294. if ($baStatus['State'] === 'Draft') {
  1295. $response = $this->setBillingAgreementDetails($setParameters);
  1296. if ($this->success) {
  1297. $response = $this->confirmBillingAgreement($confirmParameters);
  1298. }
  1299. }
  1300. // Check the Billing Agreement status again before making the Authorization.
  1301. $responseObj = $this->getBillingAgreementDetails($setParameters);
  1302. $baStatus = $responseObj->getBillingAgreementDetailsStatus($responseObj->toXml());
  1303. if ($this->success && $baStatus['State'] === 'Open') {
  1304. $response = $this->authorizeOnBillingAgreement($authorizeParameters);
  1305. }
  1306. if ($baStatus['State'] != 'Open' && $baStatus['State'] != 'Draft') {
  1307. throw new \Exception('The Billing Agreement is in the ' . $baStatus['State'] . " State. It should be in the Draft or Open State");
  1308. }
  1309. return $response;
  1310. default:
  1311. throw new \Exception('Invalid Charge Type');
  1312. }
  1313. }
  1314. /* GetProviderCreditDetails API Call - Get the details of the Provider Credit.
  1315. *
  1316. * @param requestParameters['merchant_id'] - [String]
  1317. * @param requestParameters['amazon_provider_credit_id'] - [String]
  1318. * @optional requestParameters['mws_auth_token'] - [String]
  1319. */
  1320. public function getProviderCreditDetails($requestParameters = array())
  1321. {
  1322. $parameters = array();
  1323. $parameters['Action'] = 'GetProviderCreditDetails';
  1324. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1325. $fieldMappings = array(
  1326. 'merchant_id' => 'SellerId',
  1327. 'amazon_provider_credit_id' => 'AmazonProviderCreditId',
  1328. 'mws_auth_token' => 'MWSAuthToken'
  1329. );
  1330. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1331. return ($responseObject);
  1332. }
  1333. /* GetProviderCreditReversalDetails API Call - Get details of the Provider Credit Reversal.
  1334. *
  1335. * @param requestParameters['merchant_id'] - [String]
  1336. * @param requestParameters['amazon_provider_credit_reversal_id'] - [String]
  1337. * @optional requestParameters['mws_auth_token'] - [String]
  1338. */
  1339. public function getProviderCreditReversalDetails($requestParameters = array())
  1340. {
  1341. $parameters = array();
  1342. $parameters['Action'] = 'GetProviderCreditReversalDetails';
  1343. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1344. $fieldMappings = array(
  1345. 'merchant_id' => 'SellerId',
  1346. 'amazon_provider_credit_reversal_id' => 'AmazonProviderCreditReversalId',
  1347. 'mws_auth_token' => 'MWSAuthToken'
  1348. );
  1349. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1350. return ($responseObject);
  1351. }
  1352. /* ReverseProviderCredit API Call - Reverse the Provider Credit.
  1353. *
  1354. * @param requestParameters['merchant_id'] - [String]
  1355. * @param requestParameters['amazon_provider_credit_id'] - [String]
  1356. * @optional requestParameters['credit_reversal_reference_id'] - [String]
  1357. * @param requestParameters['credit_reversal_amount'] - [String]
  1358. * @optional requestParameters['currency_code'] - [String]
  1359. * @optional requestParameters['credit_reversal_note'] - [String]
  1360. * @optional requestParameters['mws_auth_token'] - [String]
  1361. */
  1362. public function reverseProviderCredit($requestParameters = array())
  1363. {
  1364. $parameters = array();
  1365. $parameters['Action'] = 'ReverseProviderCredit';
  1366. $requestParameters = array_change_key_case($requestParameters, CASE_LOWER);
  1367. $fieldMappings = array(
  1368. 'merchant_id' => 'SellerId',
  1369. 'amazon_provider_credit_id' => 'AmazonProviderCreditId',
  1370. 'credit_reversal_reference_id' => 'CreditReversalReferenceId',
  1371. 'credit_reversal_amount' => 'CreditReversalAmount.Amount',
  1372. 'currency_code' => 'CreditReversalAmount.CurrencyCode',
  1373. 'credit_reversal_note' => 'CreditReversalNote',
  1374. 'mws_auth_token' => 'MWSAuthToken'
  1375. );
  1376. $responseObject = $this->setParametersAndPost($parameters, $fieldMappings, $requestParameters);
  1377. return ($responseObject);
  1378. }
  1379. /* Create an Array of required parameters, sort them
  1380. * Calculate signature and invoke the POST to the MWS Service URL
  1381. *
  1382. * @param AWSAccessKeyId [String]
  1383. * @param Version [String]
  1384. * @param SignatureMethod [String]
  1385. * @param Timestamp [String]
  1386. * @param Signature [String]
  1387. */
  1388. private function calculateSignatureAndParametersToString($parameters = array())
  1389. {
  1390. foreach ($parameters as $key => $value) {
  1391. // Ensure that no unexpected type coercions have happened
  1392. if ($key === 'CaptureNow' || $key === 'ConfirmNow' || $key === 'InheritShippingAddress' || $key === 'RequestPaymentAuthorization') {
  1393. if (!is_bool($value)) {
  1394. throw new \Exception($key . ' value ' . $value . ' is of type ' . gettype($value) . ' and should be a boolean value');
  1395. }
  1396. }
  1397. // Ensure boolean values are outputed as 'true' or 'false'
  1398. if (is_bool($value)) {
  1399. $parameters[$key] = json_encode($value);
  1400. }
  1401. }
  1402. $parameters['AWSAccessKeyId'] = $this->config['access_key'];
  1403. $parameters['Version'] = self::MWS_VERSION;
  1404. $parameters['SignatureMethod'] = 'HmacSHA256';
  1405. $parameters['SignatureVersion'] = 2;
  1406. $parameters['Timestamp'] = $this->getFormattedTimestamp();
  1407. uksort($parameters, 'strcmp');
  1408. $this->createServiceUrl();
  1409. $parameters['Signature'] = $this->signParameters($parameters);
  1410. $parameters = $this->getParametersAsString($parameters);
  1411. // Save these parameters in the parameters variable so that it can be returned for unit testing.
  1412. $this->parameters = $parameters;
  1413. return $parameters;
  1414. }
  1415. /* Computes RFC 2104-compliant HMAC signature for request parameters
  1416. * Implements AWS Signature, as per following spec:
  1417. *
  1418. * If Signature Version is 0, it signs concatenated Action and Timestamp
  1419. *
  1420. * If Signature Version is 1, it performs the following:
  1421. *
  1422. * Sorts all parameters (including SignatureVersion and excluding Signature,
  1423. * the value of which is being created), ignoring case.
  1424. *
  1425. * Iterate over the sorted list and append the parameter name (in original case)
  1426. * and then its value. It will not URL-encode the parameter values before
  1427. * constructing this string. There are no separators.
  1428. *
  1429. * If Signature Version is 2, string to sign is based on following:
  1430. *
  1431. * 1. The HTTP Request Method followed by an ASCII newline (%0A)
  1432. * 2. The HTTP Host header in the form of lowercase host, followed by an ASCII newline.
  1433. * 3. The URL encoded HTTP absolute path component of the URI
  1434. * (up to but not including the query string parameters);
  1435. * if this is empty use a forward '/'. This parameter is followed by an ASCII newline.
  1436. * 4. The concatenation of all query string components (names and values)
  1437. * as UTF-8 characters which are URL encoded as per RFC 3986
  1438. * (hex characters MUST be uppercase), sorted using lexicographic byte ordering.
  1439. * Parameter names are separated from their values by the '=' character
  1440. * (ASCII character 61), even if the value is empty.
  1441. * Pairs of parameter and values are separated by the '&' character (ASCII code 38).
  1442. *
  1443. */
  1444. private function signParameters(array $parameters)
  1445. {
  1446. $signatureVersion = $parameters['SignatureVersion'];
  1447. $algorithm = "HmacSHA1";
  1448. $stringToSign = null;
  1449. if (2 === $signatureVersion) {
  1450. $algorithm = "HmacSHA256";
  1451. $parameters['SignatureMethod'] = $algorithm;
  1452. $stringToSign = $this->calculateStringToSignV2($parameters);
  1453. } else {
  1454. throw new \Exception("Invalid Signature Version specified");
  1455. }
  1456. return $this->sign($stringToSign, $algorithm);
  1457. }
  1458. /* Calculate String to Sign for SignatureVersion 2
  1459. * @param array $parameters request parameters
  1460. * @return String to Sign
  1461. */
  1462. private function calculateStringToSignV2(array $parameters)
  1463. {
  1464. $data = 'POST';
  1465. $data .= "\n";
  1466. $data .= $this->mwsEndpointUrl;
  1467. $data .= "\n";
  1468. $data .= $this->mwsEndpointPath;
  1469. $data .= "\n";
  1470. $data .= $this->getParametersAsString($parameters);
  1471. $this->logMessage($this->sanitizeRequestData($data));
  1472. return $data;
  1473. }
  1474. /* Convert paremeters to Url encoded query string */
  1475. private function getParametersAsString(array $parameters)
  1476. {
  1477. $queryParameters = array();
  1478. foreach ($parameters as $key => $value) {
  1479. $queryParameters[] = $key . '=' . $this->urlEncode($value);
  1480. }
  1481. return implode('&', $queryParameters);
  1482. }
  1483. private function urlEncode($value)
  1484. {
  1485. return str_replace('%7E', '~', rawurlencode($value));
  1486. }
  1487. /* Computes RFC 2104-compliant HMAC signature */
  1488. private function sign($data, $algorithm)
  1489. {
  1490. if ($algorithm === 'HmacSHA1') {
  1491. $hash = 'sha1';
  1492. } else if ($algorithm === 'HmacSHA256') {
  1493. $hash = 'sha256';
  1494. } else {
  1495. throw new \Exception("Non-supported signing method specified");
  1496. }
  1497. return base64_encode(hash_hmac($hash, $data, $this->config['secret_key'], true));
  1498. }
  1499. /* Formats date as ISO 8601 timestamp */
  1500. private function getFormattedTimestamp()
  1501. {
  1502. return gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
  1503. }
  1504. /* invokePost takes the parameters and invokes the httpPost function to POST the parameters
  1505. * Exponential retries on error 500 and 503
  1506. * The response from the POST is an XML which is converted to Array
  1507. */
  1508. private function invokePost($parameters)
  1509. {
  1510. $response = array();
  1511. $statusCode = 200;
  1512. $this->success = false;
  1513. // Submit the request and read response body
  1514. try {
  1515. $shouldRetry = true;
  1516. $retries = 0;
  1517. do {
  1518. try {
  1519. $this->constructUserAgentHeader();
  1520. $httpCurlRequest = new HttpCurl($this->config);
  1521. $response = $httpCurlRequest->httpPost($this->mwsServiceUrl, $this->userAgent, $parameters);
  1522. $curlResponseInfo = $httpCurlRequest->getCurlResponseInfo();
  1523. $statusCode = $curlResponseInfo["http_code"];
  1524. $this->logMessage($this->userAgent);
  1525. $response = array(
  1526. 'Status' => $statusCode,
  1527. 'ResponseBody' => $response
  1528. );
  1529. $statusCode = $response['Status'];
  1530. if ($statusCode == 200) {
  1531. $shouldRetry = false;
  1532. $this->success = true;
  1533. } elseif ($statusCode == 500 || $statusCode == 503) {
  1534. $shouldRetry = true;
  1535. if ($shouldRetry && strtolower($this->config['handle_throttle'])) {
  1536. $this->pauseOnRetry(++$retries, $statusCode);
  1537. }
  1538. } else {
  1539. $shouldRetry = false;
  1540. }
  1541. } catch (\Exception $e) {
  1542. throw $e;
  1543. }
  1544. } while ($shouldRetry);
  1545. } catch (\Exception $se) {
  1546. throw $se;
  1547. }
  1548. $this->logMessage($this->sanitizeResponseData($response['ResponseBody']));
  1549. return $response;
  1550. }
  1551. /* Exponential sleep on failed request
  1552. * Up to three retries will occur if first reqest fails
  1553. * after 1.0 second, 2.2 seconds, and finally 7.0 seconds
  1554. * @param retries current retry
  1555. * @throws Exception if maximum number of retries has been reached
  1556. */
  1557. private function pauseOnRetry($retries, $status)
  1558. {
  1559. if ($retries <= self::MAX_ERROR_RETRY) {
  1560. // PHP delays are in microseconds (1 million microsecond = 1 sec)
  1561. // 1st delay is (4^1) * 100000 + 600000 = 0.4 + 0.6 second = 1.0 sec
  1562. // 2nd delay is (4^2) * 100000 + 600000 = 1.6 + 0.6 second = 2.2 sec
  1563. // 3rd delay is (4^3) * 100000 + 600000 = 6.4 + 0.6 second = 7.0 sec
  1564. $delay = (int) (pow(4, $retries) * 100000) + 600000;
  1565. usleep($delay);
  1566. } else {
  1567. throw new \Exception('Error Code: '. $status.PHP_EOL.'Maximum number of retry attempts - '. $retries .' reached');
  1568. }
  1569. }
  1570. /* Create MWS service URL and the Endpoint path */
  1571. private function createServiceUrl()
  1572. {
  1573. $this->modePath = strtolower($this->config['sandbox']) ? 'OffAmazonPayments_Sandbox' : 'OffAmazonPayments';
  1574. if (!empty($this->config['region'])) {
  1575. $region = strtolower($this->config['region']);
  1576. if (array_key_exists($region, $this->regionMappings)) {
  1577. if (!is_null($this->config['override_service_url'])) {
  1578. $this->mwsEndpointUrl = preg_replace("(https?://)", "", $this->config['override_service_url']);
  1579. } else {
  1580. $this->mwsEndpointUrl = $this->mwsServiceUrls[$this->regionMappings[$region]];
  1581. }
  1582. $this->mwsServiceUrl = 'https://' . $this->mwsEndpointUrl . '/' . $this->modePath . '/' . self::MWS_VERSION;
  1583. $this->mwsEndpointPath = '/' . $this->modePath . '/' . self::MWS_VERSION;
  1584. } else {
  1585. throw new \Exception($region . ' is not a valid region');
  1586. }
  1587. } else {
  1588. throw new \Exception("config['region'] is a required parameter and is not set");
  1589. }
  1590. }
  1591. /* Based on the config['region'] and config['sandbox'] values get the user profile URL */
  1592. private function profileEndpointUrl()
  1593. {
  1594. $profileEnvt = strtolower($this->config['sandbox']) ? "api.sandbox" : "api";
  1595. if (!empty($this->config['region'])) {
  1596. $region = strtolower($this->config['region']);
  1597. if (array_key_exists($region, $this->regionMappings) ) {
  1598. $this->profileEndpoint = 'https://' . $profileEnvt . '.' . $this->profileEndpointUrls[$region];
  1599. } else {
  1600. throw new \Exception($region . ' is not a valid region');
  1601. }
  1602. } else {
  1603. throw new \Exception("config['region'] is a required parameter and is not set");
  1604. }
  1605. }
  1606. /* Create the User Agent Header sent with the POST request */
  1607. /* Protected because of PSP module usaged */
  1608. protected function constructUserAgentHeader()
  1609. {
  1610. $this->userAgent = 'amazon-pay-sdk-php/' . self::SDK_VERSION . ' (';
  1611. if (($this->config['application_name']) || ($this->config['application_version'])) {
  1612. if ($this->config['application_name']) {
  1613. $this->userAgent .= $this->quoteApplicationName($this->config['application_name']);
  1614. if ($this->config['application_version']) {
  1615. $this->userAgent .= '/';
  1616. }
  1617. }
  1618. if ($this->config['application_version']) {
  1619. $this->userAgent .= $this->quoteApplicationVersion($this->config['application_version']);
  1620. }
  1621. $this->userAgent .= '; ';
  1622. }
  1623. $this->userAgent .= 'PHP/' . phpversion() . '; ';
  1624. $this->userAgent .= php_uname('s') . '/' . php_uname('m') . '/' . php_uname('r');
  1625. $this->userAgent .= ')';
  1626. }
  1627. /* Collapse multiple whitespace characters into a single ' ' and backslash escape '\',
  1628. * and '/' characters from a string.
  1629. * @param $s
  1630. * @return string
  1631. */
  1632. private function quoteApplicationName($s)
  1633. {
  1634. $quotedString = preg_replace('/ {2,}|\s/', ' ', $s);
  1635. $quotedString = preg_replace('/\\\\/', '\\\\\\\\', $quotedString);
  1636. $quotedString = preg_replace('/\//', '\\/', $quotedString);
  1637. return $quotedString;
  1638. }
  1639. /* Collapse multiple whitespace characters into a single ' ' and backslash escape '\',
  1640. * and '(' characters from a string.
  1641. *
  1642. * @param $s
  1643. * @return string
  1644. */
  1645. private function quoteApplicationVersion($s)
  1646. {
  1647. $quotedString = preg_replace('/ {2,}|\s/', ' ', $s);
  1648. $quotedString = preg_replace('/\\\\/', '\\\\\\\\', $quotedString);
  1649. $quotedString = preg_replace('/\\(/', '\\(', $quotedString);
  1650. return $quotedString;
  1651. }
  1652. private function sanitizeRequestData($input)
  1653. {
  1654. $patterns = array();
  1655. $patterns[0] = '/(SellerNote=)(.+)(&)/ms';
  1656. $patterns[1] = '/(SellerAuthorizationNote=)(.+)(&)/ms';
  1657. $patterns[2] = '/(SellerCaptureNote=)(.+)(&)/ms';
  1658. $patterns[3] = '/(SellerRefundNote=)(.+)(&)/ms';
  1659. $replacements = array();
  1660. $replacements[0] = '$1REMOVED$3';
  1661. $replacements[1] = '$1REMOVED$3';
  1662. $replacements[2] = '$1REMOVED$3';
  1663. $replacements[3] = '$1REMOVED$3';
  1664. return preg_replace($patterns, $replacements, $input);
  1665. }
  1666. private function sanitizeResponseData($input)
  1667. {
  1668. $patterns = array();
  1669. $patterns[0] = '/(<Buyer>)(.+)(<\/Buyer>)/ms';
  1670. $patterns[1] = '/(<PhysicalDestination>)(.+)(<\/PhysicalDestination>)/ms';
  1671. $patterns[2] = '/(<BillingAddress>)(.+)(<\/BillingAddress>)/ms';
  1672. $patterns[3] = '/(<SellerNote>)(.+)(<\/SellerNote>)/ms';
  1673. $patterns[4] = '/(<AuthorizationBillingAddress>)(.+)(<\/AuthorizationBillingAddress>)/ms';
  1674. $patterns[5] = '/(<SellerAuthorizationNote>)(.+)(<\/SellerAuthorizationNote>)/ms';
  1675. $patterns[6] = '/(<SellerCaptureNote>)(.+)(<\/SellerCaptureNote>)/ms';
  1676. $patterns[7] = '/(<SellerRefundNote>)(.+)(<\/SellerRefundNote>)/ms';
  1677. $replacements = array();
  1678. $replacements[0] = '$1 REMOVED $3';
  1679. $replacements[1] = '$1 REMOVED $3';
  1680. $replacements[2] = '$1 REMOVED $3';
  1681. $replacements[3] = '$1 REMOVED $3';
  1682. $replacements[4] = '$1 REMOVED $3';
  1683. $replacements[5] = '$1 REMOVED $3';
  1684. $replacements[6] = '$1 REMOVED $3';
  1685. $replacements[7] = '$1 REMOVED $3';
  1686. return preg_replace($patterns, $replacements, $input);
  1687. }
  1688. /* Computes RFC 2104-compliant HMAC signature */
  1689. public static function getSignature($stringToSign, $secretKey)
  1690. {
  1691. return base64_encode(hash_hmac('sha256', $stringToSign, $secretKey, true));
  1692. }
  1693. }