index.php 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. <?php
  2. /**
  3. * Payme.UZ API frontend for OpenPayz
  4. *
  5. * https://developer.help.paycom.uz/ru/protokol-merchant-api
  6. *
  7. */
  8. // подключаем API OpenPayz
  9. include ("../../libs/api.openpayz.php");
  10. class PaymeUZ {
  11. /**
  12. * Predefined stuff
  13. */
  14. const PATH_CONFIG = 'config/paymeuz.ini';
  15. const PATH_AGENTCODES = 'config/agentcodes_mapping.ini';
  16. /**
  17. * Paysys specific predefines
  18. */
  19. const HASH_PREFIX = 'PAYME_UZ_';
  20. const PAYSYS = 'PAYME_UZ';
  21. /** Transaction expiration time in milliseconds. 43 200 000 ms = 12 hours. */
  22. const EXPIRE_TIMEOUT = 43200000;
  23. /**
  24. * Agent codes using flag
  25. *
  26. * @var bool
  27. */
  28. protected $agentcodesON = false;
  29. /**
  30. * Non strict agent codes using flag
  31. *
  32. * @var bool
  33. */
  34. protected $agentcodesNonStrict = false;
  35. /**
  36. * Contains values from agentcodes_mapping.ini
  37. *
  38. * @var array
  39. */
  40. protected $agentcodesMapping = array();
  41. /**
  42. * Default UB agent code from config/paymeuz.ini
  43. *
  44. * @var
  45. */
  46. protected $agentcodeDefault = '';
  47. /**
  48. * Merchant login from PaymeUZ
  49. *
  50. * @var string
  51. */
  52. protected $paymeLogin = '';
  53. /**
  54. * Merchant cashbox password from PaymeUZ
  55. * https://developer.help.paycom.uz/ru/poisk-klyucha-i-id-kassy-v-lichnom-kabinete/poisk-klyucha-parolya-ot-kassy
  56. *
  57. * @var string
  58. */
  59. protected $paymePassword = '';
  60. /**
  61. * Request ID from Payme
  62. *
  63. * @var string
  64. */
  65. protected $paymeRequestID = null;
  66. /**
  67. * Placeholder for PaymeUZ transaction ID value
  68. *
  69. * @var string
  70. */
  71. protected $paymeTransactID = '';
  72. /**
  73. * Placeholder for PaymeUZ cashbox ID
  74. *
  75. * @var string
  76. */
  77. protected $paymeCashBoxID = '';
  78. /**
  79. * Placeholder for DEFAULT_CASHBOX_ID option
  80. *
  81. * @var string
  82. */
  83. protected $defaultCashBoxID = '';
  84. /**
  85. * Placeholder for UB API URL
  86. *
  87. * @var string
  88. */
  89. protected $ubapiURL = '';
  90. /**
  91. * Placeholder for UB API key
  92. *
  93. * @var string
  94. */
  95. protected $ubapiKey = '';
  96. /**
  97. * Placeholder for CITY_DISPLAY_IN_ADDRESS config option
  98. *
  99. * @var bool
  100. */
  101. protected $addressCityDisplay = false;
  102. /**
  103. * Instance configuration as key=>value
  104. *
  105. * @var array
  106. */
  107. protected $config = array();
  108. /**
  109. * Payment sum from request
  110. *
  111. * @var int
  112. */
  113. protected $paymentSum = 0;
  114. /**
  115. * Placeholder for a payment method JSON property
  116. *
  117. * @var string
  118. */
  119. protected $paymentMethod = '';
  120. /**
  121. * Placeholder for available payment methods
  122. *
  123. * @var array
  124. */
  125. protected $paymentMethodsAvailable = array('CheckPerformTransaction', 'CreateTransaction', 'PerformTransaction', 'CancelTransaction', 'CheckTransaction', 'GetStatement');
  126. /**
  127. * Placeholder for PaymeUZ customer ID value
  128. *
  129. * @var string
  130. */
  131. protected $customerID = '';
  132. /**
  133. *
  134. * @var
  135. */
  136. protected $customerIDFieldName = '';
  137. /**
  138. * Contains received by listener preprocessed request data
  139. *
  140. * @var array
  141. */
  142. protected $receivedJSON = array();
  143. /**
  144. * Contains all existent op_customers as virtualid => realid(login) mapping
  145. *
  146. * @var array
  147. */
  148. protected $opCustomersAll = array();
  149. /**
  150. * Placeholder for OP customer login
  151. *
  152. * @var string
  153. */
  154. protected $userLogin = '';
  155. /**
  156. * Placeholder for error codes and their descr
  157. *
  158. * @var string
  159. */
  160. protected $errorCodes = array('-32700' => array('ru' => 'Ошибка парсинга JSON', 'uz' => 'JSON парсинг хатоси', 'en' => 'JSON parse error'),
  161. '-32600' => array('ru' => 'Отсутствуют обязательные поля в RPC-запросе или тип полей не соответствует спецификации.', 'uz' => 'RPC сўровида мажбутий майдонлар йўқ ёки майдон тури спецификацияга мос келмайди.', 'en' => 'Missing required fields in RPC request or fields types does not correspond to specs'),
  162. '-32601' => array('ru' => 'Запрашиваемый метод не найден', 'uz' => 'Сўралган усул топилмади.', 'en' => 'Requested method not found'),
  163. '-32504' => array('ru' => 'Недостаточно привилегий для выполнения метода', 'uz' => 'Усулни бажариш учун етарли ваколат йўқ', 'en' => 'Not enough privileges to perform request'),
  164. '-31001' => array('ru' => 'Неверная сумма платежа', 'uz' => 'Тўлов миқдори нотўғри', 'en' => 'Incorrect payment amount'),
  165. '-31003' => array('ru' => 'Транзакция не найдена', 'uz' => 'Транзакция топилмади', 'en' => 'Transaction not found'),
  166. '-31007' => array('ru' => 'Невозможно отменить транзакцию. Услуга предоставлена потребителю в полном объеме.', 'uz' => 'Транзакцияни бекор қилиб бўлмайди. Хизмат истеъмолчига тўлиқ ҳажмда тақдим этилди', 'en' => 'Transaction can not be canceled. The service is provided to the consumer in full amount'),
  167. '-31008' => array('ru' => 'Невозможно выполнить операцию', 'uz' => 'Операцияни амалга ошириб бўлмайди', 'en' => 'Operation can not be performed'),
  168. '-31050' => array('ru' => 'Номер телефона не найден', 'uz' => 'Телефон рақами топилмади', 'en' => 'Phone number not found'),
  169. '-31051' => array('ru' => 'Транзакция с таким ID уже существует', 'uz' => 'Бундай ID билан транзакция мавжуд', 'en' => 'Transaction with such ID already exists'),
  170. '-31052' => array('ru' => 'Платёж с таким ID уже успешно проведён на стороне провайдера', 'uz' => 'Бундай ID билан тўлов провайдер томонидан муваффақиятли амалга оширилди', 'en' => 'Payment with this ID has already been successfully completed on the ISP side already'),
  171. '-31099' => array('ru' => 'Лицевой счёт не найден', 'uz' => 'Шаҳсий ҳисоб топилмади', 'en' => 'Customer ID not found')
  172. );
  173. /**
  174. * Preloads all required configuration, sets needed object properties
  175. *
  176. * @return void
  177. */
  178. public function __construct() {
  179. // fallback for getallheaders(), e.g. for nginx
  180. if (!function_exists('getallheaders')) {
  181. function getallheaders() {
  182. $headers = '';
  183. foreach ($_SERVER as $name => $value) {
  184. if (substr($name, 0, 5) == 'HTTP_') {
  185. $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
  186. }
  187. }
  188. return $headers;
  189. }
  190. }
  191. $this->loadConfig();
  192. $this->setOptions();
  193. $this->loadACMapping();
  194. }
  195. /**
  196. * Loads frontend configuration in protected prop
  197. *
  198. * @return void
  199. */
  200. protected function loadConfig() {
  201. if (file_exists(self::PATH_CONFIG)) {
  202. $this->config = parse_ini_file(self::PATH_CONFIG);
  203. } else {
  204. die('Fatal error: config file ' . self::PATH_CONFIG . ' not found!');
  205. }
  206. }
  207. /**
  208. * Loads frontend agentcodes_mapping.ini in protected prop
  209. *
  210. * @return void
  211. */
  212. protected function loadACMapping() {
  213. if ($this->agentcodesON) {
  214. if (file_exists(self::PATH_AGENTCODES)) {
  215. $this->agentcodesMapping = parse_ini_file(self::PATH_AGENTCODES);
  216. } else {
  217. die('Fatal error: agentcodes_mapping.ini file ' . self::PATH_AGENTCODES . ' not found!');
  218. }
  219. }
  220. }
  221. /**
  222. * Sets object properties based on frontend config
  223. *
  224. * @return void
  225. */
  226. protected function setOptions() {
  227. if (!empty($this->config)) {
  228. $this->agentcodesON = $this->config['USE_AGENTCODES'];
  229. $this->agentcodesNonStrict = $this->config['NON_STRICT_AGENTCODES'];
  230. $this->agentcodeDefault = $this->config['DEFAULT_AGENTCODE'];
  231. $this->defaultCashBoxID = $this->config['DEFAULT_CASHBOX_ID'];
  232. $this->customerIDFieldName = $this->config['CUSTOMERID_FIELD_NAME'];
  233. $this->paymeLogin = (empty($this->config['LOGIN']) ? '' : $this->config['LOGIN']);
  234. $this->paymePassword = (empty($this->config['PASSWORD']) ? '' : $this->config['PASSWORD']);
  235. $this->ubapiURL = $this->config['UBAPI_URL'];
  236. $this->ubapiKey = $this->config['UBAPI_KEY'];
  237. $this->addressCityDisplay = $this->config['CITY_DISPLAY_IN_ADDRESS'];
  238. } else {
  239. die('Fatal: config is empty!');
  240. }
  241. }
  242. /**
  243. * Returns current UNIX timestamp in milliseconds (13 digits)
  244. *
  245. * @return int
  246. */
  247. protected function getUnixTimestampMillisec() {
  248. $now = DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', ''));
  249. $now_ms = (int)$now->format('Uv');
  250. return ($now_ms);
  251. }
  252. /**
  253. * Gets user associated agent data JSON
  254. *
  255. * @param string $userLogin
  256. *
  257. * @return string
  258. */
  259. protected function getUBAgentData($userLogin) {
  260. $action = $this->ubapiURL . '?module=remoteapi&key=' . $this->ubapiKey . '&action=getagentdata&param=' . $userLogin;
  261. @$result = file_get_contents($action);
  262. return ($result);
  263. }
  264. /**
  265. * Validates PaymeUZ service ID and Ubilling agent ID correlation
  266. *
  267. * @param $userLogin
  268. *
  269. * @return bool
  270. */
  271. protected function getCashBoxIDAgentAssigned($userLogin) {
  272. $cashboxID = '';
  273. $agentData = json_decode($this->getUBAgentData($userLogin), true);
  274. if (!empty($agentData['id'])) {
  275. $agentID = $agentData['id'];
  276. // get Ubilling agent code to Cashbox mapping, if exists
  277. $cashboxID = (empty($this->agentcodesMapping[$agentID]) ? '' : $this->agentcodesMapping[$agentID]);
  278. }
  279. // if no mapped cashbox ID found or user does not have UB agent assigned
  280. // and $this->agentcodesNonStrict is ON - proceed with default UB agent
  281. // and a cashbox ID mapped to it or use DEFAULT_CASHBOX_ID.
  282. // if no default cashbox ID is set - user not found error will be returned.
  283. if (empty($cashboxID) and $this->agentcodesNonStrict and !empty($this->agentcodeDefault)) {
  284. $cashboxID = (empty($this->agentcodesMapping[$this->agentcodeDefault]) ? $this->defaultCashBoxID : $this->agentcodesMapping[$this->agentcodeDefault]);
  285. }
  286. return ($cashboxID);
  287. }
  288. /**
  289. * Returns user stargazer data by login
  290. *
  291. * @param string $userLogin existing stargazer login
  292. *
  293. * @return array
  294. */
  295. protected function getUserStargazerData($userLogin) {
  296. $userLogin = mysql_real_escape_string($userLogin);
  297. $query = "SELECT * from `users` WHERE `login`='" . $userLogin . "';";
  298. $result = simple_query($query);
  299. return ($result);
  300. }
  301. /**
  302. * Returns array of available or filtered by user login RealNames as login => realname
  303. *
  304. * @param string $userLogin
  305. *
  306. * @return array
  307. */
  308. protected function getUserRealnames($userLogin = '') {
  309. $result = array();
  310. $whereStr = (empty($userLogin) ? '' : " WHERE `login` = '" . $userLogin . "'");
  311. $query = "SELECT * from `realname`" . $whereStr;
  312. $realnames = simple_queryall($query);
  313. if (!empty($realnames)) {
  314. foreach ($realnames as $io => $each) {
  315. $result[$each['login']] = $each['realname'];
  316. }
  317. }
  318. $result = (empty($userLogin) ? $result : $result[$userLogin]);
  319. return($result);
  320. }
  321. /**
  322. * Returns array of available or filtered by user login addresses as login => address
  323. *
  324. * @param string $userLogin
  325. *
  326. * @return array|string
  327. */
  328. protected function getUserAddresses($userLogin = '') {
  329. $result = array();
  330. $whereStr = (empty($userLogin) ? '' : " WHERE `address`.`login` = '" . $userLogin . "'");
  331. $query = "
  332. SELECT `address`.`login`,`city`.`cityname`,`street`.`streetname`,`build`.`buildnum`,`apt`.`apt`
  333. FROM `address`
  334. INNER JOIN `apt` ON `address`.`aptid`= `apt`.`id`
  335. INNER JOIN `build` ON `apt`.`buildid`=`build`.`id`
  336. INNER JOIN `street` ON `build`.`streetid`=`street`.`id`
  337. INNER JOIN `city` ON `street`.`cityid`=`city`.`id`"
  338. . $whereStr;
  339. $addresses = simple_queryall($query);
  340. if (!empty($addresses)) {
  341. foreach ($addresses as $eachAddress) {
  342. // zero apt handle
  343. $apartment_filtered = ($eachAddress['apt'] == 0) ? '' : '/' . $eachAddress['apt'];
  344. if ($this->addressCityDisplay) {
  345. $result[$eachAddress['login']] = $eachAddress['cityname'] . ' ' . $eachAddress['streetname'] . ' ' . $eachAddress['buildnum'] . $apartment_filtered;
  346. } else {
  347. $result[$eachAddress['login']] = $eachAddress['streetname'] . ' ' . $eachAddress['buildnum'] . $apartment_filtered;
  348. }
  349. }
  350. }
  351. $result = (empty($userLogin) ? $result : $result[$userLogin]);
  352. return($result);
  353. }
  354. /**
  355. * Check transaction hash for duplicates by returning transaction data if it exists
  356. *
  357. * @param string $transactHash
  358. *
  359. * @return array
  360. */
  361. protected function getOPHashData($transactHash) {
  362. $result = array();
  363. if (!empty($transactHash)) {
  364. $transactData = simple_query("SELECT * from `op_transactions` WHERE `hash`='" . $transactHash . "'");
  365. if (!empty($transactData)) {
  366. $result = $transactData;
  367. }
  368. }
  369. return($result);
  370. }
  371. /**
  372. * Tries to get customer ID from existing transaction
  373. *
  374. * @param $transactID
  375. *
  376. * @return mixed|string
  377. */
  378. protected function getCustomerIDFromSavedTransact($transactID) {
  379. $transactData = $this->getTransactionData($transactID);
  380. $result = (empty($transactData['op_customer_id']) ? '' : $transactData['op_customer_id']);
  381. return ($result);
  382. }
  383. /**
  384. * Returns transaction data by $transactID
  385. *
  386. * @param string $transactID
  387. *
  388. * @return mixed|string
  389. */
  390. protected function getTransactionData($transactID) {
  391. $result = '';
  392. $tQuery = "SELECT * FROM `paymeuz_transactions` WHERE `transact_id` ='" . $transactID . "' ";
  393. $result = simple_query($tQuery);
  394. return($result);
  395. }
  396. /**
  397. * Returns transaction data by $transactID
  398. *
  399. * @param int $fromTimeStamp
  400. * @param int $toTimestamp
  401. *
  402. * @return mixed|string
  403. */
  404. protected function getTransactionsDataAll($fromTimeStamp = 0, $toTimestamp = 0) {
  405. $result = '';
  406. $whereStr = " WHERE ";
  407. $whereFrom = (empty($fromTimeStamp) ? "" : " `payme_transact_timestamp` >= " . $fromTimeStamp);
  408. $whereTo = (empty($toTimeStamp) ? "" : " `payme_transact_timestamp` <= " . $toTimeStamp);
  409. if (empty($whereFrom) and empty($whereTo)) {
  410. $whereStr = '';
  411. } elseif (!empty($whereFrom) and empty($whereTo)) {
  412. $whereStr.= $whereFrom;
  413. } elseif (!empty($whereFrom) and !empty($whereTo)) {
  414. $whereStr.= $whereFrom . " and " . $whereTo;
  415. } elseif (empty($whereFrom) and !empty($whereTo)) {
  416. $whereStr.= $whereTo;
  417. }
  418. $orderBy = (!empty($whereStr) ? " ORDER BY `payme_transact_timestamp` ASC " : "");
  419. $tQuery = "SELECT * FROM `paymeuz_transactions` " . $whereStr . $orderBy;
  420. $result = simple_queryall($tQuery);
  421. return($result);
  422. }
  423. /**
  424. * Checks if transaction already exists and returns it's state
  425. *
  426. * @param string $transactID
  427. *
  428. * @return bool
  429. */
  430. protected function checkTransactionExists($transactID) {
  431. $transactState = 0;
  432. $tQuery = "SELECT `transact_id`, `state` FROM `paymeuz_transactions` WHERE `transact_id` ='" . $transactID . "'";
  433. $result = simple_query($tQuery);
  434. $transactState = (empty($result['state']) ? 0 : $result['state']);
  435. return($transactState);
  436. }
  437. /**
  438. * Returns transaction expired status: true - expired, false - not expired
  439. *
  440. * @param $transactData
  441. *
  442. * @return bool
  443. */
  444. protected function checkTransactionExpired($transactData) {
  445. $curTimestamp = $this->getUnixTimestampMillisec();
  446. $transactCreateTimestamp = $transactData['create_timestamp'];
  447. $result = (($curTimestamp - $transactCreateTimestamp) > self::EXPIRE_TIMEOUT);
  448. return ($result);
  449. }
  450. /**
  451. * Saves transaction id to validate some possible duplicates
  452. *
  453. * @param string $transactID
  454. * @param int $transactTimestamp
  455. * @param int $transactAmount
  456. * @param string $opTransactID
  457. * @param string $opCustomerID
  458. * @param string $transactReceivers
  459. *
  460. * @return string
  461. */
  462. protected function saveTransaction($transactID, $opTransactID, $opCustomerID, $transactAmount, $transactTimestamp, $transactReceivers) {
  463. $transactState = 1;
  464. $opTransactDTCreate = curdatetime();
  465. $opTransactTimestamp = $this->getUnixTimestampMillisec();
  466. $tQuery = "INSERT INTO `paymeuz_transactions` (`date_create`, `transact_id`, `op_transact_id`, `op_customer_id`,
  467. `amount`, `state`, `payme_transact_timestamp`, `create_timestamp`, `receivers`)
  468. VALUES ('" . $opTransactDTCreate . "', '" . $transactID . "', '" . $opTransactID . "', '" . $opCustomerID
  469. . "', " . $transactAmount . ", " . $transactState . ", " . $transactTimestamp . ", " . $opTransactTimestamp
  470. . ", '" . $transactReceivers . "')";
  471. nr_query($tQuery);
  472. return($opTransactTimestamp);
  473. }
  474. /**
  475. * Marks saved earlier transaction as paid and returns $payTimeStamp
  476. *
  477. * @param string $transactID
  478. *
  479. * @return int
  480. */
  481. protected function markTransactAsPaid($transactID) {
  482. $payTimeStamp = $this->getUnixTimestampMillisec();
  483. $tQuery = "UPDATE `paymeuz_transactions` SET `state` = 2, `perform_timestamp` = " . $payTimeStamp . " WHERE `transact_id` ='" . $transactID . "'";
  484. nr_query($tQuery);
  485. return($payTimeStamp);
  486. }
  487. /**
  488. * Marks saved earlier and not yet paid transaction as canceled and returns $cancelTimeStamp
  489. *
  490. * @param string $transactID
  491. * @param string $cancelReason
  492. *
  493. * @return int
  494. */
  495. protected function markTransactAsCanceled($transactID, $cancelReason = '') {
  496. $cancelTimeStamp = $this->getUnixTimestampMillisec();
  497. $tQuery = "UPDATE `paymeuz_transactions` SET `state` = -1, `cancel_timestamp` = " . $cancelTimeStamp . ", `cancel_reason` = '" . $cancelReason . "' WHERE `transact_id` ='" . $transactID . "'";
  498. nr_query($tQuery);
  499. return ($cancelTimeStamp);
  500. }
  501. /**
  502. * Sets HTTP headers before reply
  503. */
  504. protected function setHTTPHeaders() {
  505. header('Content-Type: application/json; charset=UTF-8');
  506. }
  507. /**
  508. * Checks request auth and returns result
  509. * implementation is adopted from:
  510. * https://github.com/PaycomUZ/paycom-integration-php-template/blob/master/Paycom/Merchant.php
  511. *
  512. * @return bool
  513. */
  514. protected function checkAuth() {
  515. $result = false;
  516. $headers = getallheaders();
  517. if (!empty($headers['Authorization'])) {
  518. preg_match('/^\s*Basic\s+(\S+)\s*$/i', $headers['Authorization'], $matches);
  519. $decodedAuth = base64_decode($matches[1]);
  520. $result = ($decodedAuth == $this->paymeLogin . ":" . $this->paymePassword);
  521. }
  522. return ($result);
  523. }
  524. /**
  525. * "CheckPerformTransaction" request reply implementation
  526. */
  527. protected function replyPaymentAbilityCheck() {
  528. $reply = '';
  529. $userData = $this->getUserStargazerData($this->userLogin);
  530. if (empty($userData)) {
  531. $reply = $this->replyError('-31099');
  532. } else {
  533. $userBalance = $userData['Cash'];
  534. $userRealName = $this->getUserRealnames($this->userLogin);
  535. $userAddress = $this->getUserAddresses($this->userLogin);
  536. $reply = array('result' => array('allow' => true,
  537. 'additional' => array('account' => $this->customerID,
  538. 'full_name' => $userRealName,
  539. 'address' => $userAddress,
  540. 'balance' => $userBalance
  541. )
  542. ),
  543. 'id' => $this->paymeRequestID
  544. );
  545. $reply = json_encode($reply);
  546. }
  547. die($reply);
  548. }
  549. /**
  550. * "CreateTransaction" request reply implementation
  551. */
  552. protected function replyCreateTransact() {
  553. $reply = '';
  554. $transactState = $this->checkTransactionExists($this->paymeTransactID);
  555. if (empty($transactState)) {
  556. $transactTimestamp = $this->receivedJSON['params']['time'];
  557. $opTransactID = self::HASH_PREFIX . $this->paymeTransactID;
  558. if ($this->paymeCashBoxID == $this->defaultCashBoxID) {
  559. // we don't want to send money from a certain cashbox to itself
  560. $transactReceivers = null;
  561. } else {
  562. $transactReceivers = array(array('id' => $this->paymeCashBoxID, 'amount' => $this->paymentSum));
  563. }
  564. $opTransactTimestamp = $this->saveTransaction($this->paymeTransactID, $opTransactID, $this->customerID,
  565. $this->paymentSum, $transactTimestamp, json_encode($transactReceivers));
  566. $reply = array('result' => array('create_time' => $opTransactTimestamp,
  567. 'transaction' => $opTransactID,
  568. 'state' => 1,
  569. 'receivers' => $transactReceivers
  570. ),
  571. 'id' => $this->paymeRequestID
  572. );
  573. $reply = json_encode($reply);
  574. } else {
  575. if ($transactState == 1) {
  576. $transactData = $this->getTransactionData($this->paymeTransactID);
  577. if ($this->checkTransactionExpired($transactData)) {
  578. $this->markTransactAsCanceled($this->paymeTransactID, 4);
  579. $reply = $this->replyError('-31008');
  580. } else {
  581. $receivers = json_decode($transactData['receivers'], true);
  582. $reply = array('result' => array('create_time' => (int)$transactData['create_timestamp'],
  583. 'transaction' => $transactData['op_transact_id'],
  584. 'state' => (int)$transactData['state'],
  585. 'receivers' => ((empty($receivers) or trim($receivers) == 'null' or trim($receivers) == 'NULL')
  586. ? null : $receivers)
  587. ),
  588. 'id' => $this->paymeRequestID
  589. );
  590. $reply = json_encode($reply);
  591. }
  592. } else {
  593. $reply = $this->replyError('-31008');
  594. }
  595. }
  596. die($reply);
  597. }
  598. /**
  599. * "PerformTransaction" request reply implementation
  600. */
  601. protected function replyPerformTransact() {
  602. $reply = '';
  603. $transactData = $this->getTransactionData($this->paymeTransactID);
  604. if (empty($transactData)) {
  605. $reply = $this->replyError('-31003');
  606. } else {
  607. $transactState = $transactData['state'];
  608. if ($transactState != 1) {
  609. if ($transactState == 2) {
  610. $reply = array('result' => array('transaction' => $transactData['op_transact_id'],
  611. 'perform_time' => (int)$transactData['perform_timestamp'],
  612. 'state' => (int)$transactData['state'],
  613. ),
  614. 'id' => $this->paymeRequestID
  615. );
  616. $reply = json_encode($reply);
  617. } else {
  618. $reply = $this->replyError('-31008');
  619. }
  620. } elseif ($this->checkTransactionExpired($transactData)) {
  621. $this->markTransactAsCanceled($this->paymeTransactID, 4);
  622. $reply = $this->replyError('-31008');
  623. } else {
  624. $paymentSumm = round($transactData['amount'] / 100, 2);
  625. $opHash = $transactData['op_transact_id'];
  626. $opHashData = $this->getOPHashData($opHash);
  627. if (empty($opHashData)) {
  628. //push transaction to database
  629. op_TransactionAdd($opHash, $paymentSumm, $this->customerID, self::PAYSYS, 'PaymeUZ payment ID: ' . $this->paymeTransactID);
  630. op_ProcessHandlers();
  631. $payTimeStamp = $this->markTransactAsPaid($this->paymeTransactID);
  632. $reply = array('result' => array('transaction' => $opHash,
  633. 'perform_time' => $payTimeStamp,
  634. 'state' => 2,
  635. ),
  636. 'id' => $this->paymeRequestID
  637. );
  638. $reply = json_encode($reply);
  639. } else {
  640. $reply = $this->replyError('-31052');
  641. }
  642. }
  643. }
  644. die($reply);
  645. }
  646. /**
  647. * "CancelTransaction" request reply implementation
  648. * We can cancel only unpaid transactions with state "1"
  649. */
  650. protected function replyCancelTransact() {
  651. $reply = '';
  652. $transactData = $this->getTransactionData($this->paymeTransactID);
  653. if (empty($transactData)) {
  654. $reply = $this->replyError('-31003');
  655. } else {
  656. $transactState = $transactData['state'];
  657. $opTransactID = $transactData['op_transact_id'];
  658. if ($transactState != 1) {
  659. if ($transactState == 2) {
  660. $reply = $this->replyError('-31007');
  661. } else {
  662. $reply = array('result' => array('transaction' => $transactData['op_transact_id'],
  663. 'cancel_time' => (int)$transactData['cancel_timestamp'],
  664. 'state' => (int)$transactData['state'],
  665. ),
  666. 'id' => $this->paymeRequestID
  667. );
  668. $reply = json_encode($reply);
  669. }
  670. } else {
  671. $cancelReason = $this->receivedJSON['params']['reason'];
  672. $cancelTimeStamp = $this->markTransactAsCanceled($this->paymeTransactID, $cancelReason);
  673. $reply = array('result' => array('transaction' => $opTransactID,
  674. 'cancel_time' => $cancelTimeStamp,
  675. 'state' => -1,
  676. ),
  677. 'id' => $this->paymeRequestID
  678. );
  679. $reply = json_encode($reply);
  680. }
  681. }
  682. die($reply);
  683. }
  684. /**
  685. * "CheckTransaction" request reply implementation
  686. */
  687. protected function replyCheckTransact() {
  688. $reply = '';
  689. $transactData = $this->getTransactionData($this->paymeTransactID);
  690. if (empty($transactData)) {
  691. $reply = $this->replyError('-31003');
  692. } else {
  693. $opTransactID = $transactData['op_transact_id'];
  694. $transactState = $transactData['state'];
  695. $createTimeStamp = $transactData['create_timestamp'];
  696. $payTimeStamp = $transactData['perform_timestamp'];
  697. $cancelTimeStamp = $transactData['cancel_timestamp'];
  698. $cancelReason = (empty($transactData['cancel_reason']) ? null : (int)$transactData['cancel_reason']);
  699. $reply = array('result' => array('create_time' => (int)$createTimeStamp,
  700. 'perform_time' => (int)$payTimeStamp,
  701. 'cancel_time' => (int)$cancelTimeStamp,
  702. 'transaction' => $opTransactID,
  703. 'state' => (int)$transactState,
  704. 'reason' => $cancelReason,
  705. ),
  706. 'id' => $this->paymeRequestID
  707. );
  708. $reply = json_encode($reply);
  709. }
  710. die($reply);
  711. }
  712. /**
  713. * "GetStatement" request reply implementation
  714. *
  715. * @param int $dtFrom
  716. * @param int $dtTo
  717. */
  718. protected function replyStatement($dtFrom, $dtTo) {
  719. $reply = '';
  720. $transactions = array();
  721. $transactsData = $this->getTransactionsDataAll($dtFrom, $dtTo);
  722. if (!empty($transactsData)) {
  723. foreach ($transactsData as $eachRec => $eachData) {
  724. $receivers = json_decode($eachData['receivers'], true);
  725. $transactions[] = array('id' => $eachData['transact_id'],
  726. 'time' => (int)$eachData['payme_transact_timestamp'],
  727. 'amount' => $eachData['amount'],
  728. 'account' => array($this->customerIDFieldName => $this->customerID),
  729. 'create_time' => (int)$eachData['create_timestamp'],
  730. 'perform_time' => (int)$eachData['perform_timestamp'],
  731. 'cancel_time' => (int)$eachData['cancel_timestamp'],
  732. 'transaction' => (int)$eachData['op_transact_id'],
  733. 'state' => (int)$eachData['state'],
  734. 'reason' => (empty($eachData['cancel_reason']) ? null : (int)$eachData['cancel_reason']),
  735. 'receivers' => ((empty($receivers) or trim($receivers) == 'null' or trim($receivers) == 'NULL')
  736. ? null : $receivers)
  737. );
  738. }
  739. }
  740. $reply = array('result' => array('transactions' => $transactions));
  741. $reply = json_encode($reply);
  742. die($reply);
  743. }
  744. /**
  745. * Returns JSON-encoded error reply
  746. *
  747. * @param $errorCode
  748. *
  749. * @return false|string
  750. */
  751. protected function replyError($errorCode) {
  752. $reply = array('error' => array('code' => (int)$errorCode,
  753. 'message' => $this->errorCodes[$errorCode]
  754. ),
  755. 'id' => $this->paymeRequestID
  756. );
  757. $reply = json_encode($reply);
  758. return ($reply);
  759. }
  760. /**
  761. * Processes requests
  762. */
  763. protected function processRequest() {
  764. $this->opCustomersAll = op_CustomersGetAll();
  765. $this->paymeTransactID = (empty($this->receivedJSON['params']['id']) ? '' : $this->receivedJSON['params']['id']);
  766. $this->paymentSum = (empty($this->receivedJSON['params']['amount']) ? '' : $this->receivedJSON['params']['amount']);
  767. $statementFrom = (empty($this->receivedJSON['params']['from']) ? '' : $this->receivedJSON['params']['from']);
  768. $statementTo = (empty($this->receivedJSON['params']['to']) ? '' : $this->receivedJSON['params']['to']);
  769. $this->customerID = (empty($this->receivedJSON['params']['account'][$this->customerIDFieldName])
  770. ? $this->getCustomerIDFromSavedTransact($this->paymeTransactID)
  771. : $this->receivedJSON['params']['account'][$this->customerIDFieldName]);
  772. // some fields and values validations
  773. if (empty($this->opCustomersAll[$this->customerID])) {
  774. die($this->replyError('-31099'));
  775. }
  776. if (in_array($this->paymentMethod, array('CheckPerformTransaction', 'CreateTransaction'))
  777. and !is_numeric($this->paymentSum)
  778. ) {
  779. die($this->replyError('-31001'));
  780. }
  781. if ((in_array($this->paymentMethod, array('CreateTransaction', 'PerformTransaction', 'CancelTransaction', 'CheckTransaction'))
  782. and empty($this->paymeTransactID))
  783. or ($this->paymentMethod == 'GetStatement' and (empty($statementFrom) or empty($statementTo)))
  784. ) {
  785. die($this->replyError('-32600'));
  786. }
  787. $this->userLogin = $this->opCustomersAll[$this->customerID];
  788. $this->paymeCashBoxID = $this->defaultCashBoxID;
  789. if ($this->agentcodesON) {
  790. $this->paymeCashBoxID = $this->getCashBoxIDAgentAssigned($this->userLogin);
  791. if (empty($this->paymeCashBoxID)) {
  792. die($this->replyError('-31099'));
  793. }
  794. }
  795. // some fields and values validations
  796. // ('CheckPerformTransaction', 'CreateTransaction', 'PerformTransaction', 'CancelTransaction', 'CheckTransaction', 'GetStatement');
  797. switch ($this->paymentMethod) {
  798. case 'CheckPerformTransaction':
  799. $this->replyPaymentAbilityCheck();
  800. break;
  801. case 'CreateTransaction':
  802. $this->replyCreateTransact();
  803. break;
  804. case 'PerformTransaction':
  805. $this->replyPerformTransact();
  806. break;
  807. case 'CancelTransaction':
  808. $this->replyCancelTransact();
  809. break;
  810. case 'CheckTransaction':
  811. $this->replyCheckTransact();
  812. break;
  813. case 'GetStatement':
  814. $this->replyStatement($statementFrom, $statementTo);
  815. break;
  816. default:
  817. die($this->replyError('-32601'));
  818. }
  819. }
  820. /**
  821. * Listen to your heart when he's calling for you
  822. * Listen to your heart, there's nothing else you can do
  823. *
  824. * @return void
  825. */
  826. public function listen() {
  827. $rawRequest = file_get_contents('php://input');
  828. //parse_str($rawRequest, $this->receivedJSON);
  829. $this->receivedJSON = json_decode($rawRequest, true);
  830. $this->setHTTPHeaders();
  831. if (empty($this->receivedJSON)) {
  832. die($this->replyError('-32700'));
  833. } else {
  834. $this->paymeRequestID = (empty($this->receivedJSON['id']) ? null : $this->receivedJSON['id']);
  835. if (empty($this->paymeRequestID)) {
  836. die($this->replyError('-32600'));
  837. } elseif ($this->checkAuth()) {
  838. $this->paymentMethod = (empty($this->receivedJSON['method']) ? '' : $this->receivedJSON['method']);
  839. if (in_array($this->paymentMethod, $this->paymentMethodsAvailable)) {
  840. $this->processRequest();
  841. } else {
  842. die($this->replyError('-32601'));
  843. }
  844. } else {
  845. die($this->replyError('-32504'));
  846. }
  847. }
  848. }
  849. }
  850. $frontend = new PaymeUZ();
  851. $frontend->listen();