Plugin Directory

Changeset 3462401


Ignore:
Timestamp:
02/16/2026 10:44:24 AM (6 weeks ago)
Author:
purposego
Message:

Release 2.0.10

Location:
clearpost-simple-ai-auto-post
Files:
10 edited
1 copied

Legend:

Unmodified
Added
Removed
  • clearpost-simple-ai-auto-post/tags/2.0.10/includes/cron-trigger.php

    r3460182 r3462401  
    9595    }
    9696
     97    // Check our specific event's status BEFORE running cron.
     98    $saiap_event_before = saiap_get_scheduler_event_info();
     99
    97100    // Run cron - this executes due events directly.
    98101    wp_cron();
     
    110113    }
    111114
     115    // Check our specific event's status AFTER running cron.
     116    $saiap_event_after = saiap_get_scheduler_event_info();
     117
    112118    $events_processed = max( 0, $due_before - $due_after );
     119
     120    // Determine if our event ran by comparing timestamps.
     121    $saiap_event_ran = false;
     122    if ( $saiap_event_before['exists'] && $saiap_event_after['exists'] ) {
     123        // If timestamp changed, the event ran and was rescheduled.
     124        $saiap_event_ran = ( $saiap_event_before['next_run_utc'] !== $saiap_event_after['next_run_utc'] );
     125    }
    113126
    114127    return rest_ensure_response(
     
    119132            'events_remaining' => $due_after,
    120133            'timestamp'        => $now,
     134            'saiap_scheduler'  => array(
     135                'event_exists'     => $saiap_event_after['exists'],
     136                'event_ran'        => $saiap_event_ran,
     137                'next_run_utc'     => $saiap_event_after['next_run_utc'],
     138                'next_run_local'   => $saiap_event_after['next_run_local'],
     139                'seconds_until'    => $saiap_event_after['seconds_until'],
     140                'was_due'          => $saiap_event_before['was_due'],
     141                'timezone'         => $saiap_event_after['timezone'],
     142            ),
    121143        )
    122144    );
    123145}
     146
     147/**
     148 * Get detailed info about the scheduler event.
     149 *
     150 * @return array Event information.
     151 */
     152function saiap_get_scheduler_event_info() {
     153    $next_scheduled = wp_next_scheduled( 'saiap_generate_scheduled_post' );
     154    $now            = time();
     155    $timezone       = wp_timezone_string();
     156
     157    if ( false === $next_scheduled ) {
     158        return array(
     159            'exists'         => false,
     160            'next_run_utc'   => null,
     161            'next_run_local' => null,
     162            'seconds_until'  => null,
     163            'was_due'        => false,
     164            'timezone'       => $timezone,
     165        );
     166    }
     167
     168    // Convert to local time for display.
     169    $dt_utc   = new DateTime( '@' . $next_scheduled );
     170    $dt_local = new DateTime( '@' . $next_scheduled );
     171    $dt_local->setTimezone( new DateTimeZone( $timezone ) );
     172
     173    return array(
     174        'exists'         => true,
     175        'next_run_utc'   => $dt_utc->format( 'Y-m-d H:i:s' ) . ' UTC',
     176        'next_run_local' => $dt_local->format( 'Y-m-d H:i:s' ) . ' ' . $timezone,
     177        'seconds_until'  => $next_scheduled - $now,
     178        'was_due'        => ( $next_scheduled <= $now ),
     179        'timezone'       => $timezone,
     180    );
     181}
  • clearpost-simple-ai-auto-post/tags/2.0.10/includes/scheduler.php

    r3455493 r3462401  
    485485    $license_key = get_option( 'saiap_license_key' );
    486486    $domain = saiap_get_server_host();
     487    $run_id = wp_generate_uuid4();
     488    $start_time = microtime( true );
     489
     490    // Report scheduler start to premium backend.
     491    saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     492        'event'   => 'scheduler_started',
     493        'run_id'  => $run_id,
     494        'php_version' => PHP_VERSION,
     495        'wp_version'  => get_bloginfo( 'version' ),
     496        'memory_limit' => ini_get( 'memory_limit' ),
     497        'max_execution_time' => ini_get( 'max_execution_time' ),
     498    ) );
    487499
    488500    // 1. Fetch today's scheduled prompts
     
    497509    $response = wp_remote_get( $url, array( 'timeout' => 15 ) );
    498510
    499     if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
    500         // Exit if unable to fetch scheduled prompts
     511    if ( is_wp_error( $response ) ) {
     512        saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     513            'event'   => 'scheduler_error',
     514            'run_id'  => $run_id,
     515            'error'   => 'fetch_failed',
     516            'message' => $response->get_error_message(),
     517            'duration_ms' => round( ( microtime( true ) - $start_time ) * 1000 ),
     518        ) );
     519        return;
     520    }
     521
     522    $status_code = wp_remote_retrieve_response_code( $response );
     523    if ( 200 !== $status_code ) {
     524        saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     525            'event'   => 'scheduler_error',
     526            'run_id'  => $run_id,
     527            'error'   => 'fetch_non_200',
     528            'status_code' => $status_code,
     529            'body'    => substr( wp_remote_retrieve_body( $response ), 0, 500 ),
     530            'duration_ms' => round( ( microtime( true ) - $start_time ) * 1000 ),
     531        ) );
    501532        return;
    502533    }
     
    504535    $prompts = json_decode( wp_remote_retrieve_body( $response ), true );
    505536    if ( empty( $prompts['prompts'] ) ) {
     537        saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     538            'event'   => 'scheduler_completed',
     539            'run_id'  => $run_id,
     540            'prompts_found' => 0,
     541            'posts_generated' => 0,
     542            'posts_failed' => 0,
     543            'duration_ms' => round( ( microtime( true ) - $start_time ) * 1000 ),
     544        ) );
    506545        return;
    507546    }
     547
     548    // Track results for heartbeat.
     549    $posts_generated = 0;
     550    $posts_failed = 0;
     551    $prompt_count = count( $prompts['prompts'] );
    508552
    509553    // 2. Loop through prompts and generate posts
     
    642686        }
    643687
     688        // Track success/failure for heartbeat.
     689        if ( 'failed' === $status_to_set ) {
     690            $posts_failed++;
     691        } else {
     692            $posts_generated++;
     693        }
     694
    644695        // 3. Update the prompt status on the premium service
    645696        $update_url = SAIAP_PREMIUM_API_URL . '/api/prompts/update/' . $prompt['id'] . '/status';
     
    662713        ) );
    663714    }
     715
     716    // Report scheduler completion to premium backend.
     717    saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     718        'event'           => 'scheduler_completed',
     719        'run_id'          => $run_id,
     720        'prompts_found'   => $prompt_count,
     721        'posts_generated' => $posts_generated,
     722        'posts_failed'    => $posts_failed,
     723        'duration_ms'     => round( ( microtime( true ) - $start_time ) * 1000 ),
     724    ) );
    664725}
    665726add_action( 'saiap_generate_scheduled_post', 'saiap_do_generate_scheduled_post' );
     727
     728/**
     729 * Report scheduler heartbeat to premium backend for diagnostics.
     730 *
     731 * Fire-and-forget - we don't wait for response or handle errors.
     732 *
     733 * @param string $domain      Site domain.
     734 * @param string $license_key License key.
     735 * @param array  $data        Heartbeat data.
     736 */
     737function saiap_report_scheduler_heartbeat( $domain, $license_key, $data ) {
     738    $url = SAIAP_PREMIUM_API_URL . '/api/scheduler/heartbeat';
     739
     740    $payload = array_merge(
     741        array(
     742            'domain'      => $domain,
     743            'license_key' => $license_key,
     744            'timestamp'   => gmdate( 'c' ),
     745        ),
     746        $data
     747    );
     748
     749    // Fire and forget - don't block on response.
     750    wp_remote_post(
     751        $url,
     752        array(
     753            'headers'  => array( 'Content-Type' => 'application/json' ),
     754            'body'     => wp_json_encode( $payload ),
     755            'timeout'  => 5,
     756            'blocking' => false,
     757        )
     758    );
     759}
    666760
    667761/**
  • clearpost-simple-ai-auto-post/tags/2.0.10/includes/trial.php

    r3460635 r3462401  
    4040    }
    4141
     42    // Use server-provided expires_at if available.
     43    $trial_expires = get_option( 'saiap_trial_expires_at', '' );
     44    if ( ! empty( $trial_expires ) ) {
     45        return time() < strtotime( $trial_expires );
     46    }
     47
     48    // Fallback: calculate from started_at + 7 days (backwards compatibility).
    4249    $trial_started = get_option( 'saiap_trial_started_at', 0 );
    4350    if ( empty( $trial_started ) ) {
     
    110117 */
    111118function saiap_get_trial_expires_at() {
     119    // Use server-provided expires_at if available.
     120    $trial_expires = get_option( 'saiap_trial_expires_at', '' );
     121    if ( ! empty( $trial_expires ) ) {
     122        return $trial_expires;
     123    }
     124
     125    // Fallback: calculate from started_at + 7 days (backwards compatibility).
    112126    $trial_started = get_option( 'saiap_trial_started_at', '' );
    113127    if ( empty( $trial_started ) ) {
     
    179193 */
    180194function saiap_get_user_scenario() {
    181     // Check paid status first (license key trumps everything).
    182     $license_key = get_option( 'saiap_license_key', '' );
    183     if ( ! empty( $license_key ) ) {
    184         return 'paid'; // Optimistic - server validates on actual API calls.
    185     }
    186 
    187195    $trial_status = get_option( 'saiap_trial_status', 'none' );
    188 
    189     // Check trial status (already updated by saiap_maybe_expire_trial if needed).
     196    $license_key  = get_option( 'saiap_license_key', '' );
     197
     198    // Check trial status FIRST - trial users have license keys for API auth.
     199    if ( 'active' === $trial_status ) {
     200        return 'active_trial';
     201    }
     202
    190203    if ( 'expired' === $trial_status ) {
    191204        return 'expired_trial';
    192205    }
    193206
    194     if ( 'active' === $trial_status ) {
    195         // If we get here with 'active' status, trial is still within window.
    196         // (saiap_maybe_expire_trial should have been called to update if needed).
    197         return 'active_trial';
    198     }
    199 
    200     // No trial started (status is 'none') - check for API keys.
     207    // No active/expired trial - license key means paid.
     208    if ( ! empty( $license_key ) ) {
     209        return 'paid';
     210    }
     211
     212    // No trial, no license - check for BYOK API keys.
    201213    $has_api_keys = ! empty( get_option( 'saiap_openai_api_key', '' ) )
    202214                || ! empty( get_option( 'saiap_anthropic_api_key', '' ) )
     
    250262    }
    251263
    252     // On success responses, clear any error flags.
     264    // On success responses, sync trial status and clear error flags.
    253265    if ( isset( $response['isValid'] ) && $response['isValid'] ) {
    254266        delete_option( 'saiap_license_invalid' );
     267
     268        // Sync trial data from server response.
     269        if ( isset( $response['trial'] ) ) {
     270            $trial = $response['trial'];
     271
     272            // Sync status.
     273            if ( isset( $trial['status'] ) ) {
     274                $server_status = $trial['status'];
     275                if ( 'active' === $server_status ) {
     276                    update_option( 'saiap_trial_status', 'active' );
     277                } elseif ( 'expired' === $server_status ) {
     278                    update_option( 'saiap_trial_status', 'expired' );
     279                } elseif ( 'converted' === $server_status ) {
     280                    // Converted trial = paid user, clear trial status.
     281                    update_option( 'saiap_trial_status', 'none' );
     282                }
     283            }
     284
     285            // Sync expires_at (server is source of truth for trial end date).
     286            if ( isset( $trial['expires_at'] ) ) {
     287                update_option( 'saiap_trial_expires_at', $trial['expires_at'] );
     288            }
     289        }
    255290    }
    256291
     
    404439                update_option( 'saiap_trial_status', 'active' );
    405440                update_option( 'saiap_trial_started_at', $validation['trial']['started_at'] );
     441                if ( isset( $validation['trial']['expires_at'] ) ) {
     442                    update_option( 'saiap_trial_expires_at', $validation['trial']['expires_at'] );
     443                }
    406444            }
    407445            return array(
     
    428466        update_option( 'saiap_trial_status', 'active' );
    429467        update_option( 'saiap_trial_started_at', $body['trial']['started_at'] );
     468        if ( isset( $body['trial']['expires_at'] ) ) {
     469            update_option( 'saiap_trial_expires_at', $body['trial']['expires_at'] );
     470        }
    430471    }
    431472
     
    606647    }
    607648
    608     $email  = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
     649    $email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
     650    if ( empty( $email ) ) {
     651        // Fall back to WordPress admin email if JS didn't provide one.
     652        $email = get_option( 'admin_email', '' );
     653    }
    609654    $result = saiap_start_trial( $email );
    610655
  • clearpost-simple-ai-auto-post/tags/2.0.10/readme.txt

    r3460833 r3462401  
    44Requires at least: 5.0
    55Tested up to: 6.9
    6 Stable tag: 2.0.9
     6Stable tag: 2.0.10
    77Requires PHP: 7.2
    88License: GPLv2 or later
  • clearpost-simple-ai-auto-post/tags/2.0.10/simple-ai-auto-post.php

    r3460833 r3462401  
    44Description: Your AI Agent for SEO, in WordPress. An AI content marketer that knows your site, then schedules and generates posts every day.
    55Plugin URI: https://clearpostplugin.com/
    6 Version: 2.0.9
     6Version: 2.0.10
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1818
    1919// Define plugin version
    20 define( 'SAIAP_VERSION', '2.0.9' );
     20define( 'SAIAP_VERSION', '2.0.10' );
    2121
    2222// Define premium API URL
  • clearpost-simple-ai-auto-post/trunk/includes/cron-trigger.php

    r3460182 r3462401  
    9595    }
    9696
     97    // Check our specific event's status BEFORE running cron.
     98    $saiap_event_before = saiap_get_scheduler_event_info();
     99
    97100    // Run cron - this executes due events directly.
    98101    wp_cron();
     
    110113    }
    111114
     115    // Check our specific event's status AFTER running cron.
     116    $saiap_event_after = saiap_get_scheduler_event_info();
     117
    112118    $events_processed = max( 0, $due_before - $due_after );
     119
     120    // Determine if our event ran by comparing timestamps.
     121    $saiap_event_ran = false;
     122    if ( $saiap_event_before['exists'] && $saiap_event_after['exists'] ) {
     123        // If timestamp changed, the event ran and was rescheduled.
     124        $saiap_event_ran = ( $saiap_event_before['next_run_utc'] !== $saiap_event_after['next_run_utc'] );
     125    }
    113126
    114127    return rest_ensure_response(
     
    119132            'events_remaining' => $due_after,
    120133            'timestamp'        => $now,
     134            'saiap_scheduler'  => array(
     135                'event_exists'     => $saiap_event_after['exists'],
     136                'event_ran'        => $saiap_event_ran,
     137                'next_run_utc'     => $saiap_event_after['next_run_utc'],
     138                'next_run_local'   => $saiap_event_after['next_run_local'],
     139                'seconds_until'    => $saiap_event_after['seconds_until'],
     140                'was_due'          => $saiap_event_before['was_due'],
     141                'timezone'         => $saiap_event_after['timezone'],
     142            ),
    121143        )
    122144    );
    123145}
     146
     147/**
     148 * Get detailed info about the scheduler event.
     149 *
     150 * @return array Event information.
     151 */
     152function saiap_get_scheduler_event_info() {
     153    $next_scheduled = wp_next_scheduled( 'saiap_generate_scheduled_post' );
     154    $now            = time();
     155    $timezone       = wp_timezone_string();
     156
     157    if ( false === $next_scheduled ) {
     158        return array(
     159            'exists'         => false,
     160            'next_run_utc'   => null,
     161            'next_run_local' => null,
     162            'seconds_until'  => null,
     163            'was_due'        => false,
     164            'timezone'       => $timezone,
     165        );
     166    }
     167
     168    // Convert to local time for display.
     169    $dt_utc   = new DateTime( '@' . $next_scheduled );
     170    $dt_local = new DateTime( '@' . $next_scheduled );
     171    $dt_local->setTimezone( new DateTimeZone( $timezone ) );
     172
     173    return array(
     174        'exists'         => true,
     175        'next_run_utc'   => $dt_utc->format( 'Y-m-d H:i:s' ) . ' UTC',
     176        'next_run_local' => $dt_local->format( 'Y-m-d H:i:s' ) . ' ' . $timezone,
     177        'seconds_until'  => $next_scheduled - $now,
     178        'was_due'        => ( $next_scheduled <= $now ),
     179        'timezone'       => $timezone,
     180    );
     181}
  • clearpost-simple-ai-auto-post/trunk/includes/scheduler.php

    r3455493 r3462401  
    485485    $license_key = get_option( 'saiap_license_key' );
    486486    $domain = saiap_get_server_host();
     487    $run_id = wp_generate_uuid4();
     488    $start_time = microtime( true );
     489
     490    // Report scheduler start to premium backend.
     491    saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     492        'event'   => 'scheduler_started',
     493        'run_id'  => $run_id,
     494        'php_version' => PHP_VERSION,
     495        'wp_version'  => get_bloginfo( 'version' ),
     496        'memory_limit' => ini_get( 'memory_limit' ),
     497        'max_execution_time' => ini_get( 'max_execution_time' ),
     498    ) );
    487499
    488500    // 1. Fetch today's scheduled prompts
     
    497509    $response = wp_remote_get( $url, array( 'timeout' => 15 ) );
    498510
    499     if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
    500         // Exit if unable to fetch scheduled prompts
     511    if ( is_wp_error( $response ) ) {
     512        saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     513            'event'   => 'scheduler_error',
     514            'run_id'  => $run_id,
     515            'error'   => 'fetch_failed',
     516            'message' => $response->get_error_message(),
     517            'duration_ms' => round( ( microtime( true ) - $start_time ) * 1000 ),
     518        ) );
     519        return;
     520    }
     521
     522    $status_code = wp_remote_retrieve_response_code( $response );
     523    if ( 200 !== $status_code ) {
     524        saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     525            'event'   => 'scheduler_error',
     526            'run_id'  => $run_id,
     527            'error'   => 'fetch_non_200',
     528            'status_code' => $status_code,
     529            'body'    => substr( wp_remote_retrieve_body( $response ), 0, 500 ),
     530            'duration_ms' => round( ( microtime( true ) - $start_time ) * 1000 ),
     531        ) );
    501532        return;
    502533    }
     
    504535    $prompts = json_decode( wp_remote_retrieve_body( $response ), true );
    505536    if ( empty( $prompts['prompts'] ) ) {
     537        saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     538            'event'   => 'scheduler_completed',
     539            'run_id'  => $run_id,
     540            'prompts_found' => 0,
     541            'posts_generated' => 0,
     542            'posts_failed' => 0,
     543            'duration_ms' => round( ( microtime( true ) - $start_time ) * 1000 ),
     544        ) );
    506545        return;
    507546    }
     547
     548    // Track results for heartbeat.
     549    $posts_generated = 0;
     550    $posts_failed = 0;
     551    $prompt_count = count( $prompts['prompts'] );
    508552
    509553    // 2. Loop through prompts and generate posts
     
    642686        }
    643687
     688        // Track success/failure for heartbeat.
     689        if ( 'failed' === $status_to_set ) {
     690            $posts_failed++;
     691        } else {
     692            $posts_generated++;
     693        }
     694
    644695        // 3. Update the prompt status on the premium service
    645696        $update_url = SAIAP_PREMIUM_API_URL . '/api/prompts/update/' . $prompt['id'] . '/status';
     
    662713        ) );
    663714    }
     715
     716    // Report scheduler completion to premium backend.
     717    saiap_report_scheduler_heartbeat( $domain, $license_key, array(
     718        'event'           => 'scheduler_completed',
     719        'run_id'          => $run_id,
     720        'prompts_found'   => $prompt_count,
     721        'posts_generated' => $posts_generated,
     722        'posts_failed'    => $posts_failed,
     723        'duration_ms'     => round( ( microtime( true ) - $start_time ) * 1000 ),
     724    ) );
    664725}
    665726add_action( 'saiap_generate_scheduled_post', 'saiap_do_generate_scheduled_post' );
     727
     728/**
     729 * Report scheduler heartbeat to premium backend for diagnostics.
     730 *
     731 * Fire-and-forget - we don't wait for response or handle errors.
     732 *
     733 * @param string $domain      Site domain.
     734 * @param string $license_key License key.
     735 * @param array  $data        Heartbeat data.
     736 */
     737function saiap_report_scheduler_heartbeat( $domain, $license_key, $data ) {
     738    $url = SAIAP_PREMIUM_API_URL . '/api/scheduler/heartbeat';
     739
     740    $payload = array_merge(
     741        array(
     742            'domain'      => $domain,
     743            'license_key' => $license_key,
     744            'timestamp'   => gmdate( 'c' ),
     745        ),
     746        $data
     747    );
     748
     749    // Fire and forget - don't block on response.
     750    wp_remote_post(
     751        $url,
     752        array(
     753            'headers'  => array( 'Content-Type' => 'application/json' ),
     754            'body'     => wp_json_encode( $payload ),
     755            'timeout'  => 5,
     756            'blocking' => false,
     757        )
     758    );
     759}
    666760
    667761/**
  • clearpost-simple-ai-auto-post/trunk/includes/trial.php

    r3460635 r3462401  
    4040    }
    4141
     42    // Use server-provided expires_at if available.
     43    $trial_expires = get_option( 'saiap_trial_expires_at', '' );
     44    if ( ! empty( $trial_expires ) ) {
     45        return time() < strtotime( $trial_expires );
     46    }
     47
     48    // Fallback: calculate from started_at + 7 days (backwards compatibility).
    4249    $trial_started = get_option( 'saiap_trial_started_at', 0 );
    4350    if ( empty( $trial_started ) ) {
     
    110117 */
    111118function saiap_get_trial_expires_at() {
     119    // Use server-provided expires_at if available.
     120    $trial_expires = get_option( 'saiap_trial_expires_at', '' );
     121    if ( ! empty( $trial_expires ) ) {
     122        return $trial_expires;
     123    }
     124
     125    // Fallback: calculate from started_at + 7 days (backwards compatibility).
    112126    $trial_started = get_option( 'saiap_trial_started_at', '' );
    113127    if ( empty( $trial_started ) ) {
     
    179193 */
    180194function saiap_get_user_scenario() {
    181     // Check paid status first (license key trumps everything).
    182     $license_key = get_option( 'saiap_license_key', '' );
    183     if ( ! empty( $license_key ) ) {
    184         return 'paid'; // Optimistic - server validates on actual API calls.
    185     }
    186 
    187195    $trial_status = get_option( 'saiap_trial_status', 'none' );
    188 
    189     // Check trial status (already updated by saiap_maybe_expire_trial if needed).
     196    $license_key  = get_option( 'saiap_license_key', '' );
     197
     198    // Check trial status FIRST - trial users have license keys for API auth.
     199    if ( 'active' === $trial_status ) {
     200        return 'active_trial';
     201    }
     202
    190203    if ( 'expired' === $trial_status ) {
    191204        return 'expired_trial';
    192205    }
    193206
    194     if ( 'active' === $trial_status ) {
    195         // If we get here with 'active' status, trial is still within window.
    196         // (saiap_maybe_expire_trial should have been called to update if needed).
    197         return 'active_trial';
    198     }
    199 
    200     // No trial started (status is 'none') - check for API keys.
     207    // No active/expired trial - license key means paid.
     208    if ( ! empty( $license_key ) ) {
     209        return 'paid';
     210    }
     211
     212    // No trial, no license - check for BYOK API keys.
    201213    $has_api_keys = ! empty( get_option( 'saiap_openai_api_key', '' ) )
    202214                || ! empty( get_option( 'saiap_anthropic_api_key', '' ) )
     
    250262    }
    251263
    252     // On success responses, clear any error flags.
     264    // On success responses, sync trial status and clear error flags.
    253265    if ( isset( $response['isValid'] ) && $response['isValid'] ) {
    254266        delete_option( 'saiap_license_invalid' );
     267
     268        // Sync trial data from server response.
     269        if ( isset( $response['trial'] ) ) {
     270            $trial = $response['trial'];
     271
     272            // Sync status.
     273            if ( isset( $trial['status'] ) ) {
     274                $server_status = $trial['status'];
     275                if ( 'active' === $server_status ) {
     276                    update_option( 'saiap_trial_status', 'active' );
     277                } elseif ( 'expired' === $server_status ) {
     278                    update_option( 'saiap_trial_status', 'expired' );
     279                } elseif ( 'converted' === $server_status ) {
     280                    // Converted trial = paid user, clear trial status.
     281                    update_option( 'saiap_trial_status', 'none' );
     282                }
     283            }
     284
     285            // Sync expires_at (server is source of truth for trial end date).
     286            if ( isset( $trial['expires_at'] ) ) {
     287                update_option( 'saiap_trial_expires_at', $trial['expires_at'] );
     288            }
     289        }
    255290    }
    256291
     
    404439                update_option( 'saiap_trial_status', 'active' );
    405440                update_option( 'saiap_trial_started_at', $validation['trial']['started_at'] );
     441                if ( isset( $validation['trial']['expires_at'] ) ) {
     442                    update_option( 'saiap_trial_expires_at', $validation['trial']['expires_at'] );
     443                }
    406444            }
    407445            return array(
     
    428466        update_option( 'saiap_trial_status', 'active' );
    429467        update_option( 'saiap_trial_started_at', $body['trial']['started_at'] );
     468        if ( isset( $body['trial']['expires_at'] ) ) {
     469            update_option( 'saiap_trial_expires_at', $body['trial']['expires_at'] );
     470        }
    430471    }
    431472
     
    606647    }
    607648
    608     $email  = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
     649    $email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
     650    if ( empty( $email ) ) {
     651        // Fall back to WordPress admin email if JS didn't provide one.
     652        $email = get_option( 'admin_email', '' );
     653    }
    609654    $result = saiap_start_trial( $email );
    610655
  • clearpost-simple-ai-auto-post/trunk/readme.txt

    r3460833 r3462401  
    44Requires at least: 5.0
    55Tested up to: 6.9
    6 Stable tag: 2.0.9
     6Stable tag: 2.0.10
    77Requires PHP: 7.2
    88License: GPLv2 or later
  • clearpost-simple-ai-auto-post/trunk/simple-ai-auto-post.php

    r3460833 r3462401  
    44Description: Your AI Agent for SEO, in WordPress. An AI content marketer that knows your site, then schedules and generates posts every day.
    55Plugin URI: https://clearpostplugin.com/
    6 Version: 2.0.9
     6Version: 2.0.10
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1818
    1919// Define plugin version
    20 define( 'SAIAP_VERSION', '2.0.9' );
     20define( 'SAIAP_VERSION', '2.0.10' );
    2121
    2222// Define premium API URL
Note: See TracChangeset for help on using the changeset viewer.