applications ??= new ApplicationRepository(); $this->service ??= new ApplicationService($this->applications); } public function register_routes(): void { $uuid_args = [ 'uuid' => [ 'type' => 'string', 'format' => 'uuid', 'required' => true, 'validate_callback' => static fn($value): bool => wp_is_uuid((string) $value), 'sanitize_callback' => static fn($value): string => strtolower(sanitize_text_field((string) $value)), ], ]; register_rest_route(self::NS, '/identity/applications', [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [$this, 'create'], 'permission_callback' => [$this, 'logged_in'], 'args' => [ 'type' => ['type' => 'string', 'required' => true, 'enum' => ['personal', 'enterprise'], 'sanitize_callback' => 'sanitize_key'], 'person_name' => ['type' => 'string', 'required' => true, 'minLength' => 1, 'maxLength' => 50, 'sanitize_callback' => 'sanitize_text_field'], 'id_number' => ['type' => 'string', 'required' => true, 'minLength' => 15, 'maxLength' => 18, 'sanitize_callback' => 'sanitize_text_field'], 'company_name' => ['type' => 'string', 'required' => false, 'maxLength' => 191, 'sanitize_callback' => 'sanitize_text_field'], 'company_code' => ['type' => 'string', 'required' => false, 'maxLength' => 32, 'sanitize_callback' => 'sanitize_text_field'], 'consent' => ['type' => 'boolean', 'required' => true], 'meta_info' => ['type' => 'string', 'required' => false, 'maxLength' => 65535, 'sanitize_callback' => 'sanitize_text_field'], ], ]); register_rest_route(self::NS, '/identity/applications/current', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'current'], 'permission_callback' => [$this, 'logged_in'], ]); register_rest_route(self::NS, '/identity/applications/(?P[a-f0-9-]{36})', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'get'], 'permission_callback' => [$this, 'owns_application'], 'args' => $uuid_args, ]); register_rest_route(self::NS, '/identity/applications/(?P[a-f0-9-]{36})/refresh', [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [$this, 'refresh'], 'permission_callback' => [$this, 'owns_application'], 'args' => $uuid_args, ]); register_rest_route(self::NS, '/identity/applications/(?P[a-f0-9-]{36})/manual', [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [$this, 'manual'], 'permission_callback' => [$this, 'owns_application'], 'args' => $uuid_args, ]); } public function logged_in(\WP_REST_Request $request) { if (!is_user_logged_in()) { return new \WP_Error('aksrt_identity_auth', __('请先登录', 'aksrt'), ['status' => 401]); } $nonce = (string) $request->get_header('X-WP-Nonce'); if ($nonce === '' || !wp_verify_nonce($nonce, 'wp_rest')) { return new \WP_Error('aksrt_identity_nonce', __('安全校验失败,请刷新页面后重试', 'aksrt'), ['status' => 403]); } return true; } public function owns_application(\WP_REST_Request $request) { $authenticated = $this->logged_in($request); if (is_wp_error($authenticated)) { return $authenticated; } $application = $this->application_for_request($request); if (!is_array($application) || (int) $application['user_id'] !== get_current_user_id()) { return new \WP_Error('aksrt_identity_forbidden', __('无权访问该认证申请', 'aksrt'), ['status' => 403]); } return true; } public function create(\WP_REST_Request $request): \WP_REST_Response { $params = $request->get_json_params(); $params = is_array($params) ? $params : []; $operation_key = OperationKey::from_request($request, 'identity-create', get_current_user_id()); if (is_wp_error($operation_key)) { return $this->respond($operation_key); } $request_id = strtolower(trim((string) $request->get_header('Idempotency-Key'))); $result = $this->service->create(get_current_user_id(), $params, $request_id, $operation_key); return $this->respond($result, 201); } public function current(): \WP_REST_Response { $user_id = get_current_user_id(); $data = class_exists('\\AKSRT\\User\\AuthApply') ? \AKSRT\User\AuthApply::snapshot_view($user_id) : ['user_id' => $user_id, 'current' => null, 'active' => null, 'latest' => null]; return $this->no_store(new \WP_REST_Response(['success' => true, 'data' => $data])); } public function get(\WP_REST_Request $request): \WP_REST_Response { $application = $this->application_for_request($request); if (!$application) { return $this->no_store(new \WP_REST_Response([ 'success' => false, 'code' => 'aksrt_identity_missing', 'message' => __('认证申请不存在', 'aksrt'), ], 404)); } return $this->no_store(new \WP_REST_Response(['success' => true, 'data' => $this->service->view($application)])); } public function refresh(\WP_REST_Request $request): \WP_REST_Response { $application = $this->application_for_request($request); if (!$application) { return $this->no_store(new \WP_REST_Response([ 'success' => false, 'code' => 'aksrt_identity_missing', 'message' => __('认证申请不存在', 'aksrt'), ], 404)); } $operation_key = OperationKey::from_request($request, 'identity-refresh', get_current_user_id()); if (is_wp_error($operation_key)) { return $this->respond($operation_key); } return $this->respond($this->service->refresh($application, $operation_key)); } public function manual(\WP_REST_Request $request): \WP_REST_Response { $application = $this->application_for_request($request); if (!$application) { return $this->no_store(new \WP_REST_Response([ 'success' => false, 'code' => 'aksrt_identity_missing', 'message' => __('认证申请不存在', 'aksrt'), ], 404)); } $operation_key = OperationKey::from_request($request, 'identity-manual', get_current_user_id()); if (is_wp_error($operation_key)) { return $this->respond($operation_key); } $files = $request->get_file_params(); return $this->respond($this->service->move_to_manual($application, $files['evidence'] ?? null, $operation_key)); } /** @return array|null */ private function application_for_request(\WP_REST_Request $request): ?array { $uuid = (string) $request['uuid']; if ($uuid !== $this->request_uuid) { $this->request_uuid = $uuid; $this->request_application = $this->applications->find_by_uuid($uuid); } return is_array($this->request_application) ? $this->request_application : null; } private function respond($result, int $success_status = 200): \WP_REST_Response { if (is_wp_error($result)) { $data = $result->get_error_data(); $status = is_array($data) ? (int) ($data['status'] ?? 0) : 0; if ($status < 400 || $status > 599) { $status = in_array($result->get_error_code(), ['aksrt_identity_active', 'aksrt_identity_verified', 'aksrt_identity_conflict', 'aksrt_identity_idempotency_conflict'], true) ? 409 : 400; } return $this->no_store(new \WP_REST_Response([ 'success' => false, 'code' => $result->get_error_code(), 'message' => $result->get_error_message(), ], $status)); } return $this->no_store(new \WP_REST_Response(['success' => true, 'data' => $result], $success_status)); } private function no_store(\WP_REST_Response $response): \WP_REST_Response { $response->header('Cache-Control', 'no-store, private'); $response->header('Pragma', 'no-cache'); return $response; } }