AmazonPayments.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. <?php
  2. /**
  3. *
  4. * PHP Pro Bid $Id$ o1e1L5367/f25ZWKeoBHyjWWAhJ6bUijsZz4uKYuHz0=
  5. *
  6. * @link http://www.phpprobid.com
  7. * @copyright Copyright (c) 2015 Online Ventures Software & CodeCube SRL
  8. * @license http://www.phpprobid.com/license Commercial License
  9. *
  10. * @version 7.7
  11. */
  12. /**
  13. * amazon payments gateway model class
  14. */
  15. namespace Ppb\Model\PaymentGateway;
  16. use Cube\Controller\Request\AbstractRequest,
  17. Cube\Controller\Front,
  18. Cube\Config\Xml,
  19. Ppb\Service\Table\Currencies as CurrenciesService;
  20. class AmazonPayments extends AbstractPaymentGateway
  21. {
  22. /**
  23. * payment gateway name
  24. */
  25. const NAME = 'AmazonPayments';
  26. /**
  27. * required settings
  28. */
  29. const MERCHANT_ID = 'item_merchant_id_1';
  30. const ACCESS_KEY = 'aws_access_key_id';
  31. const SECRET_KEY = 'aws_secret_key_id';
  32. const REGION = 'region';
  33. /**
  34. * regions
  35. */
  36. const REGION_US = 'us';
  37. const REGION_UK = 'uk';
  38. const REGION_DE = 'de';
  39. /**
  40. * sha1 algorithm
  41. */
  42. const HMAC_SHA1_ALGORITHM = 'sha1';
  43. /**
  44. * form post url
  45. */
  46. const POST_URL = '';
  47. /**
  48. *
  49. * regions selector
  50. *
  51. * @var array
  52. */
  53. protected $_regions = array(
  54. self::REGION_US => 'United States',
  55. self::REGION_UK => 'United Kingdom',
  56. self::REGION_DE => 'Germany',
  57. );
  58. /**
  59. *
  60. * accepted currencies for each region
  61. *
  62. * @var array
  63. */
  64. protected $_currencies = array(
  65. self::REGION_US => 'USD',
  66. self::REGION_UK => 'GBP',
  67. self::REGION_DE => 'EUR',
  68. );
  69. /**
  70. *
  71. * view object
  72. *
  73. * @var \Cube\View
  74. */
  75. protected $_view;
  76. /**
  77. * amazon payments description
  78. */
  79. protected $_description = 'Click to pay using Amazon Payments Checkout.';
  80. /**
  81. *
  82. * class constructor
  83. *
  84. * @param int $userId
  85. */
  86. public function __construct($userId = null)
  87. {
  88. parent::__construct(self::NAME, $userId);
  89. }
  90. /**
  91. *
  92. * check if the gateway is enabled
  93. *
  94. * @return bool
  95. */
  96. public function enabled()
  97. {
  98. if (!empty($this->_data[self::MERCHANT_ID]) && !empty($this->_data[self::ACCESS_KEY]) && !empty($this->_data[self::SECRET_KEY])) {
  99. return true;
  100. }
  101. return false;
  102. }
  103. /**
  104. *
  105. * get setup form elements
  106. *
  107. * @return array
  108. */
  109. public function getElements()
  110. {
  111. $translate = $this->getTranslate();
  112. return array(
  113. array(
  114. 'form_id' => 'AmazonPayments',
  115. 'id' => self::MERCHANT_ID,
  116. 'element' => 'text',
  117. 'label' => $this->_('Merchant ID'),
  118. 'description' => $this->_('Found in - Amazon Payments > Settings > Account Info > Checkout Pipeline Settings'),
  119. 'attributes' => array(
  120. 'class' => 'form-control input-medium',
  121. ),
  122. ),
  123. array(
  124. 'form_id' => 'AmazonPayments',
  125. 'id' => self::ACCESS_KEY,
  126. 'element' => 'text',
  127. 'label' => $this->_('AWS Access Key ID'),
  128. 'description' => $this->_('Enter your access key id (public)'),
  129. 'attributes' => array(
  130. 'class' => 'form-control input-medium',
  131. ),
  132. ),
  133. array(
  134. 'form_id' => 'AmazonPayments',
  135. 'id' => self::SECRET_KEY,
  136. 'element' => 'text',
  137. 'label' => $this->_('AWS Secret Access Key'),
  138. 'description' => $translate->_('Enter your secret access key (private)<br>Amazon Payments IPN URL:<br>') . $this->getIpnUrl(),
  139. 'attributes' => array(
  140. 'class' => 'form-control input-medium',
  141. ),
  142. ),
  143. array(
  144. 'form_id' => 'AmazonPayments',
  145. 'id' => self::REGION,
  146. 'element' => 'select',
  147. 'label' => $this->_('Region'),
  148. 'description' => $this->_('Select the region that applies to your Amazon Payments account.'),
  149. 'multiOptions' => $this->_regions,
  150. 'attributes' => array(
  151. 'class' => 'form-control input-medium',
  152. ),
  153. )
  154. );
  155. }
  156. /**
  157. *
  158. * set transaction amount
  159. * convert all amounts to USD before going to the payment page
  160. *
  161. * @param string $amount
  162. *
  163. * @throws \RuntimeException
  164. * @return $this
  165. */
  166. public function setAmount($amount)
  167. {
  168. $currency = $this->getCurrency();
  169. if (empty($currency)) {
  170. $translate = $this->getTranslate();
  171. throw new \RuntimeException($translate->_("Please set the currency before setting the amount."));
  172. }
  173. $acceptedCurrency = $this->_currencies[$this->_getActiveRegion()];
  174. if ($currency != $acceptedCurrency) {
  175. $currenciesService = new CurrenciesService();
  176. $amount = $currenciesService->convertAmount($amount, $currency, $acceptedCurrency);
  177. $this->setCurrency($acceptedCurrency);
  178. }
  179. parent::setAmount($amount);
  180. return $this;
  181. }
  182. /**
  183. *
  184. * @return array
  185. */
  186. public function formElements()
  187. {
  188. $translate = $this->getTranslate();
  189. $view = Front::getInstance()->getBootstrap()->getResource('view');
  190. /** @var \Cube\View\Helper\Script $scriptHelper */
  191. $scriptHelper = $view->getHelper('script');
  192. switch ($this->_getActiveRegion()) {
  193. case self::REGION_US:
  194. $scriptHelper->addBodyCode("<script type='text/javascript' src='https://static-na.payments-amazon.com/cba/js/us/PaymentWidgets.js'></script>");
  195. break;
  196. case self::REGION_UK:
  197. $scriptHelper->addBodyCode('<script language=javascript src="https://static-eu.payments-amazon.com/cba/js/gb/PaymentWidgets.js"></script>');
  198. // UK sandbox.
  199. // $scriptHelper->addBodyCode('<script language=javascript src="https://static-eu.payments-amazon.com/cba/js/gb/sandbox/PaymentWidgets.js"></script>');
  200. break;
  201. case self::REGION_DE:
  202. $scriptHelper->addBodyCode('<script language=javascript src="https://static-eu.payments-amazon.com/cba/js/de/PaymentWidgets.js"></script>');
  203. break;
  204. }
  205. return array(
  206. array(
  207. 'id' => self::MERCHANT_ID,
  208. 'value' => $this->_data[self::MERCHANT_ID],
  209. 'element' => 'hidden',
  210. 'bodyCode' => "<script type=\"text/javascript\">
  211. $('#" . self::NAME . "').find('.payment-btn').html('').attr('id', 'amazonPaymentsButton');
  212. new CBA.Widgets.StandardCheckoutWidget({
  213. merchantId:'" . $this->_data[self::MERCHANT_ID] . "',
  214. orderInput: {
  215. format: 'HTML',
  216. value: '" . self::NAME . "'
  217. },
  218. buttonSettings: {
  219. size:'large',
  220. color:'orange',
  221. background:'white'
  222. }
  223. }).render('amazonPaymentsButton');
  224. </script>",
  225. ),
  226. array(
  227. 'id' => 'item_sku_1',
  228. 'value' => $this->getTransactionId(),
  229. 'element' => 'hidden',
  230. ),
  231. array(
  232. 'id' => 'item_title_1',
  233. 'value' => $this->_shortenString($this->getName(), 80),
  234. 'element' => 'hidden',
  235. ),
  236. array(
  237. 'id' => 'item_price_1',
  238. 'value' => $this->getAmount(),
  239. 'element' => 'hidden',
  240. ),
  241. array(
  242. 'id' => 'item_quantity_1',
  243. 'value' => '1',
  244. 'element' => 'hidden',
  245. ),
  246. array(
  247. 'id' => 'currency_code',
  248. 'value' => $this->getCurrency(),
  249. 'element' => 'hidden',
  250. ),
  251. array(
  252. 'id' => 'merchant_signature', // cart hash for signed carts
  253. 'value' => $this->_encryptAndEncode(
  254. $this->_createHash()),
  255. 'element' => 'hidden',
  256. ),
  257. array(
  258. 'id' => self::ACCESS_KEY,
  259. 'value' => $this->_data[self::ACCESS_KEY],
  260. 'element' => 'hidden',
  261. ),
  262. );
  263. }
  264. public function getPostUrl()
  265. {
  266. return self::POST_URL;
  267. }
  268. /**
  269. *
  270. * process ipn
  271. *
  272. * @param \Cube\Controller\Request\AbstractRequest $request
  273. *
  274. * @return bool
  275. */
  276. public function processIpn(AbstractRequest $request)
  277. {
  278. $response = false;
  279. if ($request->isPost()) {
  280. $UUID = $request->getParam('UUID');
  281. $Signature = $request->getParam('Signature'); // if empty, we have an unsigned cart, but our carts must all be signed
  282. $Timestamp = $request->getParam('Timestamp');
  283. $NotificationData = stripslashes($request->getParam('NotificationData')); // need to parse it as xml or something.
  284. $NotificationType = $request->getParam('NotificationType');
  285. $xmlObject = new Xml();
  286. $xmlObject->setData(
  287. urldecode($NotificationData));
  288. $orderDetails = $xmlObject->getData('ProcessedOrder');
  289. $this->setGatewayPaymentStatus($NotificationType)
  290. ->setGatewayTransactionCode($UUID);
  291. $generatedSignature = $this->_encryptAndEncode($UUID . $Timestamp);
  292. if (strcmp($generatedSignature, $Signature) === 0 || empty($Signature)) {
  293. $this->setTransactionId($orderDetails['ProcessedOrderItems']['ProcessedOrderItem'][0]['SKU'])
  294. ->setAmount($orderDetails['ProcessedOrderItems']['ProcessedOrderItem'][0]['Price']['Amount'])
  295. ->setCurrency($orderDetails['ProcessedOrderItems']['ProcessedOrderItem'][0]['Price']['CurrencyCode']);
  296. if ($NotificationType == 'OrderReadyToShipNotification') {
  297. $response = true;
  298. }
  299. }
  300. else {
  301. $this->setGatewayPaymentStatus('Invalid Signature');
  302. }
  303. }
  304. return $response;
  305. }
  306. /**
  307. *
  308. * generates the required hash -- it gets generated exactly list on amazon's signed cart demo
  309. *
  310. * @return string
  311. */
  312. private function _createHash()
  313. {
  314. $data = array(
  315. self::MERCHANT_ID => $this->_data[self::MERCHANT_ID],
  316. 'item_sku_1' => $this->getTransactionId(),
  317. 'item_title_1' => $this->getName(),
  318. 'item_price_1' => $this->getAmount(),
  319. 'item_quantity_1' => '1',
  320. 'currency_code' => $this->getCurrency(),
  321. self::ACCESS_KEY => $this->_data[self::ACCESS_KEY],
  322. );
  323. ksort($data);
  324. $crypt = null;
  325. foreach ($data as $key => $value) {
  326. $crypt .= $key . '=' . rawurlencode($value) . '&';
  327. }
  328. return $crypt;
  329. }
  330. /**
  331. *
  332. * encrypt and encode string for signed carts
  333. *
  334. * @param string $string
  335. *
  336. * @return string
  337. */
  338. private function _encryptAndEncode($string)
  339. {
  340. return base64_encode(hash_hmac(self::HMAC_SHA1_ALGORITHM, $string, $this->_data[self::SECRET_KEY], true));
  341. }
  342. /**
  343. *
  344. * get active region
  345. *
  346. * @return string
  347. */
  348. protected function _getActiveRegion()
  349. {
  350. if (isset($this->_data[self::REGION])) {
  351. $region = $this->_data[self::REGION];
  352. if (in_array($region, array_keys($this->_regions))) {
  353. return $region;
  354. }
  355. }
  356. return self::REGION_US;
  357. }
  358. }