BxCreditsModule.php 59 KB

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