openService = $openService; } public function create_order($data) { # $channel = Channel::find($data['distribution_channel_id']); \Log::info('$channel:'.$data['distribution_channel_id'].' param:'.json_encode($channel)); $pay_merchant = PayMerchant::find($channel->pay_merchant_id); if(empty($pay_merchant)){ \Log::info('create_order_pay_merchant_null:'.$channel->pay_merchant_id); return $this->error('50013:参数异常'); } $pay_config_info = $pay_merchant->config_info; $pay_config_infos = json_decode($pay_config_info,true); \Log::info('create_order_pay_merchant:'.json_encode($pay_merchant)); \Log::info('$pay_config_infos:');\Log::info($pay_config_infos); $insert_data = []; $insert_data['uid'] = isset($data['uid'])?$data['uid']:-1; $insert_data['price'] = isset($data['price'])?$data['price']:-1; $insert_data['status'] = 'UNPAID'; $insert_data['pay_num'] = OrderService::getChargeNum($data['uid']); $insert_data['product_id'] = isset($data['product_id'])?$data['product_id']:-1; $insert_data['distribution_channel_id'] = isset($data['distribution_channel_id'])?$data['distribution_channel_id']:-1; $insert_data['trade_no'] = isset($data['trade_no'])?$data['trade_no']:''; $insert_data['pay_merchant_source'] = $pay_merchant->source; $insert_data['pay_merchant_id'] = isset($pay_merchant->id)?$pay_merchant->id:0; $insert_data['transaction_id'] = isset($data['transaction_id'])?$data['transaction_id']:''; $insert_data['from_bid'] = isset($data['from_bid'])?$data['from_bid']:''; $insert_data['pay_end_at'] = null; $insert_data['order_type'] = isset($data['order_type'])?$data['order_type']:''; $insert_data['create_ip'] = isset($data['create_ip'])?$data['create_ip']:''; $insert_data['send_order_id'] = isset($data['send_order_id'])?$data['send_order_id']:-1; // 生成订单 OrderService::create_order($insert_data); $pay_type = $data['pay_type']; if(in_array($pay_type, ['Ali','Weixin','Byte'])){ $config = []; if($pay_type == 'Byte'){ $config = [ 'app_id'=>isset($pay_config_infos['appid'])?$pay_config_infos['appid']:'', 'secret'=>isset($pay_config_infos['secret'])?$pay_config_infos['secret']:'', 'notify_url'=>isset($pay_config_infos['notify_url'])?$pay_config_infos['notify_url']:'', 'token'=>isset($pay_config_infos['token'])?$pay_config_infos['token']:'', 'salt'=>isset($pay_config_infos['salt'])?$pay_config_infos['salt']:'', 'store_uid'=>isset($pay_config_infos['store_uid'])?$pay_config_infos['store_uid']:'',// 收款商户号,1个主体下面有多个商户号 'merchant_id'=>$channel->pay_merchant_id, 'out_order_no'=>$data['trade_no'], 'money'=>intval($data['price']*100),// 单位是分 'title'=>isset($pay_config_infos['title'])?$pay_config_infos['title']:'', 'desc'=>isset($pay_config_infos['desc'])?$pay_config_infos['desc']:'', ]; \Log::info('$config:'.json_encode($config)); $pay_client = PayFactory::getInstance($pay_type); $order_create_res = $pay_client->init($config)->applyOrderCreate($config['out_order_no'],$config['money'],$config['store_uid'],$config['title'],$config['desc'],$data['phone_type']); \Log::info('applyOrderCreateRes:'.json_encode($order_create_res)); $this->report_to_douyin($data['trade_no'],$data['openid'],0); return $order_create_res; } } } function orderCallBack($trade_no, $transaction_id,$order_id) { $order_info = OrderService::getByTradeNo($trade_no); if(empty($order_info)){ \Log::info('orderCallBack_orderinfo_isnull:'.$trade_no); return false; } // 防止重复回调 if($order_info->status == 'PAID'){ \Log::info('orderCallBack_orderinfo_hascallback_return:'.$trade_no); return false; } if(empty($transaction_id)){ \Log::info('orderCallBack_transaction_id_isnull:'.$transaction_id.' trade_no:'.$trade_no); return false; } # 签名校验 if(! $this->sign_check($order_info->pay_merchant_id)){ \Log::info('orderCallBack_sign_check_false:'.$order_info->pay_merchant_id.' trade_no:'.$trade_no); return false; } //跨日期订单处理 $is_change = $this->orderAcrossDay($order_info); DB::beginTransaction(); $uid = $order_info->uid; $user = User::find($uid); $openid = $user->openid; $distribution_channel_id = $order_info->distribution_channel_id; $product_id = $order_info->product_id; $send_order_id = $order_info->send_order_id; $fee = $order_info->price; try { //获取用户充值次数 $order_info->pay_num = $this->getChargeTimes($uid); $time_type_days = [ 'YEAR'=>365, 'HALF_YEAR'=>180, 'QUATER'=>92, 'MONTH'=>30, 'WEEK'=>7, ]; if (in_array($order_info->order_type ,['YEAR','HALF_YEAR','QUATER','MONTH','WEEK'])) { $period_order_param = [ 'uid'=>$uid, 'distribution_channel_id'=>$distribution_channel_id, 'fee'=>$fee, 'send_order_id' => $send_order_id, 'add_days' => $time_type_days[$order_info->order_type], 'trade_no' => $trade_no, 'order_type' => $order_info->order_type, ]; $this->vipPeriodOrder($period_order_param); } elseif ($order_info->order_type == 'RECHARGE') { \Log::info('RECHARGE_product_type:'.$order_info->order_type.' trade_no:'.$trade_no); $product = Product::detail($product_id); $this->userCharge($product, $uid, $fee, $trade_no); } else { \Log::info('invalid_product_type:'.$order_info->order_type.' trade_no:'.$trade_no); DB::rollback(); return false; } $order_info->status = 'PAID'; $order_info->pay_end_at = date('Y-m-d H:i:s'); $order_info->transaction_id = $transaction_id; // $order_info->tiktok_order_id = $order_id; if ($is_change) { $order_info->updated_at = $order_info->pay_end_at = date('Y-m-d H:i:s', time() + 5); } $order_info->save(); DB::commit(); } catch (\Exception $e) { \Log::error($e); DB::rollback(); return false; } // 已支付状态回传给抖音 $this->report_to_douyin($order_info->trade_no,$openid,1); return true; } function report_to_douyin($trade_no,$openid,$order_status){ /** 0:待支付 1:已支付 2:已取消(用户主动取消或者超时未支付导致的关单) 4:已核销(核销状态是整单核销,即一笔订单买了 3 个券,核销是指 3 个券核销的整单) 5:退款中 6:已退款 8:退款失败 **/ $order_detail = [ 'order_id'=>$trade_no, ]; \Log::info('report_to_douyin:trade_no:'.$trade_no.' openid:'.$openid.' order_status:'.$order_status); $push_res = $this->openService->getInstance()->pushOrder($openid,$order_status, $order_detail); \Log::info('report_to_douyin_res:'.json_encode($push_res,JSON_UNESCAPED_UNICODE)); } function sign_check($pay_merchant_id){ $pay_merchant = PayMerchant::find($pay_merchant_id); if(empty($pay_merchant)){ \Log::info('orderCallBack_pay_merchant_null:'.$pay_merchant_id); return false; } $pay_config_info = $pay_merchant->config_info; $pay_config_infos = json_decode($pay_config_info,true); \Log::info('orderCallBack_pay_merchant:'.json_encode($pay_merchant)); \Log::info('$pay_config_infos:');\Log::info($pay_config_infos); $config = [ 'app_id'=>isset($pay_config_infos['appid'])?$pay_config_infos['appid']:'', 'secret'=>isset($pay_config_infos['secret'])?$pay_config_infos['secret']:'', 'token'=>isset($pay_config_infos['token'])?$pay_config_infos['token']:'', 'salt'=>isset($pay_config_infos['salt'])?$pay_config_infos['salt']:'', ]; \Log::info('$config:'.json_encode($config)); $pay_client = PayFactory::getInstance($pay_config_infos['pay_type']); $sign_check = $pay_client->init($config)->notifyCheck(); if(!$sign_check){ \Log::info('orderCallBack_sign_check_false:'.$pay_merchant_id); return false; } \Log::info('orderCallBack_sign_check_true:'.$pay_merchant_id); return true; } function vipPeriodOrder($data) { \Log::info('start_vipPeriodOrder:uid:'.$data['uid'].' trade_no:'.$data['trade_no']); $user = User::find($data['uid']); if(empty($user)){ \Log::info('vipPeriodOrder_user_isnull:'.$data['uid']); return false; } $data['origin_end_time'] = $user->vip_limit_date; // 没设置就默认当前时间 if($user->vip_limit_date == '' || $user->vip_limit_date == '0000 00:00'){ $data['origin_end_time'] = date('Y-m-d H:i:s'); } $insert_data = []; $insert_data['uid'] = $data['uid']; $insert_data['distribution_channel_id'] = $data['distribution_channel_id']; $insert_data['fee'] = $data['fee']; $insert_data['send_order_id'] = $data['send_order_id']; $insert_data['add_days'] = $data['add_days']; $insert_data['trade_no'] = $data['trade_no']; $insert_data['begin_time'] = $data['origin_end_time'];// 原先结束的时间作为开始 $insert_data['origin_end_time'] = $data['origin_end_time']; $insert_data['end_time'] = date('Y-m-d H:i:s', strtotime($data['origin_end_time'])+$data['add_days']*86400); $insert_data['vip_type'] = $data['order_type']; // 如果已经加过时间了,就不再加、 $old = UserPeroidOrderLog::getByUidAndTradeNo($data['uid'],$data['trade_no']); \Log::info('vipPeriodOrder_old:'.json_encode($old)); if (!empty($old)) { \Log::info('vipPeriodOrder_old_is_not_null:'); return $old; } else { \Log::info('vipPeriodOrder_old_isnull:'); # 给用户延长结束时间 $user->vip_limit_date = $insert_data['end_time']; $user->save(); // 上报支付 $this->reportPay($data['uid'], $data['trade_no']); // 给邀请者发放分成收益 $this->giveRewardToFromUser($user->id, $user->from_uid, $data['fee']); # 插入记录 return UserPeroidOrderLog::firstOrCreate($insert_data); } } private function userCharge($product, $uid, $fee, $trade_no) { // 获取充值配置信息 $price = getProp($product, 'price'); $coin = (int)$price * 100; // 充值币 $given = (int)getProp($product, 'given'); // 奖励币 // 更新用户书币余额 $user = User::find($uid); if(empty($user)){ \Log::info('userCharge_user_isnull:'.$uid); return false; } $user->balance += $coin + $given; $user->charge_balance += $coin; $user->reward_balance += $given; $user->save(); // 上报支付 $this->reportPay($uid, $trade_no); // 给邀请者发放分成收益 $this->giveRewardToFromUser($user->id, $user->from_uid, $fee); return true; } public function reportPay($uid, $trade_no): bool { // 上报前判断是否符合条件(当日注册当日首充) if ((int)DB::table('orders')->where('uid', $uid)->where('status', 'PAID')->count('id') !== 0) { // 用户当天已有充值成功订单则跳过 return false; } if (DB::table('users')->where('id', $uid)->value('created_at') < date('Y-m-d 00:00:00')) { // 用户不是当天注册则跳过 return false; } # 付费回传逻辑(丢入队列) $clickid = Redis::lpop($uid.'-clickids'); if ($clickid) { $report_log = DB::table('dy_report_logs')->where(['clickid'=>$clickid, 'uid'=>$uid])->first(); if ($report_log) { // 有上报初始记录则继续 $send_order_id = getProp($report_log, 'send_order_id'); $send_order = DB::table('send_orders')->where('id', $send_order_id)->first(); if ($send_order) { // 有派单信息则继续 // 先将派单总数+1 DB::table('send_orders')->where('id', $send_order_id)->increment('report_receive_num'); // 获取当前比例和上报总数|成功数 $report_percent = getProp($send_order, 'report_percent'); $report_receive_num = (int)getProp($send_order, 'report_receive_num') + 1; $report_post_num = getProp($send_order, 'report_post_num'); if ($report_post_num/$report_receive_num*100 < $report_percent) { // 未达到上报比例才上报 $params = [ 'clickid' => $clickid, 'event_type' => 'active_pay', 'context' => [ 'ad' => [ 'callback' => $clickid, ] ], 'timestamp' => (int)(microtime(true)*1000), 'trade_no' => $trade_no, ]; ReportDy::dispatch($params)->onConnection('redis'); }else { DB::table('dy_report_logs')->where(['clickid'=>$clickid, 'event_type'=>''])->update([ 'trade_no' => $trade_no, 'event_type' => 'active_pay', 'callback_result' => '失败', 'remark' => '比例过滤' ]); } return true; } } } return false; } /** * 给邀请者发放分成收益 * @param $uid * @param $fromUid * @param $fee * @return false|void */ private function giveRewardToFromUser($uid, $fromUid, $fee) { # 给邀请者发放分成收益 $from_user = User::leftJoin('user_share_configs', 'user_share_configs.level', 'users.share_level') ->where('users.id', $fromUid)->select('users.total_earnings', 'users.enable_withdraw_earnings', 'user_share_configs.share_percent')->first(); if ($from_user) { $change_amount = round($fee * $from_user->share_percent, 2); // 写入收益明细表 $insert_log = [ 'uid' => $fromUid, 'from_uid' => $uid, 'charge_price' => $fee, 'share_percent' => $from_user->share_percent, 'withdraw_info' => '', 'charge_type' => 1, 'before_amount' => $from_user->enable_withdraw_earnings, 'change_amount' => $change_amount, 'after_amount' => $from_user->enable_withdraw_earnings + $change_amount, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]; $boolen1 = UserEarningsLogs::create($insert_log); if (!$boolen1) { \Log::error('写入收益明细失败: '.json_encode($insert_log, 256)); DB::rollBack(); return false; } $from_user->total_earnings += $change_amount; $from_user->enable_withdraw_earnings += $change_amount; $boolen2 = User::where('id', $fromUid)->update([ 'total_earnings' => $from_user->total_earnings, 'enable_withdraw_earnings' => $from_user->enable_withdraw_earnings, 'updated_at' => date('Y-m-d H:i:s'), ]); if (!$boolen2) { \Log::error('给邀请者发放分成收益失败: from_uid: '.$fromUid); DB::rollBack(); return false; } } } # 处理跨天订单,将第二天到账的订单的创建时间改为第二天,保证created_at和pay_end_at同一天,统计口径一致 function orderAcrossDay($order_info) { if (date('Y-m-d', strtotime($order_info->created_at)) == date('Y-m-d')) return false; $created_at = date('Y-m-d H:i:s', strtotime($order_info->created_at)); $trade_no = $order_info->trade_no; $init_order = [ 'distribution_channel_id' => $order_info->distribution_channel_id, 'uid' => $order_info->uid, 'product_id' => $order_info->product_id, 'price' => $order_info->price, 'pay_type' => $order_info->pay_type, 'trade_no' => 'cd-' . $trade_no, 'pay_merchant_source' => $order_info->pay_merchant_source, 'pay_merchant_id' => $order_info->pay_merchant_id, 'create_ip' => $order_info->create_ip, 'send_order_id' => $order_info->send_order_id, 'send_order_name' => $order_info->send_order_name, 'order_type' => $order_info->order_type, 'from_bid' => $order_info->from_bid, 'from_type' => $order_info->from_type, 'activity_id' => $order_info->activity_id, 'inner_send_order_id' => $order_info->inner_send_order_id, 'status' => 'UNPAID', 'transaction_id' => '', 'pay_end_at' => '0000-00-00 00:00:00', 'created_at' => $created_at, 'updated_at' => $created_at ]; try { DB::table('orders')->where('id', $order_info->id)->update([ 'created_at' => date('Y-m-d H:i:s'), 'pay_end_at' => date('Y-m-d H:i:s', time() + 5), 'updated_at' => date('Y-m-d H:i:s', time() + 5), ]); //$order_info->created_at = Carbon::now(); $order_info->created_at = date('Y-m-d H:i:s'); DB::table('orders')->insert($init_order); } catch (\Exception $e) { \Log::error('orderAcrossDay_ept:'.$e); return false; } return true; } /** * 获取用户第几次充值 * @param $uid * @return int */ function getChargeTimes($uid) { $count = OrderService::getUserChargeTimes($uid); return $count + 1; } function getByTradeNo($trade_no){ return Order::getByTradeNo($trade_no); } }