diff --git a/CHANGES.md b/CHANGES.md
index 3b02f5e..4a430c6 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,10 @@
+## 0.12.5
+
+- Added Rent Fetch Sync API key setting with asynchronous validation
+- API key field includes a separate "Validate Key" button for testing without saving
+- Automatic validation on page load when an API key is present
+- Handles multiple validation states: valid, not found, and already in use
+
## 0.12.4
- Updating hooks to match the new RF pattern for subpages in the General settings.
diff --git a/assets/js/rentfetch-rfs-api-key-validation.js b/assets/js/rentfetch-rfs-api-key-validation.js
new file mode 100644
index 0000000..f6cd06d
--- /dev/null
+++ b/assets/js/rentfetch-rfs-api-key-validation.js
@@ -0,0 +1,92 @@
+jQuery(document).ready(function ($) {
+ var $apiKeyInput = $('#rentfetch_options_rfs_api_key');
+ var $validateButton = $('#rfs_validate_api_key');
+ var $statusDiv = $('#rfs_api_key_status');
+
+ /**
+ * Display a status message
+ *
+ * @param {string} message The message to display
+ * @param {string} type The type of message: 'success', 'error', 'warning', or 'info'
+ */
+ function displayStatus(message, type) {
+ var cssClass = 'notice notice-' + type;
+ $statusDiv
+ .html('
' + message + '
')
+ .show();
+ }
+
+ /**
+ * Validate the API key
+ *
+ * @param {boolean} showLoadingState Whether to show a loading state on the button
+ */
+ function validateApiKey(showLoadingState) {
+ var apiKey = $apiKeyInput.val().trim();
+
+ if (!apiKey) {
+ displayStatus('Please enter an API key.', 'warning');
+ return;
+ }
+
+ if (showLoadingState) {
+ $validateButton.prop('disabled', true).text('Validating...');
+ }
+
+ $.post(rfs_ajax_object.ajax_url, {
+ action: 'rfs_validate_api_key',
+ api_key: apiKey,
+ _ajax_nonce: rfs_ajax_object.nonce, // Must match parameter name in PHP: check_ajax_referer('rfs_ajax_nonce', '_ajax_nonce')
+ })
+ .done(function (response) {
+ if (response.success) {
+ var data = response.data;
+ var status = data.status;
+ var message = data.message;
+
+ switch (status) {
+ case 'valid':
+ displayStatus(message, 'success');
+ break;
+ case 'not_found':
+ displayStatus(message, 'error');
+ break;
+ case 'in_use':
+ displayStatus(message, 'warning');
+ break;
+ default:
+ displayStatus(message, 'info');
+ break;
+ }
+ } else {
+ displayStatus(
+ 'Error validating API key: ' +
+ (response.data || 'Unknown error'),
+ 'error'
+ );
+ }
+ })
+ .fail(function (xhr, status, error) {
+ displayStatus(
+ 'Network error while validating API key: ' + error,
+ 'error'
+ );
+ })
+ .always(function () {
+ if (showLoadingState) {
+ $validateButton.prop('disabled', false).text('Validate Key');
+ }
+ });
+ }
+
+ // Validate on button click
+ $validateButton.on('click', function (e) {
+ e.preventDefault();
+ validateApiKey(true);
+ });
+
+ // Validate on page load if API key exists
+ if ($apiKeyInput.length && $apiKeyInput.val().trim()) {
+ validateApiKey(false);
+ }
+});
diff --git a/lib/admin/ajax-validate-rfs-api-key.php b/lib/admin/ajax-validate-rfs-api-key.php
new file mode 100644
index 0000000..2eae995
--- /dev/null
+++ b/lib/admin/ajax-validate-rfs-api-key.php
@@ -0,0 +1,140 @@
+get_error_message() );
+ }
+
+ wp_send_json_success( $validation_result );
+}
+add_action( 'wp_ajax_rfs_validate_api_key', 'rfs_validate_api_key_ajax_handler' );
+
+/**
+ * Validate the API key with the Rent Fetch API
+ *
+ * @param string $api_key The API key to validate.
+ *
+ * @return array|WP_Error The validation result or WP_Error on failure.
+ */
+function rfs_validate_api_key_with_api( $api_key ) {
+
+ // Get the site URL for validation
+ $site_url = get_site_url();
+
+ // Extract just the domain name (host) from the URL
+ $parsed_url = wp_parse_url( $site_url );
+ if ( is_array( $parsed_url ) && isset( $parsed_url['host'] ) ) {
+ $site_url = $parsed_url['host'];
+ }
+
+ // Prepare the request body
+ $body = array(
+ 'api_key' => $api_key,
+ 'site_url' => $site_url,
+ );
+
+ // Get the API URL (allow override via constant for testing)
+ $api_url = defined( 'RENTFETCH_API_VALIDATE_URL' )
+ ? constant( 'RENTFETCH_API_VALIDATE_URL' )
+ : 'https://api.rentfetch.net/wp-json/rentfetchapi/v1/validate-key';
+
+ // Make the API request
+ $response = wp_remote_post(
+ $api_url,
+ array(
+ 'method' => 'POST',
+ 'body' => wp_json_encode( $body ),
+ 'headers' => array(
+ 'Content-Type' => 'application/json',
+ ),
+ 'timeout' => 15,
+ )
+ );
+
+ // Check for errors
+ if ( is_wp_error( $response ) ) {
+ return new WP_Error(
+ 'api_request_failed',
+ 'Unable to connect to Rent Fetch API: ' . $response->get_error_message()
+ );
+ }
+
+ $response_code = wp_remote_retrieve_response_code( $response );
+ $response_body = wp_remote_retrieve_body( $response );
+
+ // Parse the response
+ $data = json_decode( $response_body, true );
+
+ // Handle different response codes
+ switch ( $response_code ) {
+ case 200:
+ // Valid API key
+ return array(
+ 'status' => 'valid',
+ 'message' => isset( $data['message'] )
+ ? $data['message']
+ : 'API key is valid and ready to use.',
+ );
+
+ case 404:
+ // API key not found
+ return array(
+ 'status' => 'not_found',
+ 'message' => isset( $data['message'] )
+ ? $data['message']
+ : 'API key not found. Please check your key and try again.',
+ );
+
+ case 409:
+ // API key already in use on another site
+ $existing_site = isset( $data['existing_site'] ) ? $data['existing_site'] : 'another site';
+ return array(
+ 'status' => 'in_use',
+ 'message' => isset( $data['message'] )
+ ? $data['message']
+ : sprintf( 'This API key is already in use on %s.', $existing_site ),
+ );
+
+ default:
+ // Unexpected response
+ return new WP_Error(
+ 'unexpected_response',
+ sprintf(
+ 'Unexpected response from API (HTTP %d): %s',
+ $response_code,
+ isset( $data['message'] ) ? $data['message'] : wp_remote_retrieve_response_message( $response )
+ )
+ );
+ }
+}
diff --git a/lib/admin/sync-options.php b/lib/admin/sync-options.php
index 413e9bc..377f39d 100644
--- a/lib/admin/sync-options.php
+++ b/lib/admin/sync-options.php
@@ -13,6 +13,20 @@ function rentfetch_remove_premium_functionality_notice() {
*/
add_action( 'rentfetch_do_settings_general_data_sync', 'rentfetch_settings_sync', 25 );
function rentfetch_settings_sync() {
+
+ // Enqueue the API key validation script
+ wp_enqueue_script( 'rentfetch-rfs-api-key-validation' );
+
+ // Localize script to pass AJAX URL and nonce
+ wp_localize_script(
+ 'rentfetch-rfs-api-key-validation',
+ 'rfs_ajax_object',
+ array(
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
+ 'nonce' => wp_create_nonce( 'rfs_ajax_nonce' ),
+ )
+ );
+
?>
Data Sync
@@ -46,6 +60,21 @@ function rentfetch_settings_sync() {
+
+
+
+
+
Enter your Rent Fetch Sync API key to enable synchronization features.