PluginProbe ʕ •ᴥ•ʔ
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI / 3.5.3
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI v3.5.3
3.6.0 3.5.3 3.5.2 3.5.1 3.5.0 3.4.8 3.4.7 3.4.6 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5.1 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.10 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.6.1 1.6.7 1.7.0 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.2 1.7.2.1 1.7.2.2 1.7.3 1.7.4 1.7.5 1.7.5.1 1.7.5.2 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.8.0 1.8.0.1 1.8.1 1.8.2 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.0.1 1.9.1 1.9.2 1.9.3 1.9.4 1.9.4.1 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.0.1 2.0.1 2.0.2 2.0.3 2.0.3.1 2.0.4 2.0.4.1 2.0.5 2.0.6 2.0.7 2.0.8 2.0.8.1 2.0.9 3.0.0 3.0.0.1 3.0.1 3.0.2 3.0.3 3.0.3.1 3.0.4 3.0.4.1 3.0.4.2 3.0.5 3.0.5.1 3.0.5.2 3.0.6 3.0.6.1 3.0.7.1 3.0.8 3.0.8.1 3.0.9 3.0.9.1 3.0.9.2 3.0.9.3 3.0.9.4 3.0.9.5 3.1.0 3.1.1 3.1.2 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.0 3.4.0 3.4.1 3.4.2 3.4.2.1 3.4.3 3.4.4 3.4.5 trunk 1.0 1.0.1 1.0.2 1.0.3
everest-forms / includes / Integrations / AI / class-evf-ai-registration.php
everest-forms / includes / Integrations / AI Last commit date
class-evf-ai-ajax.php 1 month ago class-evf-ai-api.php 1 month ago class-evf-ai-form-builder.php 1 month ago class-evf-ai-loader.php 1 month ago class-evf-ai-registration.php 1 week ago
class-evf-ai-registration.php
216 lines
1 <?php
2 /**
3 * EVF AI Registration — manages site token lifecycle.
4 *
5 * Mirrors WPForms' LiteConnect pattern: the plugin silently registers with
6 * the gateway on first use and stores a site_token in wp_options.
7 * Pro users additionally activate their EVF license key to upgrade limits.
8 *
9 * Options used:
10 * evf_ai_credentials { site_token, tier, registered_at }
11 */
12
13 defined( 'ABSPATH' ) || exit;
14
15 class EVF_AI_Registration {
16
17 const OPTION_KEY = 'evf_ai_credentials';
18 const LOCK_TRANSIENT = 'evf_ai_registration_lock';
19 const VERIFY_PREFIX = 'evf_ai_verify_'; // transient prefix for ownership tokens
20 const VERIFY_TTL = 300; // 5 minutes
21
22 /**
23 * Get the stored site token, or null if not yet registered.
24 */
25 public static function get_site_token(): ?string {
26 $creds = get_option( self::OPTION_KEY );
27 return ! empty( $creds['site_token'] ) ? $creds['site_token'] : null;
28 }
29
30 /**
31 * Get the stored tier ("free" or "pro").
32 */
33 public static function get_tier(): string {
34 $creds = get_option( self::OPTION_KEY );
35 return $creds['tier'] ?? 'free';
36 }
37
38 /**
39 * True if the site has been registered with the gateway.
40 */
41 public static function is_registered(): bool {
42 return (bool) self::get_site_token();
43 }
44
45 /**
46 * True if this is a local / development site where the AI features
47 * (Create with AI, in-builder AI Assistant) should be disabled.
48 *
49 * The gateway cannot verify domain ownership for non-public hosts, so the
50 * AI features can never work on local installs — we hide them entirely
51 * instead of letting the user hit a "not available" error.
52 *
53 * Detection: WP environment type "local" OR a non-public host
54 * (localhost / loopback IP / .local / .test / .localhost TLD).
55 *
56 * @return bool
57 */
58 public static function is_local_site(): bool {
59 $host = wp_parse_url( site_url(), PHP_URL_HOST );
60 $host = $host ? strtolower( $host ) : '';
61
62 $is_local = ( function_exists( 'wp_get_environment_type' ) && 'local' === wp_get_environment_type() )
63 || in_array( $host, array( 'localhost', '127.0.0.1', '::1' ), true );
64
65 if ( ! $is_local ) {
66 foreach ( array( '.local', '.test', '.localhost' ) as $suffix ) {
67 if ( '' !== $host && substr( $host, -strlen( $suffix ) ) === $suffix ) {
68 $is_local = true;
69 break;
70 }
71 }
72 }
73
74 /**
75 * Filter whether the current site is treated as local for AI features — the final say
76 * over the heuristics above, e.g. to test the live gateway from a .local domain (set
77 * this to __return_false), or to block a host the heuristics miss.
78 *
79 * @param bool $is_local Whether the built-in heuristics consider this site local.
80 * @param string $host The detected site host.
81 */
82 return (bool) apply_filters( 'everest_forms_ai_is_local_site', $is_local, $host );
83 }
84
85 /**
86 * Clear stored credentials and registration lock.
87 * Called automatically when the gateway returns "Invalid token" so the
88 * site re-registers transparently on the next request.
89 */
90 public static function clear_credentials(): void {
91 delete_option( self::OPTION_KEY );
92 delete_transient( self::LOCK_TRANSIENT );
93 }
94
95 /**
96 * Register this site with the gateway (called silently on first AI use).
97 * Uses a transient lock to prevent concurrent registration attempts.
98 *
99 * @return bool True on success.
100 */
101 public static function register(): bool {
102 // Prevent duplicate registration
103 if ( self::is_registered() ) {
104 return true;
105 }
106
107 // Transient lock — only one registration attempt per 60 seconds
108 if ( get_transient( self::LOCK_TRANSIENT ) ) {
109 return false;
110 }
111 set_transient( self::LOCK_TRANSIENT, true, 60 );
112
113 // Generate a one-time ownership token the gateway will call back to verify.
114 // The gateway hits /wp-json/everest-forms/v1/gateway-verify?token=... and we
115 // confirm the transient exists — proving this WordPress install issued the request.
116 $verify_token = wp_generate_password( 32, false );
117 set_transient( self::VERIFY_PREFIX . $verify_token, 1, self::VERIFY_TTL );
118
119 $response = EVF_AI_API::register_site( $verify_token );
120
121 if ( is_wp_error( $response ) || empty( $response['site_token'] ) ) {
122 delete_transient( self::VERIFY_PREFIX . $verify_token );
123 return false;
124 }
125
126 update_option(
127 self::OPTION_KEY,
128 array(
129 'site_token' => sanitize_text_field( $response['site_token'] ),
130 'tier' => sanitize_key( $response['tier'] ?? 'free' ),
131 'registered_at' => time(),
132 )
133 );
134
135 delete_transient( self::LOCK_TRANSIENT );
136 return true;
137 }
138
139 /**
140 * Activate pro tier using the EVF Pro license key.
141 * Called automatically when EVF Pro license is activated.
142 *
143 * @param string $license_key
144 * @return bool
145 */
146 public static function activate_pro( string $license_key ): bool {
147 $token = self::get_site_token();
148
149 // Register first if needed
150 if ( ! $token ) {
151 if ( ! self::register() ) {
152 return false;
153 }
154 $token = self::get_site_token();
155 }
156
157 $response = EVF_AI_API::activate_license( $token, $license_key );
158
159 if ( is_wp_error( $response ) || empty( $response['tier'] ) ) {
160 return false;
161 }
162
163 $creds = get_option( self::OPTION_KEY, array() );
164 $creds['tier'] = sanitize_key( $response['tier'] );
165 $creds['plan'] = sanitize_key( $response['plan'] ?? '' );
166 update_option( self::OPTION_KEY, $creds );
167
168 return 'pro' === $creds['tier'];
169 }
170
171 /**
172 * Register the public REST endpoint the gateway calls to verify domain ownership.
173 * Hooked to rest_api_init from class-evf-ai-loader.php.
174 */
175 public static function register_verify_endpoint(): void {
176 register_rest_route(
177 'everest-forms/v1',
178 '/gateway-verify',
179 array(
180 'methods' => WP_REST_Server::READABLE,
181 'callback' => array( __CLASS__, 'handle_verify_request' ),
182 'permission_callback' => '__return_true',
183 )
184 );
185 }
186
187 /**
188 * Respond to the gateway's ownership callback.
189 *
190 * The gateway sends the same verify_token we generated in register().
191 * We check the transient (one-time, 5-minute TTL) and delete it on success
192 * so the token cannot be reused.
193 *
194 * @param WP_REST_Request $request Incoming REST request.
195 * @return WP_REST_Response
196 */
197 public static function handle_verify_request( WP_REST_Request $request ): WP_REST_Response {
198 $token = sanitize_text_field( $request->get_param( 'token' ) );
199
200 if ( ! $token ) {
201 return new WP_REST_Response( array( 'valid' => false ), 400 );
202 }
203
204 $stored = get_transient( self::VERIFY_PREFIX . $token );
205
206 if ( ! $stored ) {
207 return new WP_REST_Response( array( 'valid' => false ), 403 );
208 }
209
210 // One-time use — delete immediately so the token cannot be replayed.
211 delete_transient( self::VERIFY_PREFIX . $token );
212
213 return new WP_REST_Response( array( 'valid' => true ), 200 );
214 }
215 }
216