BxCreditsModule.php 59 KB

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