delete_all(); ( new KeyMinter() )->revoke( Options::key_id() ); Options::clear_connection(); OrderEvents::unschedule_all(); ProductSync::unschedule(); SyncRun::stop(); Log::info( 'Disconnected (' . $event . ').' ); } public static function on_deactivate(): void { self::disconnect( 'deactivated' ); // Also when not connected: nothing may stay scheduled for a deactivated plugin, and no key it can no longer limit. ( new KeyMinter() )->revoke_pending(); ProductSync::unschedule(); SyncRun::stop(); } /** * Best effort: local cleanup happens whatever the backend answers. */ private static function send_lifecycle_event( string $event ): void { $body = (string) wp_json_encode( array( 'event' => $event, 'store_url' => Config::store_url(), 'tenant_id' => Options::tenant_id(), 'ts' => time(), ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- HMAC signature encoding, same scheme as WooCommerce webhooks. $signature = base64_encode( hash_hmac( 'sha256', $body, Options::webhook_secret(), true ) ); $response = wp_remote_post( Config::api_base() . '/webhooks/woo/lifecycle', array( 'timeout' => self::TIMEOUT, 'sslverify' => true, 'redirection' => 0, 'headers' => array( 'Content-Type' => 'application/json', 'X-Cortex-Signature' => $signature, ), 'body' => $body, ) ); if ( is_wp_error( $response ) ) { Log::warning( 'Lifecycle event not delivered: ' . $response->get_error_message() ); return; } $status = (int) wp_remote_retrieve_response_code( $response ); if ( $status < 200 || $status >= 300 ) { Log::warning( 'Lifecycle event answered HTTP ' . $status ); } } }