LiqPay.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <?php
  2. /**
  3. * Liqpay Payment Module
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is available through the world-wide-web at this URL:
  9. * http://opensource.org/licenses/osl-3.0.php
  10. *
  11. * @category LiqPay
  12. * @package liqpay/liqpay
  13. * @version 3.0
  14. * @author Liqpay
  15. * @copyright Copyright (c) 2014 Liqpay
  16. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  17. *
  18. * EXTENSION INFORMATION
  19. *
  20. * LIQPAY API https://www.liqpay.ua/documentation/en
  21. *
  22. */
  23. /**
  24. * Payment method liqpay process
  25. *
  26. * @author Liqpay <support@liqpay.ua>
  27. */
  28. // tests/Unit/LiqPayTest.php
  29. class LiqPay
  30. {
  31. private $_api_url = 'https://www.liqpay.ua/api/';
  32. private $_checkout_url = 'https://www.liqpay.ua/api/3/checkout';
  33. protected $_supportedCurrencies = array(
  34. 'EUR', 'USD', 'UAH');
  35. protected $_supportedLangs = ['uk', 'ru', 'en'];
  36. private $_public_key;
  37. private $_private_key;
  38. private $_server_response_code = null;
  39. protected $_button_translations = array(
  40. 'ru' => 'Оплатить',
  41. 'uk' => 'Сплатити',
  42. 'en' => 'Pay'
  43. );
  44. protected $_actions = array(
  45. "pay", "hold", "subscribe", "paydonate"
  46. );
  47. public CurlRequester $curlRequester;
  48. /**
  49. * Constructor.
  50. *
  51. * @param string $public_key
  52. * @param string $private_key
  53. * @param string $api_url (optional)
  54. *
  55. * @throws InvalidArgumentException
  56. */
  57. public function __construct($public_key, $private_key, $api_url = null)
  58. {
  59. if (empty($public_key)) {
  60. throw new InvalidArgumentException('public_key is empty');
  61. }
  62. if (empty($private_key)) {
  63. throw new InvalidArgumentException('private_key is empty');
  64. }
  65. $this->curlRequester = new CurlRequester();
  66. $this->_public_key = $public_key;
  67. $this->_private_key = $private_key;
  68. if (null !== $api_url) {
  69. $this->_api_url = $api_url;
  70. }
  71. }
  72. /**
  73. * Call API
  74. *
  75. * @param string $path
  76. * @param array $params
  77. * @param int $timeout
  78. *
  79. * @return array|null
  80. */
  81. public function api($path, $params = array(), $timeout = 5)
  82. {
  83. if (!isset($params['version'])) {
  84. throw new InvalidArgumentException('version is null');
  85. }
  86. $url = $this->_api_url . $path;
  87. $public_key = $this->_public_key;
  88. $private_key = $this->_private_key;
  89. $data = $this->encode_params(array_merge(compact('public_key'), $params));
  90. $signature = $this->str_to_sign($private_key . $data . $private_key);
  91. $postfields = http_build_query(array(
  92. 'data' => $data,
  93. 'signature' => $signature
  94. ));
  95. $server_output = $this->curlRequester->make_curl_request($url, $postfields);
  96. return json_decode($server_output);
  97. }
  98. /**
  99. * Return last api response http code
  100. *
  101. * @return string|null
  102. */
  103. public function get_response_code()
  104. {
  105. return $this->_server_response_code;
  106. }
  107. /**
  108. * cnb_form
  109. *
  110. * @param array $params
  111. *
  112. * @return string
  113. *
  114. * @throws InvalidArgumentException
  115. */
  116. public function cnb_form($params)
  117. {
  118. $language = 'uk';
  119. if (isset($params['language']) && in_array($params['language'], $this->_supportedLangs)) {
  120. $language = $params['language'];
  121. }
  122. $params = $this->cnb_params($params);
  123. $data = $this->encode_params($params);
  124. $signature = $this->cnb_signature($params);
  125. return sprintf('
  126. <form method="POST" action="%s" accept-charset="utf-8">
  127. %s
  128. %s
  129. <script type="text/javascript" src="https://static.liqpay.ua/libjs/sdk_button.js"></script>
  130. <sdk-button label="%s" background="#77CC5D" onClick="submit()"></sdk-button>
  131. </form>
  132. ',
  133. $this->_checkout_url,
  134. sprintf('<input type="hidden" name="%s" value="%s" />', 'data', $data),
  135. sprintf('<input type="hidden" name="%s" value="%s" />', 'signature', $signature),
  136. $this->_button_translations[$language]
  137. );
  138. }
  139. /**
  140. * cnb_form raw data for custom form
  141. *
  142. * @param $params
  143. * @return array
  144. */
  145. public function cnb_form_raw($params)
  146. {
  147. $params = $this->cnb_params($params);
  148. return array(
  149. 'url' => $this->_checkout_url,
  150. 'data' => $this->encode_params($params),
  151. 'signature' => $this->cnb_signature($params)
  152. );
  153. }
  154. /**
  155. * cnb_signature
  156. *
  157. * @param array $params
  158. *
  159. * @return string
  160. */
  161. public function cnb_signature($params)
  162. {
  163. $params = $this->cnb_params($params);
  164. $private_key = $this->_private_key;
  165. $json = $this->encode_params($params);
  166. $signature = $this->str_to_sign($private_key . $json . $private_key);
  167. return $signature;
  168. }
  169. /**
  170. * cnb_params
  171. *
  172. * @param array $params
  173. *
  174. * @return array $params
  175. */
  176. protected function cnb_params($params)
  177. {
  178. $params['public_key'] = $this->_public_key;
  179. if (!isset($params['version'])) {
  180. throw new InvalidArgumentException('version is null');
  181. }
  182. if (!isset($params['amount'])) {
  183. throw new InvalidArgumentException('amount is null');
  184. }
  185. if (!isset($params['currency'])) {
  186. throw new InvalidArgumentException('currency is null');
  187. }
  188. if (!in_array($params['currency'], $this->_supportedCurrencies)) {
  189. throw new InvalidArgumentException('currency is not supported');
  190. }
  191. if (!isset($params['action'])) {
  192. throw new InvalidArgumentException('action is null');
  193. }
  194. if (!in_array($params['action'], $this->_actions)) {
  195. throw new InvalidArgumentException('action is not supported');
  196. }
  197. if (!isset($params['order_id'])) {
  198. throw new InvalidArgumentException('order_id is null');
  199. }
  200. if (!isset($params['description'])) {
  201. throw new InvalidArgumentException('description is null');
  202. }
  203. return $params;
  204. }
  205. /**
  206. * encode_params
  207. *
  208. * @param array $params
  209. * @return string
  210. */
  211. protected function encode_params($params)
  212. {
  213. return base64_encode(json_encode($params));
  214. }
  215. /**
  216. * decode_params
  217. *
  218. * @param string $params
  219. * @return array
  220. */
  221. public function decode_params($params)
  222. {
  223. return json_decode(base64_decode($params), true);
  224. }
  225. /**
  226. * str_to_sign
  227. *
  228. * @param string $str
  229. *
  230. * @return string
  231. */
  232. public function str_to_sign($str)
  233. {
  234. $signature = base64_encode(sha1($str, 1));
  235. return $signature;
  236. }
  237. }
  238. class CurlRequester
  239. {
  240. /**
  241. * make_curl_request
  242. * @param $url string
  243. * @param $postfields string
  244. * @param int $timeout
  245. * @return bool|string
  246. */
  247. public function make_curl_request($url, $postfields, $timeout = 5) {
  248. $ch = curl_init();
  249. curl_setopt($ch, CURLOPT_URL, $url);
  250. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // Avoid MITM vulnerability http://phpsecurity.readthedocs.io/en/latest/Input-Validation.html#validation-of-input-sources
  251. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // Check the existence of a common name and also verify that it matches the hostname provided
  252. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); // The number of seconds to wait while trying to connect
  253. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); // The maximum number of seconds to allow cURL functions to execute
  254. curl_setopt($ch, CURLOPT_POST, true);
  255. curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
  256. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  257. $server_output = curl_exec($ch);
  258. $this->_server_response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  259. curl_close($ch);
  260. return $server_output;
  261. }
  262. }