TradeOrderService.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2017~2021 LARAVEL研发中心
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://www.laravel.cn
  8. // +----------------------------------------------------------------------
  9. // | Author: laravel开发员 <laravel.qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace App\Services\Oapi;
  12. use App\Models\ApiModel;
  13. use App\Models\CapitalLogModel;
  14. use App\Models\MemberModel;
  15. use App\Models\MemberPaymentModel;
  16. use App\Models\TradeOrderModel;
  17. use App\Services\Api\MemberPaymentService;
  18. use App\Services\BaseService;
  19. use App\Services\ChatMessageService;
  20. use App\Services\Common\ApiService;
  21. use App\Services\Common\MemberService;
  22. use App\Services\ConfigService;
  23. use App\Services\RedisService;
  24. /**
  25. * 用户交易订单-服务类
  26. * Class TradeOrderService
  27. * @package App\Services\Oapi
  28. */
  29. class TradeOrderService extends BaseService
  30. {
  31. // 静态对象
  32. protected static $instance = null;
  33. /**
  34. * 构造函数
  35. * @since 2020/11/10
  36. * TradeOrderService constructor.
  37. */
  38. public function __construct()
  39. {
  40. $this->model = new TradeOrderModel();
  41. $this->memberModel = new MemberModel();
  42. $this->apiModel = new ApiModel();
  43. $this->capitalModel = new CapitalLogModel();
  44. $this->paymentModel = new MemberPaymentModel();
  45. }
  46. /**
  47. * 静态入口
  48. * @return static|null
  49. */
  50. public static function make()
  51. {
  52. if (!self::$instance) {
  53. self::$instance = (new static());
  54. }
  55. return self::$instance;
  56. }
  57. /**
  58. * @param $orderNo
  59. * @return array
  60. */
  61. public function getInfoByNo($orderNo, $userId=0)
  62. {
  63. $info = $this->model->from('trade_order as a')
  64. ->leftJoin('member as b', 'b.id', '=', 'a.business_id')
  65. ->leftJoin('member as c', 'c.id', '=', 'a.user_id')
  66. ->leftJoin('user as u', 'u.user_id', '=', 'a.business_id')
  67. ->where(['order_no'=> $orderNo])
  68. ->where(function ($query) use($userId){
  69. $query->where(['a.mark' => 1])->where('a.status', '>=', 0);
  70. if($userId>0){
  71. $query->where(['a.user_id'=> $userId]);
  72. }
  73. })
  74. ->select(['a.*', 'b.username','b.realname','b.idcard_check','b.credit','u.bond', 'c.username as c_username','c.realname as c_realname'])
  75. ->first();
  76. $info = $info? $info->toArray() : [];
  77. if($info){
  78. $overTime = ConfigService::make()->getConfigByCode('trade_order_overtime');
  79. $overTime = $overTime ? $overTime : 5;
  80. $info['idcardData'] = $info['idcard_data'] ? json_decode($info['idcard_data'], true) : [];
  81. $info['paymentData'] = $info['payment_data'] ? json_decode($info['payment_data'], true) : [];
  82. $info['create_time_text'] = $info['create_time'] ? datetime($info['create_time'], 'Y-m-d H:i:s') : '';
  83. $info['update_time_text'] = $info['update_time'] ? datetime($info['update_time'], 'Y-m-d H:i:s') : '';
  84. $info['time_text'] = $info['create_time'] ? datetime($info['create_time'], 'H:i') : '';
  85. $info['utime_text'] = $info['update_time'] ? datetime($info['update_time'], 'H:i') : '';
  86. $info['pay_time_text'] = $info['pay_time'] ? datetime($info['pay_time'], 'Y-m-d H:i') : '';
  87. $info['username_text'] = $info['username'] ? format_account($info['username']) : '';
  88. $info['c_username_text'] = $info['c_username'] ? format_account($info['c_username']) : '';
  89. $info['exception_img'] = $info['exception_img'] ? get_image_url($info['exception_img']) : '';
  90. $info['pay_img'] = $info['pay_img'] ? get_image_url($info['pay_img']) : '';
  91. $info['paymentData']['qrcode'] = isset($info['paymentData']['qrcode']) && $info['paymentData']['qrcode'] ? get_image_url($info['paymentData']['qrcode']) : '';
  92. $payType = isset($info['pay_type']) ? $info['pay_type'] : 0;
  93. $info['pay_name'] = isset($payTypes[$payType]) ? $payTypes[$payType] : '其他';
  94. $info['chat_key'] = getChatKey($info['user_id'],$info['business_id']);
  95. $overTime = max(0, intval(strtotime($info['create_time'])) + $overTime * 60 - time());
  96. $info['overtime_text'] = in_array($info['status'], [1, 2]) && $overTime ? date('i:s', $overTime) : '00:00';
  97. $info['overtime'] = $overTime;
  98. }
  99. return $info;
  100. }
  101. /**
  102. *
  103. * @param $txid 币交易单号
  104. * @return array
  105. */
  106. public function getInfoByTradeHash($txid, $userId=0)
  107. {
  108. $info = $this->model->from('trade_order as a')
  109. ->leftJoin('member as b', 'b.id', '=', 'a.business_id')
  110. ->leftJoin('member as c', 'c.id', '=', 'a.user_id')
  111. ->leftJoin('user as u', 'u.user_id', '=', 'a.business_id')
  112. ->where(['txid'=> $txid])
  113. ->where(function ($query) use($userId){
  114. $query->where(['a.mark' => 1])->where('a.status', '>=', 0);
  115. if($userId>0){
  116. $query->where(['a.user_id'=> $userId]);
  117. }
  118. })
  119. ->select(['a.*', 'b.username','b.realname','b.idcard_check','b.credit','u.bond', 'c.username as c_username','c.realname as c_realname'])
  120. ->first();
  121. $info = $info? $info->toArray() : [];
  122. if($info){
  123. $overTime = ConfigService::make()->getConfigByCode('trade_order_overtime');
  124. $overTime = $overTime ? $overTime : 5;
  125. $info['idcardData'] = $info['idcard_data'] ? json_decode($info['idcard_data'], true) : [];
  126. $info['paymentData'] = $info['payment_data'] ? json_decode($info['payment_data'], true) : [];
  127. $info['create_time_text'] = $info['create_time'] ? datetime($info['create_time'], 'Y-m-d H:i:s') : '';
  128. $info['update_time_text'] = $info['update_time'] ? datetime($info['update_time'], 'Y-m-d H:i:s') : '';
  129. $info['time_text'] = $info['create_time'] ? datetime($info['create_time'], 'H:i') : '';
  130. $info['utime_text'] = $info['update_time'] ? datetime($info['update_time'], 'H:i') : '';
  131. $info['pay_time_text'] = $info['pay_time'] ? datetime($info['pay_time'], 'Y-m-d H:i') : '';
  132. $info['username_text'] = $info['username'] ? format_account($info['username']) : '';
  133. $info['c_username_text'] = $info['c_username'] ? format_account($info['c_username']) : '';
  134. $info['exception_img'] = $info['exception_img'] ? get_image_url($info['exception_img']) : '';
  135. $info['pay_img'] = $info['pay_img'] ? get_image_url($info['pay_img']) : '';
  136. $info['paymentData']['qrcode'] = isset($info['paymentData']['qrcode']) && $info['paymentData']['qrcode'] ? get_image_url($info['paymentData']['qrcode']) : '';
  137. $payType = isset($info['pay_type']) ? $info['pay_type'] : 0;
  138. $info['pay_name'] = isset($payTypes[$payType]) ? $payTypes[$payType] : '其他';
  139. $info['chat_key'] = getChatKey($info['user_id'],$info['business_id']);
  140. $overTime = max(0, intval(strtotime($info['create_time'])) + $overTime * 60 - time());
  141. $info['overtime_text'] = in_array($info['status'], [1, 2]) && $overTime ? date('i:s', $overTime) : '00:00';
  142. $info['overtime'] = $overTime;
  143. }
  144. return $info;
  145. }
  146. /**
  147. * 获取未支付或处理的订单数
  148. * @param $userId
  149. * @param int $type
  150. * @return mixed
  151. */
  152. public function checkOrderNoCatch($userId, $type = 1)
  153. {
  154. return $this->model->where(['user_id' => $userId, 'type' => $type, 'mark' => 1])->whereIn('status', [1, 2, 5, 7])->count('id');
  155. }
  156. /**
  157. * 客户买入
  158. * @param $userId
  159. * @param $params
  160. * @return false|int|number
  161. */
  162. public function buy($userId, $params)
  163. {
  164. $num = isset($params['num']) ? floatval($params['num']) : 0;
  165. $tradeType = isset($params['trade_type']) ? intval($params['trade_type']) : 0;
  166. $apiId = isset($params['api_id']) ? intval($params['api_id']) : 0;
  167. $contactType = isset($params['contact_type']) ? intval($params['contact_type']) : 0;
  168. $usdtAddress = isset($params['usdt_address']) ? trim($params['usdt_address']) : '';
  169. $ptOrderNo = isset($params['order_no']) ? trim($params['order_no']) : '';
  170. $notifyUrl = isset($params['notify_url']) ? trim($params['notify_url']) : '';
  171. $tradeType = $tradeType==3? $tradeType : 2;
  172. // BC平台需要注册登录
  173. if ($userId <= 0 && $tradeType == 3) {
  174. $this->error = '1013';
  175. return false;
  176. }
  177. if(empty($notifyUrl)){
  178. $this->error = '6007';
  179. return false;
  180. }
  181. if(empty($ptOrderNo)){
  182. $this->error = '6006';
  183. return false;
  184. }
  185. if($apiId<=0){
  186. $this->error = '6005';
  187. return false;
  188. }
  189. if(!in_array($contactType, [1,2])){
  190. $this->error = '2209';
  191. return false;
  192. }
  193. if(empty($usdtAddress)){
  194. $this->error = '2235';
  195. return false;
  196. }
  197. // 验证参数
  198. $config = ConfigService::make()->getConfigOptionByGroup(5);
  199. $tradeOpen = isset($config['trade_usdt_open']) ? $config['trade_usdt_open'] : 0;
  200. $tradeMinNum = isset($config['trade_min_num']) ? $config['trade_min_num'] : 0;
  201. $tradeMaxNum = isset($config['trade_max_num']) ? $config['trade_max_num'] : 0;
  202. $trademinMoney = isset($config['trade_min_money']) ? $config['trade_min_money'] : 0;
  203. $tradeMaxMoney = isset($config['trade_max_money']) ? $config['trade_max_money'] : 0;
  204. $tradePrice = isset($config['usdt_buy_price']) ? $config['usdt_buy_price'] : 0;
  205. $tradeLimitNum = isset($config['trade_no_catch']) ? $config['trade_no_catch'] : 0;
  206. // 是否开启交易
  207. if ($tradeOpen != 1) {
  208. $this->error = '1013';
  209. return false;
  210. }
  211. if ($tradePrice <= 0) {
  212. $this->error = '3002';
  213. return false;
  214. }
  215. // 验证数量或金额
  216. if ($num < $tradeMinNum || $num > $tradeMaxNum) {
  217. $this->error = '3003';
  218. return false;
  219. }
  220. $total = moneyFormat($num * $tradePrice, 6);
  221. // 用户信息,BC平台需要注册认证
  222. if($tradeType == 3){
  223. $userInfo = MemberService::make()->getInfo($userId);
  224. $status = isset($userInfo['status']) ? $userInfo['status'] : 0;
  225. $idcardCheck = isset($userInfo['idcard_check']) ? $userInfo['idcard_check'] : 0;
  226. //$username = isset($userInfo['username']) && $userInfo['username'] ? format_account($userInfo['username']) : '';
  227. if ($status != 1) {
  228. $this->error = '2009';
  229. return false;
  230. }
  231. if ($idcardCheck != 1) {
  232. $this->error = '2014';
  233. return false;
  234. }
  235. }
  236. // 客户身份信息
  237. $idcardData = [
  238. 'idcard' => isset($userInfo['idcard']) ? $userInfo['idcard'] : $params['idcard'],
  239. 'realname' => isset($userInfo['realname']) ? $userInfo['realname'] : $params['realname'],
  240. 'bank_name' => isset($params['bank_name']) ? $params['bank_name'] : $params['bank_name'],
  241. 'bank_card' => isset($params['bank_card']) ? $params['bank_card'] : $params['bank_card'],
  242. 'idcard_check' => isset($userInfo['idcard_check']) ? $userInfo['idcard_check'] : (($params['realname'] && $params['idcard'])? 1:2),
  243. 'idcard_front_img' => isset($userInfo['idcard_front_img']) ? $userInfo['idcard_front_img'] : '',
  244. 'idcard_back_img' => isset($userInfo['idcard_back_img']) ? $userInfo['idcard_back_img'] : '',
  245. 'idcard_hand_img' => isset($userInfo['idcard_hand_img']) ? $userInfo['idcard_hand_img'] : '',
  246. ];
  247. $apiInfo = ApiService::make()->getInfo($apiId);
  248. $apiName = isset($apiInfo['account'])? $apiInfo['account'] : '';
  249. if(empty($apiInfo)){
  250. $this->error = '6005';
  251. return false;
  252. }
  253. // 未处理订单
  254. /*$noCatchOrder = $this->checkOrderNoCatch($userId, 1);
  255. if ($tradeLimitNum > 0 && $noCatchOrder >= $tradeLimitNum) {
  256. $this->error = lang(3005, ['num' => $tradeLimitNum]);
  257. return false;
  258. }*/
  259. // 匹配交易商家
  260. $businessInfo = \App\Services\Api\MemberService::make()->getTradeMember($num, 1, $userId);
  261. if (empty($businessInfo)) {
  262. $this->error = '3004';
  263. return false;
  264. }
  265. if($businessInfo['usdt_num']<$num){
  266. $this->error = '3004';
  267. return false;
  268. }
  269. // 币商收款方式
  270. $payment = MemberPaymentService::make()->getPayment($businessInfo['id']);
  271. if (empty($payment)) {
  272. $this->error = '3015';
  273. return false;
  274. }
  275. $this->model->startTrans();
  276. $orderNo = get_order_num('PT');
  277. $data = [
  278. 'user_id' => $userId,
  279. 'api_id' => $apiId,
  280. 'business_id' => $businessInfo['id'],
  281. 'order_no' => $orderNo,
  282. 'type' => 1,
  283. 'source' => $apiName,
  284. 'usdt_address' => $usdtAddress,
  285. 'pt_order_no' => $ptOrderNo,
  286. 'notify_url' => $notifyUrl,
  287. 'contact_type' => $contactType,
  288. 'trade_type' => $tradeType==2? $tradeType : 3,
  289. 'pay_type' => isset($params['pay_type']) ? floatval($params['pay_type']) : 1,
  290. 'price' => $tradePrice,
  291. 'num' => $num,
  292. 'total' => $total,
  293. 'payment_id' => isset($payment['id']) ? intval($payment['id']) : 0,
  294. 'idcard_data' => $idcardData ? json_encode($idcardData, 256) : '',
  295. 'payment_data' => $payment ? json_encode($payment, 256) : '',
  296. 'create_time' => time(),
  297. 'update_time' => time(),
  298. 'status' => 1,
  299. 'mark' => 1,
  300. ];
  301. if (!$order = $this->model->edit($data)) {
  302. $this->model->rollBack();
  303. $this->error = '3023';
  304. return false;
  305. }
  306. if(!$this->memberModel->where(['id'=> $businessInfo['id']])->decrement('usdt_num', $num)){
  307. $this->model->rollBack();
  308. $this->error = '3020';
  309. return false;
  310. }
  311. $data = [
  312. 'order_no'=> $orderNo,
  313. 'user_id'=> $businessInfo['id'],
  314. 'type'=> 7,
  315. 'pay_type'=> 1,
  316. 'change_type'=> 2,
  317. 'num'=> $num,
  318. 'total'=> $total,
  319. 'balance'=> floatval($businessInfo['usdt_num']-$num),
  320. 'create_time'=> time(),
  321. 'update_time'=> time(),
  322. 'status'=> 1,
  323. 'mark'=>1,
  324. 'remark'=> '交易员卖给平台',
  325. ];
  326. if(!$this->capitalModel->edit($data)){
  327. $this->model->rollBack();
  328. $this->error = '3014';
  329. return false;
  330. }
  331. // 订单通知
  332. $data = [
  333. 'from_uid' => $userId,
  334. 'to_uid' => $businessInfo['id'],
  335. 'type' => 2,
  336. 'order_no' => $orderNo,
  337. 'chat_key' => getChatKey($userId, $businessInfo['id']),
  338. 'message' => "您有来自外来客户买单订单:{$orderNo}的消息,请尽快回复!",
  339. 'message_type' => 1,
  340. 'data_type' => 2,
  341. 'create_time' => time(),
  342. 'update_time' => time(),
  343. 'status' => 1,
  344. 'mark' => 1,
  345. ];
  346. if (!ChatMessageService::make()->pushMessage($data)) {
  347. $this->model->rollBack();
  348. $this->error = '3031';
  349. return false;
  350. }
  351. $this->model->commit();
  352. $host = env('WEB_URL','');
  353. $host = $host? $host : request()->header('HOST');
  354. $token = isset($params['token'])? $params['token'] : '';
  355. $url = $host.'/h5/#/pages/h5/confirm?order_no='.$data['order_no'].'&token='.$token;
  356. return ['token'=> $token, 'order_no'=> $data['order_no'],'url'=> $url];
  357. }
  358. /**
  359. * 客户卖出
  360. * @param $userId
  361. * @param $params
  362. * @return false|int|number
  363. */
  364. public function sell($userId, $params)
  365. {
  366. $num = isset($params['num']) ? floatval($params['num']) : 0;
  367. $tradeType = isset($params['trade_type']) ? intval($params['trade_type']) : 0;
  368. $apiId = isset($params['api_id']) ? intval($params['api_id']) : 0;
  369. $contactType = isset($params['contact_type']) ? intval($params['contact_type']) : 0;
  370. $usdtAddress = isset($params['usdt_address']) ? trim($params['usdt_address']) : '';
  371. $txid = isset($params['txid']) ? trim($params['txid']) : '';
  372. $ptOrderNo = isset($params['order_no']) ? trim($params['order_no']) : '';
  373. $notifyUrl = isset($params['notify_url']) ? trim($params['notify_url']) : '';
  374. $tradeType = $tradeType==3? $tradeType : 2;
  375. if ($userId <= 0 && $tradeType == 3) {
  376. $this->error = '1013';
  377. return false;
  378. }
  379. if(empty($notifyUrl)){
  380. $this->error = '6007';
  381. return false;
  382. }
  383. if(empty($txid)){
  384. $this->error = '6008';
  385. return false;
  386. }
  387. if(empty($ptOrderNo)){
  388. $this->error = '6006';
  389. return false;
  390. }
  391. if($apiId<=0){
  392. $this->error = '2209';
  393. return false;
  394. }
  395. if(!in_array($contactType, [1,2])){
  396. $this->error = '2209';
  397. return false;
  398. }
  399. if(empty($usdtAddress)){
  400. $this->error = '2235';
  401. return false;
  402. }
  403. // 收款银行信息
  404. $bankCard = isset($params['bank_card'])? $params['bank_card'] : '';
  405. $bankName = isset($params['bank_name'])? $params['bank_name'] : '';
  406. $realname = isset($params['realname'])? $params['realname'] : '';
  407. if(empty($bankCard) || empty($bankName) || empty($realname)){
  408. $this->error = '6009';
  409. return false;
  410. }
  411. // 验证参数
  412. $config = ConfigService::make()->getConfigOptionByGroup(5);
  413. $tradeOpen = isset($config['trade_usdt_open']) ? $config['trade_usdt_open'] : 0;
  414. $tradeMinNum = isset($config['trade_min']) ? $config['trade_min'] : 0;
  415. $tradeMaxNum = isset($config['trade_max']) ? $config['trade_max'] : 0;
  416. $tradePrice = isset($config['usdt_sell_price']) ? $config['usdt_sell_price'] : 0;
  417. $tradeLimitNum = isset($config['trade_no_catch']) ? $config['trade_no_catch'] : 0;
  418. // 是否开启交易
  419. if ($tradeOpen != 1) {
  420. $this->error = '1013';
  421. return false;
  422. }
  423. if ($tradePrice <= 0) {
  424. $this->error = '3002';
  425. return false;
  426. }
  427. // 验证数量或金额
  428. $total = moneyFormat($num * $tradePrice, 6);
  429. if ($num < $tradeMinNum || ($tradeMaxNum && $num > $tradeMaxNum)) {
  430. $this->error = '3003';
  431. return false;
  432. }
  433. // 用户信息
  434. if($tradeType == 3){
  435. $userInfo = MemberService::make()->getInfo($userId);
  436. $status = isset($userInfo['status']) ? $userInfo['status'] : 0;
  437. $idcardCheck = isset($userInfo['idcard_check']) ? $userInfo['idcard_check'] : 0;
  438. $username = isset($userInfo['username']) && $userInfo['username'] ? format_account($userInfo['username']) : '';
  439. if ($status != 1) {
  440. $this->error = '2009';
  441. return false;
  442. }
  443. if ($idcardCheck != 1) {
  444. $this->error = '2014';
  445. return false;
  446. }
  447. }
  448. $idcardData = [
  449. 'idcard' => isset($userInfo['idcard']) ? $userInfo['idcard'] : $params['idcard'],
  450. 'realname' => isset($userInfo['realname']) ? $userInfo['realname'] : $params['realname'],
  451. 'bank_name' => isset($params['bank_name']) ? $params['bank_name'] : $params['bank_name'],
  452. 'bank_card' => isset($params['bank_card']) ? $params['bank_card'] : $params['bank_card'],
  453. 'idcard_check' => isset($userInfo['idcard_check']) ? $userInfo['idcard_check'] : (($params['realname'] && $params['idcard'])? 1:2),
  454. 'idcard_front_img' => isset($userInfo['idcard_front_img']) ? $userInfo['idcard_front_img'] : '',
  455. 'idcard_back_img' => isset($userInfo['idcard_back_img']) ? $userInfo['idcard_back_img'] : '',
  456. 'idcard_hand_img' => isset($userInfo['idcard_hand_img']) ? $userInfo['idcard_hand_img'] : '',
  457. ];
  458. $apiInfo = ApiService::make()->getInfo($apiId);
  459. $apiName = isset($apiInfo['account'])? $apiInfo['account'] : '';
  460. if(empty($apiInfo)){
  461. $this->error = '6005';
  462. return false;
  463. }
  464. // 未处理订单
  465. /*$noCatchOrder = $this->checkOrderNoCatch($userId, 2);
  466. if ($tradeLimitNum > 0 && $noCatchOrder >= $tradeLimitNum) {
  467. $this->error = lang(3005, ['num' => $tradeLimitNum]);
  468. return false;
  469. }*/
  470. $paymentData = [
  471. 'payment_id' => 0,
  472. 'type' => isset($params['type'])? intval($params['type']) : 1,
  473. 'logo' => isset($params['logo']) ? get_image_url($params['logo']) : '',
  474. 'real_name' => isset($params['realname'])? trim($params['realname']) : '',
  475. 'bank_name' => isset($params['bank_name'])? $params['bank_name'] : '',
  476. 'bank_card' => isset($params['bank_card'])? $params['bank_card'] : '',
  477. 'branch_name' => isset($params['branch_name'])? $params['branch_name'] : '',
  478. 'qrcode' => isset($params['qrcode']) && $params['qrcode'] ? get_image_url($params['qrcode']) : '',
  479. 'account' => isset($params['account'])? $params['account'] : '',
  480. ];
  481. $orderNo = get_order_num('PS');
  482. $data = [
  483. 'user_id' => $userId,
  484. 'api_id' => $apiId,
  485. 'business_id' => 0,
  486. 'order_no' => $orderNo,
  487. 'txid' => $txid,
  488. 'type' => 2,
  489. 'source' => $apiName,
  490. 'usdt_address' => $usdtAddress,
  491. 'pt_order_no' => $ptOrderNo,
  492. 'notify_url' => $notifyUrl,
  493. 'contact_type' => $contactType,
  494. 'trade_type' => $tradeType==2? $tradeType : 3,
  495. 'pay_type' => isset($params['pay_type']) ? floatval($params['pay_type']) : 1,
  496. 'price' => $tradePrice,
  497. 'num' => $num,
  498. 'payment_id' => 0,
  499. 'idcard_data' => json_encode($idcardData, 256),
  500. 'payment_data' => json_encode($paymentData, 256),
  501. 'total' => $total,
  502. 'create_time' => time(),
  503. 'status' => 0,
  504. 'mark' => 1,
  505. ];
  506. $this->model->startTrans();
  507. if (!$order = $this->model->edit($data)) {
  508. $this->error = '3012';
  509. $this->model->rollBack();
  510. return false;
  511. }
  512. $this->model->commit();
  513. $host = env('WEB_URL','');
  514. $host = $host? $host : request()->header('HOST');
  515. $token = isset($params['token'])? $params['token'] : '';
  516. $url = $host.'/h5/#/pages/h5/confirm?order_no='.$data['order_no'].'&token='.$token;
  517. return ['token'=> $token, 'order_no'=> $data['order_no'],'url'=> $url];
  518. }
  519. /**
  520. * 订单打款处理
  521. * @param $userId 用户ID
  522. * @param $params 打款参数
  523. * @return false
  524. */
  525. public function pay($userId, $params)
  526. {
  527. $orderId = isset($params['id']) ? $params['id'] : 0;
  528. if ($orderId <= 0) {
  529. $this->error = '1013';
  530. return false;
  531. }
  532. $orderInfo = $this->model->where(['id' => $orderId, 'mark' => 1, 'type' => 1])
  533. ->whereIn('status', [1, 2, 5, 7])
  534. ->select(['id', 'order_no','api_id','user_id', 'business_id', 'type','trade_type', 'payment_id', 'num', 'total', 'status'])
  535. ->first();
  536. $type = isset($orderInfo['type']) ? $orderInfo['type'] : 0;
  537. $tradeType = isset($orderInfo['trade_type']) ? $orderInfo['trade_type'] : 0;
  538. $apiId = isset($orderInfo['api_id']) ? $orderInfo['api_id'] : 0;
  539. $orderUserId = isset($orderInfo['user_id']) ? $orderInfo['user_id'] : 0;
  540. if (empty($orderInfo)) {
  541. $this->error = '3016';
  542. return false;
  543. }
  544. if($tradeType == 3 && ($userId<=0 || $orderUserId != $userId)){
  545. $this->error = '6012';
  546. return false;
  547. }
  548. if ($type != 1) {
  549. $this->error = '3024';
  550. return false;
  551. }
  552. if(empty($apiId)){
  553. $this->error = '6011';
  554. return false;
  555. }
  556. // 用户信息
  557. if($userId>0){
  558. $userInfo = MemberService::make()->getInfo($userId);
  559. $status = isset($userInfo['status']) ? $userInfo['status'] : 0;
  560. if ($status != 1) {
  561. $this->error = '2009';
  562. return false;
  563. }
  564. }
  565. if($apiId>0){
  566. $apiInfo = ApiService::make()->getInfo($apiId);
  567. if (empty($apiInfo)) {
  568. $this->error = '6011';
  569. return false;
  570. }
  571. }
  572. $data = [
  573. 'status' => 3,
  574. 'pay_type' => $params['pay_type'],
  575. 'pay_img' => $params['pay_img'],
  576. 'pay_remark' => $params['pay_remark'],
  577. 'pay_time' => time(),
  578. ];
  579. if (!$this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1])->update($data)) {
  580. $this->error = '3018';
  581. return false;
  582. }
  583. return true;
  584. }
  585. /**
  586. * 订单确认处理
  587. * @param $userId 用户ID
  588. * @param $params 打款参数
  589. * @return false
  590. */
  591. public function collection($userId, $params)
  592. {
  593. $orderId = isset($params['id']) ? $params['id'] : 0;
  594. if ($orderId <= 0) {
  595. $this->error = '1013';
  596. return false;
  597. }
  598. $orderInfo = $this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1, 'type' => 2])
  599. ->select(['id', 'user_id', 'order_no', 'business_id', 'payment_id', 'type', 'num', 'total', 'status'])
  600. ->first();
  601. $businessId = isset($orderInfo['business_id']) ? $orderInfo['business_id'] : 0;
  602. $tradeType = isset($orderInfo['type']) ? $orderInfo['type'] : 0;
  603. if (empty($orderInfo) || empty($businessId)) {
  604. $this->error = '3016';
  605. return false;
  606. }
  607. if ($orderInfo['status'] != 3) {
  608. $this->error = '3026';
  609. return false;
  610. }
  611. if ($tradeType != 2) {
  612. $this->error = '3024';
  613. return false;
  614. }
  615. $this->model->startTrans();
  616. // 订单状态更新
  617. if (!$this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1])->update(['status' => 4, 'update_time' => time()])) {
  618. $this->model->rollBack();
  619. $this->error = '3023';
  620. return false;
  621. }
  622. // 交易处理
  623. if ($orderInfo['num'] > 0) {
  624. $info = $this->memberModel->where(['id' => $businessId, 'status' => 1, 'mark' => 1])->select(['id', 'username','usdt_num', 'user_type'])->first();
  625. if (empty($info)) {
  626. $this->model->rollBack();
  627. $this->error = '3019';
  628. return false;
  629. }
  630. // 商家进币
  631. if (!$this->memberModel->where(['id' => $businessId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) {
  632. $this->model->rollBack();
  633. $this->error = '3019';
  634. return false;
  635. }
  636. // 明细处理
  637. $data = [
  638. 'order_no' => $orderInfo['order_no'],
  639. 'user_id' => $businessId,
  640. 'type' => 1,
  641. 'pay_type' => 1,
  642. 'change_type' => 1,
  643. 'num' => $orderInfo['num'],
  644. 'total' => $orderInfo['total'],
  645. 'balance' => floatval($info['usdt_num'] + $orderInfo['num']),
  646. 'create_time' => time(),
  647. 'remark' => '交易员买入',
  648. 'status' => 1,
  649. 'mark' => 1,
  650. ];
  651. if (!$this->capitalModel->edit($data)) {
  652. $this->error = '3014';
  653. $this->model->rollBack();
  654. return false;
  655. }
  656. }
  657. $this->model->commit();
  658. return true;
  659. }
  660. /**
  661. * 取消订单
  662. * @param $userId
  663. * @param $params
  664. * @return false
  665. */
  666. public function cancel($userId, $params)
  667. {
  668. $orderId = isset($params['id']) ? intval($params['id']) : 0;
  669. if ($orderId <= 0) {
  670. $this->error = '1013';
  671. return false;
  672. }
  673. $orderInfo = $this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1])
  674. ->select(['id', 'order_no', 'business_id', 'type', 'num', 'total', 'status'])
  675. ->first();
  676. $tradeType = isset($orderInfo['type']) ? $orderInfo['type'] : 0;
  677. $businessId = isset($orderInfo['business_id']) ? $orderInfo['business_id'] : 0;
  678. if (empty($orderInfo) || $businessId<=0) {
  679. $this->error = '3016';
  680. return false;
  681. }
  682. if ($orderInfo['status'] == 3) {
  683. $this->error = '3027';
  684. return false;
  685. }
  686. if ($orderInfo['status'] == 4) {
  687. $this->error = '3028';
  688. return false;
  689. }
  690. if ($orderInfo['status'] == 7) {
  691. $this->error = '3030';
  692. return false;
  693. }
  694. if (!in_array($orderInfo['status'], [1, 2])) {
  695. $this->error = '3029';
  696. return false;
  697. }
  698. $this->model->startTrans();
  699. // 订单状态更新
  700. $updateData = ['status' => 8, 'update_time' => time(), 'exception_remark' => '客户取消'];
  701. if (!$this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1])->update($updateData)) {
  702. $this->model->rollBack();
  703. $this->error = '3023';
  704. return false;
  705. }
  706. // 出售订单,USDT退回
  707. if ($tradeType == 2 && $orderInfo['num']>0) {
  708. $info = $this->memberModel->where(['id' => $userId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first();
  709. if (empty($info)) {
  710. $this->model->rollBack();
  711. $this->error = '3019';
  712. return false;
  713. }
  714. // 退还币给客户
  715. if (!$this->memberModel->where(['id' => $userId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) {
  716. $this->model->rollBack();
  717. $this->error = '3019';
  718. return false;
  719. }
  720. // 明细处理
  721. $data = [
  722. 'order_no' => $orderInfo['order_no'],
  723. 'user_id' => $userId,
  724. 'type' => 3,
  725. 'pay_type' => 1,
  726. 'change_type' => 1,
  727. 'num' => $orderInfo['num'],
  728. 'total' => $orderInfo['total'],
  729. 'balance' => floatval($info['usdt_num'] + $orderInfo['num']),
  730. 'create_time' => time(),
  731. 'update_time' => time(),
  732. 'remark' => '客户取消退还',
  733. 'status' => 1,
  734. 'mark' => 1,
  735. ];
  736. if (!$this->capitalModel->edit($data)) {
  737. $this->error = '3014';
  738. $this->model->rollBack();
  739. return false;
  740. }
  741. }
  742. // 买入取消
  743. else if($tradeType == 1 && $orderInfo['num']>0){
  744. $info = $this->memberModel->where(['id' => $businessId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first();
  745. if (empty($info)) {
  746. $this->model->rollBack();
  747. $this->error = '3019';
  748. return false;
  749. }
  750. // 退还币给客户
  751. if (!$this->memberModel->where(['id' => $businessId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) {
  752. $this->model->rollBack();
  753. $this->error = '3019';
  754. return false;
  755. }
  756. // 明细处理
  757. $data = [
  758. 'order_no' => $orderInfo['order_no'],
  759. 'user_id' => $businessId,
  760. 'type' => 3,
  761. 'pay_type' => 1,
  762. 'change_type' => 1,
  763. 'num' => $orderInfo['num'],
  764. 'total' => $orderInfo['total'],
  765. 'balance' => floatval($info['usdt_num'] + $orderInfo['num']),
  766. 'create_time' => time(),
  767. 'update_time' => time(),
  768. 'remark' => '客户取消退还',
  769. 'status' => 1,
  770. 'mark' => 1,
  771. ];
  772. if (!$this->capitalModel->edit($data)) {
  773. $this->error = '3014';
  774. $this->model->rollBack();
  775. return false;
  776. }
  777. }
  778. $this->model->commit();
  779. return true;
  780. }
  781. /**
  782. * 回调通知
  783. * @param $notifyUrl
  784. * @param $data
  785. * @return bool
  786. */
  787. public function notify($notifyUrl, $data)
  788. {
  789. if(empty($notifyUrl) || empty($data)){
  790. return false;
  791. }
  792. $orderNo = isset($data['transaction_id'])? $data['transaction_id'] : '';
  793. $cacheKey = "caches:notify:no_{$orderNo}";
  794. if(RedisService::get($cacheKey.'_lock')){
  795. return false;
  796. }
  797. RedisService::set($cacheKey.'_lock', ['url'=> $notifyUrl,'data'=> $data], rand(3, 5));
  798. $result = curl_api($notifyUrl, $data,[], 10);
  799. RedisService::set($cacheKey, ['url'=> $notifyUrl,'data'=> $data,'result'=> $result], 7200);
  800. return true;
  801. }
  802. }