// +---------------------------------------------------------------------- namespace App\Services\Common; use App\Models\CapitalLogModel; use App\Models\MemberModel; use App\Models\MemberPaymentModel; use App\Models\TradeOrderModel; use App\Services\Api\MemberPaymentService; use App\Services\BaseService; use App\Services\ChatMessageService; use App\Services\ConfigService; use App\Services\RedisService; use App\Services\UsdtWalletService; /** * 用户交易订单-服务类 * Class TradeOrderService * @package App\Services\Common */ class TradeOrderService extends BaseService { // 静态对象 protected static $instance = null; /** * 构造函数 * @since 2020/11/10 * TradeOrderService constructor. */ public function __construct() { $this->model = new TradeOrderModel(); $this->memberModel = new MemberModel(); $this->capitalModel = new CapitalLogModel(); $this->paymentModel = new MemberPaymentModel(); } /** * 静态入口 * @return static|null */ public static function make() { if (!self::$instance) { self::$instance = (new static()); } return self::$instance; } /** * 订单列表 * @param $params * @param int $pageSize * @return array */ public function getDataList($params, $pageSize = 15) { $list = $this->model->from('trade_order as a') ->leftJoin('member as b', 'b.id', '=', 'a.business_id') ->leftJoin('member as c', 'c.id', '=', 'a.user_id') ->where(function ($query) use ($params) { $query->where(['a.mark' => 1])->where('a.status', '>', 0); $orderNo = isset($params['order_no']) && $params['order_no'] ? trim($params['order_no']) : ''; if ($orderNo) { $query->where('a.order_no', 'like', "%{$orderNo}%"); } $type = isset($params['type']) ? intval($params['type']) : 0; if ($type > 0) { $query->where(['a.type' => $type]); } $businessName = isset($params['business_name']) ? trim($params['business_name']) : ''; if ($businessName) { $query->where('b.username','like',"%{$businessName}%"); } $userName = isset($params['username']) ? trim($params['username']) : ''; if ($userName) { $query->where('c.username','like',"%{$userName}%"); } $apiId = isset($params['api_id']) ? intval($params['api_id']) : 0; if ($apiId > 0) { $query->where(['a.api_id' => $apiId]); } $tradeType = isset($params['trade_type']) ? intval($params['trade_type']) : 0; if ($tradeType > 0) { $query->where(['a.trade_type' => $tradeType]); } // 日期 $date = isset($params['date']) ? $params['date'] : []; $start = isset($date[0]) ? $date[0] : ''; $end = isset($date[1]) ? $date[1] : ''; $end = $start >= $end ? '' : $end; if ($start) { $query->where('a.create_time', '>=', strtotime($start)); } if ($end) { $query->where('a.create_time', '<', strtotime($end)); } $refundStatus = isset($params['refund_status']) ? intval($params['refund_status']) : 0; if ($refundStatus > 0) { $query->where(['a.refund_status' => $refundStatus]); } $exceptionStatus = isset($params['exception_status']) ? intval($params['exception_status']) : 0; if ($exceptionStatus > 0) { $query->where(['a.exception_status' => $exceptionStatus]); } $status = isset($params['status']) ? intval($params['status']) : 0; if ($status > 0) { $query->where(['a.status' => $status]); } $userId = isset($params['user_id']) ? $params['user_id'] : 0; if ($userId > 0) { $query->where('a.user_id', $userId); } $businessId = isset($params['business_id']) ? $params['business_id'] : 0; if ($businessId > 0) { $query->where('a.business_id', $businessId); } }) ->select(['a.*', 'b.username','b.realname as realname', 'c.username as c_username','c.realname as c_realname']) ->orderBy('a.create_time', 'desc') ->orderBy('a.id', 'desc') ->paginate($pageSize > 0 ? $pageSize : 9999999); $list = $list ? $list->toArray() : []; if ($list) { $payTypes = [1 => '银行卡', 2 => '微信', 3 => '支付宝', 4 => '其他']; $overTime = ConfigService::make()->getConfigByCode('trade_order_overtime'); $overTime = $overTime ? $overTime : 0; foreach ($list['data'] as &$item) { $item['idcardData'] = $item['idcard_data'] ? json_decode($item['idcard_data'], true) : []; $item['paymentData'] = $item['payment_data'] ? json_decode($item['payment_data'], true) : []; $item['create_time_text'] = $item['create_time'] ? datetime($item['create_time'], 'Y-m-d H:i:s') : ''; $item['update_time_text'] = $item['update_time'] ? datetime($item['update_time'], 'Y-m-d H:i:s') : ''; $item['time_text'] = $item['create_time'] ? datetime($item['create_time'], 'H:i') : ''; $item['utime_text'] = $item['update_time']>0? datetime($item['update_time'], 'm-d H:i') : datetime($item['create_time'], 'm-d H:i'); $item['pay_time_text'] = $item['pay_time'] ? datetime($item['pay_time'], 'Y-m-d H:i') : ''; $item['username_text'] = $item['username'] ? format_account($item['username']) : ''; $item['c_username_text'] = $item['c_username'] ? format_account($item['c_username']) : ''; $item['exception_img'] = $item['exception_img'] ? get_image_url($item['exception_img']) : ''; $item['exception_img1'] = $item['exception_img1'] ? get_image_url($item['exception_img1']) : ''; $item['exception_img2'] = $item['exception_img2'] ? get_image_url($item['exception_img2']) : ''; $item['refund_img'] = $item['refund_img'] ? get_image_url($item['refund_img']) : ''; $item['refund_img1'] = $item['refund_img1'] ? get_image_url($item['refund_img1']) : ''; $item['refund_img2'] = $item['refund_img2'] ? get_image_url($item['refund_img2']) : ''; $item['pay_img'] = $item['pay_img'] ? get_image_url($item['pay_img']) : ''; $item['paymentData']['qrcode'] = isset($item['paymentData']['qrcode']) && $item['paymentData']['qrcode'] ? get_image_url($item['paymentData']['qrcode']) : ''; $overTime = max(0, intval(strtotime($item['create_time'])) + $overTime * 60 - time()); $item['overtime_text'] = in_array($item['status'], [1, 2]) && $overTime ? date('i:s', $overTime) : ''; $payType = isset($item['pay_type']) ? $item['pay_type'] : 0; $item['pay_name'] = isset($payTypes[$payType]) ? $payTypes[$payType] : '其他'; $item['chat_key'] = getChatKey($item['user_id'], $item['business_id']); } } return [ 'pageSize' => $pageSize, 'total' => isset($list['total']) ? $list['total'] : 0, 'list' => isset($list['data']) ? $list['data'] : [] ]; } /** * 用户交易量 * @param $userId * @param $date * @return mixed */ public function getTotalByDate($userId, $date) { $total = $this->model->where(['business_id'=> $userId, 'status'=> 4, 'mark'=>1]) ->where(function($query) use($date){ $start = isset($date[0]) ? $date[0] : ''; $end = isset($date[1]) ? $date[1] : ''; $end = $start >= $end ? '' : $end; if ($start) { $query->where('pay_time', '>=', strtotime($start)); } if ($end) { $query->where('pay_time', '<', strtotime($end)); } })->sum('total'); return $total; } /** * 用户交易量 * @param $userId * @param $date * @return mixed */ public function getTotalByApiAndDate($apiId, $type=1, $date=[]) { $where = ['api_id'=> $apiId, 'status'=> 4, 'mark'=>1]; if($type){ $where['type'] = $type; } $total = $this->model->where($where) ->where(function($query) use($date){ $start = isset($date[0]) ? $date[0] : ''; $end = isset($date[1]) ? $date[1] : ''; $end = $start >= $end ? '' : $end; if ($start) { $query->where('pay_time', '>=', strtotime($start)); } if ($end) { $query->where('pay_time', '<', strtotime($end)); } })->sum('total'); return $total; } /** * @param $orderNo * @return array */ public function getInfoByNo($orderNo, $userId = 0) { $info = $this->model->from('trade_order as a') ->leftJoin('member as b', 'b.id', '=', 'a.business_id') ->leftJoin('member as c', 'c.id', '=', 'a.user_id') ->leftJoin('user as u', 'u.user_id', '=', 'a.business_id') ->where(['order_no' => $orderNo]) ->where(function ($query) use ($userId) { $query->where(['a.mark' => 1])->where('a.status', '>', 0); if ($userId > 0) { $query->where(['a.user_id' => $userId]); } }) ->select(['a.*', 'b.username', 'b.idcard_check', 'b.credit', 'u.bond', 'c.username as c_username']) ->first(); $info = $info ? $info->toArray() : []; if ($info) { $overTime = ConfigService::make()->getConfigByCode('trade_order_overtime'); $overTime = $overTime ? $overTime : 5; $info['idcardData'] = $info['idcard_data'] ? json_decode($info['idcard_data'], true) : []; $info['paymentData'] = $info['payment_data'] ? json_decode($info['payment_data'], true) : []; $info['create_time_text'] = $info['create_time'] ? datetime($info['create_time'], 'Y-m-d H:i:s') : ''; $info['update_time_text'] = $info['update_time'] ? datetime($info['update_time'], 'Y-m-d H:i:s') : ''; $info['time_text'] = $info['create_time'] ? datetime($info['create_time'], 'H:i') : ''; $info['utime_text'] = $info['update_time'] ? datetime($info['update_time'], 'H:i') : ''; $info['pay_time_text'] = $info['pay_time'] ? datetime($info['pay_time'], 'Y-m-d H:i') : ''; $info['username_text'] = $info['username'] ? format_account($info['username']) : ''; $info['c_username_text'] = $info['c_username'] ? format_account($info['c_username']) : ''; $info['exception_img'] = $info['exception_img'] ? get_image_url($info['exception_img']) : ''; $info['pay_img'] = $info['pay_img'] ? get_image_url($info['pay_img']) : ''; $info['paymentData']['qrcode'] = isset($info['paymentData']['qrcode']) && $info['paymentData']['qrcode'] ? get_image_url($info['paymentData']['qrcode']) : ''; $payType = isset($info['pay_type']) ? $info['pay_type'] : 0; $info['pay_name'] = isset($payTypes[$payType]) ? $payTypes[$payType] : '其他'; $info['chat_key'] = getChatKey($info['user_id'], $info['business_id']); $overTime = max(0, intval(strtotime($info['create_time'])) + $overTime * 60 - time()); $info['overtime_text'] = in_array($info['status'], [1, 2]) && $overTime ? date('i:s', $overTime) : ''; $info['overtime'] = $overTime; } return $info; } /** * 获取待处理订单 * @param $params * @return array */ public function getWaitOrder($params) { $info = $this->model->from('trade_order as a') ->leftJoin('member as b', 'b.id', '=', 'a.business_id') ->where(function ($query) use ($params) { $query->where(['a.mark' => 1])->whereIn('a.status', [1, 2, 3, 7])->where('a.status', '>', 0); $orderNo = isset($params['order_no']) && $params['order_no'] ? trim($params['order_no']) : ''; if ($orderNo) { $query->where('a.order_no', 'like', "%{$orderNo}%"); } $type = isset($params['type']) ? intval($params['type']) : 0; if ($type > 0) { $query->where(['a.type' => $type]); } $userId = isset($params['user_id']) ? $params['user_id'] : 0; if ($userId > 0) { $query->where('a.user_id', $userId); } $businessId = isset($params['business_id']) ? $params['business_id'] : 0; if ($businessId > 0) { $query->where('a.business_id', $businessId); } }) ->select(['a.id', 'a.order_no', 'a.type', 'a.pay_type', 'a.user_id', 'a.business_id', 'a.num', 'a.total', 'a.payment_data', 'a.status', 'a.create_time', 'b.username']) ->orderBy('a.create_time', 'desc') ->first(); $info = $info ? $info->toArray() : []; if ($info) { $overTime = ConfigService::make()->getConfigByCode('trade_order_overtime'); $overTime = $overTime ? $overTime : 5; $info['paymentData'] = $info['payment_data'] ? json_decode($info['payment_data'], true) : []; $info['create_time_text'] = $info['create_time'] ? format_time(strtotime($info['create_time'])) : ''; $info['time_text'] = $info['create_time'] ? datetime($info['create_time'], 'H:i') : ''; $info['username_text'] = $info['username'] ? format_account($info['username']) : ''; $info['paymentData']['qrcode'] = isset($info['paymentData']['qrcode']) && $info['paymentData']['qrcode'] ? get_image_url($info['paymentData']['qrcode']) : ''; $info['paymentData']['bank_card_text'] = isset($info['paymentData']['bank_card']) && $info['paymentData']['bank_card'] ? format_bank_card($info['paymentData']['bank_card']) : '未设置'; $info['paymentData']['account_text'] = isset($info['paymentData']['account']) && $info['paymentData']['account'] ? format_account($info['paymentData']['account']) : '未设置'; $overTime = max(0, intval(strtotime($info['create_time'])) + $overTime * 60 - time()); $info['overtime_text'] = in_array($info['status'], [1, 2]) && $overTime ? date('H:i', $overTime) : ''; } return $info; } /** * 成交交易笔数 * @param int $type 类型:1-买单,2-卖单 * @param int $bsid 承兑商用户ID * @param int $status 状态:4-成交 * @return mixed */ public function getCompleteCount($type = 1, $bsid = 0, $status = 4) { return (int)$this->model->where(['type' => $type, 'status' => $status, 'mark' => 1]) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } })->count('id'); } /** * 成交交易金额 * @param int $type 类型:1-买单,2-卖单 * @param int $bsid 承兑商用户ID * @param int $status 状态:4-成交 * @param string $field 统计字段:total-总金额(默认),num-交易USDT数量 * @return mixed */ public function getCompleteTotal($type = 1, $bsid = 0, $status = 4, $field = 'total') { $total = $this->model->where(['type' => $type, 'status' => $status, 'mark' => 1]) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } })->sum($field); return moneyFormat($total, 2); } /** * 今日成交交易金额 * @param int $type 类型:1-买单,2-卖单 * @param int $bsid 承兑商用户ID * @param int $status 状态:4-成交 * @param string $field 统计字段:total-总金额(默认),num-交易USDT数量 * @return mixed */ public function getCompleteTotalByDay($type = 1, $bsid = 0, $status = 4, $field = 'total') { $total = $this->model->where(['type' => $type, 'status' => $status, 'mark' => 1]) ->where('pay_time', '>=', strtotime(date('Y-m-d'))) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } })->sum($field); return moneyFormat($total, 6); } /** * 24小时成交交易金额 * @param int $time 时间/小时 * @param int $bsid 承兑商用户ID * @param int $status 状态:4-成交 * @param string $field 统计字段:total-总金额(默认),num-交易USDT数量 * @return mixed */ public function getCompleteTotalByTime($time = 24, $bsid = 0, $status = 4, $field = 'total') { $total = $this->model->where(['status' => $status, 'mark' => 1]) ->where('pay_time', '>=', time() - $time * 3600) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } })->sum($field); return moneyFormat($total, 4); } /** * 24小时成交交易金额 * @param int $time 时间/小时 * @param int $bsid 承兑商用户ID * @param int $status 状态:4-成交 * @param string $field 统计字段:total-总金额(默认),num-交易USDT数量 * @return mixed */ public function getCompleteCountByTime($time = 24, $bsid = 0, $status = 4, $field = 'id') { $total = $this->model->where(['status' => $status, 'mark' => 1]) ->where('pay_time', '>=', time() - $time * 3600) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } })->count($field); return intval($total); } /** * 获取某个时间段的买卖交易量比 * @param int $type 状态类型:1-成交的 * @param int $bsid 承兑商用户ID * @param int $time 时间/分钟,默认15分钟 * @return array */ public function getCountRateByTime($type = 1, $bsid = 0, $time = 15) { $where = ['mark' => 1]; if ($type == 1) { $where['status'] = 4; } $buyCount = $this->model->where(['type' => 1])->where($where) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } }) ->where('pay_time', '>=', time() - $time * 60) ->count('id'); $sellCount = $this->model->where(['type' => 2])->where($where) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } }) ->where('pay_time', '>=', time() - $time * 60) ->count('id'); $rate = ($buyCount || $sellCount) > 0 ? moneyFormat($buyCount / ($buyCount + $sellCount)) * 100 : 0; return ['buy' => $buyCount, 'sell' => $sellCount, 'buy_rate' => $rate, 'sell_rate' => ($sellCount > 0 ? 100 - $rate : 0)]; } /** * 获取某个时间段的交易成功率 * @param int $type 状态类型:1-成交的 * @param int $bsid 承兑商用户ID * @param int $time 时间/小时,默认24小时 * @return array */ public function getRateByTime($bsid = 0, $time = 24) { $successCount = $this->model->where(['status' => 4, 'mark' => 1]) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } }) ->where('pay_time', '>=', time() - $time * 3600) ->count('id'); $count = $this->model->where(['mark' => 1]) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } }) ->where(function ($query) use ($time) { $time = time() - $time * 3600; $query->where('pay_time', '>=', $time) ->orWhereRaw("(pay_time = 0 and create_time >={$time})"); }) ->count('id'); $rate = $count > 0 ? moneyFormat($successCount / $count * 100) : 0; return ['rate' => $rate, 'success' => $successCount, 'count' => $count]; } /** * 获取未支付或处理的订单数 * @param $userId * @param int $type * @return mixed */ public function checkOrderNoCatch($userId, $type = 1) { return $this->model->where(['user_id' => $userId, 'type' => $type, 'mark' => 1])->whereIn('status', [1, 2, 7])->count('id'); } /** * 客户买入 * @param $userId * @param $params * @return false|int|number */ public function buy($userId, $params) { $num = isset($params['num']) ? floatval($params['num']) : 0; $numType = isset($params['num_type']) ? intval($params['num_type']) : 1; if ($userId <= 0) { $this->error = '1013'; return false; } // 验证参数 $config = ConfigService::make()->getConfigOptionByGroup(5); $tradeOpen = isset($config['trade_usdt_open']) ? $config['trade_usdt_open'] : 0; $tradeMinNum = isset($config['trade_min_num']) ? $config['trade_min_num'] : 0; $tradeMaxNum = isset($config['trade_max_num']) ? $config['trade_max_num'] : 0; $trademinMoney = isset($config['trade_min_money']) ? $config['trade_min_money'] : 0; $tradeMaxMoney = isset($config['trade_max_money']) ? $config['trade_max_money'] : 0; $tradePrice = isset($config['usdt_buy_price']) ? $config['usdt_buy_price'] : 0; $tradeLimitNum = isset($config['trade_no_catch']) ? $config['trade_no_catch'] : 0; // 是否开启交易 if ($tradeOpen != 1) { $this->error = '1013'; return false; } if ($tradePrice <= 0) { $this->error = '3002'; return false; } // 验证数量或金额 $total = 0; if ($numType == 1) { if ($num < $tradeMinNum || $num > $tradeMaxNum) { $this->error = '3003'; return false; } $total = moneyFormat($num * $tradePrice, 6); } else { if ($num < $trademinMoney || $num > $tradeMaxMoney) { $this->error = '3003'; return false; } $total = moneyFormat($num, 6); $num = moneyFormat($num / $tradePrice, 6); } // 用户信息 $userInfo = MemberService::make()->getInfo($userId); $status = isset($userInfo['status']) ? $userInfo['status'] : 0; $idcardCheck = isset($userInfo['idcard_check']) ? $userInfo['idcard_check'] : 0; $username = isset($userInfo['username']) && $userInfo['username'] ? format_account($userInfo['username']) : ''; if ($status != 1) { $this->error = '2009'; return false; } if ($idcardCheck != 1) { $this->error = '2014'; return false; } // 未处理订单 $noCatchOrder = $this->checkOrderNoCatch($userId, 1); if ($tradeLimitNum > 0 && $noCatchOrder >= $tradeLimitNum) { $this->error = lang(3005, ['num' => $tradeLimitNum]); return false; } // 匹配交易商家 $businessInfo = \App\Services\Api\MemberService::make()->getTradeMember($num, 1, $userId); if (empty($businessInfo)) { $this->error = '3004'; return false; } if ($businessInfo['usdt_num'] < $num) { $this->error = '3004'; return false; } // 客户身份信息 $idcardData = [ 'idcard' => isset($userInfo['idcard']) ? $userInfo['idcard'] : '', 'idcard_check' => isset($userInfo['idcard_check']) ? $userInfo['idcard_check'] : 0, 'idcard_front_img' => isset($userInfo['idcard_front_img']) ? $userInfo['idcard_front_img'] : '', 'idcard_back_img' => isset($userInfo['idcard_back_img']) ? $userInfo['idcard_back_img'] : '', 'idcard_hand_img' => isset($userInfo['idcard_hand_img']) ? $userInfo['idcard_hand_img'] : '', ]; // 币商收款方式 $payment = MemberPaymentService::make()->getPayment($businessInfo['id']); if (empty($payment)) { $this->error = '3015'; return false; } if($payment['type'] == 1){ $idcardData['bank_name'] = isset($payment['bank_name'])? $payment['bank_name'] : ''; $idcardData['bank_card'] = isset($payment['bank_card'])? $payment['bank_card'] : ''; }else{ $idcardData['bank_name'] = $payment['type']==2? '微信':'支付宝'; $idcardData['bank_card'] = isset($payment['account'])? $payment['account'] : ''; } $this->model->startTrans(); $orderNo = get_order_num('OT'); $data = [ 'user_id' => $userId, 'business_id' => isset($businessInfo['id']) ? $businessInfo['id'] : 0, 'order_no' => $orderNo, 'type' => 1, 'pay_type' => isset($params['pay_type']) ? floatval($params['pay_type']) : 1, 'price' => $tradePrice, 'num' => $num, 'total' => $total, 'payment_id' => isset($payment['id']) ? intval($payment['id']) : 0, 'idcard_data' => $idcardData ? json_encode($idcardData, 256) : '', 'payment_data' => $payment ? json_encode($payment, 256) : '', 'create_time' => time(), 'update_time' => time(), 'status' => 1, 'mark' => 1, ]; if (!$order = $this->model->edit($data)) { $this->model->rollBack(); $this->error = '3023'; return false; } if (!$this->memberModel->where(['id' => $businessInfo['id']])->decrement('usdt_num', $num)) { $this->model->rollBack(); $this->error = '3020'; return false; } $data = [ 'order_no' => $orderNo, 'user_id' => $businessInfo['id'], 'type' => 2, 'pay_type' => 1, 'change_type' => 2, 'num' => $num, 'total' => $total, 'balance' => floatval($businessInfo['usdt_num'] - $num), 'create_time' => time(), 'update_time' => time(), 'status' => 1, 'mark' => 1, 'remark' => '交易员卖出', ]; if (!$this->capitalModel->edit($data)) { $this->model->rollBack(); $this->error = '3014'; return false; } // 订单通知 $data = [ 'from_uid' => $userId, 'to_uid' => $businessInfo['id'], 'type' => 2, 'order_no' => $orderNo, 'chat_key' => getChatKey($userId, $businessInfo['id']), 'message' => "您有来自客户买单订单:{$orderNo}的消息,请尽快回复!", 'message_type' => 1, 'data_type' => 2, 'create_time' => time(), 'update_time' => time(), 'status' => 1, 'mark' => 1, ]; if (!ChatMessageService::make()->pushMessage($data)) { $this->model->rollBack(); $this->error = '3031'; return false; } $this->model->commit(); $data['id'] = $order; return $data; } /** * 客户卖出 * @param $userId * @param $params * @return false|int|number */ public function sell($userId, $params) { $num = isset($params['num']) ? floatval($params['num']) : 0; if ($userId <= 0) { $this->error = '1013'; return false; } // 验证参数 $config = ConfigService::make()->getConfigOptionByGroup(5); $tradeOpen = isset($config['trade_usdt_open']) ? $config['trade_usdt_open'] : 0; $tradeMinNum = isset($config['trade_min']) ? $config['trade_min'] : 0; $tradeMaxNum = isset($config['trade_max']) ? $config['trade_max'] : 0; $tradePrice = isset($config['usdt_sell_price']) ? $config['usdt_sell_price'] : 0; $tradeLimitNum = isset($config['trade_no_catch']) ? $config['trade_no_catch'] : 0; // 是否开启交易 if ($tradeOpen != 1) { $this->error = '1013'; return false; } if ($tradePrice <= 0) { $this->error = '3002'; return false; } // 验证数量或金额 $total = moneyFormat($num * $tradePrice, 6); if ($num < $tradeMinNum || ($tradeMaxNum && $num > $tradeMaxNum)) { $this->error = '3003'; return false; } // 用户信息 $userInfo = MemberService::make()->getInfo($userId); $status = isset($userInfo['status']) ? $userInfo['status'] : 0; $idcardCheck = isset($userInfo['idcard_check']) ? $userInfo['idcard_check'] : 0; $usdtNum = isset($userInfo['usdt_num']) ? $userInfo['usdt_num'] : 0; $username = isset($userInfo['username']) && $userInfo['username'] ? format_account($userInfo['username']) : ''; if ($status != 1) { $this->error = '2009'; return false; } if ($idcardCheck != 1) { $this->error = '2014'; return false; } if ($usdtNum < $num) { $this->error = '3011'; return false; } // 未处理订单 $noCatchOrder = $this->checkOrderNoCatch($userId, 2); if ($tradeLimitNum > 0 && $noCatchOrder >= $tradeLimitNum) { $this->error = lang(3005, ['num' => $tradeLimitNum]); return false; } // 匹配交易商家 $businessInfo = \App\Services\Api\MemberService::make()->getTradeMember($num, 2, $userId); if (empty($businessInfo)) { $this->error = '3004'; return false; } $idcardData = [ 'idcard' => isset($userInfo['idcard']) ? $userInfo['idcard'] : '', 'idcard_check' => isset($userInfo['idcard_check']) ? $userInfo['idcard_check'] : 0, 'idcard_front_img' => isset($userInfo['idcard_front_img']) ? $userInfo['idcard_front_img'] : '', 'idcard_back_img' => isset($userInfo['idcard_back_img']) ? $userInfo['idcard_back_img'] : '', 'idcard_hand_img' => isset($userInfo['idcard_hand_img']) ? $userInfo['idcard_hand_img'] : '', ]; // 收款方式 $paymentId = isset($params['payment_id']) ? $params['payment_id'] : 0; $paymentInfo = MemberPaymentService::make()->getInfo($paymentId); if (empty($paymentInfo)) { $this->error = '3010'; return false; } $paymentData = [ 'payment_id' => $paymentInfo['id'], 'type' => $paymentInfo['type'], 'logo' => $paymentInfo['logo'] ? get_image_url($paymentInfo['logo']) : '', 'real_name' => $paymentInfo['real_name'], 'bank_name' => $paymentInfo['bank_name'], 'bank_card' => $paymentInfo['bank_card'], 'branch_name' => $paymentInfo['branch_name'], 'qrcode' => $paymentInfo['qrcode'] ? get_image_url($paymentInfo['qrcode']) : '', 'account' => $paymentInfo['account'], ]; if($paymentData['type'] == 1){ $idcardData['bank_name'] = isset($paymentInfo['bank_name'])? $paymentInfo['bank_name'] : ''; $idcardData['bank_card'] = isset($paymentInfo['bank_card'])? $paymentInfo['bank_card'] : ''; }else{ $idcardData['bank_name'] = $paymentData['type']==2? '微信':'支付宝'; $idcardData['bank_card'] = isset($paymentInfo['account'])? $paymentInfo['account'] : ''; } $orderNo = get_order_num('OT'); $data = [ 'user_id' => $userId, 'business_id' => isset($businessInfo['id']) ? $businessInfo['id'] : 0, 'order_no' => $orderNo, 'type' => 2, 'pay_type' => isset($params['pay_type']) ? floatval($params['pay_type']) : 1, 'price' => $tradePrice, 'num' => $num, 'payment_id' => $paymentId, 'idcard_data' => json_encode($idcardData, 256), 'payment_data' => json_encode($paymentData, 256), 'total' => $total, 'create_time' => time(), 'status' => 1, 'mark' => 1, ]; $this->model->startTrans(); if (!$order = $this->model->edit($data)) { $this->error = '3012'; $this->model->rollBack(); return false; } // 扣除币 if (!$this->memberModel->where(['id' => $userId])->decrement('usdt_num', $num)) { $this->error = '3013'; $this->model->rollBack(); return false; } // 账户明细 $data = [ 'order_no' => $data['order_no'], 'user_id' => $userId, 'type' => 2, 'change_type' => 2, 'num' => $num, 'total' => $total, 'balance' => floatval($userInfo['usdt_num'] - $num), 'create_time' => time(), 'update_time' => time(), 'remark' => '客户卖出', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } // 订单通知 $data = [ 'from_uid' => $userId, 'to_uid' => $businessInfo['id'], 'type' => 2, 'order_no' => $orderNo, 'chat_key' => getChatKey($userId, $businessInfo['id']), 'message' => "您有来自客户卖单订单:{$orderNo}的消息,请尽快回复!", 'message_type' => 1, 'data_type' => 3, 'create_time' => time(), 'update_time' => time(), 'status' => 1, 'mark' => 1, ]; if (!ChatMessageService::make()->pushMessage($data)) { $this->model->rollBack(); $this->error = '3023'; return false; } $this->model->commit(); $data['id'] = $order; return $data; } /** * 订单打款处理 * @param $userId 用户ID * @param $params 打款参数 * @return false */ public function pay($userId, $params) { $orderId = isset($params['id']) ? $params['id'] : 0; if ($orderId <= 0) { $this->error = '1013'; return false; } $orderInfo = $this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1, 'type' => 1]) ->whereIn('status', [1, 2, 5, 7]) ->select(['id', 'order_no', 'business_id', 'type', 'payment_id', 'num', 'total', 'status']) ->first(); $tradeType = isset($orderInfo['type']) ? $orderInfo['type'] : 0; if (empty($orderInfo)) { $this->error = '3016'; return false; } if ($tradeType != 1) { $this->error = '3024'; return false; } // 用户信息 $userInfo = MemberService::make()->getInfo($userId); $status = isset($userInfo['status']) ? $userInfo['status'] : 0; if ($status != 1) { $this->error = '2009'; return false; } // 交易密码 $tradePassword = isset($params['trade_password']) ? trim($params['trade_password']) : ''; $password = isset($userInfo['trade_password']) ? trim($userInfo['trade_password']) : ''; if (empty($password)) { $this->error = '2015'; return false; } if (!$tradePassword || get_password($tradePassword . md5($tradePassword . 'otc')) != $password) { $this->error = '2016'; return false; } $data = [ 'status' => 3, 'pay_type' => $params['pay_type'], 'pay_img' => $params['pay_img'], 'pay_remark' => $params['pay_remark'], 'pay_time' => time(), ]; if (!$this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1])->update($data)) { $this->error = '3018'; return false; } return true; } /** * 商家订单打款处理 * @param $userId 用户ID * @param $params 打款参数 * @return false */ public function businessPay($businessId, $params) { $orderId = isset($params['id']) ? $params['id'] : 0; if ($orderId <= 0) { $this->error = '1013'; return false; } $orderInfo = $this->model->where(['business_id' => $businessId, 'id' => $orderId, 'mark' => 1, 'type' => 2]) ->whereIn('status', [1, 2, 5, 7]) ->select(['id', 'user_id', 'order_no', 'business_id', 'type', 'payment_id', 'num', 'total', 'status']) ->first(); $tradeType = isset($orderInfo['type']) ? $orderInfo['type'] : 0; if (empty($orderInfo)) { $this->error = '3016'; return false; } if ($tradeType != 2) { $this->error = '3025'; return false; } // 用户信息 $userInfo = MemberService::make()->getInfo($businessId); $status = isset($userInfo['status']) ? $userInfo['status'] : 0; $username = isset($userInfo['username']) ? $userInfo['username'] : 0; if ($status != 1) { $this->error = '2009'; return false; } // 交易密码 $tradePassword = isset($params['trade_password']) ? trim($params['trade_password']) : ''; $password = isset($userInfo['trade_password']) ? trim($userInfo['trade_password']) : ''; if (empty($password)) { $this->error = '2015'; return false; } if (!$tradePassword || get_password($tradePassword . md5($tradePassword . 'otc')) != $password) { $this->error = '2016'; return false; } if (strpos($params['pay_img'], "temp")) { $params['pay_img'] = save_image($params['pay_img'], 'images'); } else { $params['pay_img'] = str_replace(IMG_URL, "", $params['pay_img']); } $data = [ 'status' => 3, 'pay_type' => $params['pay_type'], 'pay_img' => $params['pay_img'], 'pay_remark' => $params['pay_remark'], 'pay_time' => time(), ]; if (!$this->model->where(['business_id' => $businessId, 'id' => $orderId, 'mark' => 1])->update($data)) { $this->error = '3018'; return false; } $this->error = '3017'; return true; } /** * 订单确认处理 * @param $userId 用户ID * @param $params 打款参数 * @return false */ public function collection($userId, $params) { $orderId = isset($params['id']) ? $params['id'] : 0; if ($orderId <= 0) { $this->error = '1013'; return false; } $orderInfo = $this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1, 'type' => 2]) ->select(['id', 'user_id', 'order_no', 'business_id', 'payment_id', 'type', 'num', 'total', 'status']) ->first(); $businessId = isset($orderInfo['business_id']) ? $orderInfo['business_id'] : 0; $tradeType = isset($orderInfo['type']) ? $orderInfo['type'] : 0; if (empty($orderInfo) || empty($businessId)) { $this->error = '3016'; return false; } if ($orderInfo['status'] != 3) { $this->error = '3026'; return false; } if ($tradeType != 2) { $this->error = '3024'; return false; } $this->model->startTrans(); // 订单状态更新 if (!$this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1])->update(['status' => 4, 'update_time' => time()])) { $this->model->rollBack(); $this->error = '3023'; return false; } // 交易处理 if ($orderInfo['num'] > 0) { $info = $this->memberModel->where(['id' => $businessId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3019'; return false; } // 商家进币 if (!$this->memberModel->where(['id' => $businessId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3019'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $businessId, 'type' => 1, 'pay_type' => 1, 'change_type' => 1, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] + $orderInfo['num']), 'create_time' => time(), 'remark' => '交易员买入', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } } $this->model->commit(); return true; } /** * 商家订单确认处理 * @param $businessId 币商用户ID,平台处理时为0 * @param $params 打款参数 * @return false */ public function businessCollection($businessId, $params) { $orderId = isset($params['id']) ? $params['id'] : 0; $catchUid = isset($params['catch_uid']) ? $params['catch_uid'] : 0; $type = isset($params['type']) ? $params['type'] : 0; if ($orderId <= 0) { $this->error = '1013'; return false; } // 平台或币商处理 $where = ['id' => $orderId, 'mark' => 1]; if ($businessId) { $where['business_id'] = $businessId; } if ($type) { $where['type'] = $type; } $orderInfo = $this->model->where($where) ->select(['id', 'user_id','api_id','pt_order_no', 'order_no', 'business_id', 'type','contact_type','is_cancel','usdt_address','notify_url', 'trade_type', 'num', 'total','pay_time', 'status']) ->first(); $userId = isset($orderInfo['user_id']) ? $orderInfo['user_id'] : 0; $orderType = isset($orderInfo['type']) ? $orderInfo['type'] : 0; $tradeType = isset($orderInfo['trade_type']) ? $orderInfo['trade_type'] : 0; if (empty($orderInfo) || (empty($userId) && $tradeType != 2)) { $this->error = '3016'; return false; } $status = isset($params['status']) ? intval($params['status']) : 4; if ($status == 4 && $orderInfo['status'] != 3 && $businessId > 0) { $this->error = '3026'; return false; } // if ($status == 5 && $orderInfo['status'] == 8 && $businessId > 0) { // $this->error = '3026'; // return false; // } if ($status == 5 && $businessId <= 0 && $orderInfo['status'] == 4) { $this->error = '3026'; return false; } if ($orderType != 1 && $status != 5) { $this->error = '3024'; return false; } // 用户信息 if ($businessId > 0) { $userInfo = MemberService::make()->getInfo($businessId); $userStatus = isset($userInfo['status']) ? $userInfo['status'] : 0; $username = isset($userInfo['username']) ? $userInfo['username'] : 0; if ($userStatus != 1) { $this->error = '2009'; return false; } // 交易密码 $tradePassword = isset($params['trade_password']) ? trim($params['trade_password']) : ''; $password = isset($userInfo['trade_password']) ? trim($userInfo['trade_password']) : ''; if (empty($password)) { $this->error = '2015'; return false; } if (!$tradePassword || get_password($tradePassword . md5($tradePassword . 'otc')) != $password) { $this->error = '2016'; return false; } } $this->model->startTrans(); // 订单状态更新,异常或确认 $updateData = ['status' => $status, 'exception_catch_user' => $catchUid, 'update_time' => time()]; // 异常处理数据 if (isset($params['exception_type'])) { $updateData['exception_type'] = intval($params['exception_type']); } if (isset($params['exception_sub_type'])) { $updateData['exception_sub_type'] = intval($params['exception_sub_type']); } if (isset($params['exception_img'])) { $updateData['exception_img'] = trim($params['exception_img']); if (strpos($updateData['exception_img'], "temp")) { $updateData['exception_img'] = save_image($updateData['exception_img'], 'images'); } else { $updateData['exception_img'] = str_replace(IMG_URL, "", $updateData['exception_img']); } } if (isset($params['exception_img1'])) { $updateData['exception_img1'] = trim($params['exception_img1']); if (strpos($updateData['exception_img1'], "temp")) { $updateData['exception_img1'] = save_image($updateData['exception_img1'], 'images'); } else { $updateData['exception_img1'] = str_replace(IMG_URL, "", $updateData['exception_img1']); } } if (isset($params['exception_img2'])) { $updateData['exception_img2'] = trim($params['exception_img2']); if (strpos($updateData['exception_img2'], "temp")) { $updateData['exception_img2'] = save_image($updateData['exception_img2'], 'images'); } else { $updateData['exception_img2'] = str_replace(IMG_URL, "", $updateData['exception_img2']); } } if (isset($params['refund_img'])) { $updateData['refund_img'] = trim($params['refund_img']); if (strpos($updateData['refund_img'], "temp")) { $updateData['refund_img'] = save_image($updateData['refund_img'], 'images'); } else { $updateData['refund_img'] = str_replace(IMG_URL, "", $updateData['refund_img']); } } if (isset($params['refund_img1'])) { $updateData['refund_img1'] = trim($params['refund_img1']); if (strpos($updateData['refund_img1'], "temp")) { $updateData['refund_img1'] = save_image($updateData['refund_img1'], 'images'); } else { $updateData['refund_img1'] = str_replace(IMG_URL, "", $updateData['refund_img1']); } } if (isset($params['refund_img2'])) { $updateData['refund_img2'] = trim($params['refund_img2']); if (strpos($updateData['refund_img2'], "temp")) { $updateData['refund_img2'] = save_image($updateData['refund_img2'], 'images'); } else { $updateData['refund_img2'] = str_replace(IMG_URL, "", $updateData['refund_img2']); } } if (isset($params['refund_payment'])) { $updateData['refund_payment'] = trim($params['refund_payment']); } if (isset($params['refund_status'])) { $updateData['refund_status'] = intval($params['refund_status']); } if (isset($params['exception_remark'])) { $updateData['exception_remark'] = trim($params['exception_remark']); } if (isset($params['exception_status'])) { $updateData['exception_status'] = intval($params['exception_status']); } if (isset($params['refund_money'])) { $updateData['refund_money'] = intval($params['refund_money']); } if (!$this->model->where($where)->update($updateData)) { $this->model->rollBack(); $this->error = '3023'; return false; } // 交易处理,如果是确认状态 $exceptionType = isset($params['exception_type'])? $params['exception_type'] : 0; $exceptionStatus = isset($params['exception_status'])? $params['exception_status'] : 0; $refundStatus = isset($params['refund_status'])? $params['refund_status'] : 0; $isCancel = isset($orderInfo['is_cancel']) ? $orderInfo['is_cancel'] : 0; if ($orderInfo['num'] > 0 && ($status == 4 || ($exceptionStatus==2 && $refundStatus==0)) && $userId > 0 && $tradeType == 1) { // 若订单被系统取消过,重新扣除 if($isCancel == 1){ // 商家 $info = $this->memberModel->where(['id' => $orderInfo['business_id'], 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3048'; return false; } if($info['usdt_num'] < $orderInfo['num']){ $this->model->rollBack(); $this->error = '3004'; return false; } // 商家重新扣除币 if (!$this->memberModel->where(['id' => $orderInfo['business_id'], 'mark' => 1])->decrement('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3047'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $orderInfo['business_id'], 'type' => 2, 'pay_type' => 1, 'change_type' => 2, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] - $orderInfo['num']), 'create_time' => time(), 'update_time' => time(), 'remark' => '交易员卖出重新扣除', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } } $info = $this->memberModel->where(['id' => $userId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3019'; return false; } // 客户币进账 if (!$this->memberModel->where(['id' => $userId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3019'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $userId, 'type' => 1, 'pay_type' => 1, 'change_type' => 1, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] + $orderInfo['num']), 'create_time' => time(), 'update_time' => time(), 'remark' => '客户买入', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } // 更新收款地址数据 $paymentId = isset($orderInfo['payment_id']) ? $orderInfo['payment_id'] : 0; $info = $this->paymentModel->where(['id' => $paymentId, 'mark' => 1])->select(['id', 'used_at'])->first(); if ($paymentId && $info) { $usedAt = isset($info['used_at']) ? $info['used_at'] : ''; if ($usedAt < date('Y-m-d')) { if (!$this->paymentModel->where(['id' => $paymentId])->update(['used_num' => 1, 'used_quota' => $orderInfo['total'], 'update_time' => time()])) { $this->model->rollBack(); $this->error = '3018'; return false; } } else { $res1 = $this->paymentModel->where(['id' => $paymentId])->increment('used_num', 1); $res2 = $this->paymentModel->where(['id' => $paymentId])->increment('used_quota', floatval($orderInfo['total'])); if (!$res1 || !$res2) { $this->model->rollBack(); $this->error = '3014'; return false; } } } } // 外来平台需要出币和回调通知 else if(in_array($tradeType, [2,3]) && ($status == 4 || ($refundStatus==0 && $exceptionStatus==2))){ $usdtAddress = isset($orderInfo['usdt_address'])? $orderInfo['usdt_address'] : ''; $contactType = isset($orderInfo['contact_type'])? $orderInfo['contact_type'] : ''; $notifyUrl = isset($orderInfo['notify_url'])? $orderInfo['notify_url'] : ''; if($usdtAddress && $contactType && $notifyUrl){ // TRC 出币 if($contactType == 1 && !$result = UsdtWalletService::make()->usdtTrcTransfer($usdtAddress,$orderInfo['num'])){ $this->model->rollBack(); $this->error = '3045'; return false; } // ERC 出币 else if($contactType == 2 && !$result = UsdtWalletService::make()->usdtErcTransfer($usdtAddress,$orderInfo['num'])){ $this->model->rollBack(); $this->error = '3045'; return false; } // 更新出币数据和发送回调 $txid = isset($result['txID'])? $result['txID'] : ''; if($txid) { if (!$this->model->where($where)->update(['txid' => $txid, 'notify_num' => 1, 'notify_time' => date('Y-m-d H:i:s')])) { $this->model->rollBack(); $this->error = '3045'; return false; } // 发送回调通知 $notifyData = [ 'transaction_id' => $orderInfo['order_no'], 'order_no' => $orderInfo['pt_order_no'], 'usdt' => $orderInfo['num'], 'amount' => $orderInfo['total'], 'contact_type' => $orderInfo['contact_type'], 'usdt_address' => $orderInfo['usdt_address'], 'hash' => $txid, 'status'=> 1, 'pay_time'=> $orderInfo['pay_time']? $orderInfo['pay_time'] : time(), 'service_time'=> time(), ]; \App\Services\Oapi\TradeOrderService::make()->notify($notifyUrl, $notifyData); } } } $this->model->commit(); $this->error = $status == 5 ? 3032 : 3021; return true; } /** * 取消订单 * @param $userId * @param $params * @return false */ public function cancel($userId, $params) { $orderId = isset($params['id']) ? intval($params['id']) : 0; if ($orderId <= 0) { $this->error = '1013'; return false; } $orderInfo = $this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1]) ->select(['id', 'order_no', 'business_id', 'type', 'num', 'total', 'status']) ->first(); $tradeType = isset($orderInfo['type']) ? $orderInfo['type'] : 0; $businessId = isset($orderInfo['business_id']) ? $orderInfo['business_id'] : 0; if (empty($orderInfo) || $businessId <= 0) { $this->error = '3016'; return false; } if ($orderInfo['status'] == 3) { $this->error = '3027'; return false; } if ($orderInfo['status'] == 4) { $this->error = '3028'; return false; } if ($orderInfo['status'] == 7) { $this->error = '3030'; return false; } if (!in_array($orderInfo['status'], [1, 2])) { $this->error = '3029'; return false; } $this->model->startTrans(); // 订单状态更新 $updateData = ['status' => 8, 'update_time' => time(), 'exception_remark' => '客户取消']; if (!$this->model->where(['user_id' => $userId, 'id' => $orderId, 'mark' => 1])->update($updateData)) { $this->model->rollBack(); $this->error = '3023'; return false; } // 出售订单,USDT退回 if ($tradeType == 2 && $orderInfo['num'] > 0) { $info = $this->memberModel->where(['id' => $userId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3019'; return false; } // 退还币给客户 if (!$this->memberModel->where(['id' => $userId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3019'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $userId, 'type' => 3, 'pay_type' => 1, 'change_type' => 1, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] + $orderInfo['num']), 'create_time' => time(), 'update_time' => time(), 'remark' => '客户取消退还', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } } // 买入取消 else if ($tradeType == 1 && $orderInfo['num'] > 0) { $info = $this->memberModel->where(['id' => $businessId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3019'; return false; } // 退还币给客户 if (!$this->memberModel->where(['id' => $businessId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3019'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $businessId, 'type' => 3, 'pay_type' => 1, 'change_type' => 1, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] + $orderInfo['num']), 'create_time' => time(), 'update_time' => time(), 'remark' => '客户取消退还', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } } $this->model->commit(); return true; } /** * 取消订单 * @param $businessId 币商用户,平台为0 * @param $params * @return false */ public function businessCancel($businessId, $params) { $orderId = isset($params['id']) ? intval($params['id']) : 0; $catchUid = isset($params['catch_uid']) ? intval($params['catch_uid']) : 0; if ($orderId <= 0) { $this->error = '1013'; return false; } $where = ['id' => $orderId, 'mark' => 1]; if ($businessId) { $where['business_id'] = $businessId; } $orderInfo = $this->model->where($where) ->select(['id', 'user_id', 'order_no', 'business_id', 'type','contact_type','trade_type', 'num', 'total', 'status']) ->first(); $type = isset($orderInfo['type']) ? $orderInfo['type'] : 0; $tradeType = isset($orderInfo['trade_type']) ? $orderInfo['trade_type'] : 0; $userId = isset($orderInfo['user_id']) ? $orderInfo['user_id'] : 0; $orderBusinessId = isset($orderInfo['business_id']) ? $orderInfo['business_id'] : 0; if (empty($orderInfo) || $orderBusinessId <= 0) { $this->error = '3016'; return false; } if (empty($userId) && $tradeType != 2) { $this->error = '3019'; return false; } if ($orderInfo['status'] == 4) { $this->error = '3028'; return false; } if (!in_array($orderInfo['status'], [1, 2, 3, 5, 7])) { $this->error = '3029'; return false; } // 用户信息 $status = isset($params['status']) ? $params['status'] : 8; if ($businessId > 0 && $status != 8) { $userInfo = MemberService::make()->getInfo($businessId); $status = isset($userInfo['status']) ? $userInfo['status'] : 0; $username = isset($userInfo['username']) ? $userInfo['username'] : 0; if ($status != 1) { $this->error = '2009'; return false; } // 交易密码 $tradePassword = isset($params['trade_password']) ? trim($params['trade_password']) : ''; $password = isset($userInfo['trade_password']) ? trim($userInfo['trade_password']) : ''; if (empty($password)) { $this->error = '2015'; return false; } if (!$tradePassword || get_password($tradePassword . md5($tradePassword . 'otc')) != $password) { $this->error = '2016'; return false; } } $this->model->startTrans(); // 订单状态更新,或异常处理 $updateData = ['status' => $status, 'exception_catch_user' => $catchUid, 'update_time' => time(), 'exception_remark' => $businessId? '交易员取消':'客服取消']; // 异常处理数据 if (isset($params['exception_type'])) { $updateData['exception_type'] = intval($params['exception_type']); } if (isset($params['exception_sub_type'])) { $updateData['exception_sub_type'] = intval($params['exception_sub_type']); } if (isset($params['exception_img'])) { $updateData['exception_img'] = trim($params['exception_img']); if (strpos($updateData['exception_img'], "temp")) { $updateData['exception_img'] = save_image($updateData['exception_img'], 'images'); } else { $updateData['exception_img'] = str_replace(IMG_URL, "", $updateData['exception_img']); } } if (isset($params['exception_img1'])) { $updateData['exception_img1'] = trim($params['exception_img1']); if (strpos($updateData['exception_img1'], "temp")) { $updateData['exception_img1'] = save_image($updateData['exception_img1'], 'images'); } else { $updateData['exception_img1'] = str_replace(IMG_URL, "", $updateData['exception_img1']); } } if (isset($params['exception_img2'])) { $updateData['exception_img2'] = trim($params['exception_img2']); if (strpos($updateData['exception_img2'], "temp")) { $updateData['exception_img2'] = save_image($updateData['exception_img2'], 'images'); } else { $updateData['exception_img2'] = str_replace(IMG_URL, "", $updateData['exception_img2']); } } if (isset($params['refund_img'])) { $updateData['refund_img'] = trim($params['refund_img']); if (strpos($updateData['refund_img'], "temp")) { $updateData['refund_img'] = save_image($updateData['refund_img'], 'images'); } else { $updateData['refund_img'] = str_replace(IMG_URL, "", $updateData['refund_img']); } } if (isset($params['refund_img1'])) { $updateData['refund_img1'] = trim($params['refund_img1']); if (strpos($updateData['refund_img1'], "temp")) { $updateData['refund_img1'] = save_image($updateData['refund_img1'], 'images'); } else { $updateData['refund_img1'] = str_replace(IMG_URL, "", $updateData['refund_img1']); } } if (isset($params['refund_img2'])) { $updateData['refund_img2'] = trim($params['refund_img2']); if (strpos($updateData['refund_img2'], "temp")) { $updateData['refund_img2'] = save_image($updateData['refund_img2'], 'images'); } else { $updateData['refund_img2'] = str_replace(IMG_URL, "", $updateData['refund_img2']); } } if (isset($params['refund_payment'])) { $updateData['refund_payment'] = trim($params['refund_payment']); } if (isset($params['exception_remark'])) { $updateData['exception_remark'] = trim($params['exception_remark']); } if (isset($params['exception_status'])) { $updateData['exception_status'] = intval($params['exception_status']); } if (isset($params['refund_status'])) { $updateData['refund_status'] = intval($params['refund_status']); } if (isset($params['refund_money'])) { $updateData['refund_money'] = intval($params['refund_money']); } if (!$this->model->where($where)->update($updateData)) { $this->model->rollBack(); $this->error = '3023'; return false; } // 平台内部订单取消 if($tradeType == 1){ // 出售订单,状态已取消,USDT退回 if ($type == 2 && $status == 8 && $orderInfo['num'] > 0) { $info = $this->memberModel->where(['id' => $userId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3019'; return false; } // 退还币给客户 if (!$this->memberModel->where(['id' => $userId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3019'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $userId, 'type' => 3, 'pay_type' => 1, 'change_type' => 1, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] + $orderInfo['num']), 'create_time' => time(), 'update_time' => time(), 'remark' => '交易员取消买入退还', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } } // 客户买入订单取消 else if ($type == 1 && $status == 8 && $orderInfo['num'] > 0) { $info = $this->memberModel->where(['id' => $orderBusinessId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3019'; return false; } // 退还币给客户 if (!$this->memberModel->where(['id' => $orderBusinessId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3019'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $orderBusinessId, 'type' => 3, 'pay_type' => 1, 'change_type' => 1, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] + $orderInfo['num']), 'create_time' => time(), 'update_time' => time(), 'remark' => '交易员取消卖出退还', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } } }else{ // 卖出取消不需要退平台USDT,需要客服处理,线上退还币给平台方 // 客户买入取消 if ($type == 1 && $status == 8 && $orderInfo['num'] > 0) { $info = $this->memberModel->where(['id' => $orderBusinessId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3019'; return false; } // 退还币给客户 if (!$this->memberModel->where(['id' => $orderBusinessId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3019'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $orderBusinessId, 'type' => 3, 'pay_type' => 1, 'change_type' => 1, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] + $orderInfo['num']), 'create_time' => time(), 'update_time' => time(), 'remark' => '交易员取消卖出退还', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } } } $this->model->commit(); $this->error = $status == 5 ? 3032 : 3034; return true; } /** * 无效订单处理 * @return false */ public function catchInvalidOrder() { $cacheKey = "caches:trades:cancels:"; if (RedisService::get($cacheKey . 'lock')) { return false; } RedisService::set($cacheKey . 'lock', 1, rand(3, 5)); $overtime = ConfigService::make()->getConfigByCode('trade_order_overtime'); $cancelTime = ConfigService::make()->getConfigByCode('trade_order_cancel'); $catchNum = ConfigService::make()->getConfigByCode('trade_order_catch_num'); $catchNum = $catchNum > 0 ? $catchNum : 200; // 处理超时订单 if ($overtime > 0) { $this->model->where(['mark' => 1]) ->where('status', '<=', 2) ->where('status', '>', 0) ->where('create_time', '<=', time() - $overtime * 60) ->update(['status' => 7, 'catch_at' => time()]); } if ($cancelTime <= 0) { $this->error = '1023'; return false; } $fail = 0; $success = 0; $this->model->where(function ($query) use ($cancelTime) { // 已更新为超时的订单 $query->where(['mark' => 1, 'status' => 7]) ->where('catch_at', '<=', time() - $cancelTime * 60); }) ->orWhere(function ($query) use ($cancelTime, $overtime) { $query->where('mark', '=', 1) ->where('status', '<=', 2) ->where('status', '>', 0) ->where('create_time', '<=', time() - ($cancelTime + $overtime) * 60); }) ->select(['id', 'user_id', 'business_id', 'order_no', 'type', 'trade_type', 'num', 'total']) ->take($catchNum) ->get() ->each(function ($item, $k) use ($cacheKey, &$fail, &$success) { // 客户卖出订单退还 $date = date('Y-m-d H:i:s'); $type = isset($item['type']) ? $item['type'] : 0; if ($type == 2) { // 内部订单退还USDT给客户 if ($item['trade_type'] == 1) { if (!$this->orderReback($item['user_id'], $item)) { $fail++; RedisService::set($cacheKey . "order_{$item['order_no']}:u{$item['user_id']}_fail", ['order' => $item, 'msg' => lang($this->error), 'date' => $date], 3600); } else { $success++; RedisService::set($cacheKey . "order_{$item['order_no']}:u{$item['user_id']}_success", ['order' => $item, 'msg' => lang($this->error), 'date' => $date], 3600); } } } else { if (!$this->orderReback($item['business_id'], $item)) { $fail++; RedisService::set($cacheKey . "order_{$item['order_no']}:b{$item['business_id']}_fail", ['order' => $item, 'msg' => lang($this->error), 'date' => $date], 3600); } else { $success++; RedisService::set($cacheKey . "order_{$item['order_no']}:b{$item['business_id']}_success", ['order' => $item, 'msg' => lang($this->error), 'date' => $date], 3600); } } }); return ['success' => $success, 'fail' => $fail]; } /** * 订单取消退还处理 * @param $userId * @param $orderInfo * @return bool */ protected function orderReback($userId, $orderInfo) { try { if ($orderInfo['num'] <= 0) { return false; } $this->model->startTrans(); $updateData = ['status' => 8, 'update_time' => time(),'is_cancel'=>1, 'catch_at' => time(),'exception_status'=> 0, 'exception_remark' => '系统取消']; if (!$this->model->where(['id' => $orderInfo['id']])->update($updateData)) { $this->model->rollBack(); $this->error = '3043'; return false; } $info = $this->memberModel->where(['id' => $userId, 'status' => 1, 'mark' => 1])->select(['id', 'username', 'usdt_num', 'user_type'])->first(); if (empty($info)) { $this->model->rollBack(); $this->error = '3019'; return false; } // 退还币给客户 if (!$this->memberModel->where(['id' => $userId, 'mark' => 1])->increment('usdt_num', $orderInfo['num'])) { $this->model->rollBack(); $this->error = '3019'; return false; } // 明细处理 $data = [ 'order_no' => $orderInfo['order_no'], 'user_id' => $userId, 'type' => 3, 'pay_type' => 1, 'change_type' => 1, 'num' => $orderInfo['num'], 'total' => $orderInfo['total'], 'balance' => floatval($info['usdt_num'] + $orderInfo['num']), 'create_time' => time(), 'update_time' => time(), 'remark' => '系统自动取消退还', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } $this->model->commit(); $this->error = '3044'; return true; } catch (\Exception $exception) { $this->error = $exception->getMessage(); return false; } } /** * 重新派单 * @param $id 订单ID * @param $businessId 新派单用户 * @return false */ public function reassign($id, $businessId) { if (empty($id) || empty($businessId)) { $this->error = '1013'; return false; } $orderInfo = $this->model->where(['id' => $id]) ->select(['id', 'user_id', 'order_no', 'business_id', 'type', 'num', 'total', 'status']) ->first(); $tradeType = isset($orderInfo['type']) ? $orderInfo['type'] : 0; if (empty($orderInfo)) { $this->error = '3016'; return false; } // 状态 if (!in_array($orderInfo['status'], [1, 2, 3, 5, 7])) { $this->error = '3039'; return false; } if ($businessId == $orderInfo['business_id']) { $this->error = '3035'; return false; } // 派单用户信息 $businessInfo = $this->memberModel->from('member as a') ->leftJoin('member_setting as ms', 'ms.user_id', '=', 'a.id') ->where(['a.id' => $businessId, 'a.status' => 1, 'a.mark' => 1]) ->select(['a.id', 'a.username', 'a.usdt_num', 'ms.buy_online', 'ms.buy_online_time', 'ms.sell_online', 'ms.sell_online_time']) ->first(); if (empty($businessInfo)) { $this->error = '3036'; return false; } // 余额 if ($businessInfo['usdt_num'] < $orderInfo['num']) { $this->error = '3036'; return false; } // 在线状态 if ($tradeType == 1) { if ($businessInfo['buy_online'] != 1 || $businessInfo['buy_online_time'] <= time()) { $this->error = '3036'; return false; } } else if ($tradeType == 2) { if ($businessInfo['sell_online'] != 1 || $businessInfo['sell_online_time'] <= time()) { $this->error = '3036'; return false; } } $payment = MemberPaymentService::make()->getPayment($businessId); if (empty($payment)) { $this->error = '3040'; return false; } $updateData = ['business_id' => $businessId, 'status' => 1, 'mark' => 1, 'create_time' => time(), 'pay_time' => 0, 'pay_img' => '', 'payment_data' => json_encode($payment, 256), 'exception_type' => 0, 'exception_remark' => '平台重新派单']; if ($this->model->where(['id' => $id])->update($updateData)) { $this->error = '3041'; return true; } $this->error = '3042'; return false; } /** * 派单交易完成通知 * @return int[] */ public function catchOrderNotify() { $fail = 0; $success = 0; $times = [1=>'30',2=>'60',3=>'120',4=>'300',5=>'600',6=>'3600']; $this->model->from('trade_order as a') ->leftJoin('member as b','b.id','=','a.business_id') ->leftJoin('member as c','c.id','=','a.user_id') ->where(function ($query) { // 已更新为超时的订单 $query->where(['a.mark' => 1]) ->where(function($query){ $query->where('a.status',4)->orWhere(['a.status'=>5,'a.exception_status'=>2,'a.refund_status'=>0]); }) ->where('a.notify_time', '<=', time() - 3700); }) ->where('a.api_id','>', 0) ->where('a.trade_type','>', 1) ->select(['a.id','a.user_id','a.business_id', 'order_no','a.txid','a.pt_order_no','a.usdt_address','a.type','a.trade_type','a.contact_type','a.status','a.price','a.num','a.total','a.notify_num','a.notify_time','a.notify_url','a.create_time','a.pay_type','a.pay_time','b.username','c.username as c_username']) ->take(200) ->get() ->each(function ($item, $k) use (&$fail, &$success, $times) { $cacheKey = "caches:task:orderNotify:".$item['order_no'].':'; // 客户卖出订单退还 $date = date('Y-m-d H:i:s'); $type = isset($item['type']) ? $item['type'] : 0; $notifyTime = isset($item['notify_time']) && $item['notify_time']? strtotime($item['notify_time']) : 0; $notifyNum = isset($item['notify_num'])? $item['notify_num'] : ''; $notifyUrl = isset($item['notify_url'])? $item['notify_url'] : ''; $txid = isset($item['txid'])? $item['txid'] : ''; $time = isset($times[$notifyNum])? $times[$notifyNum] : 0; // 已交易完成且,出币 if($txid && (($time>0 && $notifyTime>=time()-$time) || ($time==0 && $notifyNum==0))){ RedisService::set($cacheKey.'temp', ['data'=> $item,'time'=> $time,'date'=> $date], 7200); if ($this->model->where(['id'=> $item['id']])->update(['notify_num' => $notifyNum+1, 'notify_time' => date('Y-m-d H:i:s')])) { // 发送回调通知 $notifyData = [ 'transaction_id' => $item['order_no'], 'order_no' => $item['pt_order_no'], 'usdt' => $item['num'], 'amount' => $item['total'], 'contact_type' => $item['contact_type'], 'usdt_address' => $item['usdt_address'], 'hash' => $txid, 'status'=> 1, 'pay_time'=> $item['pay_time']? $item['pay_time'] : time(), 'service_time'=> time(), ]; $success++; \App\Services\Oapi\TradeOrderService::make()->notify($notifyUrl, $notifyData); } $fail++; } }); return ['success' => $success, 'fail' => $fail]; } }