request('directForm',$userId,array_merge($data,['order_source'=>$source])); } public function directSubmit(int $userId, array $data, string $source): array { return $this->request('directSubmit',$userId,array_merge($data,['order_source'=>$source])); } public function directStatus(int $userId, array $data, string $source): array { return $this->request('directStatus',$userId,array_merge($data,['order_source'=>$source])); } public function options(int $userId, string $orderSource): array { return $this->request('options', $userId, ['order_source' => $orderSource]); } public function start(int $userId, array $data, string $orderSource): array { return $this->request('start', $userId, array_merge($data, ['order_source' => $orderSource])); } public function application(int $userId, string $publicId, string $submissionToken = ''): array { return $this->request('application', $userId, ['public_id' => $publicId] + ($submissionToken !== '' ? ['submission_token' => $submissionToken] : [])); } public function saveDraft(int $userId, array $data): array { return $this->request('saveDraft', $userId, $data); } public function submit(int $userId, array $data): array { return self::decorateSubmitForCheck($this->request('submit', $userId, $data)); } public function orderDetail(int $userId, array $data): array { return $this->request('orderDetail', $userId, $data); } public function orderPayment(int $userId, array $data): array { return $this->request('orderPayment', $userId, $data); } public function agree(string $docCode): array { if (!preg_match('/^[A-Za-z0-9._-]{1,64}$/', $docCode)) return ['code' => 0, 'msg' => '协议参数错误', 'data' => []]; return $this->request('agree', 0, ['doc_code' => $docCode]); } public static function decorateSubmitForCheck(array $result): array { if ((int)($result['code'] ?? 0) === 1 && !empty($result['data']['requires_payment']) && !empty($result['data']['order_sn'])) $result['data']['pay_url'] = 'https://www.qlevisa.com/index/pay/h5pay.html?order_sn=' . rawurlencode((string)$result['data']['order_sn']); return $result; } public static function nativePaymentForCheck(array $data): array { $cents = (int)($data['total_price'] ?? 0); if (empty($data['requires_payment']) || $cents <= 0 || !is_array($data['target_orders'] ?? null) || count($data['target_orders']) !== 1) return []; $target = null; foreach ($data['target_orders'] as $item) { if (is_array($item) && (int)($item['applicant_index'] ?? -1) === 0) { $target = $item; break; } } if ($target === null) $target = $data['target_orders'][0] ?? null; $table = trim((string)($target['target_table'] ?? '')); $orderSn = trim((string)($target['target_order_sn'] ?? '')); if (!preg_match('/^[A-Za-z0-9_]+$/', $table) || !preg_match('/^[A-Za-z0-9_-]{1,100}$/', $orderSn)) return []; return ['target_table' => $table, 'order_sn' => $orderSn, 'money' => intdiv($cents, 100) . '.' . str_pad((string)($cents % 100), 2, '0', STR_PAD_LEFT)]; } public function payload(int $userId, array $params = []): array { $source = (string)($params['order_source'] ?? ''); unset($params['order_source']); return ['user_token' => authcode((string)json_encode(['purpose' => 'visa_api', 'user_id' => $userId, 'order_source' => $source], JSON_UNESCAPED_UNICODE), 'ENCODE', MINIAPI_TOKEN_KEY, 120)] + $params; } public function endpoint(string $userUrl, string $action): string { if (!in_array($action, self::ACTIONS, true)) return ''; $endpoint = preg_replace('#/users/[^/?]+(?:\?.*)?$#i', '/visa_service/' . $action, rtrim(trim($userUrl), '/')); return is_string($endpoint) && $endpoint !== rtrim(trim($userUrl), '/') ? $endpoint : ''; } public function dataSource(string $type, array $filters): array { try { $query = self::dataSourceQueryForCheck($type, $filters); } catch (\RuntimeException $e) { return ['code' => 0, 'msg' => '数据源参数错误', 'data' => []]; } $url = $this->endpoint((string)config('app.user_url'), 'dataSource'); $response = $url ? Httpcurl::request($url, 'post', http_build_query($query)) : false; if (!is_array($response) || !isset($response[0]) || !empty($response[3])) { Log::warning('miniapi visa data source request failed'); return ['code' => 0, 'msg' => '数据源暂不可用', 'data' => []]; } $result = json_decode($response[0], true); return is_array($result) ? $result : ['code' => 0, 'msg' => '数据源暂不可用', 'data' => []]; } public static function dataSourceQueryForCheck(string $type, array $filters): array { if (!preg_match('/^[A-Za-z0-9_.-]+$/', $type)) throw new \RuntimeException('invalid'); $query = ['type' => $type]; foreach (self::DATA_SOURCE_PARAMS as $key) { $value = $filters[$key] ?? null; if (!is_scalar($value) || $value === '') continue; $value = (string)$value; if (strlen($value) > 128 || ($key === 'model' && !preg_match('/^[A-Za-z0-9_.-]+$/', $value)) || ($key === 'source_code' && !preg_match('/^[A-Za-z0-9._-]{1,20}$/', $value)) || (in_array($key, ['page', 'size'], true) && !preg_match('/^[1-9][0-9]*$/', $value))) continue; $query[$key] = $value; } return $query; } private function request(string $action, int $userId, array $params): array { $url = $this->endpoint((string)config('app.user_url'), $action); if ($url === '') return ['code' => 0, 'msg' => '签证服务接口地址异常', 'data' => []]; $response = Httpcurl::request($url, 'post', http_build_query($this->payload($userId, $params))); if (!is_array($response) || !isset($response[0]) || !empty($response[3])) { Log::warning('miniapi visa application request failed'); return ['code' => 0, 'msg' => '服务异常,请稍后重试', 'data' => []]; } $result = json_decode($response[0], true); return is_array($result) ? $result : ['code' => 0, 'msg' => '服务异常,请稍后重试', 'data' => []]; } }