Notify.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. <?php
  2. /**
  3. * Notify.php UTF-8
  4. * 通知CP
  5. *
  6. * @date : 2018/4/28 1:58
  7. *
  8. * @license 这不是一个自由软件,未经授权不许任何使用和传播。
  9. * @author : wuyonghong <wyh@huosdk.com>
  10. * @version : HUOSDK 8.0
  11. */
  12. namespace huo\controller\pay;
  13. use huo\controller\agent\AgentCache;
  14. use huo\controller\common\Base;
  15. use huo\controller\conf\PaywayConf;
  16. use huo\controller\finance\Income;
  17. use huo\controller\game\Game;
  18. use huo\controller\integral\MemIa;
  19. use huo\controller\member\MemCache;
  20. use huo\controller\member\MemWallet;
  21. use huo\controller\request\Request;
  22. use huo\controller\wallet\WalletRequest;
  23. use huo\model\game\GameextModel;
  24. use huo\model\log\OrderCpLogModel;
  25. use huo\model\member\MemGameModel;
  26. use huo\model\member\MemoauthModel;
  27. use huolib\constant\AgentConst;
  28. use huolib\constant\CommonConst;
  29. use huolib\constant\IaConst;
  30. use huolib\constant\OrderConst;
  31. use huolib\constant\WalletConst;
  32. use huolib\pay\Pay;
  33. use huolib\status\CommonStatus;
  34. use huolib\status\IntegralStatus;
  35. use huolib\status\OrderStatus;
  36. use huolib\status\SettleStatus;
  37. use huolib\tool\StrUtils;
  38. use huolib\tool\Time;
  39. use huomp\controller\game\GameMini;
  40. use huomp\model\weixin\OaMchModel;
  41. use huoMpAd\MpReportData;
  42. use think\Config;
  43. use think\Exception;
  44. use think\Log;
  45. class Notify extends Base {
  46. protected function retSucMsg($code, $data = []) {
  47. $_msg = OrderStatus::getMsg($code);
  48. return $this->huoSuccess($code, $_msg, $data);
  49. }
  50. protected function retErrMsg($code) {
  51. $_err_msg = OrderStatus::getMsg($code);
  52. return $this->huoError($code, $_err_msg);
  53. }
  54. /**
  55. * CP通知
  56. *
  57. * @param string $order_id
  58. *
  59. * @return array|mixed
  60. */
  61. public function notify($order_id = '') {
  62. if (empty($order_id)) {
  63. return $this->retErrMsg(OrderStatus::ORDER_ID_EMPTY);
  64. }
  65. $_order_data = SdkOrderCache::ins()->getInfoByOrderId($order_id);
  66. if (false == $_order_data) {
  67. return $this->retErrMsg(OrderStatus::ORDER_NOT_EXISTS);
  68. }
  69. if (OrderConst::PAY_STATUS_SUC != $_order_data['status']) {
  70. Log::write(
  71. "func=".__FUNCTION__."&class=".__CLASS__."&code=".OrderStatus::ORDER_NOT_PAY.'&order_data.'
  72. .json_encode($_order_data),
  73. LOG::LOG
  74. );
  75. return $this->retErrMsg(OrderStatus::ORDER_NOT_PAY);
  76. }
  77. if (OrderConst::CP_STATUS_SUC == $_order_data['cp_status']) {
  78. return $this->retSucMsg(OrderStatus::NO_ERROR);
  79. }
  80. $_param = $this->setCpParam($_order_data);
  81. $_cp_url = (new Game())->getCpPaybackUrl($_order_data['app_id']);
  82. if (empty($_cp_url)) {
  83. Log::write(
  84. "func=".__FUNCTION__."&class=".__CLASS__."&code=".OrderStatus::NOTIFY_URL_EMPTY.'&order_data.'
  85. .json_encode($_order_data),
  86. LOG::ERROR
  87. );
  88. return $this->retErrMsg(OrderStatus::NOTIFY_URL_EMPTY);
  89. }
  90. $_nc_data = $this->notifyCp($_cp_url, $_param);
  91. if (empty($_nc_data)) {
  92. return $this->retErrMsg(OrderStatus::UNKNOWN_ERROR);
  93. }
  94. /* 更新订单信息 */
  95. $_order_data['cp_status'] = $_nc_data[1];
  96. $_order_data['notify_cnt'] += $_nc_data[0];
  97. $_order_data['last_notify_time'] = time();
  98. if (OrderConst::CP_STATUS_SUC == $_nc_data[1]) {
  99. $_order_data['finish_time'] = time();
  100. $_rs = SdkOrderCache::ins()->updateOrder($_order_data['order_id'], $_order_data);
  101. if (true == $_rs) {
  102. $this->updateAfterNotify($_order_data);
  103. $_code = OrderStatus::NO_ERROR;
  104. } else {
  105. $_code = OrderStatus::INNER_ERROR;
  106. }
  107. } else {
  108. // 兼容前端接口查单和支付通知同时修改cp通知状态导致重复问题
  109. $_order_data = SdkOrderCache::ins()->getInfoByOrderId($order_id);
  110. if (OrderConst::CP_STATUS_SUC == $_order_data['cp_status']) {
  111. $_code = OrderStatus::NO_ERROR;
  112. return $this->retSucMsg($_code);
  113. }
  114. $_order_data['is_handle'] = OrderConst::ORDER_IS_HANDLE;
  115. SdkOrderCache::ins()->updateOrder($_order_data['order_id'], $_order_data);
  116. $_code = OrderStatus::NOTIFY_FAIL;
  117. $_class_name = '\\huoOrderRepeat\\controller\\OrderRepeat';
  118. if (Config::get('ORDER_REPEAT_QUEUE') && class_exists($_class_name)) {
  119. (new $_class_name())->cpNotifyQueue($_order_data['order_id']);
  120. }
  121. }
  122. /* 插入通知Log */
  123. $_data['pay_id'] = get_val($_order_data, 'id', 0);
  124. $_data['order_id'] = get_val($_order_data, 'order_id', '');
  125. $_data['cp_order_id'] = get_val($_order_data, 'cp_order_id', 0);
  126. $_data['status'] = get_val($_order_data, 'status', '');
  127. $_data['cp_status'] = get_val($_order_data, 'cp_status', 0);
  128. $_data['cp_payback_url'] = $_cp_url;
  129. $_data['params'] = $_param;
  130. $_data['ext'] = get_val($_order_data, 'ext', '');
  131. $_data['notify_cnt'] = get_val($_order_data, 'notify_cnt', '');
  132. $_oc_model = new OrderCpLogModel();
  133. $_oc_model->insertLog($_data);
  134. return $this->retSucMsg($_code);
  135. }
  136. /**
  137. * @param $_order_data
  138. */
  139. public function updateAfterNotify($_order_data) {
  140. // TODO: wuyonghong 2018/4/28 通知后更新
  141. }
  142. /**
  143. * @param $cp_url
  144. * @param $param
  145. *
  146. * @return array
  147. */
  148. public function notifyCp($cp_url, $param) {
  149. $_i = 0;
  150. $_cp_status = 1;
  151. while (1) {
  152. $_i++;
  153. $_cp_rs = Request::cpPayback($cp_url, $param);
  154. if ($_cp_rs > 0) {
  155. $_cp_status = 2;
  156. break;
  157. } else {
  158. $_cp_status = 3;
  159. sleep(1);
  160. }
  161. if ($_i == 3) {
  162. break;
  163. }
  164. }
  165. return [$_i, $_cp_status];
  166. }
  167. /**
  168. * 设置通知CP参数
  169. *
  170. * @param array $order_data
  171. *
  172. * @return bool|string
  173. */
  174. public function setCpParam($order_data = []) {
  175. $_cp_rq_data['app_id'] = get_val($order_data, 'app_id');
  176. $_cp_rq_data['cp_order_id'] = get_val($order_data, 'cp_order_id');
  177. $_cp_rq_data['mem_id'] = get_val($order_data, 'mg_mem_id', 0);/* 使用游戏玩家ID */
  178. $_cp_rq_data['order_id'] = get_val($order_data, 'order_id');
  179. $_cp_rq_data['order_status'] = get_val($order_data, 'status');
  180. $_cp_rq_data['pay_time'] = get_val($order_data, 'create_time');
  181. $_cp_rq_data['product_id'] = get_val($order_data, 'product_id');
  182. $_cp_rq_data['product_name'] = get_val($order_data, 'product_name');
  183. $_cp_rq_data['product_price'] = get_val($order_data, 'amount');
  184. $_cp_rq_data['ext'] = get_val($order_data, 'ext');
  185. $_param = StrUtils::argSort($_cp_rq_data);
  186. $_signstr = StrUtils::createLinkString($_param);
  187. /* 获取游戏信息 */
  188. $_app_key = (new Game())->getAppKey($_cp_rq_data['app_id']);
  189. if (empty($_app_key)) {
  190. return false;
  191. }
  192. $_sign = md5($_signstr."&app_key=".$_app_key);
  193. $_cp_rq_data['sign'] = $_sign;
  194. return $_signstr."&sign=".$_sign;
  195. }
  196. /**
  197. * 支付回调处理
  198. *
  199. * @param $product_id
  200. * @param $order_id
  201. * @param $trade_no
  202. * @param $amount
  203. * @param $payway
  204. */
  205. public function payNotify($product_id, $order_id, $trade_no, $amount, $payway) {
  206. $_notify_data['product_id'] = $product_id;
  207. $_notify_data['order_id'] = $order_id;
  208. $_notify_data['trade_no'] = $trade_no;
  209. $_notify_data['amount'] = $amount;
  210. $_notify_data['payway'] = $payway;
  211. $_soc_class = SdkOrderCache::ins();
  212. if (empty($order_id)) {
  213. Log::write(
  214. "func=".__FUNCTION__."&class=".__CLASS__."&step1&code=".OrderStatus::ORDER_ID_EMPTY.'&notify_data.'
  215. .http_build_query($_notify_data),
  216. LOG::ERROR
  217. );
  218. return false;
  219. }
  220. if (empty($amount)) {
  221. Log::write(
  222. "func=".__FUNCTION__."&class=".__CLASS__."&step2&code=".OrderStatus::ORDER_AMOUNT_IS_ZERO
  223. .'&notify_data.'
  224. .http_build_query($_notify_data),
  225. LOG::ERROR
  226. );
  227. return false;
  228. }
  229. $_order_data = $_soc_class->getInfoByOrderId($order_id);
  230. if (false == $_order_data) {
  231. Log::write(
  232. "func=".__FUNCTION__."&class=".__CLASS__."&step3&code=".OrderStatus::ORDER_NOT_EXISTS.'&notify_data.'
  233. .http_build_query($_notify_data),
  234. LOG::ERROR
  235. );
  236. return false;
  237. }
  238. $_amount = number_format($amount, 2, '.', '');
  239. $_order_amount = number_format($_order_data['real_amount'], 2, '.', '');
  240. if (($_amount < $_order_amount)) {
  241. Log::write(
  242. "func=".__FUNCTION__."&class=".__CLASS__."&step4&code=".OrderStatus::NOTIFY_AMOUNT_ERROR.'&notify_data.'
  243. .http_build_query($_notify_data).'&order_data='.http_build_query($_order_data),
  244. LOG::ERROR
  245. );
  246. return false;
  247. }
  248. if (OrderConst::PAY_STATUS_SUC == $_order_data['status']) {
  249. Log::write(
  250. "func=".__FUNCTION__."&class=".__CLASS__."&step5&code=".OrderStatus::ORDER_HAS_PAY.'&notify_data.'
  251. .http_build_query($_notify_data).'&order_data='.http_build_query($_order_data),
  252. LOG::LOG
  253. );
  254. return false;
  255. }
  256. /* 更新订单支付状态 */
  257. $_order_data['status'] = OrderConst::PAY_STATUS_SUC;
  258. $_order_data['payway'] = $payway;
  259. $_order_data['remark'] = $trade_no;
  260. $_order_data['pay_time'] = time();
  261. $_rs = $_soc_class->updateOrder($_order_data['order_id'], $_order_data);
  262. if (false == $_rs) {
  263. return false;
  264. }
  265. /* Modified by wuyonghong BEGIN 2018/3/26 ISSUES:5314 LTV */
  266. /* 充值时计算LTV */
  267. $_ltv_class = new \ltv\Ltv();
  268. $_mem_data = (new \huo\model\member\MemberModel())->getInfoById(
  269. $_order_data['mem_id']
  270. );
  271. $_ltv_class->charge(
  272. $_mem_data['app_id'],
  273. $_mem_data['agent_id'],
  274. $_order_data['app_id'],
  275. $_order_data['amount'],
  276. $_order_data['update_time'],
  277. $_mem_data['create_time']
  278. );
  279. /* END 2018/3/26 ISSUES:5314 */
  280. /* 通知CP */
  281. $this->doAfterPayNotify($order_id);
  282. // 买量数据回传
  283. try {
  284. (new MpReportData())->payReport($order_id);
  285. } catch (\Exception $exception) {
  286. \think\Log::write(['pppppp', $exception->getTraceAsString(), 'error', true]);
  287. }
  288. return true;
  289. }
  290. /**
  291. * 支付成功后 需要做的操作
  292. *
  293. * @param $order_id
  294. *
  295. * @return array
  296. */
  297. public function doAfterPayNotify($order_id) {
  298. /* 更新游戏信息 */
  299. $this->upGameAfterPay($order_id);
  300. /* 更新玩家信息 */
  301. $_rs = $this->upMemAfterPay($order_id);
  302. if (CommonStatus::NO_ERROR != $_rs['code']) {
  303. return $this->huoReturn($_rs);
  304. }
  305. /* 计算渠道收益 */
  306. $this->upAgentAfterPay($order_id);
  307. /* 异步处理数据 */
  308. (new \huo\controller\queue\Order())->fromSdkOrder($order_id);
  309. /* 通知CP */
  310. $_rdata = $this->notify($order_id);
  311. return $this->huoReturn($_rdata);
  312. }
  313. /**
  314. * 更新渠道数据
  315. *
  316. * @param $order_id
  317. */
  318. public function upAgentAfterPay($order_id) {
  319. /* 计算渠道收益 */
  320. (new Income())->incomeFromSdkOrder($order_id);
  321. }
  322. /**
  323. * 更新玩家数据
  324. *
  325. * @param $order_id
  326. */
  327. public function upMemAfterPay($order_id) {
  328. $_soc_class = SdkOrderCache::ins();
  329. $_order_data = $_soc_class->getInfoByOrderId($order_id);
  330. /* 扣除平台币 平台币扣除不成功返回 */
  331. if (StrUtils::compareNumber($_order_data['ptb_amount'], CommonConst::CONST_ZERO_COMPARE) > 0) {
  332. $_wr_class = new WalletRequest();
  333. $_wr_class->setDataFromSdkOrder($_order_data);
  334. $_order_data['status'] = OrderConst::PAY_STATUS_SUC;
  335. $_wr_class->setStatus($_order_data['status']);
  336. $_rs = (new MemWallet())->createPoOrder($_wr_class);
  337. if (SettleStatus::NO_ERROR != $_rs) {
  338. /* 记录所有Log */
  339. return $this->huoError($_rs, SettleStatus::getMsg($_rs));
  340. } else {
  341. $_order_data['pay_time'] = time();
  342. $_soc_class->updateOrder($order_id, $_order_data);
  343. }
  344. }
  345. /* 扣除游戏币 游戏币扣除不成功返回 */
  346. if (StrUtils::compareNumber($_order_data['gm_amount'], CommonConst::CONST_ZERO_COMPARE) > 0) {
  347. $_wr_class = new WalletRequest();
  348. $_wr_class->setDataFromSdkOrder($_order_data);
  349. $_order_data['status'] = OrderConst::PAY_STATUS_SUC;
  350. $_wr_class->setStatus($_order_data['status']);
  351. $_rs = (new MemWallet())->createGmoOrder($_wr_class);
  352. if (SettleStatus::NO_ERROR != $_rs) {
  353. /* 记录所有Log */
  354. return $this->huoError($_rs, SettleStatus::getMsg($_rs));
  355. } else {
  356. $_order_data['pay_time'] = time();
  357. $_soc_class->updateOrder($order_id, $_order_data);
  358. }
  359. }
  360. /* 充值成功获取积分 */
  361. $_mem_id = $_order_data['mem_id'];
  362. if (!empty($_mem_id)) {
  363. $_rs = (new MemIa($_mem_id))->doItgAct(IaConst::IA_DAY_FIRST_CHARGE, $order_id);
  364. if (CommonStatus::NO_ERROR != $_rs['code']) {
  365. if (IntegralStatus::ITG_IA_ERROR != $_rs['code']) {
  366. Log::write(
  367. "func=".__FUNCTION__."&class=".__CLASS__."&returndata=".json_encode($_rs).'&order_data='
  368. .http_build_query($_order_data),
  369. LOG::LOG
  370. );
  371. } else {
  372. Log::write(
  373. "func=".__FUNCTION__."&class=".__CLASS__."&returndata=".json_encode($_rs).'&order_data='
  374. .http_build_query($_order_data),
  375. LOG::ERROR
  376. );
  377. }
  378. }
  379. }
  380. /* 更新玩家信息 */
  381. $_mc_class = MemCache::ins();
  382. $_me_data = $_mc_class->getMeInfoById($_mem_id);
  383. $_me_data['last_money'] = $_order_data['amount'];
  384. $_me_data['sum_money'] += $_order_data['amount'];
  385. // 玩家游戏充值
  386. $_mem_game_model = new MemGameModel();
  387. $_mem_game_model->where(['id' => $_order_data['mg_mem_id']])
  388. ->setInc('sum_money', $_order_data['amount']);
  389. list($_today_start, $_today_end) = Time::today();
  390. if ($_today_start > $_me_data['last_pay_time']) {
  391. /* 非本日充值 */
  392. $_me_data['day_money'] = $_order_data['amount'];
  393. } else {
  394. $_me_data['day_money'] += $_order_data['amount'];
  395. }
  396. list($_week_start, $_week_end) = Time::week();
  397. if ($_week_start > $_me_data['last_pay_time']) {
  398. /* 非本周充值 */
  399. $_me_data['week_money'] = $_order_data['amount'];
  400. } else {
  401. $_me_data['week_money'] += $_order_data['amount'];
  402. }
  403. list($_month_start, $_month_end) = Time::month();
  404. if ($_month_start > $_me_data['last_pay_time']) {
  405. /* 非本月充值 */
  406. $_me_data['month_money'] = $_order_data['amount'];
  407. } else {
  408. $_me_data['month_money'] += $_order_data['amount'];
  409. }
  410. $_me_data['order_suc_cnt'] += 1;
  411. $_me_data['last_pay_time'] = $_order_data['pay_time'];
  412. $_mc_class->updateMeCache($_mem_id, $_me_data);
  413. /* Modified by chenbingling BEGIN 2019/12/2 ISSUES:10837 实名认证统计 */
  414. (new \huoIdentify\controller\Pay())->updateMoney($_order_data['mem_id'], $_order_data['amount']);
  415. /* END 2019/12/2 ISSUES:10837 */
  416. $_code = CommonStatus::NO_ERROR;
  417. return $this->huoError($_code, SettleStatus::getMsg($_code));
  418. }
  419. /**
  420. * @param string $payway
  421. *
  422. * @param string $product_id
  423. *
  424. * @param int $app_id 应用ID
  425. * @param string $order_id 订单id
  426. *
  427. * @return bool
  428. */
  429. public function notifyUrl($payway, $product_id = '', $app_id = 0, $order_id = '') {
  430. try {
  431. $_pay_class = Pay::ins()->get($payway);
  432. } catch (Exception $e) {
  433. Log::write(
  434. "func=".__FUNCTION__."&class=".__CLASS__."&payway=".$payway.'&data='.json_encode($_REQUEST),
  435. LOG::ERROR
  436. );
  437. exit('fail');
  438. }
  439. $_config = [];
  440. switch ($product_id) {
  441. case WalletConst::WALLET_PRODUCT_MEM_PTB:
  442. case WalletConst::WALLET_PRODUCT_MEM_GM:
  443. case WalletConst::WALLET_PRODUCT_AGENT_PTB:
  444. case WalletConst::WALLET_PRODUCT_ACCOUNT:
  445. {
  446. $_order_class = '\\huo\\controller\\wallet\\Notify';
  447. $_func = 'payNotify';
  448. }
  449. break;
  450. case WalletConst::WALLET_PRODUCT_MP:
  451. $_order_class = '\\huo\\controller\\pay\\Notify';
  452. $_func = 'payNotify';
  453. $_pay_class->setOrderClass($_order_class);
  454. $_pay_class->setFunc($_func);
  455. $_config = (new GameMini())->getPayConfByMpAppId($app_id);
  456. return $_pay_class->notifyUrl($_config);
  457. case WalletConst::WALLET_PRODUCT_WXKF:
  458. $_order_class = '\\huo\\controller\\pay\\Notify';
  459. $_func = 'payNotify';
  460. $_pay_class->setOrderClass($_order_class);
  461. $_pay_class->setFunc($_func);
  462. $_config = $_config = (new OaMchModel())->getPayConfByOaId($app_id);;
  463. return $_pay_class->notifyUrl($_config);
  464. default:
  465. {
  466. $_order_class = '\\huo\\controller\\pay\\Notify';
  467. $_func = 'payNotify';
  468. $_order_info = SdkOrderCache::ins()->getInfoByOrderId($order_id);
  469. $_app_id = get_val($_order_info, 'app_id', CommonConst::CONST_ZERO);
  470. $_config = (new PaywayConf())->getConfByAppPayway($_app_id, $payway);
  471. }
  472. }
  473. $_pay_class->setOrderClass($_order_class);
  474. $_pay_class->setFunc($_func);
  475. return $_pay_class->notifyUrl($_config);
  476. }
  477. /**
  478. * 更新游戏信息
  479. *
  480. * @param $order_id
  481. *
  482. * @return :
  483. * @author : chengshibin <csb@huosdk.com>
  484. * @date : 2019/2/18 18:33
  485. * @since : HUOSDK 8.0
  486. * @modified: 2019/2/18 18:33
  487. */
  488. public function upGameAfterPay($order_id) {
  489. $_order_data = SdkOrderCache::ins()->getInfoByOrderId($order_id);
  490. $_app_id = $_order_data['app_id'];
  491. /* 充值成功获取积分 */
  492. $_mem_id = $_order_data['mem_id'];
  493. /* 更新玩家信息 */
  494. $_mc_class = MemCache::ins();
  495. $_mem_data = $_mc_class->getInfoById($_mem_id);
  496. $_me_data = $_mc_class->getMeInfoById($_mem_id);
  497. /* 更新游戏扩展信息 */
  498. $_game_ext_class = new GameextModel();
  499. $_game_ext_data = $_game_ext_class->getDetail($_app_id);
  500. $_game_ext_data['sum_money'] += $_order_data['amount'];
  501. $_game_ext_data['sum_real_money'] += $_order_data['real_amount'];
  502. /*首充总额*/
  503. if (0 == $_me_data['last_pay_time']) {
  504. $_game_ext_data['first_pay_money'] += $_order_data['amount'];
  505. /*首付当日总额*/
  506. if (date('Y-m-d') == date('Y-m-d', $_mem_data['create_time'])) {
  507. $_game_ext_data['first_pay_sum_money'] += $_order_data['amount'];
  508. $_game_ext_data['reg_sum_money'] += $_order_data['amount'];
  509. }
  510. }
  511. $_game_ext_class->updateData($_game_ext_data, $_app_id);
  512. $_code = CommonStatus::NO_ERROR;
  513. return $this->huoError($_code, SettleStatus::getMsg($_code));
  514. }
  515. }