'ok', # 小于0为失败 -1 => 'appid为空', -2 => '产品错误', -3 => 'appid无效', -4 => '余额不足或者没有传入价格', -5 => 'sign不能为空', -6 => 'nonce不能为空', -7 => 'time不能为空', -8 => 'sign已失效', -9 => 'sign验证失败', -10 => '参数错误', -100 => '请求错误', -101 => '订单不存在', -102 => '订单号重复', -103 => '请求错误,请求格式不正确,请使用json请求', -104 => '回调渠道订单号不匹配,请核对', -105 => '该产品验证规则不过,请联系管理员', -201 => '安全进价低于实际进价', -601 => '该商户已经超过日限额,下单失败', -602 => '充值失败,请核对上游', -801 => 'ip: 请求错误,不在白名单', -802 => '方法不存在', -803 => '版本号不正确', -1000 => '系统维护中', ); #找不到方法错误响应 public function __call($name, $arguments){ $this->error('请求'.$name.'错误', '{-null-}', -802); } public function initialize(): void { parent::initialize(); $this->sign_type = sysconf('sign_type'); if ($this->check) { $this->check(); } } # 获取输入的信息 public function input(): void { $this->input = input(); if (!$this->input) { $this->no(-100); } $this->appid = input('appid'); if (!$this->appid) { $this->no(-1); } $this->proid = input('proid'); if ($this->proid) { # 根据产品id 获取信息 $pinfo = \app\channel\service\ProductBaseService::instance()->getOne($this->proid); if (!$pinfo) { $this->no(-2); } $this->input['proid'] = $this->proid; $this->product = $pinfo['key']; if ($this->product != 'dhcz') { if (!str_contains($this->product, '_cash')) { $this->input['cash'] = $pinfo['value']; } } else { $this->proid = false; } } else { $product = input('product'); if ($product) { $this->product = $product; } } if (!$this->product) { $this->no(-2); } # 从数据库中或者缓存中取出商户的信息,并验证是否有效 $this->getMerchant(); if($this->product == 'dhcz_mnp'){ $this->getMnp_isp($this->input['mobile']); $this->input['product'] =$this->product; } #验证安全进价是否匹配 if($this->product && $this->product != 'query' && $this->product != '' && isset($this->input['safe_price'])){ $safe_price = $this->input['safe_price']; // $actual_price = ; } } /** * 检测输入信息是否合法 */ protected function check() { if(!str_contains($this->request->header('content_type'), "application/json"))$this->no(-103); $this->input(); $input = $this->input; if($this->product == 'dy'){ if(!isset($this->input['num'])){ $this->no(-105,'该产品需要输入数量'); } $num_cash = $this->input['cash'] * $this->input['num']??1; if($num_cash < 10){ $this->no(-105,'面值至少10元及以上'); } } $ip_white_data = $this->merchant['ip_white']; $request_ip = $this->getRealIp(); // if() $ip_white_array = $ip_white_data?explode(',', $ip_white_data):[]; if(!$ip_white_array || !in_array($request_ip, $ip_white_array)){ $this->no(-801,'ip:'.$request_ip.',请求错误,不在白名单内。'); } // var_dump(1);die; if ($this->proid && !str_contains($this->product, '_cash')) { unset($input['cash']); } if($input['appid'] != 'app574223018568' && $input['appid'] != 'app114861412096'){#测试验签 if(isset($this->input['signature'])){ $this->no(-10); } // $input['signature'] = $this->input['sign']; // unset($input['sign']); #兼容v4.0版本以下接口签名字段方法 $signature = AuthService::check($input, $this->key, $this->sign_type); if (is_numeric($signature)) { $this->no($signature); } } unset($this->input['sign']); // unset($this->input['signature']); if (isset($this->input['nonce'])) { unset($this->input['nonce']); } if (isset($this->input['time'])) { unset($this->input['time']); } } public function getRealIp(): string { $forwardedFor = $this->request->header('x-forwarded-for'); if ($forwardedFor) { // 可能有多个IP,第一个通常是真实的 $ips = explode(',', $forwardedFor); return trim($ips[0]); } return $this->request->ip(); // 如果没有X-Forwarded-For头,则返回当前请求的IP } #检测运营商并更新运营商 public function getMnp_isp($mobile) { $settingPhoneMNPStatus = sysconf('settingPhoneMNPStatus'); $this->input['old_product'] = $this->product; if ($settingPhoneMNPStatus == 1) { $mnp_cid = sysconf('PhoneMnp_cid'); $param = [ 'mobile' => $mobile, ]; if($mnp_cid){ $mnp_array = ChannelService::instance()->call('phone_mnp', $mnp_cid, $param); if(is_array($mnp_array) && isset($mnp_array['new_isp'])){ $mnp_isp = $mnp_array['new_isp']; if($mnp_isp == 1){ $this->product = 'ydcz'; $this->input['mnp_isp_array'] = $mnp_array; $this->Kou_Mnp($mobile); return $this->product ; }elseif($mnp_isp == 2){ $this->product = 'ltcz'; $this->input['mnp_isp_array'] = $mnp_array; $this->Kou_Mnp($mobile); return $this->product ; }elseif($mnp_isp == 3){ $this->product = 'dxcz'; $this->input['mnp_isp_array'] = $mnp_array; $this->Kou_Mnp($mobile); return $this->product ; } } } } $isp = Service::instance()->isp($this->input['mobile']); if ($isp == 1) { $this->product = 'ydcz'; } elseif ($isp == 2) { $this->product = 'ltcz'; } elseif ($isp == 3) { $this->product = 'dxcz'; } return $this->product ; } public function Kou_Mnp($mobile) { $new_cash = 0.002; if ($this->merchant['account_surplus'] >= $new_cash) { $account_type = 1; } elseif ($this->merchant['credit_surplus'] >= $new_cash) { $account_type = 2; } $redis = RedisService::getInstance(); // 记录扣费到 Redis // 根据 this->mid 分别记录不同用户的调用次数 $redis_key = 'merchant_cash_deduction:' . $this->mid; $deduction_count = $redis->incr($redis_key,1); if ($deduction_count % 10 == 0) { $new_cash = $new_cash*10; MerchantLogService::instance()->add($this->mid, $new_cash, $account_type, 3, 'API扣费: 业务余额扣除 - 10次调用携号转网'); // 重置 Redis 中的调用次数 $redis->set($redis_key, 0); } } # 检测开放或者维护时间 protected function checkOpenTime($opentime): void { if ($opentime && strstr($opentime, ':')) { $opentime = str_replace(':', '', $opentime); if (str_contains($opentime, '-')) { $value = explode('-', $opentime); $cur = intval(date('Hi')); $value[0] = intval($value[0]); $value[1] = intval($value[1]); if ($value[1] < $value[0]) { if ($cur >= $value[0] || $cur < $value[1]) { $this->no(-1000); } } else { if ($cur >= $value[0] && $cur < $value[1]) { $this->no(-1000); } } } } } /** * 对输入的信息进行加密 */ protected function getSignature() { $this->input(); $input = $this->input; if ($this->proid && !str_contains($this->product, '_cash')) { unset($input['cash']); } return AuthService::get($input, $this->key, $this->sign_type); } # 获取商户信息 protected function getMerchant(): void { $this->merchant = MerchantService::instance()->get($this->appid); if (!$this->merchant) { $this->no(-3); } if ($this->product != 'query') { if (empty($this->input['cash'])) { $this->no(-4); } if ($this->product == 'dxdc') { $cash = 0.23;//这里后续处理 if ($this->merchant['credit_surplus'] < $cash && $this->merchant['account_surplus'] < $cash) { $this->no(-4); } } else { if ($this->merchant['credit_surplus'] < $this->input['cash'] && $this->merchant['account_surplus'] < $this->input['cash']) { $this->no(-4); } } } $this->mid = $this->merchant['id']; if ($this->sign_type == 2) { $this->key = $this->merchant['appsecret']; } else { $this->key = $this->appid . '|' . $this->merchant['appsecret']; } } # 查找订单 protected function getOrder($order_id, $merchant_order_id = false) { $orderService = OrderService::instance(); $order = $orderService->get($order_id, $merchant_order_id, $this->mid); if (!$order) { $orderLastweekHistoryService = OrderLastweekHistoryService::instance(); $order = $orderLastweekHistoryService->get($order_id, $merchant_order_id, $this->mid); } if (!$order) { $orderLastHistoryService = OrderLastHistoryService::instance(); $order = $orderLastHistoryService->get($order_id, $merchant_order_id, $this->mid); } if (!$order) { $orderTwoHistoryService = OrderTwoHistoryService::instance(); $order = $orderTwoHistoryService->get($order_id, $merchant_order_id, $this->mid); } if (!$order) { $orderHistoryService = OrderHistoryService::instance(); $order = $orderHistoryService->get($order_id, $merchant_order_id, $this->mid); } return $order; } # 发送通知 protected function notify($order_id, $request, $status = false, $die = true) { $orderService = OrderService::instance(); $order = $orderService->get($order_id); if (!$order) { $oldorder = OrderHistoryService::instance()->get($order_id); if ($oldorder) { $channelService = ChannelService::instance(); $oldupdate = $channelService->call('notify', $oldorder['cid'], $request, false, $oldorder); $merchant = MerchantService::instance()->get($oldorder['mid']); if ($oldupdate['status'] == 3 && $oldorder['status'] == 2 && isset($merchant['callback_repeat']) && $merchant['callback_repeat'] > 2) { if ($oldorder['cid'] == '2') { OrderHistoryService::instance()->db()->where(array('order_id' => $order_id))->update(['status' => '3', 'buy_back' => '1']); if (isset($oldupdate['yes']) && $oldupdate['yes']) { echo $oldupdate['yes']; die; } echo 'success'; die; } } } } if ($order) { $up = $nup = false; if ($order['status'] != 2 && $order['status'] != 3 && $order['status'] != 5 && $order['status'] != -6) { $up = true; } if(isset($order['channel_order_id']) && $order['channel_order_id'] && isset($request['channel_order_id']) && $request['channel_order_id'] && $order['channel_order_id'] != $request['channel_order_id']) { $this->no(-104); } // var_dump($order);die; #识别是否重复回调 // $repeated // if ($order['']) if (!$status && $request) { $channelService = ChannelService::instance(); $update = $channelService->call('notify', $order['cid'], $request, false, $order); $merchant = MerchantService::instance()->get($order['mid']); if (isset($order['param']) && $order['param']) { $order['param'] = json_decode($order['param'], true); } else { $order['param'] = false; } #渠道返销 if ($update['status'] == 3 && $order['status'] == 2 && isset($merchant['callback_repeat']) && $merchant['callback_repeat'] >= 2) { // # TODO 暂时写死渠道,后续需修改为可以自主选择 if ($order['cid'] == '2') { $order->db()->where(array('order_id' => $order_id))->update(['status' => '3', 'buy_back' => '1']); if (isset($update['yes']) && $update['yes']) { echo $update['yes']; die; } echo 'success'; die; } } // echo '123';die; if (isset($order['merchant_callback_at']) && $order['merchant_callback_at']) { echo 'fail'; die; } if ($update['status'] == 3 && isset($merchant['stop']) && $merchant['stop'] > 2) { # 是否暂停 $num = $order['num'] + 1; $order_id = $order['order_id'] . '_' . $num; # 该渠道下单失败,重新下单,并暂停,等待手动启动 # 记录失败的订单信息 $orderAutoService = OrderAutoService::instance(); $estate = $orderAutoService->up($order); if ($estate) { $orderAutoService->upChannelMsg($order['order_id'], $update['status'], $update['data'], $order['num']); # 修改状态 $orderService->upStatus($order['order_id'], -5, '', false, $num); } if ($die) { if (isset($update['yes']) && $update['yes']) { echo $update['yes'];die; } echo 'success';die; } else { return true; } } elseif ($update['status'] == 3 && isset($merchant['callback_fc']) && $merchant['callback_fc'] > $order['num']) { $num = $order['num'] + 1; # 该渠道下单失败,开始复充 # 记录失败的订单信息 $orderAutoService = OrderAutoService::instance(); $estate = $orderAutoService->up($order); if ($estate) { $orderAutoService->upChannelMsg($order['order_id'], $update['status'], $update['data'], $order['num']); # 修改状态 $orderService->upStatus($order['order_id'], -4, '', $merchant['callback_fc_time'], $num); } if ($die) { if (isset($update['yes']) && $update['yes']) { echo $update['yes'];die; } echo 'success';die; } else { return true; } } elseif ($update['status'] == 3 && $merchant['callback_auto'] == 2 && !$order['apply_refund']) { # 该渠道下单失败,重新开始找另外一个渠道 $num = $order['num'] + 1; $new_order_id = $order['order_id'] . '_' . $num; // $orderAutoService = OrderAutoService::instance(); $use = $orderAutoService->get($order['order_id']); $this->use_product[] = $order['pid']; foreach ($use as $k => $v) { $this->use_product[] = $v['pid']; } $isp = false; if (isset($order['isp']) && $order['isp'] && $order['isp'] > 0) { $isp_config = \app\merchant\service\PercentService::instance()->getIsp(); $isp = $isp_config[$order['isp']]; } $new_product = $this->getProduct($order['mid'], $order['product_key'], $this->use_product, $order['cash'], $isp); if ($new_product && $order['param']) { # 记录失败的订单信息 $orderAutoService->up($order); $orderAutoService->upChannelMsg($order['order_id'], $update['status'], $update['data'], $order['num']); $channelService->use($order['mid'], $new_product, $order['param'], false, $new_order_id, $this->use_product); if ($die) { if (isset($update['yes']) && $update['yes']) { echo $update['yes'];die; } echo 'success';die; } else { return true; } } } if ($update && $up) { $nup = true; $orderService->upChannelMsg($order['id'], $update['status'], $update['data']); } } elseif ($status) { $update['status'] = $status; $update['data'] = $request; } if ($update && $order['merchant_callback_error'] != 1) { //$update['order'] = $order; $update['project_id'] = false; if (isset($order['project_id'])) { $update['project_id'] = $order['project_id']; } if (isset($order['card_id'])) { $update['card_id'] = $order['card_id']; } $update['account'] = $order['account']; $update['cash'] = $order['cash']; $update['merchant_order_id'] = $order['merchant_order_id']; $update['order_id'] = $order_id; $update['notify_num'] = $order['merchant_callback_num']; if (isset($order['isp']) && $order['isp']) { $update['isp'] = $order['isp']; } if ($order['param'] && isset($order['param']['notify']) && $order['param']['notify']) { $update['notify'] = $order['param']['notify']; } // if ($order['mid'] == 33 && $order['cid'] == 10026) { // if (isset($update['s_number'])) { // unset($update['s_number']); // } // if (isset($update['msg'])) { // unset($update['msg']); // } // } # 商户后续操作 if ($up) { if (isset($order['error_account_oper']) && $order['error_account_oper'] == 1) { # 需要进行扣费 MerchantService::instance()->up($order['id'], $order['mid'], $order['pid'], $order['product_key'], $update); } else { # 直接发送 MerchantService::instance()->notify($order['id'], $order['mid'], $order['pid'], $order['product_key'], $update); } } } if ($update) { if ($up && $nup) { $orderService->upChannelMsg($order['id'], $update['status'], $update['data']); } if ($die) { if (isset($update['yes']) && $update['yes']) { echo $update['yes'];die; } echo 'success';die; } else { return true; } } } if ($die) { echo 'fail';die; } else { return false; } } private function getProduct($mid, $product, $use_product, $cash, $isp = false) { $product_data = MerchantProductService::instance()->getAll($mid, $product); $where['mid'] = $mid; $where['pid'] = $use_product[0]; $old_data = MerchantProductService::instance()->db()->where($where)->find(); if ($product_data) { foreach ($product_data as $k => $v) { if ($v['cash']) { if ($isp && (strstr($v['cash'], '联通') || strstr($v['cash'], '移动') || strstr($v['cash'], '电信'))) { $cash = $isp; } $temp = explode(',', $v['cash']); if (!in_array($cash, $temp)) { continue; } } if (!$old_data) { $old_data = ['sort' => '10']; } if (!in_array($v['pid'], $use_product) && $v['sort'] < $old_data['sort']) { return $v['pid']; } } } return false; } # 通用签名方法,如果有渠道不同的签名方式,需要单独实现 protected function sign($request, $type = 'sha1', $suffix = '') { return AuthService::signature($request, $type, $suffix); } # 调取服务 protected function channel($merchant, $product, $async = false, $param = false, $order = '') { if (!$param) { $param = $this->input; } #Todo 2024-7-14 需要优化卡密采购是否异步,且异步无法推送问题 // if ($product == 'cardbuy') { // $async = false; // } return ChannelService::instance()->use($merchant, $product, $param, $async, $order); } /** * 返回失败的消息 * @param mixed $info * @param string $data * @param integer $code */ protected function no(int $code = 0, $info = '', $data = '{-null-}') { $msg = $this->code[$code] ?? 'error'; if ($info) { $msg .= ':' . $info; } $data = '{-null-}'; $this->error($msg, $data, $code); } /** * 返回成功的消息 * @param mixed $info * @param string $data * @param integer $code */ protected function yes($data = '{-null-}', $info = 'ok', $code = 1) { if (is_string($data) && $data != 'ok' && $data != 'success') { if ($data == '订单号重复') { return $this->no(-102); } return $this->no(-100, $data); } $this->success($info, $data, $code); } # 记录日志 protected function log($data) { Log::write('gateway', 'callback', $data); } # 队列 protected function queue($key, $value = false) { $redis = RedisService::getInstance(); if (!$value) { return $redis->pop($key); } else { return $redis->push($key, $value); } } }