false, 'message' => 'Not connected']; } $api_base = Defaults::merchant_api_base_url(); $api_base = rtrim($api_base, '/'); $url = $api_base . $path; $headers = [ 'Authorization' => 'Bearer ' . $token, 'Accept' => 'application/json', ]; $args = [ 'timeout' => 20, 'headers' => $headers, 'method' => strtoupper($method), ]; if ($jsonBody !== null) { $args['headers']['Content-Type'] = 'application/json'; $args['body'] = wp_json_encode($jsonBody); } $res = wp_remote_request($url, $args); if (is_wp_error($res)) { return ['ok' => false, 'message' => $res->get_error_message()]; } $status = (int) wp_remote_retrieve_response_code($res); $body = (string) wp_remote_retrieve_body($res); $data = json_decode($body, true); // If token expired unexpectedly, try one refresh then retry once. if ($status === 401) { $ref = OAuth::refresh_token(); if ($ref['ok']) { $token2 = OAuth::get_valid_access_token(); if ($token2) { $args['headers']['Authorization'] = 'Bearer ' . $token2; $res2 = wp_remote_request($url, $args); if (!is_wp_error($res2)) { $status = (int) wp_remote_retrieve_response_code($res2); $body = (string) wp_remote_retrieve_body($res2); $data = json_decode($body, true); } } } } if ($status < 200 || $status >= 300) { $msg = is_array($data) && isset($data['detail']) ? (string) $data['detail'] : $body; return ['ok' => false, 'message' => $msg, 'status' => $status]; } return ['ok' => true, 'data' => $data, 'status' => $status]; } /** * Convenience: list chat_widget channels (connections). * * @return array{ok:bool, channels?:array>, message?:string} */ public static function list_chat_widget_channels(): array { $res = self::get('/v1/channels/providers/chat_widget/connections'); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to list channels']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when listing channels']; } return ['ok' => true, 'channels' => $res['data']]; } /** * Get channel details (includes public_key). * * @return array{ok:bool, channel?:array, message?:string} */ public static function get_channel(string $channel_id): array { $res = self::get('/v1/channels/' . rawurlencode($channel_id)); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to get channel']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when fetching channel']; } return ['ok' => true, 'channel' => $res['data']]; } /** * List agents. * * @return array{ok:bool, agents?:array>, message?:string} */ public static function list_agents(): array { $res = self::get('/v1/agents'); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to list agents']; } $data = $res['data']; if (!is_array($data)) { return ['ok' => false, 'message' => 'Unexpected response when listing agents']; } // Merchant API returns PaginatedResponse[AgentOut]: // { items: [...], total: n, page: n, page_size: n } if (isset($data['items']) && is_array($data['items'])) { return ['ok' => true, 'agents' => $data['items']]; } // Back-compat: some environments may return a raw list. return ['ok' => true, 'agents' => $data]; } /** * Create an agent. * * @return array{ok:bool, agent?:array, message?:string} */ public static function create_agent(array $payload): array { $res = self::post('/v1/agents', $payload); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to create agent']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when creating agent']; } return ['ok' => true, 'agent' => $res['data']]; } /** * Create chat_widget channel connection. * * @return array{ok:bool, channel?:array, message?:string} */ public static function create_chat_widget_channel(array $payload): array { $res = self::post('/v1/channels/providers/chat_widget/connections', $payload); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to create channel']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when creating channel']; } return ['ok' => true, 'channel' => $res['data']]; } /** * Update chat_widget channel connection. * * @return array{ok:bool, channel?:array, message?:string} */ public static function update_chat_widget_channel(string $channel_id, array $payload): array { $res = self::put('/v1/channels/providers/chat_widget/connections/' . rawurlencode($channel_id), $payload); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to update channel']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when updating channel']; } return ['ok' => true, 'channel' => $res['data']]; } /** * List provider connections. * * @return array{ok:bool, connections?:array>, message?:string} */ public static function list_provider_connections(string $provider): array { $res = self::get('/v1/actions/providers/' . rawurlencode($provider) . '/connections'); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to list connections']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when listing connections']; } return ['ok' => true, 'connections' => $res['data']]; } /** * Create provider connection. * * @return array{ok:bool, connection?:array, message?:string} */ public static function create_provider_connection(string $provider, array $payload): array { $res = self::post('/v1/actions/providers/' . rawurlencode($provider) . '/connections', $payload); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to create connection']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when creating connection']; } return ['ok' => true, 'connection' => $res['data']]; } /** * Update provider connection. * * @return array{ok:bool, connection?:array, message?:string} */ public static function update_provider_connection(string $provider, string $connection_id, array $payload): array { $res = self::put('/v1/actions/providers/' . rawurlencode($provider) . '/connections/' . rawurlencode($connection_id), $payload); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to update connection']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when updating connection']; } return ['ok' => true, 'connection' => $res['data']]; } /** * List provider actions. * * @return array{ok:bool, actions?:array>, message?:string} */ public static function list_provider_actions(string $provider): array { $res = self::get('/v1/actions/providers/' . rawurlencode($provider) . '/actions'); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to list actions']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when listing actions']; } return ['ok' => true, 'actions' => $res['data']]; } /** * List action providers catalog. * * @return array{ok:bool, providers?:array>, message?:string} */ public static function list_action_providers(): array { $res = self::get('/v1/actions/providers'); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to list providers']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when listing providers']; } return ['ok' => true, 'providers' => $res['data']]; } /** * Get current account/user context. * * @return array{ok:bool, me?:array, message?:string} */ public static function get_me_context(): array { $res = self::get('/v1/me'); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to load account context']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when loading account context']; } return ['ok' => true, 'me' => $res['data']]; } /** * List agent provider configs for a provider. * * @return array{ok:bool, configs?:array>, message?:string} */ public static function list_agent_provider_configs(string $agent_id, string $provider): array { $res = self::get('/v1/agents/' . rawurlencode($agent_id) . '/actions/providers/' . rawurlencode($provider) . '/configs'); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to list provider configs']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when listing provider configs']; } return ['ok' => true, 'configs' => $res['data']]; } /** * Create agent provider config for a provider. * * @return array{ok:bool, config?:array, message?:string} */ public static function create_agent_provider_config(string $agent_id, string $provider, array $payload): array { $res = self::post('/v1/agents/' . rawurlencode($agent_id) . '/actions/providers/' . rawurlencode($provider) . '/configs', $payload); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to create provider config']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when creating provider config']; } return ['ok' => true, 'config' => $res['data']]; } /** * Update agent provider config for a provider. * * @return array{ok:bool, config?:array, message?:string} */ public static function update_agent_provider_config(string $agent_id, string $provider, string $config_id, array $payload): array { $res = self::put('/v1/agents/' . rawurlencode($agent_id) . '/actions/providers/' . rawurlencode($provider) . '/configs/' . rawurlencode($config_id), $payload); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to update provider config']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when updating provider config']; } return ['ok' => true, 'config' => $res['data']]; } /** * List web sources for an agent. * * @return array{ok:bool, sources?:array>, message?:string} */ public static function list_agent_web_sources(string $agent_id): array { $res = self::get('/v1/agents/' . rawurlencode($agent_id) . '/sources/web'); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to list web sources']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when listing web sources']; } return ['ok' => true, 'sources' => $res['data']]; } /** * Create a web source for an agent. * * @return array{ok:bool, source?:array, message?:string} */ public static function create_agent_web_source(string $agent_id, array $payload): array { $res = self::post('/v1/agents/' . rawurlencode($agent_id) . '/sources/web', $payload); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to create web source']; } if (!is_array($res['data'])) { return ['ok' => false, 'message' => 'Unexpected response when creating web source']; } return ['ok' => true, 'source' => $res['data']]; } /** * Delete a web source for an agent. * * @return array{ok:bool, message?:string} */ public static function delete_agent_web_source(string $agent_id, string $web_source_id): array { $res = self::delete('/v1/agents/' . rawurlencode($agent_id) . '/sources/web/' . rawurlencode($web_source_id)); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to delete web source']; } return ['ok' => true]; } /** * Refresh a web source for an agent. * * @return array{ok:bool, message?:string} */ public static function refresh_agent_web_source(string $agent_id, string $web_source_id): array { $res = self::post('/v1/agents/' . rawurlencode($agent_id) . '/sources/web/' . rawurlencode($web_source_id) . '/refresh', []); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to refresh web source']; } return ['ok' => true]; } /** * Report that the WooCommerce plugin was activated for onboarding credits. * Idempotent: API grants at most once per account. * * @return array{ok:bool, message?:string} */ public static function report_plugin_activated(string $plugin = 'woocommerce'): array { $res = self::post('/v1/onboarding/plugins/activated', ['plugin' => $plugin]); if (!$res['ok']) { return ['ok' => false, 'message' => $res['message'] ?? 'Failed to report plugin activation']; } return ['ok' => true]; } }