BxPaymentDb.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup Payment Payment
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. class BxPaymentDb extends BxBaseModPaymentDb
  12. {
  13. function __construct(&$oConfig)
  14. {
  15. parent::__construct($oConfig);
  16. }
  17. public function getModulesWithPayments()
  18. {
  19. $sQuery = $this->prepare("SELECT `name` FROM `" . $this->_sPrefix . "modules`");
  20. return $this->getColumn($sQuery);
  21. }
  22. /**
  23. * Payment details methods
  24. */
  25. public function getForm($sPaymentProvider = '')
  26. {
  27. $sWhereClause = '';
  28. if(!empty($sPaymentProvider))
  29. $sWhereClause .= $this->prepareAsString(" AND `tp`.`name`=?", $sPaymentProvider);
  30. $sQuery = "SELECT
  31. `tp`.`id` AS `provider_id`,
  32. `tp`.`name` AS `provider_name`,
  33. `tp`.`caption` AS `provider_caption`,
  34. `tp`.`description` AS `provider_description`,
  35. `tp`.`option_prefix` AS `provider_option_prefix`,
  36. `tp`.`for_owner_only` AS `provider_for_owner_only`,
  37. `tp`.`single_seller` AS `provider_single_seller`,
  38. `tpo`.`id` AS `id`,
  39. `tpo`.`name` AS `name`,
  40. `tpo`.`type` AS `type`,
  41. `tpo`.`value` AS `value`,
  42. `tpo`.`caption` AS `caption`,
  43. `tpo`.`description` AS `description`,
  44. `tpo`.`extra` AS `extra`,
  45. `tpo`.`check_type` AS `check_type`,
  46. `tpo`.`check_params` AS `check_params`,
  47. `tpo`.`check_error` AS `check_error`,
  48. `tpo`.`extended` AS `extended`
  49. FROM `" . $this->_sPrefix . "providers` AS `tp`
  50. LEFT JOIN `" . $this->_sPrefix . "providers_options` AS `tpo` ON `tp`.`id`=`tpo`.`provider_id`
  51. WHERE `tp`.`active`='1'" . $sWhereClause . "
  52. ORDER BY `tp`.`order` ASC, `tpo`.`order` ASC";
  53. return $this->getAll($sQuery);
  54. }
  55. public function getProviders($aParams = array())
  56. {
  57. $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
  58. $sWhereClause = $sOrderClause = "";
  59. if(!empty($aParams['type']))
  60. switch($aParams['type']) {
  61. case 'by_name':
  62. $aMethod['name'] = 'getRow';
  63. $aMethod['params'][1] = array(
  64. 'name' => $aParams['name']
  65. );
  66. $sWhereClause = " AND `tp`.`name`=:name";
  67. break;
  68. case 'for_single':
  69. $aMethod['name'] = 'getAllWithKey';
  70. $aMethod['params'][1] = 'name';
  71. $sWhereClause = " AND `tp`.`for_single`='1'";
  72. if(!empty($aParams['active']))
  73. $sWhereClause .= " AND `tp`.`active`='1'";
  74. if(isset($aParams['order']) && $aParams['order'] === true)
  75. $sOrderClause = "`tp`.`order` ASC";
  76. break;
  77. case 'for_recurring':
  78. $aMethod['name'] = 'getAllWithKey';
  79. $aMethod['params'][1] = 'name';
  80. $sWhereClause = " AND `tp`.`for_recurring`='1'";
  81. if(!empty($aParams['active']))
  82. $sWhereClause .= " AND `tp`.`active`='1'";
  83. if(isset($aParams['order']) && $aParams['order'] === true)
  84. $sOrderClause = "`tp`.`order` ASC";
  85. break;
  86. case 'all':
  87. $aMethod['name'] = 'getAllWithKey';
  88. $aMethod['params'][1] = 'name';
  89. if(!empty($aParams['active']))
  90. $sWhereClause = " AND `tp`.`active`='1'";
  91. if(isset($aParams['order']) && $aParams['order'] === true)
  92. $sOrderClause = "`tp`.`order` ASC";
  93. break;
  94. }
  95. if(!empty($sOrderClause))
  96. $sOrderClause = " ORDER BY " . $sOrderClause;
  97. $aMethod['params'][0] = "SELECT
  98. `tp`.`id` AS `id`,
  99. `tp`.`name` AS `name`,
  100. `tp`.`caption` AS `caption`,
  101. `tp`.`description` AS `description`,
  102. `tp`.`option_prefix` AS `option_prefix`,
  103. `tp`.`for_visitor` AS `for_visitor`,
  104. `tp`.`for_single` AS `for_single`,
  105. `tp`.`for_recurring` AS `for_recurring`,
  106. `tp`.`class_name` AS `class_name`,
  107. `tp`.`class_file` AS `class_file`
  108. FROM `" . $this->_sPrefix . "providers` AS `tp`
  109. WHERE 1" . $sWhereClause . $sOrderClause;
  110. return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
  111. }
  112. public function getOption($aParams)
  113. {
  114. $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
  115. $sSelectClause = "`tpo`.*";
  116. $sJoinClause = $sWhereClause = "";
  117. if(!empty($aParams['type']))
  118. switch($aParams['type']) {
  119. case 'by_pid_and_name':
  120. $aMethod['name'] = 'getRow';
  121. $aMethod['params'][1] = array(
  122. 'provider_id' => $aParams['provider_id'],
  123. 'name' => $aParams['name'],
  124. );
  125. $sWhereClause = " AND `tpo`.`provider_id`=:provider_id AND `tpo`.`name`=:name";
  126. break;
  127. }
  128. $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $this->_sPrefix . "providers_options` AS `tpo` " . $sJoinClause . " WHERE 1" . $sWhereClause;
  129. return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
  130. }
  131. public function getOptions($iUserId = BX_PAYMENT_EMPTY_ID, $iProviderId = 0)
  132. {
  133. $aBinding = [
  134. 'user_id' => $iUserId
  135. ];
  136. if($iUserId == BX_PAYMENT_EMPTY_ID && empty($iProviderId))
  137. return $this->getAll("SELECT `id`, `name`, `type` FROM `" . $this->_sPrefix . "providers_options`");
  138. $sWhereAddon = "";
  139. if(!empty($iProviderId)) {
  140. $aBinding['provider_id'] = $iProviderId;
  141. $sWhereAddon = " AND `tpo`.`provider_id`=:provider_id";
  142. }
  143. $sQuery = "SELECT
  144. `tpo`.`name` AS `name`,
  145. `tuv`.`value` AS `value`
  146. FROM `" . $this->_sPrefix . "providers_options` AS `tpo`
  147. LEFT JOIN `" . $this->_sPrefix . "user_values` AS `tuv` ON `tpo`.`id`=`tuv`.`option_id`
  148. WHERE 1" . $sWhereAddon . " AND `tuv`.`user_id`=:user_id ORDER BY `tpo`.`order`";
  149. return $this->getAllWithKey($sQuery, 'name', $aBinding);
  150. }
  151. public function getOptionsByProvider($mixedProvider)
  152. {
  153. $aBindings = [
  154. 'provider' => $mixedProvider
  155. ];
  156. $sJoinClause = $sWhereClause = '';
  157. if(!is_numeric($mixedProvider)) {
  158. $sJoinClause = " LEFT JOIN `" . $this->_sPrefix . "providers` AS `tp` ON `tpo`.`provider_id`=`tp`.`id`";
  159. $sWhereClause = " AND `tp`.`name`=:provider";
  160. }
  161. else
  162. $sWhereClause = " AND `tpo`.`provider_id`=:provider";
  163. return $this->getAll("SELECT `tpo`.`id`, `tpo`.`name`, `tpo`.`type` FROM `" . $this->_sPrefix . "providers_options` AS `tpo`" . $sJoinClause . " WHERE 1" . $sWhereClause, $aBindings);
  164. }
  165. public function updateOption($iUserId, $iOptionId, $sValue)
  166. {
  167. $sQuery = $this->prepare("REPLACE INTO `" . $this->_sPrefix . "user_values` SET `user_id`=?, `option_id`=?, `value`=?", $iUserId, $iOptionId, $sValue);
  168. return $this->query($sQuery);
  169. }
  170. public function getCurrencyExchangeRate($sCode)
  171. {
  172. return $this->getOne("SELECT `rate` FROM `" . $this->_sPrefix . "currencies` WHERE `code`=:code LIMIT 1", [
  173. 'code' => $sCode
  174. ]);
  175. }
  176. public function updateCurrencyExchangeRate($sCode, $fRate)
  177. {
  178. return $this->query("INSERT INTO `" . $this->_sPrefix . "currencies`(`code`, `rate`) VALUES(:code, :rate) ON DUPLICATE KEY UPDATE `rate`=:rate", [
  179. 'code' => $sCode,
  180. 'rate' => $fRate
  181. ]);
  182. }
  183. public function deleteCurrencyExchangeRate($sCode)
  184. {
  185. return $this->query("DELETE FROM `" . $this->_sPrefix . "currencies` WHERE `code`=:code", [
  186. 'code' => $sCode
  187. ]) !== false;
  188. }
  189. /**
  190. * Shopping cart methods.
  191. */
  192. public function getCartItems($iId)
  193. {
  194. $sQuery = $this->prepare("SELECT `items` FROM `" . $this->_sPrefix . "cart` WHERE `client_id`=? LIMIT 1", $iId);
  195. return $this->getOne($sQuery);
  196. }
  197. public function getCartContent($iId)
  198. {
  199. $aCart = $this->getRow("SELECT * FROM `" . $this->_sPrefix . "cart` WHERE `client_id`=:client_id LIMIT 1", [
  200. 'client_id' => $iId
  201. ]);
  202. if(empty($aCart) || !is_array($aCart))
  203. $aCart = ['items' => '', 'customs' => []];
  204. if(!empty($aCart['customs']))
  205. $aCart['customs'] = is_string($aCart['customs']) ? $this->_oConfig->s2a($aCart['customs'], false) : $aCart['customs'];
  206. else
  207. $aCart['customs'] = [];
  208. return $aCart;
  209. }
  210. public function setCartItems($iId, $sItems, $aCustoms = array())
  211. {
  212. $sItems = trim($sItems, ":");
  213. if(empty($sItems))
  214. return $this->query("DELETE FROM `" . $this->_sPrefix . "cart` WHERE `client_id`=:client_id LIMIT 1", array(
  215. 'client_id' => $iId
  216. ));
  217. return $this->query("REPLACE INTO `" . $this->_sPrefix . "cart` SET `client_id`=:client_id, `items`=:items, `customs`=:customs", array(
  218. 'client_id' => $iId,
  219. 'items' => $sItems,
  220. 'customs' => !empty($aCustoms) && is_array($aCustoms) ? $this->_oConfig->a2s($aCustoms, false) : ''
  221. ));
  222. }
  223. public function getVendorInfoProvidersSingle($iVendorId, $bActiveOnly = false)
  224. {
  225. $aParams = ['type' => 'for_single', 'order' => true];
  226. if($bActiveOnly)
  227. $aParams['active'] = true;
  228. return $this->getVendorInfoProviders($iVendorId, $aParams);
  229. }
  230. public function getVendorInfoProvidersRecurring($iVendorId, $bActiveOnly = false)
  231. {
  232. $aParams = ['type' => 'for_recurring', 'order' => true];
  233. if($bActiveOnly)
  234. $aParams['active'] = true;
  235. return $this->getVendorInfoProviders($iVendorId, $aParams);
  236. }
  237. public function getVendorInfoProviders($iVendorId, $aParams = [])
  238. {
  239. if(empty($aParams))
  240. $aParams = ['type' => 'all', 'order' => true];
  241. $aProviders = $this->getProviders($aParams);
  242. $aOptions = $this->getOptions($iVendorId);
  243. $aResult = [];
  244. foreach($aProviders as $sProvider => $aProvider) {
  245. if(!isset($aOptions[$aProvider['option_prefix'] . 'active']) || !in_array($aOptions[$aProvider['option_prefix'] . 'active']['value'], ['on', 1]))
  246. continue;
  247. if(isset($aOptions[$aProvider['option_prefix'] . 'hidden']) && in_array($aOptions[$aProvider['option_prefix'] . 'hidden']['value'], ['on', 1]))
  248. continue;
  249. foreach($aOptions as $sName => $aOption)
  250. if(strpos($sName, $aProvider['option_prefix']) !== false)
  251. $aProvider['options'][$sName] = $aOption;
  252. $aResult[$sProvider] = $aProvider;
  253. }
  254. return $aResult;
  255. }
  256. public function getAdminsIds()
  257. {
  258. $sQuery = "SELECT
  259. `tp`.`id` AS `id`,
  260. `tp`.`type` AS `type`
  261. FROM `sys_profiles` AS `tp`
  262. INNER JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` AND `ta`.`role`&" . BX_DOL_ROLE_ADMIN . "
  263. WHERE
  264. `tp`.`type`<>'system' AND `tp`.`status`='active'
  265. ORDER BY `tp`.`id` ASC";
  266. $aAdmins = $this->getAllWithKey($sQuery, 'id');
  267. foreach($aAdmins as $iId => $aAdmin)
  268. if(!BxDolService::call($aAdmin['type'], 'act_as_profile'))
  269. unset($aAdmins[$iId]);
  270. return array_keys($aAdmins);
  271. }
  272. /*
  273. * Pending Orders methods
  274. */
  275. public function getOrderPending($aParams)
  276. {
  277. $aMethod = array('name' => 'getRow', 'params' => array(0 => 'query'));
  278. $sWhereClause = $sLimitClause = '';
  279. switch($aParams['type']) {
  280. case 'id':
  281. $aMethod['params'][1] = array(
  282. 'id' => $aParams['id']
  283. );
  284. $sWhereClause = " AND `id`=:id";
  285. $sLimitClause = " LIMIT 1";
  286. break;
  287. case 'order':
  288. $aMethod['params'][1] = array(
  289. 'order' => $aParams['order']
  290. );
  291. $sWhereClause = " AND `order`=:order";
  292. $sLimitClause = " LIMIT 1";
  293. break;
  294. case 'mixed':
  295. $aMethod['name'] = 'getAll';
  296. $sWhereClause = ' AND ' . $this->arrayToSQL($aParams['conditions'], ' AND ');
  297. break;
  298. }
  299. $aMethod['params'][0] = "SELECT * FROM `" . $this->_sPrefix . "transactions_pending` WHERE 1 " . $sWhereClause . $sLimitClause;
  300. return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
  301. }
  302. public function insertOrderPending($iClientId, $sType, $sProvider, $aCartInfo, $aCustom = [])
  303. {
  304. $sItems = "";
  305. foreach($aCartInfo['items'] as $aItem) {
  306. $sItems .= $this->_oConfig->descriptorA2S(array($aItem['author_id'], $aItem['module_id'], $aItem['id'], $aItem['quantity'])) . ':';
  307. if(empty($aItem['addons']) || !is_array($aItem['addons']))
  308. continue;
  309. foreach($aItem['addons'] as $sAddon => $aAddon)
  310. $sItems .= $this->_oConfig->descriptorA2S(array($aAddon['author_id'], $aAddon['module_id'], $aAddon['id'], $aAddon['quantity'])) . ':';
  311. }
  312. $sCurrency = $aCartInfo['vendor_currency_code'];
  313. $sCurrencyDefault = $this->_oConfig->getDefaultCurrencyCode();
  314. $aData = ['cur_def' => $sCurrencyDefault];
  315. if(strcmp($sCurrency, $sCurrencyDefault) != 0)
  316. $aData['cur_rate'] = $this->getCurrencyExchangeRate($sCurrency);
  317. $aSet = [
  318. 'client_id' => $iClientId,
  319. 'seller_id' => $aCartInfo['vendor_id'],
  320. 'type' => $sType,
  321. 'provider' => $sProvider,
  322. 'items' => trim($sItems, ':'),
  323. 'customs' => !empty($aCustom) && is_array($aCustom) ? $this->_oConfig->a2s($aCustom, false) : '',
  324. 'amount' => $aCartInfo['items_price'],
  325. 'currency' => $sCurrency,
  326. 'data' => !empty($aData) ? serialize($aData) : ''
  327. ];
  328. return (int)$this->query("INSERT INTO `" . $this->_sPrefix . "transactions_pending` SET " . $this->arrayToSQL($aSet) . ", `date`=UNIX_TIMESTAMP()") > 0 ? $this->lastId() : 0;
  329. }
  330. public function updateOrderPending($iId, $aValues)
  331. {
  332. $sQuery = $this->prepare("UPDATE `" . $this->_sPrefix . "transactions_pending` SET " . $this->arrayToSQL($aValues) . " WHERE `id`=?", $iId);
  333. return (int)$this->query($sQuery) > 0;
  334. }
  335. public function deleteOrderPending($mixedId)
  336. {
  337. if(!is_array($mixedId))
  338. $mixedId = array($mixedId);
  339. return (int)$this->query("DELETE FROM `" . $this->_sPrefix . "transactions_pending` WHERE `id` IN (" . $this->implode_escape($mixedId) . ")") > 0;
  340. }
  341. /*
  342. * Processed Orders
  343. */
  344. public function getOrderProcessed($aParams)
  345. {
  346. $aMethod = array('name' => 'getRow', 'params' => array(0 => 'query'));
  347. $sSelectClause = "`tt`.`id`, `tt`.`license`, `ttp`.`type`, `tt`.`client_id`, `tt`.`seller_id`, `tt`.`author_id`, `tt`.`module_id`, `tt`.`item_id`, `tt`.`item_count`, `tt`.`amount`, `tt`.`date`, `ttp`.`order`, `ttp`.`error_msg`, `ttp`.`provider`";
  348. $sWhereClause = $sGroupClause = "";
  349. switch($aParams['type']) {
  350. case 'id':
  351. $aMethod['params'][1] = array(
  352. 'id' => $aParams['id']
  353. );
  354. $sWhereClause = " AND `tt`.`id`=:id";
  355. break;
  356. case 'new':
  357. $aMethod['name'] = 'getAll';
  358. $aMethod['params'][1] = array(
  359. 'seller_id' => $aParams['seller_id']
  360. );
  361. $sWhereClause = " AND `tt`.`seller_id`=:seller_id AND `tt`.`new`='1'";
  362. break;
  363. case 'pending_id':
  364. if(empty($aParams['with_key'])) {
  365. $aMethod['name'] = 'getAll';
  366. $aMethod['params'][1] = array(
  367. 'pending_id' => $aParams['pending_id']
  368. );
  369. }
  370. else {
  371. $aMethod['name'] = 'getAllWithKey';
  372. $aMethod['params'][1] = $aParams['with_key'];
  373. $aMethod['params'][2] = array(
  374. 'pending_id' => $aParams['pending_id']
  375. );
  376. }
  377. $sWhereClause = " AND `tt`.`pending_id`=:pending_id";
  378. break;
  379. case 'clients':
  380. $aMethod['name'] = 'getColumn';
  381. $aMethod['params'][1] = array(
  382. 'seller_id' => $aParams['seller_id']
  383. );
  384. $sSelectClause = "DISTINCT `tt`.`client_id`";
  385. $sWhereClause = " AND `tt`.`seller_id`=:seller_id";
  386. break;
  387. case 'authors':
  388. $aMethod['name'] = 'getColumn';
  389. $aMethod['params'][1] = array(
  390. 'seller_id' => $aParams['seller_id']
  391. );
  392. $sSelectClause = "DISTINCT `tt`.`author_id`";
  393. $sWhereClause = " AND `tt`.`seller_id`=:seller_id";
  394. break;
  395. case 'license':
  396. $aMethod['name'] = 'getAll';
  397. $aMethod['params'][1] = array(
  398. 'license' => $aParams['license']
  399. );
  400. $sWhereClause = " AND `tt`.`license`=:license";
  401. break;
  402. case 'income':
  403. $aMethod['name'] = 'getAll';
  404. $aMethod['params'][1] = array(
  405. 'period_start' => $aParams['period_start'],
  406. 'period_end' => $aParams['period_end']
  407. );
  408. $sSelectClause = '`tt`.`author_id` AS `id`, SUM(`tt`.`amount`) AS `amount`';
  409. $sWhereClause = ' AND `tt`.`date`>=:period_start AND `tt`.`date`<=:period_end';
  410. $sGroupClause = '`tt`.`author_id`';
  411. break;
  412. case 'mixed':
  413. $aMethod['name'] = 'getAll';
  414. $sWhereClause = ' AND ' . $this->arrayToSQL($aParams['conditions'], ' AND ');
  415. break;
  416. }
  417. if(!empty($sGroupClause))
  418. $sGroupClause = ' GROUP BY ' . $sGroupClause;
  419. $aMethod['params'][0] = "SELECT
  420. " . $sSelectClause . "
  421. FROM `" . $this->_sPrefix . "transactions` AS `tt`
  422. LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id`
  423. WHERE 1" . $sWhereClause . $sGroupClause;
  424. return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
  425. }
  426. public function insertOrderProcessed($aValues)
  427. {
  428. return $this->query("INSERT INTO `" . $this->_sPrefix . "transactions` SET " . $this->arrayToSQL($aValues) . ", `date`=UNIX_TIMESTAMP()");
  429. }
  430. public function updateOrderProcessed($iId, $aValues)
  431. {
  432. return $this->updateOrdersProcessed($aValues, array('id' => $iId));
  433. }
  434. public function updateOrdersProcessed($aValues, $aWhere)
  435. {
  436. if(empty($aValues) || !is_array($aValues) || empty($aWhere) || !is_array($aWhere))
  437. return false;
  438. return (int)$this->query("UPDATE `" . $this->_sPrefix . "transactions` SET " . $this->arrayToSQL($aValues) . " WHERE " . $this->arrayToSQL($aWhere, ' AND ')) > 0;
  439. }
  440. public function deleteOrderProcessed($mixedId)
  441. {
  442. if(!is_array($mixedId))
  443. $mixedId = array($mixedId);
  444. return (int)$this->query("DELETE FROM `" . $this->_sPrefix . "transactions` WHERE `id` IN (" . $this->implode_escape($mixedId) . ")") > 0;
  445. }
  446. public function getOrderHistory($aParams)
  447. {
  448. return $this->getOrderProcessed($aParams);
  449. }
  450. /**
  451. * Processed transactions with their order, a page at a time by transaction id: for
  452. * reading the whole history in steps (analytics imports past payments this way).
  453. */
  454. public function getTransactionsAfter($iAfterId, $iLimit)
  455. {
  456. return $this->getAll("SELECT `tt`.`id`, `tt`.`pending_id`, `tt`.`client_id`, `tt`.`seller_id`, `tt`.`author_id`, `tt`.`module_id`, `tt`.`item_id`, `tt`.`item_count`, `tt`.`amount`, `tt`.`currency`, `tt`.`date`, `ttp`.`type`, `ttp`.`provider`, `ttp`.`order`, `ttp`.`currency` AS `pending_currency`, `ttp`.`amount` AS `pending_amount`, `ttp`.`data`
  457. FROM `" . $this->_sPrefix . "transactions` AS `tt`
  458. LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id`
  459. WHERE `tt`.`id` > :after
  460. ORDER BY `tt`.`id` ASC
  461. LIMIT " . max(1, min(5000, (int)$iLimit)), [
  462. 'after' => (int)$iAfterId
  463. ]);
  464. }
  465. /**
  466. * Subscriptions, active or deleted, with their order, a page at a time by pending order id.
  467. */
  468. public function getSubscriptionsAfter($iAfterPendingId, $iLimit, $bDeleted = false)
  469. {
  470. $sSelectClause = "`ts`.`pending_id`, `ts`.`subscription_id`, `ts`.`period`, `ts`.`period_unit`, `ts`.`trial`, `ts`.`date_add`, `ts`.`date_next`, `ts`.`status`, `ttp`.`client_id`, `ttp`.`seller_id`, `ttp`.`type`, `ttp`.`provider`, `ttp`.`items`, `ttp`.`amount`, `ttp`.`currency`, `ttp`.`order`, `ttp`.`data`";
  471. if($bDeleted)
  472. $sSelectClause .= ", `ts`.`reason`, `ts`.`deleted`";
  473. return $this->getAll("SELECT " . $sSelectClause . "
  474. FROM `" . $this->_sPrefix . "subscriptions" . ($bDeleted ? "_deleted" : "") . "` AS `ts`
  475. LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `ts`.`pending_id`=`ttp`.`id`
  476. WHERE `ts`.`pending_id` > :after
  477. ORDER BY `ts`.`pending_id` ASC
  478. LIMIT " . max(1, min(5000, (int)$iLimit)), [
  479. 'after' => (int)$iAfterPendingId
  480. ]);
  481. }
  482. /*
  483. * Subscriptions methods
  484. */
  485. public function getOrderSubscription($aParams)
  486. {
  487. $aResult = $this->getOrderPending($aParams);
  488. if(($iPendingId = $aParams['id'] ?? 0)) {
  489. $aSubscription = $this->getSubscription(['type' => 'pending_id', 'pending_id' => $iPendingId]);
  490. if(!$aSubscription || !is_array($aSubscription))
  491. $aSubscription = $this->getSubscription(['type' => 'pending_id', 'pending_id' => $iPendingId, 'from_deleted' => true]);
  492. if($aSubscription && is_array($aSubscription))
  493. $aResult = array_merge($aResult, [
  494. 'customer_id' => $aSubscription['customer_id'],
  495. 'deleted' => $aSubscription['deleted'] ?? false,
  496. 'deleted_reason' => $aSubscription['reason'] ?? '',
  497. ]);
  498. }
  499. return $aResult;
  500. }
  501. public function getSubscription($aParams)
  502. {
  503. $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
  504. $sTable = $this->_sPrefix . 'subscriptions';
  505. if(($sK = 'from_deleted') && isset($aParams[$sK]) && $aParams[$sK])
  506. $sTable .= '_deleted';
  507. $sSelectClause = "`ts`.*";
  508. $sJoinClause = $sWhereClause = $sLimitClause = '';
  509. switch($aParams['type']) {
  510. case 'pending_id':
  511. $aMethod['name'] = 'getRow';
  512. $aMethod['params'][1] = array(
  513. 'pending_id' => $aParams['pending_id']
  514. );
  515. $sWhereClause = " AND `ts`.`pending_id`=:pending_id";
  516. $sLimitClause = " LIMIT 1";
  517. break;
  518. case 'mixed':
  519. $sWhereClause = " AND " . $this->arrayToSQL($aParams['conditions'], ' AND ');
  520. break;
  521. case 'mixed_ext':
  522. $sSelectClause .= ", `ttp`.`client_id`, `ttp`.`seller_id`, `ttp`.`type`, `ttp`.`provider`, `ttp`.`order`";
  523. $sJoinClause = "LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `ts`.`pending_id`=`ttp`.`id`";
  524. $sWhereClause = " AND " . $this->arrayToSQL($aParams['conditions'], ' AND ');
  525. break;
  526. case 'time_tracker':
  527. $aMethod['params'][1] = array(
  528. 'status_active' => $aParams['status_active'],
  529. 'status_trial' => $aParams['status_trial'],
  530. 'status_unpaid' => $aParams['status_unpaid'],
  531. 'pay_attempts_max' => $aParams['pay_attempts_max'],
  532. 'pay_attempts_interval' => $aParams['pay_attempts_interval']
  533. );
  534. $sSelectClause .= ", `ttp`.`client_id`, `ttp`.`seller_id`, `ttp`.`type`, `ttp`.`provider`, `ttp`.`amount`, `ttp`.`order`";
  535. $sJoinClause = "LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `ts`.`pending_id`=`ttp`.`id` INNER JOIN `" . $this->_sPrefix . "providers` AS `tp` ON `ttp`.`provider`=`tp`.`name` AND `tp`.`time_tracker`='1'";
  536. $sWhereClause = " AND `ts`.`date_next`<>0 AND `ts`.`date_next`<=UNIX_TIMESTAMP() AND (`ts`.`status`=:status_active OR `ts`.`status`=:status_trial OR (`ts`.`status`=:status_unpaid AND `ts`.`pay_attempts`<:pay_attempts_max AND DATE_ADD(FROM_UNIXTIME(`ts`.`date_next`), INTERVAL `ts`.`pay_attempts`*:pay_attempts_interval SECOND)<=NOW()))";
  537. break;
  538. }
  539. $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $sTable . "` AS `ts` " . $sJoinClause . " WHERE 1 " . $sWhereClause . $sLimitClause;
  540. return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
  541. }
  542. public function isSubscriptionByPending($iPending)
  543. {
  544. $aSubscription = $this->getSubscription(array(
  545. 'type' => 'pending_id',
  546. 'pending_id' => $iPending
  547. ));
  548. return !empty($aSubscription) && is_array($aSubscription);
  549. }
  550. public function insertSubscription($aValues)
  551. {
  552. if(empty($aValues) || !is_array($aValues))
  553. return false;
  554. $sSetClause = $this->arrayToSQL($aValues);
  555. if(empty($aValues['date_add']))
  556. $sSetClause .= ", `date_add`=UNIX_TIMESTAMP()";
  557. return (int)$this->query("INSERT INTO `" . $this->_sPrefix . "subscriptions` SET " . $sSetClause) > 0;
  558. }
  559. public function updateSubscription($aValues, $aWhere)
  560. {
  561. if(empty($aValues) || !is_array($aValues) || empty($aWhere) || !is_array($aWhere))
  562. return false;
  563. return (int)$this->query("UPDATE `" . $this->_sPrefix . "subscriptions` SET " . $this->arrayToSQL($aValues) . " WHERE " . $this->arrayToSQL($aWhere, ' AND ')) > 0;
  564. }
  565. public function deleteSubscription($mixedId, $sReason)
  566. {
  567. if(!is_array($mixedId))
  568. $mixedId = array($mixedId);
  569. //--- Move to deleted subscriptions table.
  570. $sQuery = "INSERT IGNORE INTO `" . $this->_sPrefix . "subscriptions_deleted` SELECT *, :reason AS `reason`, UNIX_TIMESTAMP() AS `deleted` FROM `" . $this->_sPrefix . "subscriptions` WHERE `id` IN (" . $this->implode_escape($mixedId) . ")";
  571. $this->query($sQuery, array(
  572. 'reason' => $sReason
  573. ));
  574. return (int)$this->query("DELETE FROM `" . $this->_sPrefix . "subscriptions` WHERE `id` IN (" . $this->implode_escape($mixedId) . ")") > 0;
  575. }
  576. //--- Order Administration ---//
  577. public function onProfileDelete($iId)
  578. {
  579. $sQuery = $this->prepare("DELETE FROM `" . $this->_sPrefix . "cart` WHERE `client_id`=?", $iId);
  580. $this->query($sQuery);
  581. $sQuery = $this->prepare("DELETE FROM `" . $this->_sPrefix . "user_values` WHERE `user_id`=?", $iId);
  582. $this->query($sQuery);
  583. }
  584. /*
  585. * Commissions methods
  586. */
  587. public function getCommissions($aParams = array())
  588. {
  589. $CNF = &$this->_oConfig->CNF;
  590. $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
  591. $sSelectClause = "`tc`.*";
  592. $sWhereClause = $sOrderClause = "";
  593. if(!empty($aParams['type']))
  594. switch($aParams['type']) {
  595. case 'max_order':
  596. $aMethod['name'] = 'getOne';
  597. $aMethod['params'][1] = array();
  598. $sSelectClause = "IFNULL(MAX(`tc`.`order`), 0)";
  599. break;
  600. case 'id':
  601. $aMethod['name'] = 'getRow';
  602. $aMethod['params'][1] = array(
  603. 'id' => $aParams['id']
  604. );
  605. $sWhereClause = " AND `tc`.`id`=:id";
  606. break;
  607. case 'acl_id':
  608. $aMethod['name'] = 'getAll';
  609. $aMethod['params'][1] = array(
  610. 'acl_id' => $aParams['acl_id']
  611. );
  612. $sWhereClause = " AND (`tc`.`acl_id`=:acl_id OR `tc`.`acl_id`=0)";
  613. $sOrderClause = "`tc`.`order` ASC";
  614. break;
  615. case 'all':
  616. if(!empty($aParams['active']))
  617. $sWhereClause = " AND `tc`.`active`='1'";
  618. break;
  619. }
  620. if(!empty($sOrderClause))
  621. $sOrderClause = " ORDER BY " . $sOrderClause;
  622. $aMethod['params'][0] = "SELECT " . $sSelectClause . "
  623. FROM `" . $CNF['TABLE_COMMISSIONS'] . "` AS `tc`
  624. WHERE 1" . $sWhereClause . $sOrderClause;
  625. return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
  626. }
  627. public function getInvoices($aParams = array())
  628. {
  629. $CNF = &$this->_oConfig->CNF;
  630. $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
  631. $sSelectClause = "`ti`.*";
  632. $sWhereClause = "";
  633. if(!empty($aParams['type']))
  634. switch($aParams['type']) {
  635. case 'index':
  636. $aMethod['name'] = 'getOne';
  637. $aMethod['params'][1] = array(
  638. 'commissionaire_id' => $aParams['commissionaire_id'],
  639. 'committent_id' => $aParams['committent_id'],
  640. );
  641. $sSelectClause = "COUNT(`ti`.`id`) + 1";
  642. $sWhereClause = " AND `ti`.`commissionaire_id`=:commissionaire_id AND `ti`.`committent_id`=:committent_id";
  643. break;
  644. case 'id':
  645. $aMethod['name'] = 'getRow';
  646. $aMethod['params'][1] = array(
  647. 'id' => $aParams['id']
  648. );
  649. $sWhereClause = " AND `ti`.`id`=:id";
  650. break;
  651. case 'commissionaire_id':
  652. $aMethod['params'][1] = array(
  653. 'commissionaire_id' => $aParams['commissionaire_id']
  654. );
  655. $sWhereClause = " AND `ti`.`commissionaire_id`=:commissionaire_id";
  656. break;
  657. case 'committent_id':
  658. $aMethod['params'][1] = array(
  659. 'committent_id' => $aParams['committent_id']
  660. );
  661. $sWhereClause = " AND `ti`.`committent_id`=:committent_id";
  662. if(!empty($aParams['period_start'])) {
  663. $aMethod['params'][1]['period_start'] = (int)$aParams['period_start'];
  664. $sWhereClause .= " AND `ti`.`period_start`=:period_start";
  665. }
  666. if(!empty($aParams['period_end'])) {
  667. $aMethod['params'][1]['period_end'] = (int)$aParams['period_end'];
  668. $sWhereClause .= " AND `ti`.`period_end`=:period_end";
  669. }
  670. break;
  671. case 'expiring':
  672. $aMethod['params'][1] = array(
  673. 'notify_days' => $this->_oConfig->getInvoiceExpirationNotify(),
  674. 'status' => BX_PAYMENT_INV_STATUS_UNPAID
  675. );
  676. $sWhereClause = " AND DATE_SUB(FROM_UNIXTIME(`ti`.`date_due`), INTERVAL :notify_days DAY) < NOW() AND DATE_SUB(FROM_UNIXTIME(`ti`.`date_due`), INTERVAL (:notify_days - 1) DAY) > NOW() AND `ti`.`status`=:status";
  677. break;
  678. case 'overdue':
  679. $aMethod['params'][1] = array(
  680. 'status_unpaid' => BX_PAYMENT_INV_STATUS_UNPAID,
  681. 'status_overdue' => BX_PAYMENT_INV_STATUS_OVERDUE
  682. );
  683. $sWhereClause = " AND `ti`.`date_due` < UNIX_TIMESTAMP() AND (`ti`.`status`=:status_unpaid OR (`ti`.`status`=:status_overdue AND `ti`.`ntf_due`='0'))";
  684. break;
  685. case 'status':
  686. $aMethod['params'][1] = array(
  687. 'status' => $aParams['status']
  688. );
  689. $sWhereClause = " AND `ti`.`status`=:status";
  690. if(isset($aParams['count']) && $aParams['count'] === true) {
  691. $sSelectClause = "COUNT(`ti`.`id`)";
  692. $aMethod['name'] = 'getOne';
  693. }
  694. if(isset($aParams['committent_id'])) {
  695. $aMethod['params'][1]['committent_id'] = $aParams['committent_id'];
  696. $sWhereClause .= " AND `ti`.`committent_id`=:committent_id";
  697. }
  698. break;
  699. case 'all_count':
  700. $sSelectClause = "COUNT(`ti`.`id`)";
  701. $aMethod['name'] = 'getOne';
  702. break;
  703. case 'all':
  704. break;
  705. }
  706. $aMethod['params'][0] = "SELECT " . $sSelectClause . "
  707. FROM `" . $CNF['TABLE_INVOICES'] . "` AS `ti`
  708. WHERE 1" . $sWhereClause;
  709. return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
  710. }
  711. /**
  712. * Analytics: commission invoices issued in a period (`to` exclusive) by status and currency.
  713. */
  714. public function getAnalyticsInvoices($iFrom, $iTo)
  715. {
  716. return $this->getAll("SELECT `status`, `currency`, COUNT(*) AS `count`, SUM(`amount`) AS `amount` FROM `" . $this->_oConfig->CNF['TABLE_INVOICES'] . "` WHERE `date_issue` >= :from AND `date_issue` < :to GROUP BY `status`, `currency`", [
  717. 'from' => (int)$iFrom,
  718. 'to' => (int)$iTo
  719. ]);
  720. }
  721. public function insertInvoice($aValues)
  722. {
  723. $CNF = &$this->_oConfig->CNF;
  724. return $this->query("INSERT INTO `" . $CNF['TABLE_INVOICES'] . "` SET " . $this->arrayToSQL($aValues));
  725. }
  726. public function updateInvoice($mixedId, $aValues)
  727. {
  728. $CNF = &$this->_oConfig->CNF;
  729. if(!is_array($mixedId))
  730. $mixedId = array($mixedId);
  731. return (int)$this->query("UPDATE `" . $CNF['TABLE_INVOICES'] . "` SET " . $this->arrayToSQL($aValues) . " WHERE `id` IN (" . $this->implode_escape($mixedId) . ')') > 0;
  732. }
  733. public function deleteInvoice($mixedId)
  734. {
  735. $CNF = &$this->_oConfig->CNF;
  736. if(!is_array($mixedId))
  737. $mixedId = array($mixedId);
  738. return (int)$this->query("DELETE FROM `" . $CNF['TABLE_INVOICES'] . "` WHERE `id` IN (" . $this->implode_escape($mixedId) . ")") > 0;
  739. }
  740. /**
  741. * For Stripe integration only
  742. */
  743. public function getStrpPaymentPending($sSubscriptionId)
  744. {
  745. $aResult = $this->getRow("SELECT * FROM `" . $this->_sPrefix . "stripe_payments_pending` WHERE `subscription_id`=:subscription_id", [
  746. 'subscription_id' => $sSubscriptionId
  747. ]);
  748. if(!empty($aResult) && is_array($aResult))
  749. $this->deleteStrpPaymentPending($sSubscriptionId);
  750. return $aResult;
  751. }
  752. public function insertStrpPaymentPending($sSubscriptionId, $fAmount, $sCurrency)
  753. {
  754. return $this->query("INSERT IGNORE INTO `" . $this->_sPrefix . "stripe_payments_pending` SET " . $this->arrayToSQL([
  755. 'subscription_id' => $sSubscriptionId,
  756. 'amount' => $fAmount,
  757. 'currency' => $sCurrency
  758. ])) !== false;
  759. }
  760. public function deleteStrpPaymentPending($sSubscriptionId)
  761. {
  762. return $this->query("DELETE FROM `" . $this->_sPrefix . "stripe_payments_pending` WHERE `subscription_id`=:subscription_id", [
  763. 'subscription_id' => $sSubscriptionId
  764. ]) !== false;
  765. }
  766. }
  767. /** @} */