'GET', 'callback' => [ $this, 'get_status' ], 'permission_callback' => [ $this, 'check_permission' ], ] ); register_rest_route( self::NAMESPACE, '/domain-verification/start', [ 'methods' => 'POST', 'callback' => [ $this, 'start' ], 'permission_callback' => [ $this, 'check_permission' ], ] ); register_rest_route( self::NAMESPACE, '/domain-verification/check', [ 'methods' => 'POST', 'callback' => [ $this, 'check' ], 'permission_callback' => [ $this, 'check_permission' ], ] ); } /** * GET /mintall/v1/domain-verification * * Returns current status from the backend, or an empty shape if verification * has never been started for this account. */ public function get_status() { $api_key = $this->get_api_key(); if ( is_wp_error( $api_key ) ) { return $api_key; } $response = Mintall_Domain::verify( $api_key ); if ( is_wp_error( $response ) ) { if ( $this->is_not_started_error( $response ) ) { return new \WP_REST_Response( $this->empty_status() ); } return $this->error_response( $response->get_error_message(), $this->status_from_wp_error( $response ) ); } return new \WP_REST_Response( $this->shape_response( $response ) ); } /** * POST /mintall/v1/domain-verification/start */ public function start() { $api_key = $this->get_api_key(); if ( is_wp_error( $api_key ) ) { return $api_key; } $response = Mintall_Domain::start( $api_key ); if ( is_wp_error( $response ) ) { return $this->error_response( $response->get_error_message(), $this->status_from_wp_error( $response ) ); } return new \WP_REST_Response( array_merge( $this->shape_response( $response ), [ 'status' => $response['status'] ?? 'pending', 'record_name' => $response['record_name'] ?? self::RECORD_NAME, 'record_value' => $response['record_value'] ?? null, ] ) ); } /** * POST /mintall/v1/domain-verification/check */ public function check() { $api_key = $this->get_api_key(); if ( is_wp_error( $api_key ) ) { return $api_key; } $response = Mintall_Domain::verify( $api_key ); if ( is_wp_error( $response ) ) { if ( $this->is_not_started_error( $response ) ) { return new \WP_REST_Response( $this->empty_status() ); } return $this->error_response( $response->get_error_message(), $this->status_from_wp_error( $response ) ); } return new \WP_REST_Response( $this->shape_response( $response ) ); } /** * Normalize a backend payload to the shape the frontend consumes, * including the current WP site host so the UI can compare it. */ private function shape_response( $response ) { return array_merge( [ 'status' => $response['status'] ?? null, 'domain' => $response['domain'] ?? null, 'record_name' => self::RECORD_NAME, 'record_value' => null, 'expires_at' => $response['expires_at'] ?? null, 'verified_at' => $response['verified_at'] ?? null, 'detail' => $response['detail'] ?? null, ], $this->current_site_info() ); } private function empty_status() { return array_merge( [ 'status' => null, 'domain' => null, 'record_name' => self::RECORD_NAME, 'record_value' => null, 'expires_at' => null, 'verified_at' => null, 'detail' => null, ], $this->current_site_info() ); } /** * Current customer-facing host derived from WP Settings → General → Site Address. * Adds a flag for hosts that DNS-TXT verification cannot work on. */ private function current_site_info() { $host = wp_parse_url( home_url(), PHP_URL_HOST ); $host = is_string( $host ) ? strtolower( $host ) : ''; return [ 'current_site_host' => $host ?: null, 'current_site_url' => home_url(), 'host_is_verifiable' => $host ? $this->is_verifiable_host( $host ) : false, ]; } private function is_verifiable_host( $host ) { if ( in_array( $host, [ 'localhost', '127.0.0.1', '::1' ], true ) ) { return false; } if ( preg_match( '/\.(local|test|localhost|internal|lan)$/i', $host ) ) { return false; } // Bare IPv4 or IPv6. if ( filter_var( $host, FILTER_VALIDATE_IP ) ) { return false; } // Single-label hostnames (e.g. "mystore") aren't reachable from public DNS. if ( strpos( $host, '.' ) === false ) { return false; } return true; } private function is_not_started_error( \WP_Error $error ) { return $error->get_error_message() === self::NOT_STARTED_SENTINEL; } private function status_from_wp_error( \WP_Error $error ) { $data = $error->get_error_data(); if ( is_array( $data ) && isset( $data['status'] ) ) { return (int) $data['status']; } return 500; } }