BxCreditsModule.php 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  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 Credits Credits
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. define('BX_CREDITS_TRANSFER_TYPE_PURCHASE', 'purchase');
  12. define('BX_CREDITS_TRANSFER_TYPE_CHECKOUT', 'checkout');
  13. define('BX_CREDITS_TRANSFER_TYPE_CANCELLATION', 'cancellation');
  14. define('BX_CREDITS_TRANSFER_TYPE_GRANT', 'grant');
  15. define('BX_CREDITS_TRANSFER_TYPE_SEND', 'send');
  16. define('BX_CREDITS_TRANSFER_TYPE_WITHDRAW', 'withdraw');
  17. define('BX_CREDITS_TRANSFER_TYPE_SERVICE', 'service');
  18. define('BX_CREDITS_DIRECTION_IN', 'in');
  19. define('BX_CREDITS_DIRECTION_OUT', 'out');
  20. define('BX_CREDITS_ORDER_TYPE_SINGLE', 'single'); //--- one-time payment
  21. define('BX_CREDITS_ORDER_TYPE_RECURRING', 'recurring'); //--- recurring payment (subscription)
  22. class BxCreditsModule extends BxBaseModGeneralModule
  23. {
  24. function __construct(&$aModule)
  25. {
  26. parent::__construct($aModule);
  27. }
  28. public function actionGetProfiles()
  29. {
  30. $sTerm = bx_get('term');
  31. $a = BxDolService::call('system', 'profiles_search', array($sTerm), 'TemplServiceProfiles');
  32. echoJson($a);
  33. }
  34. public function actionCheckBundleName()
  35. {
  36. $CNF = &$this->_oConfig->CNF;
  37. $sName = bx_process_input(bx_get('name'));
  38. if(empty($sName))
  39. return echoJson(array());
  40. $sResult = '';
  41. $iId = (int)bx_get('id');
  42. if(!empty($iId)) {
  43. $aBundle = $this->_oDb->getBundle(array('type' => 'id', 'id' => $iId));
  44. if(strcmp($sName, $aBundle[$CNF['FIELD_NAME']]) == 0)
  45. $sResult = $sName;
  46. }
  47. echoJson(array(
  48. 'name' => !empty($sResult) ? $sResult : $this->_oConfig->getBundleName($sName)
  49. ));
  50. }
  51. public function actionCheckout()
  52. {
  53. $iBuyerId = bx_get_logged_profile_id();
  54. if(!$iBuyerId)
  55. return echoJson(['code' => 1, 'msg' => _t('_bx_credits_err_unknown_buyer')]);
  56. $sErrIncorrectData = _t('_bx_credits_err_incorrect_data');
  57. $aData = $this->_oConfig->getCheckoutData();
  58. if(empty($aData) || !is_array($aData))
  59. return echoJson(['code' => 2, 'msg' => $sErrIncorrectData]);
  60. $fConversion = $this->_oConfig->getConversionRateUse();
  61. $iPrecision = $this->_oConfig->getPrecision();
  62. $iSellerId = (int)$aData['seller'];
  63. $fAmount = (float)$aData['amount'];
  64. $aCustomData = $this->_oConfig->deconstructCheckoutCustomData($aData['custom']);
  65. $oPayments = BxDolPayments::getInstance();
  66. $aPpOrders = $oPayments->getPendingOrdersInfo(['id' => (int)$aCustomData[1]]);
  67. if(!empty($aPpOrders) && is_array($aPpOrders)) {
  68. $aPpOrder = reset($aPpOrders);
  69. if(empty($aPpOrder) || !is_array($aPpOrder) || (int)$aPpOrder['seller_id'] != $iSellerId)
  70. return echoJson(['code' => 3, 'msg' => $sErrIncorrectData]);
  71. $fAmountM = (float)$aPpOrder['amount'];
  72. $sCurrencyDefault = $oPayments->getCurrencyCode();
  73. if(strcmp($aPpOrder['currency'], $sCurrencyDefault) != 0)
  74. $fAmountM = $oPayments->convert ($fAmountM, $aPpOrder['currency'], $sCurrencyDefault);
  75. if($this->_oConfig->convertM2C($fAmountM, $fConversion, $iPrecision) != $fAmount)
  76. return echoJson(['code' => 3, 'msg' => $sErrIncorrectData]);
  77. }
  78. $fBalance = (float)$this->_oDb->getProfile(['type' => 'balance', 'id' => $iBuyerId]);
  79. if($fAmount > $fBalance)
  80. return echoJson(['code' => 4, 'msg' => _t('_bx_credits_err_low_balance')]);
  81. $sOrder = $this->_oConfig->getOrder();
  82. $sInfo = '_bx_credits_txt_history_info_checkout';
  83. $sData = serialize([
  84. 'conversion' => $fConversion,
  85. 'precision' => $iPrecision
  86. ]);
  87. $this->updateProfileBalance($iBuyerId, $iSellerId, -$fAmount, BX_CREDITS_TRANSFER_TYPE_CHECKOUT, $sOrder, $sInfo, $sData);
  88. $this->updateProfileBalance($iSellerId, $iBuyerId, $fAmount, BX_CREDITS_TRANSFER_TYPE_CHECKOUT, $sOrder, $sInfo, $sData);
  89. /**
  90. * @hooks
  91. * @hookdef hook-bx_credits-checkout 'bx_credits', 'checkout' - hook after checkout with creadits was performed
  92. * - $unit_name - equals `bx_credits`
  93. * - $action - equals `checkout`
  94. * - $object_id - not used
  95. * - $sender_id - not used
  96. * - $extra_params - array of additional params with the following array keys:
  97. * - `seller` - [int] seller profile id
  98. * - `buyer` - [int] buyer profile id
  99. * - `amount` - [float] amount was paid
  100. * - `order` - [string] order number for processed checkout action
  101. * @hook @ref hook-bx_credits-checkout
  102. */
  103. bx_alert($this->getName(), 'checkout', 0, false, [
  104. 'seller' => $iSellerId,
  105. 'buyer' => $iBuyerId,
  106. 'amount' => $fAmount,
  107. 'order' => $sOrder
  108. ]);
  109. return echoJson([
  110. 'code' => 0,
  111. 'redirect' => bx_append_url_params($aData['return_data_url'], ['o' => $sOrder, 'c' => $aData['custom']])
  112. ]);
  113. }
  114. public function actionSubscribe()
  115. {
  116. $iBuyerId = bx_get_logged_profile_id();
  117. if(!$iBuyerId)
  118. return echoJson(array('code' => 1, 'msg' => _t('_bx_credits_err_unknown_buyer')));
  119. $aData = $this->_oConfig->getCheckoutData();
  120. if(empty($aData) || !is_array($aData))
  121. return echoJson(array('code' => 2, 'msg' => _t('_bx_credits_err_incorrect_data')));
  122. $iSellerId = (int)$aData['seller'];
  123. $fAmount = (float)$aData['amount'];
  124. $fBalance = (float)$this->_oDb->getProfile(array('type' => 'balance', 'id' => $iBuyerId));
  125. if($fAmount > $fBalance)
  126. return echoJson(array('code' => 3, 'msg' => _t('_bx_credits_err_low_balance')));
  127. $sUnique = $this->_oConfig->getOrder(9);
  128. $sCustomer = 'bx_cus_' . $sUnique;
  129. $sSubscription = 'bx_sub_' . $sUnique;
  130. /**
  131. * @hooks
  132. * @hookdef hook-bx_credits-subscribe 'bx_credits', 'subscribe' - hook after subscription was registered
  133. * - $unit_name - equals `bx_credits`
  134. * - $action - equals `subscribe`
  135. * - $object_id - not used
  136. * - $sender_id - not used
  137. * - $extra_params - array of additional params with the following array keys:
  138. * - `seller` - [int] seller profile id
  139. * - `buyer` - [int] buyer profile id
  140. * - `amount` - [float] amount was paid
  141. * - `trial` - [boolean] if trial period is available or not
  142. * - `customer` - [string] unique customer id for newly registered subscription
  143. * - `subscription` - [string] unique subscription id
  144. * @hook @ref hook-bx_credits-subscribe
  145. */
  146. bx_alert($this->getName(), 'subscribe', 0, false, [
  147. 'seller' => $iSellerId,
  148. 'buyer' => $iBuyerId,
  149. 'amount' => $fAmount,
  150. 'trial' => $aData['trial'],
  151. 'customer' => $sCustomer,
  152. 'subscription' => $sSubscription
  153. ]);
  154. return echoJson([
  155. 'code' => 0,
  156. 'redirect' => bx_append_url_params($aData['return_data_url'], [
  157. 'cs' => $sCustomer,
  158. 'sb' => $sSubscription,
  159. 'tr' => $aData['trial'],
  160. 'c' => $aData['custom']
  161. ])
  162. ]);
  163. }
  164. public function serviceGetSafeServices()
  165. {
  166. return array (
  167. 'GetBlockBundles' => '',
  168. 'GetBlockOrders' => '',
  169. 'GetBlockHistory' => '',
  170. );
  171. }
  172. public function serviceGetCheckoutUrl()
  173. {
  174. return $this->_oConfig->getCheckoutUrl();
  175. }
  176. public function serviceConvertMoneyToCredits($fMoney, $fRate = false, $iPrecision = false)
  177. {
  178. return $this->_oConfig->convertM2C($fMoney, $fRate, $iPrecision);
  179. }
  180. public function serviceConvertCreditsToMoney($fCredits, $fRate = false, $iPrecision = false)
  181. {
  182. return $this->_oConfig->convertC2M($fCredits, $fRate, $iPrecision);
  183. }
  184. public function serviceValidateCheckout($iSeller, $iBuyer, $fAmount, $sOrder)
  185. {
  186. $fAmount = (float)$fAmount;
  187. $aOut = $this->_oDb->getHistory(array('type' => 'row_by', 'by' => array('first_pid' => $iBuyer, 'direction' => BX_CREDITS_DIRECTION_OUT, 'order' => $sOrder)));
  188. if(empty($aOut) || !is_array($aOut) || (int)$aOut['second_pid'] != (int)$iSeller)
  189. return false;
  190. $aDataOut = unserialize($aOut['data']);
  191. if($this->_oConfig->convertM2C($fAmount, $aDataOut['conversion'], $aDataOut['precision']) != (float)$aOut['amount'])
  192. return false;
  193. $aIn = $this->_oDb->getHistory(array('type' => 'row_by', 'by' => array('first_pid' => $iSeller, 'direction' => BX_CREDITS_DIRECTION_IN, 'order' => $sOrder)));
  194. if(empty($aIn) || !is_array($aIn) || (int)$aIn['second_pid'] != (int)$iBuyer)
  195. return false;
  196. $aDataIn = unserialize($aIn['data']);
  197. if($this->_oConfig->convertM2C($fAmount, $aDataIn['conversion'], $aDataIn['precision']) != (float)$aIn['amount'])
  198. return false;
  199. return true;
  200. }
  201. public function serviceGetBlockCheckout()
  202. {
  203. $oBuyer = BxDolProfile::getInstance();
  204. if(!$oBuyer)
  205. return MsgBox(_t('_bx_credits_err_unknown_buyer'));
  206. $iSeller = (int)bx_get('seller');
  207. $oSeller = BxDolProfile::getInstance($iSeller);
  208. if(!$oSeller)
  209. return MsgBox(_t('_bx_credits_err_unknown_seller'));
  210. $fAmountM = $fAmountMInDef = (float)bx_get('amount');
  211. $oPayments = BxDolPayments::getInstance();
  212. $sCurrency = bx_process_input(bx_get('currency_code'));
  213. $sCurrencyDefault = $oPayments->getCurrencyCode();
  214. if(strcmp($sCurrency, $sCurrencyDefault) != 0)
  215. $fAmountMInDef = $oPayments->convert($fAmountM, $sCurrency, $sCurrencyDefault);
  216. $fAmountC = $this->_oConfig->convertM2C($fAmountMInDef);
  217. $aData = [
  218. 'seller' => $iSeller,
  219. 'currency' => [
  220. 'code' => $sCurrency,
  221. 'sign' => bx_process_input(bx_get('currency_sign')),
  222. ],
  223. 'amountm' => $fAmountM,
  224. 'amountc' => $fAmountC
  225. ];
  226. $iItemsCount = (int)bx_get('items_count');
  227. for($i = 0; $i < $iItemsCount; $i++) {
  228. $aData['items'][] = [
  229. 'title' => bx_process_input(bx_get('item_title_' . $i)),
  230. 'quantity' => (int)bx_get('item_quantity_' . $i)
  231. ];
  232. }
  233. $this->_oConfig->setCheckoutData([
  234. 'seller' => $iSeller,
  235. 'amount' => $fAmountC,
  236. 'custom' => bx_process_input(bx_get('custom')),
  237. 'return_data_url' => bx_process_input(bx_get('return_data_url')),
  238. ]);
  239. return $this->_oTemplate->getBlockCheckout($oBuyer, $oSeller, $aData);
  240. }
  241. public function serviceGetPopupSubscribe($aData)
  242. {
  243. $oBuyer = BxDolProfile::getInstance();
  244. if(!$oBuyer)
  245. return ['msg' => _t('_bx_credits_err_unknown_buyer')];
  246. $iSeller = (int)$aData['seller'];
  247. $oSeller = BxDolProfile::getInstance($iSeller);
  248. if(!$oSeller)
  249. return ['msg' => _t('_bx_credits_err_unknown_seller')];
  250. $fAmountM = $fAmountMInDef = (float)$aData['amount'];
  251. $oPayments = BxDolPayments::getInstance();
  252. $sCurrency = $aData['currency_code'];
  253. $sCurrencyDefault = $oPayments->getCurrencyCode();
  254. if(strcmp($sCurrency, $sCurrencyDefault) != 0)
  255. $fAmountMInDef = $oPayments->convert($fAmountM, $sCurrency, $sCurrencyDefault);
  256. $fAmountC = $this->_oConfig->convertM2C($fAmountMInDef);
  257. $aData = array_merge($aData, [
  258. 'currency' => [
  259. 'code' => $sCurrency,
  260. 'sign' => $aData['currency_sign'],
  261. ],
  262. 'amountm' => $fAmountM,
  263. 'amountc' => $fAmountC
  264. ]);
  265. $this->_oConfig->setCheckoutData([
  266. 'seller' => $iSeller,
  267. 'amount' => $fAmountC,
  268. 'trial' => (int)$aData['trial'] > 0,
  269. 'custom' => bx_process_input($aData['custom']),
  270. 'return_data_url' => bx_process_input($aData['return_data_url']),
  271. ]);
  272. return $this->_oTemplate->getPopupSubscribe($oBuyer, $oSeller, $aData);
  273. }
  274. public function serviceGetMenuItemAddonAmount()
  275. {
  276. $iProfileId = bx_get_logged_profile_id();
  277. if(!$iProfileId)
  278. return '';
  279. return array(
  280. 'unit' => $this->_oTemplate->getUnit(),
  281. 'value' => $this->convertC2S($this->_oDb->getProfile(array('type' => 'balance', 'id' => $iProfileId)), false)
  282. );
  283. }
  284. /**
  285. * @page service Service Calls
  286. * @section bx_credits Credits
  287. * @subsection bx_credits-page_blocks Page Blocks
  288. * @subsubsection bx_credits-get_block_bundles get_block_bundles
  289. *
  290. * @code bx_srv('bx_credits', 'get_block_bundles'); @endcode
  291. *
  292. * Get page block with bundles
  293. *
  294. * @see BxCreditsModule::serviceGetBlockBundles
  295. */
  296. /**
  297. * @ref bx_credits-get_block_bundles "get_block_bundles"
  298. */
  299. public function serviceGetBlockBundles()
  300. {
  301. return $this->_oTemplate->getBlockBundles();
  302. }
  303. /**
  304. * @page service Service Calls
  305. * @section bx_credits Credits
  306. * @subsection bx_credits-page_blocks Page Blocks
  307. * @subsubsection bx_credits-get_block_orders_note get_block_orders_note
  308. *
  309. * @code bx_srv('bx_credits', 'get_block_orders_note', [...]); @endcode
  310. *
  311. * Get page block with a notice for orders usage.
  312. *
  313. * @return HTML string with block content to display on the site.
  314. *
  315. * @see BxCreditsModule::serviceBlockLicensesNote
  316. */
  317. /**
  318. * @ref bx_credits-get_block_orders_note "get_block_orders_note"
  319. */
  320. public function serviceGetBlockOrdersNote()
  321. {
  322. return MsgBox(_t('_bx_credits_page_block_content_orders_common_note'));
  323. }
  324. /**
  325. * @page service Service Calls
  326. * @section bx_credits Credits
  327. * @subsection bx_credits-page_blocks Page Blocks
  328. * @subsubsection bx_credits-get_block_orders get_block_orders
  329. *
  330. * @code bx_srv('bx_credits', 'get_block_orders', [...]); @endcode
  331. *
  332. * Get page block with a list of orders purchased by currently logged member.
  333. *
  334. * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
  335. *
  336. * @see BxCreditsModule::serviceGetBlockOrders
  337. */
  338. /**
  339. * @ref bx_credits-get_block_orders "get_block_orders"
  340. */
  341. public function serviceGetBlockOrders($sType = 'common')
  342. {
  343. return $this->_getBlockOrders($sType);
  344. }
  345. /**
  346. * @page service Service Calls
  347. * @section bx_credits Credits
  348. * @subsection bx_credits-page_blocks Page Blocks
  349. * @subsubsection bx_credits-get_block_history get_block_history
  350. *
  351. * @code bx_srv('bx_credits', 'get_block_history', [...]); @endcode
  352. *
  353. * Get page block with a list of all changes with credits.
  354. *
  355. * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
  356. *
  357. * @see BxCreditsModule::serviceGetBlockHistory
  358. */
  359. /**
  360. * @ref bx_credits-get_block_history "get_block_history"
  361. */
  362. public function serviceGetBlockHistory($sType = 'common')
  363. {
  364. return $this->_getBlockHistory($sType);
  365. }
  366. /**
  367. * @page service Service Calls
  368. * @section bx_credits Credits
  369. * @subsection bx_credits-page_blocks Page Blocks
  370. * @subsubsection bx_credits-get_block_profiles get_block_profiles
  371. *
  372. * @code bx_srv('bx_credits', 'get_block_profiles', [...]); @endcode
  373. *
  374. * Get page block with a list of profiles for managing their parameters.
  375. *
  376. * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
  377. *
  378. * @see BxCreditsModule::serviceGetBlockProfiles
  379. */
  380. /**
  381. * @ref bx_credits-get_block_profiles "get_block_profiles"
  382. */
  383. public function serviceGetBlockProfiles($sType = 'common')
  384. {
  385. return $this->_getBlockProfiles($sType);
  386. }
  387. /**
  388. * @page service Service Calls
  389. * @section bx_credits Credits
  390. * @subsection bx_credits-payments Payments
  391. * @subsubsection bx_credits-get_payment_data get_payment_data
  392. *
  393. * @code bx_srv('bx_credits', 'get_payment_data', [...]); @endcode
  394. *
  395. * Get an array with module's description. Is needed for payments processing module.
  396. *
  397. * @return an array with module's description.
  398. *
  399. * @see BxCreditsModule::serviceGetPaymentData
  400. */
  401. /**
  402. * @ref bx_credits-get_payment_data "get_payment_data"
  403. */
  404. public function serviceGetPaymentData()
  405. {
  406. $CNF = &$this->_oConfig->CNF;
  407. $oPermalink = BxDolPermalinks::getInstance();
  408. $aResult = $this->_aModule;
  409. $aResult['url_browse_order_common'] = bx_absolute_url($oPermalink->permalink($CNF['URL_ORDERS_COMMON'], array('filter' => '{order}')));
  410. $aResult['url_browse_order_administration'] = bx_absolute_url($oPermalink->permalink($CNF['URL_ORDERS_ADMINISTRATION'], array('filter' => '{order}')));
  411. return $aResult;
  412. }
  413. /**
  414. * @page service Service Calls
  415. * @section bx_credits Credits
  416. * @subsection bx_credits-payments Payments
  417. * @subsubsection bx_credits-get_cart_item get_cart_item
  418. *
  419. * @code bx_srv('bx_credits', 'get_cart_item', [...]); @endcode
  420. *
  421. * Get an array with prodict's description. Is used in Shopping Cart in payments processing module.
  422. *
  423. * @param $iItemId product's ID.
  424. * @return an array with prodict's description. Empty array is returned if something is wrong.
  425. *
  426. * @see BxCreditsModule::serviceGetCartItem
  427. */
  428. /**
  429. * @ref bx_credits-get_cart_item "get_cart_item"
  430. */
  431. public function serviceGetCartItem($iItemId)
  432. {
  433. $CNF = &$this->_oConfig->CNF;
  434. if(!$iItemId)
  435. return array();
  436. $aItem = $this->_oDb->getBundle(array('type' => 'id', 'id' => $iItemId));
  437. if(empty($aItem) || !is_array($aItem))
  438. return array();
  439. return array (
  440. 'id' => $aItem[$CNF['FIELD_ID']],
  441. 'author_id' => $this->_oConfig->getAuthor(),
  442. 'name' => $aItem[$CNF['FIELD_NAME']],
  443. 'title' => _t($aItem[$CNF['FIELD_TITLE']]),
  444. 'description' => $this->_oConfig->getBundleDescription($aItem),
  445. 'url' => $this->_oConfig->getBundleUrl($aItem),
  446. 'price_single' => $aItem[$CNF['FIELD_PRICE']],
  447. 'price_recurring' => '',
  448. 'period_recurring' => 1,
  449. 'period_unit_recurring' => '',
  450. 'trial_recurring' => ''
  451. );
  452. }
  453. /**
  454. * @page service Service Calls
  455. * @section bx_credits Credits
  456. * @subsection bx_credits-payments Payments
  457. * @subsubsection bx_credits-get_cart_items get_cart_items
  458. *
  459. * @code bx_srv('bx_credits', 'get_cart_items', [...]); @endcode
  460. *
  461. * Get an array with prodicts' descriptions by seller. Is used in Manual Order Processing in payments processing module.
  462. *
  463. * @param $iSellerId seller ID.
  464. * @return an array with prodicts' descriptions. Empty array is returned if something is wrong or seller doesn't have any products.
  465. *
  466. * @see BxCreditsModule::serviceGetCartItems
  467. */
  468. /**
  469. * @ref bx_credits-get_cart_items "get_cart_items"
  470. */
  471. public function serviceGetCartItems($iSellerId)
  472. {
  473. $CNF = &$this->_oConfig->CNF;
  474. $iAuthorId = $this->_oConfig->getAuthor();
  475. if($iSellerId != $iAuthorId && !BxDolAcl::getInstance()->isMemberLevelInSet([MEMBERSHIP_ID_ADMINISTRATOR], $iSellerId))
  476. return array();
  477. $aItems = $this->_oDb->getBundle(array('type' => 'all', 'active' => 1));
  478. $aResult = array();
  479. foreach($aItems as $aItem)
  480. $aResult[] = array(
  481. 'id' => $aItem[$CNF['FIELD_ID']],
  482. 'author_id' => $iAuthorId,
  483. 'name' => $aItem[$CNF['FIELD_NAME']],
  484. 'title' => _t($aItem[$CNF['FIELD_TITLE']]),
  485. 'description' => $this->_oConfig->getBundleDescription($aItem),
  486. 'url' => $this->_oConfig->getBundleUrl($aItem),
  487. 'price_single' => $aItem[$CNF['FIELD_PRICE']],
  488. 'price_recurring' => '',
  489. 'period_recurring' => 1,
  490. 'period_unit_recurring' => '',
  491. 'trial_recurring' => ''
  492. );
  493. return $aResult;
  494. }
  495. /**
  496. * @page service Service Calls
  497. * @section bx_credits Credits
  498. * @subsection bx_credits-payments Payments
  499. * @subsubsection bx_credits-register_cart_item register_cart_item
  500. *
  501. * @code bx_srv('bx_credits', 'register_cart_item', [...]); @endcode
  502. *
  503. * Register a processed single time payment inside the Credits module. Is called with payment processing module after the payment was registered there.
  504. *
  505. * @param $iClientId client ID.
  506. * @param $iSellerId seller ID
  507. * @param $iItemId item ID.
  508. * @param $iItemCount the number of purchased items.
  509. * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
  510. * @param $sLicense license number genereted with payment processing module for internal usage
  511. * @return an array with purchased prodict's description. Empty array is returned if something is wrong.
  512. *
  513. * @see BxCreditsModule::serviceRegisterCartItem
  514. */
  515. /**
  516. * @ref bx_credits-register_cart_item "register_cart_item"
  517. */
  518. public function serviceRegisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
  519. {
  520. return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_CREDITS_ORDER_TYPE_SINGLE);
  521. }
  522. /**
  523. * @page service Service Calls
  524. * @section bx_credits Credits
  525. * @subsection bx_credits-payments Payments
  526. * @subsubsection bx_credits-register_subscription_item register_subscription_item
  527. *
  528. * @code bx_srv('bx_credits', 'register_subscription_item', [...]); @endcode
  529. *
  530. * Register a processed subscription (recurring payment) inside the Credits module. Is called with payment processing module after the subscription was registered there.
  531. *
  532. * @param $iClientId client ID.
  533. * @param $iSellerId seller ID
  534. * @param $iItemId item ID.
  535. * @param $iItemCount the number of purchased items.
  536. * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
  537. * @param $sLicense license number genereted with payment processing module for internal usage
  538. * @return an array with subscribed prodict's description. Empty array is returned if something is wrong.
  539. *
  540. * @see BxCreditsModule::serviceRegisterSubscriptionItem
  541. */
  542. /**
  543. * @ref bx_credits-register_subscription_item "register_subscription_item"
  544. */
  545. public function serviceRegisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
  546. {
  547. return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_CREDITS_ORDER_TYPE_RECURRING);
  548. }
  549. /**
  550. * @page service Service Calls
  551. * @section bx_credits Credits
  552. * @subsection bx_credits-payments Payments
  553. * @subsubsection bx_credits-reregister_cart_item reregister_cart_item
  554. *
  555. * @code bx_srv('bx_credits', 'reregister_cart_item', [...]); @endcode
  556. *
  557. * Reregister a single time payment inside the Credits module. Is called with payment processing module after the payment was reregistered there.
  558. *
  559. * @param $iClientId client ID.
  560. * @param $iSellerId seller ID
  561. * @param $iItemIdOld old item ID.
  562. * @param $iItemIdNew new item ID.
  563. * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
  564. * @return an array with purchased prodict's description. Empty array is returned if something is wrong.
  565. *
  566. * @see BxCreditsModule::serviceReregisterCartItem
  567. */
  568. /**
  569. * @ref bx_credits-reregister_cart_item "reregister_cart_item"
  570. */
  571. public function serviceReregisterCartItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
  572. {
  573. return array();
  574. }
  575. /**
  576. * @page service Service Calls
  577. * @section bx_credits Credits
  578. * @subsection bx_credits-payments Payments
  579. * @subsubsection bx_credits-reregister_subscription_item reregister_subscription_item
  580. *
  581. * @code bx_srv('bx_credits', 'reregister_subscription_item', [...]); @endcode
  582. *
  583. * Reregister a subscription (recurring payment) inside the Credits module. Is called with payment processing module after the subscription was reregistered there.
  584. *
  585. * @param $iClientId client ID.
  586. * @param $iSellerId seller ID
  587. * @param $iItemIdOld old item ID.
  588. * @param $iItemIdNew new item ID.
  589. * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
  590. * @return an array with subscribed prodict's description. Empty array is returned if something is wrong.
  591. *
  592. * @see BxCreditsModule::serviceReregisterSubscriptionItem
  593. */
  594. /**
  595. * @ref bx_credits-reregister_subscription_item "reregister_subscription_item"
  596. */
  597. public function serviceReregisterSubscriptionItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
  598. {
  599. return array();
  600. }
  601. /**
  602. * @page service Service Calls
  603. * @section bx_credits Credits
  604. * @subsection bx_credits-payments Payments
  605. * @subsubsection bx_credits-unregister_cart_item unregister_cart_item
  606. *
  607. * @code bx_srv('bx_credits', 'unregister_cart_item', [...]); @endcode
  608. *
  609. * Unregister an earlier processed single time payment inside the Credits module. Is called with payment processing module after the payment was unregistered there.
  610. *
  611. * @param $iClientId client ID.
  612. * @param $iSellerId seller ID
  613. * @param $iItemId item ID.
  614. * @param $iItemCount the number of purchased items.
  615. * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
  616. * @param $sLicense license number genereted with payment processing module for internal usage
  617. * @return boolean value determining where the payment was unregistered or not.
  618. *
  619. * @see BxCreditsModule::serviceUnregisterCartItem
  620. */
  621. /**
  622. * @ref bx_credits-unregister_cart_item "unregister_cart_item"
  623. */
  624. public function serviceUnregisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
  625. {
  626. return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_CREDITS_ORDER_TYPE_SINGLE);
  627. }
  628. /**
  629. * @page service Service Calls
  630. * @section bx_credits Credits
  631. * @subsection bx_credits-payments Payments
  632. * @subsubsection bx_credits-unregister_subscription_item unregister_subscription_item
  633. *
  634. * @code bx_srv('bx_credits', 'unregister_subscription_item', [...]); @endcode
  635. *
  636. * Unregister an earlier processed subscription (recurring payment) inside the Credits module. Is called with payment processing module after the subscription was unregistered there.
  637. *
  638. * @param $iClientId client ID.
  639. * @param $iSellerId seller ID
  640. * @param $iItemId item ID.
  641. * @param $iItemCount the number of purchased items.
  642. * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
  643. * @param $sLicense license number genereted with payment processing module for internal usage
  644. * @return boolean value determining where the subscription was unregistered or not.
  645. *
  646. * @see BxCreditsModule::serviceUnregisterSubscriptionItem
  647. */
  648. /**
  649. * @ref bx_credits-unregister_subscription_item "unregister_subscription_item"
  650. */
  651. public function serviceUnregisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
  652. {
  653. return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_CREDITS_ORDER_TYPE_RECURRING);
  654. }
  655. /**
  656. * @page service Service Calls
  657. * @section bx_credits Credits
  658. * @subsection bx_credits-payments Payments
  659. * @subsubsection bx_credits-cancel_subscription_item cancel_subscription_item
  660. *
  661. * @code bx_srv('bx_credits', 'cancel_subscription_item', [...]); @endcode
  662. *
  663. * Cancel an earlier processed subscription (recurring payment) inside the Credits module. Is called with payment processing module after the subscription was canceled there.
  664. *
  665. * @param $iClientId client ID.
  666. * @param $iSellerId seller ID
  667. * @param $iItemId item ID.
  668. * @param $iItemCount the number of purchased items.
  669. * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
  670. * @return boolean value determining where the subscription was canceled or not.
  671. *
  672. * @see BxCreditsModule::serviceCancelSubscriptionItem
  673. */
  674. /**
  675. * @ref bx_credits-cancel_subscription_item "cancel_subscription_item"
  676. */
  677. public function serviceCancelSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder)
  678. {
  679. return true;
  680. }
  681. public function serviceGetProfileBalance($iProfileId = 0)
  682. {
  683. return $this->getProfileBalance($iProfileId);
  684. }
  685. public function serviceUpdateProfileBalance($iFirstPid, $fAmount, $iSecondPid = 0, $sOrder = '', $sInfo = '')
  686. {
  687. if(empty($sInfo))
  688. $sInfo = '_bx_credits_txt_history_info_service';
  689. return $this->updateProfileBalance($iFirstPid, $iSecondPid, $fAmount, BX_CREDITS_TRANSFER_TYPE_SERVICE, $sOrder, $sInfo);
  690. }
  691. public function serviceMakePayment($iBuyerPid, $fAmount, $iSellerPid, $sOrder = '')
  692. {
  693. $sInfo = '_bx_credits_txt_history_info_checkout';
  694. if(!$this->updateProfileBalance($iBuyerPid, $iSellerPid, -$fAmount, BX_CREDITS_TRANSFER_TYPE_CHECKOUT, $sOrder, $sInfo))
  695. return false;
  696. if(!$this->updateProfileBalance($iSellerPid, $iBuyerPid, $fAmount, $sOrder, BX_CREDITS_TRANSFER_TYPE_CHECKOUT, $sInfo)) {
  697. $this->updateProfileBalance($iBuyerPid, 0, $fAmount, BX_CREDITS_TRANSFER_TYPE_CANCELLATION, $sOrder, '_bx_credits_txt_history_info_cancellation');
  698. return false;
  699. }
  700. return true;
  701. }
  702. /**
  703. * Delete all content by profile
  704. * @param $iProfileId profile id
  705. * @return number of deleted items
  706. */
  707. public function serviceDeleteEntitiesByAuthor ($iProfileId)
  708. {
  709. $this->_oDb->deleteProfile(array('id' => $iProfileId));
  710. $this->_oDb->deleteHistory(array('first_pid' => $iProfileId));
  711. }
  712. /*
  713. * Common methods
  714. */
  715. public function convertC2S($fCredits, $bWithUnit = true)
  716. {
  717. $sResult = '';
  718. if($bWithUnit)
  719. $sResult .= $this->_oTemplate->getUnit();
  720. $iPrecision = $this->_oConfig->getPrecision();
  721. $sResult .= sprintf("%01." . $iPrecision . "f", round((float)$fCredits, $iPrecision));
  722. return $sResult;
  723. }
  724. public function getProfileLimits($iProfileId = 0)
  725. {
  726. if(empty($iProfileId))
  727. $iProfileId = bx_get_logged_profile_id();
  728. $aResults = [
  729. 'wdw_clearing' => $this->_oConfig->getWithdrawClearing(),
  730. 'wdw_minimum' => $this->_oConfig->getWithdrawMinimum(),
  731. 'wdw_remaining' => $this->_oConfig->getWithdrawRemaining(),
  732. ];
  733. if(empty($iProfileId))
  734. return $aResults;
  735. $aProfile = $this->_oDb->getProfile(['type' => 'id', 'id' => $iProfileId]);
  736. foreach($aResults as $sKey => $mixedValue)
  737. if(!empty($aProfile[$sKey]))
  738. $aResults[$sKey] = $aProfile[$sKey];
  739. return $aResults;
  740. }
  741. public function getProfileBalance($iProfileId = 0)
  742. {
  743. if(empty($iProfileId))
  744. $iProfileId = bx_get_logged_profile_id();
  745. if(empty($iProfileId))
  746. return 0;
  747. return (float)$this->_oDb->getProfile(array('type' => 'balance', 'id' => $iProfileId));
  748. }
  749. public function getProfileBalanceCleared($iProfileId = 0)
  750. {
  751. if(empty($iProfileId))
  752. $iProfileId = bx_get_logged_profile_id();
  753. if(empty($iProfileId))
  754. return 0;
  755. $fCleared = (float)$this->_oDb->getHistory(['type' => 'cleared', 'profile' => $iProfileId]);
  756. $fSpent = (float)$this->_oDb->getHistory(['type' => 'spent', 'profile' => $iProfileId]);
  757. return $fCleared > $fSpent ? $fCleared - $fSpent : 0;
  758. }
  759. public function updateProfileBalance($iFirstPid, $iSecondPid, $fAmount, $sType, $sOrder = '', $sInfo = '', $sData = '')
  760. {
  761. $CNF = &$this->_oConfig->CNF;
  762. if(empty($sOrder))
  763. $sOrder = $this->_oConfig->getOrder();
  764. if(empty($sData))
  765. $sData = serialize(array(
  766. 'conversion' => $this->_oConfig->getConversionRateUse(),
  767. 'precision' => $this->_oConfig->getPrecision()
  768. ));
  769. if(!$this->_oDb->updateProfileBalance($iFirstPid, $fAmount))
  770. return false;
  771. $fAmountAbs = abs($fAmount);
  772. $sDirection = $fAmount > 0 ? BX_CREDITS_DIRECTION_IN : BX_CREDITS_DIRECTION_OUT;
  773. $iHistoryId = $this->_oDb->insertHistory([
  774. 'first_pid' => $iFirstPid,
  775. 'second_pid' => $iSecondPid,
  776. 'amount' => $fAmountAbs,
  777. 'type' => $sType,
  778. 'direction' => $sDirection,
  779. 'order' => $sOrder,
  780. 'data' => $sData,
  781. 'info' => $sInfo,
  782. 'date' => time()
  783. ]);
  784. if(!$iHistoryId)
  785. return false;
  786. /**
  787. * @hooks
  788. * @hookdef hook-bx_credits-update_balance 'bx_credits', 'update_balance' - hook after profile's credits balance was updated (credits were transferred from one profile to another)
  789. * - $unit_name - equals `bx_credits`
  790. * - $action - equals `update_balance`
  791. * - $object_id - not used
  792. * - $sender_id - not used
  793. * - $extra_params - array of additional params with the following array keys:
  794. * - `first_pid` - [int] first profile id
  795. * - `second_pid` - [int] second profile id
  796. * - `amount` - [float] amount was transferred
  797. * - `type` - [string] transfer type: purchase, checkout, cancellation, grant, send, withdraw, service
  798. * - `direction` - [string] transfer direction: in or out
  799. * - `order` - [string] unique order number associated with the transfer
  800. * @hook @ref hook-bx_credits-update_balance
  801. */
  802. bx_alert($this->getName(), 'update_balance', 0, false, [
  803. 'first_pid' => $iFirstPid,
  804. 'second_pid' => $iSecondPid,
  805. 'amount' => $fAmountAbs,
  806. 'type' => $sType,
  807. 'direction' => $sDirection,
  808. 'order' => $sOrder,
  809. ]);
  810. $oSecondProfile = BxDolProfile::getInstanceMagic($iSecondPid);
  811. sendMailTemplate($CNF['ETEMPLATE_' . strtoupper($sDirection)], 0, $iFirstPid, array(
  812. 'second_profile_name' => $oSecondProfile->getDisplayName(),
  813. 'amount' => $fAmountAbs,
  814. ));
  815. return $iHistoryId;
  816. }
  817. public function processGrant($iUserId, $iProfileId, $fAmount, $sMessage = '')
  818. {
  819. $CNF = &$this->_oConfig->CNF;
  820. $oProfile = BxDolProfile::getInstance($iProfileId);
  821. if(!$oProfile)
  822. return ['code' => 2, 'msg' => '_bx_credits_err_profile_not_found'];
  823. $sInfo = '_bx_credits_txt_history_info_grant';
  824. $iHistoryId = $this->updateProfileBalance($iProfileId, 0, $fAmount, BX_CREDITS_TRANSFER_TYPE_GRANT, '', $sInfo);
  825. if(!$iHistoryId)
  826. return ['code' => 3, 'msg' => '_bx_credits_err_cannot_update_balance'];
  827. sendMailTemplate($CNF['ETEMPLATE_GRANTED'], 0, $iProfileId, [
  828. 'amount' => $fAmount,
  829. 'message' => $sMessage
  830. ]);
  831. /**
  832. * @hooks
  833. * @hookdef hook-bx_credits-granted 'bx_credits', 'granted' - hook after some amount of credits was granted to a profile
  834. * - $unit_name - equals `bx_credits`
  835. * - $action - equals `granted`
  836. * - $object_id - not used
  837. * - $sender_id - not used
  838. * - $extra_params - array of additional params with the following array keys:
  839. * - `profile` - [int] recipient profile id
  840. * - `amount` - [float] amount was granted
  841. * @hook @ref hook-bx_credits-granted
  842. */
  843. bx_alert($this->getName(), 'granted', 0, false, [
  844. 'profile' => $iProfileId,
  845. 'amount' => $fAmount,
  846. ]);
  847. return ['code' => 0, 'id' => $iHistoryId];
  848. }
  849. public function processSend($iUserId, $iProfileId, $fAmount, $sMessage = '')
  850. {
  851. $CNF = &$this->_oConfig->CNF;
  852. $fAmountAvail = $this->getProfileBalance($iUserId);
  853. if($fAmount > $fAmountAvail)
  854. return ['code' => 1, 'msg' => '_bx_credits_err_low_balance'];
  855. $oProfile = null;
  856. if(!$iProfileId || !($oProfile = BxDolProfile::getInstance($iProfileId)))
  857. return ['code' => 2, 'msg' => '_bx_credits_err_profile_not_found'];
  858. $sOrder = $this->_oConfig->getOrder();
  859. $sInfo = !empty($sMessage) ? $sMessage : '_bx_credits_txt_history_info_send';
  860. $iHistoryId = $this->updateProfileBalance($iUserId, $iProfileId, -$fAmount, BX_CREDITS_TRANSFER_TYPE_SEND, $sOrder, $sInfo);
  861. if(!$iHistoryId)
  862. return ['code' => 3, 'msg' => '_bx_credits_err_cannot_update_balance'];
  863. if(!$this->updateProfileBalance($iProfileId, $iUserId, $fAmount, BX_CREDITS_TRANSFER_TYPE_SEND, $sOrder, $sInfo)) {
  864. $sInfo = '_bx_credits_txt_history_info_cancellation';
  865. $iHistoryId = $this->updateProfileBalance($iUserId, 0, $fAmount, BX_CREDITS_TRANSFER_TYPE_CANCELLATION, $sOrder, $sInfo);
  866. return ['code' => 3, 'id' => $iHistoryId, 'msg' => '_bx_credits_err_cannot_update_balance'];
  867. }
  868. $oUser = BxDolProfile::getInstance($iUserId);
  869. sendMailTemplate($CNF['ETEMPLATE_RECEIVED'], 0, $iProfileId, [
  870. 'performer_id' => $iUserId,
  871. 'performer_name' => $oUser->getDisplayName(),
  872. 'performer_link' => $oUser->getUrl(),
  873. 'amount' => $fAmount,
  874. 'order' => $sOrder,
  875. 'message' => $sMessage
  876. ]);
  877. /**
  878. * @hooks
  879. * @hookdef hook-bx_credits-sent 'bx_credits', 'sent' - hook after some amount of credits was sent from one profile to another
  880. * - $unit_name - equals `bx_credits`
  881. * - $action - equals `sent`
  882. * - $object_id - not used
  883. * - $sender_id - sender profile id
  884. * - $extra_params - array of additional params with the following array keys:
  885. * - `performer` - [int] sender profile id
  886. * - `profile` - [int] recipient profile id
  887. * - `amount` - [float] amount was sent
  888. * - `order` - [string] unique order number associated with 'sent' action
  889. * @hook @ref hook-bx_credits-sent
  890. */
  891. bx_alert($this->getName(), 'sent', 0, $iUserId, [
  892. 'performer' => $iUserId,
  893. 'profile' => $iProfileId,
  894. 'amount' => $fAmount,
  895. 'order' => $sOrder,
  896. ]);
  897. return ['code' => 0, 'id' => $iHistoryId];
  898. }
  899. public function processWithdrawRequest($iUserId, $iProfileId, $fAmount, $sMessage = '')
  900. {
  901. $CNF = &$this->_oConfig->CNF;
  902. if(empty($iProfileId))
  903. $iProfileId = bx_get_logged_profile_id ();
  904. if(empty($iProfileId))
  905. return ['code' => 1, 'msg' => '_bx_credits_err_login_required'];
  906. $oProfile = BxDolProfile::getInstance($iProfileId);
  907. if(!$oProfile)
  908. return ['code' => 1, 'msg' => '_bx_credits_err_login_required'];
  909. $fBalanceCleared = $this->getProfileBalanceCleared($iProfileId);
  910. if($fAmount > $fBalanceCleared)
  911. return ['code' => 2, 'msg' => '_bx_credits_err_low_balance'];
  912. $aProfileInfo = $this->_oDb->getProfile(['type' => 'id', 'id' => $iProfileId]);
  913. $iWithdrawMinimum = !empty($aProfileInfo['wdw_minimum']) ? (int)$aProfileInfo['wdw_minimum'] : (int)getParam($CNF['PARAM_WITHDRAW_MINIMUM']);
  914. if($fAmount < $iWithdrawMinimum)
  915. return ['code' => 3, 'msg' => _t('_bx_credits_err_withdraw_minimum', $iWithdrawMinimum)];
  916. $fBalance = $this->getProfileBalance($iProfileId);
  917. $iWithdrawRemaining = !empty($aProfileInfo['wdw_remaining']) ? (int)$aProfileInfo['wdw_remaining'] : (int)getParam($CNF['PARAM_WITHDRAW_REMAINING']);
  918. if(($fBalance - $fAmount) < $iWithdrawRemaining)
  919. return ['code' => 4, 'msg' => _t('_bx_credits_err_withdraw_remaining', $iWithdrawRemaining)];
  920. $aResult = ['code' => 5, 'msg' => '_bx_credits_err_cannot_send'];
  921. $fRate = $this->_oConfig->getConversionRateWithdraw();
  922. $aTemplateVars = [
  923. 'profile_link' => $oProfile->getUrl(),
  924. 'profile_name' => $oProfile->getDisplayName(),
  925. 'amount' => $fAmount,
  926. 'rate' => $fRate,
  927. 'message' => $sMessage,
  928. 'confirm_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_HISTORY_ADMINISTRATION']))
  929. ];
  930. $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($CNF['ETEMPLATE_WITHDRAW_REQUESTED'], $aTemplateVars);
  931. if(!$aTemplate)
  932. return $aResult;
  933. $sEmail = $this->_oConfig->getWithdrawEmail();
  934. if(!sendMail($sEmail, $aTemplate['Subject'], $aTemplate['Body'], 0, [], BX_EMAIL_SYSTEM, 'html', false, [], true))
  935. return $aResult;
  936. /**
  937. * @hooks
  938. * @hookdef hook-bx_credits-withdraw_requested 'bx_credits', 'withdraw_requested' - hook after a profile requested to withdraw some amount of credits from his balance
  939. * - $unit_name - equals `bx_credits`
  940. * - $action - equals `withdraw_requested`
  941. * - $object_id - not used
  942. * - $sender_id - performer profile id
  943. * - $extra_params - array of additional params with the following array keys:
  944. * - `profile` - [int] performer profile id
  945. * - `amount` - [float] amount to withdraw
  946. * - `rate` - [float] withdraw conversion rate
  947. * @hook @ref hook-bx_credits-withdraw_requested
  948. */
  949. bx_alert($this->getName(), 'withdraw_requested', 0, $iProfileId, [
  950. 'profile' => $iProfileId,
  951. 'amount' => $fAmount,
  952. 'rate' => $fRate
  953. ]);
  954. return ['code' => 0];
  955. }
  956. public function processWithdrawConfirm($iUserId, $iProfileId, $fAmount, $sMessage = '')
  957. {
  958. $CNF = &$this->_oConfig->CNF;
  959. $oProfile = BxDolProfile::getInstance($iProfileId);
  960. if(!$oProfile)
  961. return ['code' => 1, 'msg' => '_bx_credits_err_profile_not_found'];
  962. $fBalance = $this->getProfileBalance($iProfileId);
  963. if($fAmount > $fBalance)
  964. return ['code' => 2, 'msg' => '_bx_credits_err_low_balance'];
  965. $sInfo = '_bx_credits_txt_history_info_withdraw';
  966. $iHistoryId = $this->updateProfileBalance($iProfileId, 0, -$fAmount, BX_CREDITS_TRANSFER_TYPE_WITHDRAW, '', $sInfo);
  967. if(!$iHistoryId)
  968. return ['code' => 3, 'msg' => '_bx_credits_err_cannot_update_balance'];
  969. sendMailTemplate($CNF['ETEMPLATE_WITHDRAW_SENT'], 0, $iProfileId, [
  970. 'amount' => $fAmount,
  971. 'message' => $sMessage
  972. ]);
  973. /**
  974. * @hooks
  975. * @hookdef hook-bx_credits-withdraw_sent 'bx_credits', 'withdraw_sent' - hook after the amount of credits requested to withdraw was sent
  976. * - $unit_name - equals `bx_credits`
  977. * - $action - equals `withdraw_sent`
  978. * - $object_id - not used
  979. * - $sender_id - performer profile id
  980. * - $extra_params - array of additional params with the following array keys:
  981. * - `profile` - [int] performer profile id
  982. * - `amount` - [float] amount to withdraw
  983. * - `rate` - [float] withdraw conversion rate
  984. * @hook @ref hook-bx_credits-withdraw_sent
  985. */
  986. bx_alert($this->getName(), 'withdraw_sent', 0, $iUserId, [
  987. 'performer' => $iUserId,
  988. 'profile' => $iProfileId,
  989. 'amount' => $fAmount,
  990. ]);
  991. return ['code' => 0, 'id' => $iHistoryId];
  992. }
  993. public function processClearing()
  994. {
  995. $CNF = &$this->_oConfig->CNF;
  996. $aItems = $this->_oDb->getHistory(['type' => 'clearing', 'clearing' => $this->_oConfig->getWithdrawClearing()]);
  997. if(empty($aItems) || !is_array($aItems))
  998. return;
  999. $iNow = time();
  1000. foreach($aItems as $aItem) {
  1001. $this->_oDb->updateHistory([$CNF['FIELD_H_CLEARED'] => $iNow], [$CNF['FIELD_H_ID'] => $aItem['id']]);
  1002. //TODO: Some action(s) related to clearing can be performed here.
  1003. }
  1004. }
  1005. /*
  1006. * Internal methods
  1007. */
  1008. protected function _serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
  1009. {
  1010. $CNF = &$this->_oConfig->CNF;
  1011. $aItem = $this->serviceGetCartItem($iItemId);
  1012. if(empty($aItem) || !is_array($aItem))
  1013. return array();
  1014. $aBundle = $this->_oDb->getBundle(array('type' => 'id', 'id' => $iItemId));
  1015. if(empty($aBundle) || !is_array($aBundle))
  1016. return array();
  1017. $iTrial = 0;
  1018. $sDuration = '';
  1019. $sAction = 'register';
  1020. if($sType == BX_CREDITS_ORDER_TYPE_RECURRING && isset($CNF['FIELD_DURATION_RECURRING'], $CNF['FIELD_TRIAL_RECURRING'])) {
  1021. $iTrial = $aBundle[$CNF['FIELD_TRIAL_RECURRING']];
  1022. $sDuration = $aBundle[$CNF['FIELD_DURATION_RECURRING']];
  1023. if($this->_oDb->isOrderByPbo($iClientId, $iItemId, $sOrder))
  1024. $sAction = 'prolong';
  1025. }
  1026. if(!$this->_oDb->{$sAction . 'Order'}($iClientId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType, $sDuration, $iTrial))
  1027. return array();
  1028. $sInfo = '_bx_credits_txt_history_info_purchase';
  1029. $fAmount = ((float)$aBundle[$CNF['FIELD_AMOUNT']] + (float)$aBundle[$CNF['FIELD_BONUS']]) * $iItemCount;
  1030. if(!$this->updateProfileBalance($iClientId, 0, $fAmount, BX_CREDITS_TRANSFER_TYPE_PURCHASE, '', $sInfo))
  1031. return array();
  1032. /**
  1033. * @hooks
  1034. * @hookdef hook-bx_credits-order_register 'bx_credits', 'order_register' - hook after the payment for a bundle of credits was processed with payment processing module
  1035. * - $unit_name - equals `bx_credits`
  1036. * - $action - equals `order_register`
  1037. * - $object_id - not used
  1038. * - $sender_id - not used
  1039. * - $extra_params - array of additional params with the following array keys:
  1040. * - `profile_id` - [int] client (buyer) profile id
  1041. * - `bundle_id` - [int] bundle id
  1042. * - `product_id` - [int] bundle id
  1043. * - `count` - [int] number of items in order
  1044. * - `order` - [string] order number provided with payment processing module
  1045. * - `license` - [string] license number provided with payment processing module
  1046. * - `type` - [string] payment type ('single' or 'recurring'), current version supports 'single' type payments for bundles only
  1047. * - `duration` - [string] isn't supported
  1048. * - `trial` - [string] isn't supported
  1049. * @hook @ref hook-bx_credits-order_register
  1050. */
  1051. /**
  1052. * @hooks
  1053. * @hookdef hook-bx_credits-order_prolong 'bx_credits', 'order_prolong' - hook isn't currently used because 'single' type payments for bundles are only supported
  1054. * It's equivalent to @ref hook-bx_credits-order_register
  1055. * @hook @ref hook-bx_credits-order_prolong
  1056. */
  1057. bx_alert($this->getName(), 'order_' . $sAction, 0, false, [
  1058. 'profile_id' => $iClientId,
  1059. 'bundle_id' => $iItemId,
  1060. 'product_id' => $iItemId, //--- Alias for 'bundle_id'
  1061. 'count' => $iItemCount,
  1062. 'order' => $sOrder,
  1063. 'license' => $sLicense,
  1064. 'type' => $sType,
  1065. 'duration' => $sDuration,
  1066. 'trial' => $iTrial
  1067. ]);
  1068. $oClient = BxDolProfile::getInstanceMagic($iClientId);
  1069. $oSeller = BxDolProfile::getInstanceMagic($iSellerId);
  1070. $sSellerUrl = $oSeller->getUrl();
  1071. $sSellerName = $oSeller->getDisplayName();
  1072. sendMailTemplate($CNF['ETEMPLATE_PURCHASED'], 0, $iClientId, array(
  1073. 'client_name' => $oClient->getDisplayName(),
  1074. 'bundle_name' => $aBundle[$CNF['FIELD_NAME']],
  1075. 'bundle_title' => _t($aBundle[$CNF['FIELD_TITLE']]),
  1076. 'bundle_url' => $this->_oConfig->getBundleUrl($aBundle),
  1077. 'vendor_url' => $sSellerUrl,
  1078. 'vendor_name' => $sSellerName,
  1079. 'order' => $sOrder,
  1080. 'license' => $sLicense,
  1081. 'notes' => _t('_bx_credits_txt_purchased_note', $sSellerUrl, $sSellerName),
  1082. ));
  1083. return $aItem;
  1084. }
  1085. protected function _serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
  1086. {
  1087. $CNF = &$this->_oConfig->CNF;
  1088. $aBundle = $this->_oDb->getBundle(array('type' => 'id', 'id' => $iItemId));
  1089. if(empty($aBundle) || !is_array($aBundle))
  1090. return false;
  1091. if(!$this->_oDb->unregisterOrder($iClientId, $iItemId, $sOrder, $sLicense, $sType))
  1092. return false;
  1093. $fAmount = (float)$aBundle[$CNF['FIELD_AMOUNT']] + (float)$aBundle[$CNF['FIELD_BONUS']];
  1094. $aProfile = $this->_oDb->getProfile(array('type' => 'id', 'id' => $iClientId));
  1095. if(!empty($aProfile) && is_array($aProfile))
  1096. $this->_oDb->updateProfile(array('balance' => (float)$aProfile['balance'] - $fAmount), array('id' => $aProfile['id']));
  1097. /**
  1098. * @hooks
  1099. * @hookdef hook-bx_credits-order_unregister 'bx_credits', 'order_unregister' - hook after the payment for a bundle of credits was refunded with payment processing module
  1100. * - $unit_name - equals `bx_credits`
  1101. * - $action - equals `order_unregister`
  1102. * - $object_id - not used
  1103. * - $sender_id - not used
  1104. * - $extra_params - array of additional params with the following array keys:
  1105. * - `profile_id` - [int] client (buyer) profile id
  1106. * - `bundle_id` - [int] bundle id
  1107. * - `product_id` - [int] bundle id
  1108. * - `count` - [int] number of items in order
  1109. * - `order` - [string] order number provided with payment processing module
  1110. * - `license` - [string] license number provided with payment processing module
  1111. * - `type` - [string] payment type ('single' or 'recurring'), current version supports 'single' type payments for bundles only
  1112. * @hook @ref hook-bx_credits-order_unregister
  1113. */
  1114. bx_alert($this->getName(), 'order_unregister', 0, false, [
  1115. 'profile_id' => $iClientId,
  1116. 'bundle_id' => $iItemId,
  1117. 'product_id' => $iItemId, //--- Alias for 'bundle_id'
  1118. 'count' => $iItemCount,
  1119. 'order' => $sOrder,
  1120. 'license' => $sLicense,
  1121. 'type' => $sType,
  1122. ]);
  1123. return true;
  1124. }
  1125. protected function _getBlockOrders($sType)
  1126. {
  1127. $CNF = &$this->_oConfig->CNF;
  1128. $sGrid = $CNF['OBJECT_GRID_ORDERS_' . strtoupper($sType)];
  1129. $oGrid = BxDolGrid::getObjectInstance($sGrid);
  1130. if(!$oGrid)
  1131. return '';
  1132. return [
  1133. 'content' => $oGrid->getCode(),
  1134. 'menu' => $CNF['OBJECT_MENU_MANAGE_SUBMENU']
  1135. ];
  1136. }
  1137. protected function _getBlockHistory($sType)
  1138. {
  1139. $CNF = &$this->_oConfig->CNF;
  1140. $sGrid = $CNF['OBJECT_GRID_HISTORY_' . strtoupper($sType)];
  1141. $oGrid = BxDolGrid::getObjectInstance($sGrid);
  1142. if(!$oGrid)
  1143. return '';
  1144. return [
  1145. 'content' => $oGrid->getCode(),
  1146. 'menu' => $CNF['OBJECT_MENU_MANAGE_SUBMENU']
  1147. ];
  1148. }
  1149. protected function _getBlockProfiles($sType)
  1150. {
  1151. $CNF = &$this->_oConfig->CNF;
  1152. $sKey = 'OBJECT_GRID_PROFILES_' . strtoupper($sType);
  1153. if(empty($CNF[$sKey]))
  1154. return '';
  1155. $sGrid = $CNF[$sKey];
  1156. $oGrid = BxDolGrid::getObjectInstance($sGrid);
  1157. if(!$oGrid)
  1158. return '';
  1159. return [
  1160. 'content' => $oGrid->getCode(),
  1161. 'menu' => $CNF['OBJECT_MENU_MANAGE_SUBMENU']
  1162. ];
  1163. }
  1164. }
  1165. /** @} */