123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- <?php
- namespace App\Services\Pay;
- use App\Jobs\ReportDy;
- use App\Models\Product\Product;
- use App\Models\User\User;
- use App\Models\User\UserEarningsLogs;
- use App\Models\User\UserPeroidOrderLog;
- use App\Services\Order\OrderService;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Redis;
- use Vinkla\Hashids\Facades\Hashids;
- use Ycpay\Factory as PayFactory;
- use Log;
- use App\Services\User\UserService;
- use App\Models\Channel\Channel;
- use App\Models\Order\Order;
- use App\Models\Pay\PayMerchant;
- use App\Services\OpenApi\OpenService;
- class PayService
- {
- private $openService;
- public function __construct(
- OpenService $openService
- )
- {
- $this->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);
- }
- }
|