← All changes
|
admin/pages/license/src/Services/LicenseService.php
+78
-9
3.1.5
→
3.2.2
View file →
| @@ -220,12 +220,14 @@ | ||
| 220 | 220 | |
| 221 | 221 | // Process every license result — update local storage and notices |
| 222 | 222 | foreach( $key_to_product as $license_key => $product_id ) { |
| 223 | 223 | if( ! isset( $batch_results[ $license_key ] ) ) continue; |
| 224 | - | |
| 225 | - $result = $batch_results[ $license_key ]; | |
| 226 | - $license = $licenses[ $product_id ]; | |
| 227 | - | |
| 224 | + | |
| 225 | + $result = $batch_results[ $license_key ]; | |
| 226 | + $license = $licenses[ $product_id ]; | |
| 227 | + $product_id = $this->sync_product_key( $product_id, $result ); | |
| 228 | + if( $product_id === '' ) continue; | |
| 229 | + | |
| 228 | 230 | if( ! empty( $result['success'] ) && ! empty( $result['data'] ) ) { |
| 229 | 231 | $this->save( $product_id, $result['data'] ); |
| 230 | 232 | |
| 231 | 233 | // Clear any expired notice for this license |
| @@ -302,9 +304,11 @@ | ||
| 302 | 304 | $is_terminal = in_array( $server_reason, self::REFUND_REASONS, true ) |
| 303 | 305 | || in_array( $server_status, self::TERMINAL_STATUSES, true ) |
| 304 | 306 | || $server_reason === 'not_found'; |
| 305 | 307 | |
| 306 | - if( $is_terminal ) { | |
| 308 | + if( $is_terminal && self::is_legacy_license( $license ) ) { | |
| 309 | + $this->keep_legacy_license( $product_id, $license ); | |
| 310 | + } elseif( $is_terminal ) { | |
| 307 | 311 | $plugin_slug = $license['plugin_slug'] ?? ''; |
| 308 | 312 | $this->remove( $product_id ); |
| 309 | 313 | |
| 310 | 314 | if( $plugin_slug ) { |
| @@ -346,8 +350,51 @@ | ||
| 346 | 350 | } |
| 347 | 351 | } |
| 348 | 352 | |
| 349 | 353 | /** |
| 354 | + * A license migrated from the old gVectors store (legacy activation key). | |
| 355 | + */ | |
| 356 | + private static function is_legacy_license( array $license ): bool { | |
| 357 | + return ( $license['plan_name'] ?? '' ) === 'Legacy License'; | |
| 358 | + } | |
| 359 | + | |
| 360 | + /** | |
| 361 | + * A legacy license the server no longer knows or reports invalid: old-store owners keep using their | |
| 362 | + * addon for life, so the license is kept as expired (no updates) and the addon is never deactivated or deleted. | |
| 363 | + */ | |
| 364 | + private function keep_legacy_license( string $product_id, array $license ): void { | |
| 365 | + $license['status'] = 'expired'; | |
| 366 | + $license['last_validated'] = time(); | |
| 367 | + $this->save( $product_id, $license ); | |
| 368 | + $this->update_expired_notice( $product_id ); | |
| 369 | + } | |
| 370 | + | |
| 371 | + /** | |
| 372 | + * If the server reports a real Paddle product_id different from the local key | |
| 373 | + * (e.g. a migrated legacy license stored under its plugin slug), move the local | |
| 374 | + * license to that key so the store UI treats it like any purchased license. | |
| 375 | + * Returns the key to continue with, or '' when the entry was dropped because an | |
| 376 | + * active license already exists under the server's product_id. | |
| 377 | + */ | |
| 378 | + private function sync_product_key( string $product_id, array $result ): string { | |
| 379 | + $server_pid = $result['data']['product_id'] ?? ''; | |
| 380 | + if( ! is_string( $server_pid ) || strpos( $server_pid, 'pro_' ) !== 0 || $server_pid === $product_id ) return $product_id; | |
| 381 | + | |
| 382 | + $licenses = $this->get_all(); | |
| 383 | + if( ! isset( $licenses[ $product_id ] ) ) return $product_id; | |
| 384 | + | |
| 385 | + $keep_existing = isset( $licenses[ $server_pid ] ) && $this->is_active( $server_pid ); | |
| 386 | + if( ! $keep_existing ) { | |
| 387 | + $licenses[ $server_pid ] = $licenses[ $product_id ]; | |
| 388 | + $licenses[ $server_pid ]['product_id'] = $server_pid; | |
| 389 | + } | |
| 390 | + unset( $licenses[ $product_id ] ); | |
| 391 | + update_option( $this->option_key, $licenses ); | |
| 392 | + | |
| 393 | + return $keep_existing ? '' : $server_pid; | |
| 394 | + } | |
| 395 | + | |
| 396 | + /** | |
| 350 | 397 | * Remove a license for a product |
| 351 | 398 | */ |
| 352 | 399 | public function remove( string $product_id ): bool { |
| 353 | 400 | $licenses = $this->get_all(); |
| @@ -405,9 +452,11 @@ | ||
| 405 | 452 | if( empty( $license ) ) return; |
| 406 | 453 | |
| 407 | 454 | $plugin_slug = $license['plugin_slug'] ?? ''; |
| 408 | 455 | if( empty( $plugin_slug ) ) return; |
| 409 | - | |
| 456 | + // Addon not physically installed — no notice needed | |
| 457 | + if( ! is_dir( WP_PLUGIN_DIR . '/' . $plugin_slug ) ) return; | |
| 458 | + | |
| 410 | 459 | $expired_notices = get_option( $this->expired_notice_option, [] ); |
| 411 | 460 | $expired_notices[ $plugin_slug ] = [ |
| 412 | 461 | 'product_name' => $license['product_name'] ?? $plugin_slug, |
| 413 | 462 | 'status' => $license['status'] ?? 'expired', |
| @@ -465,8 +514,13 @@ | ||
| 465 | 514 | 'addon_deleted' => false, |
| 466 | 515 | ]; |
| 467 | 516 | } |
| 468 | 517 | |
| 518 | + $product_id = $this->sync_product_key( $product_id, $response ); | |
| 519 | + if( $product_id === '' ) { | |
| 520 | + return [ 'valid' => false, 'reason' => 'duplicate', 'error' => '', 'status' => '', 'removed' => true, 'addon_deleted' => false ]; | |
| 521 | + } | |
| 522 | + | |
| 469 | 523 | // License is valid on the server |
| 470 | 524 | if( ! empty( $response['success'] ) && ! empty( $response['data'] ) ) { |
| 471 | 525 | $this->save( $product_id, $response['data'] ); |
| 472 | 526 | |
| @@ -508,8 +562,21 @@ | ||
| 508 | 562 | $is_terminal = in_array( $server_reason, self::REFUND_REASONS, true ) |
| 509 | 563 | || in_array( $server_status, self::TERMINAL_STATUSES, true ) |
| 510 | 564 | || $server_reason === 'not_found'; |
| 511 | 565 | |
| 566 | + if( $is_terminal && self::is_legacy_license( $license ) ) { | |
| 567 | + $this->keep_legacy_license( $product_id, $license ); | |
| 568 | + | |
| 569 | + return [ | |
| 570 | + 'valid' => false, | |
| 571 | + 'reason' => $server_reason ?: $server_status, | |
| 572 | + 'error' => $server_error, | |
| 573 | + 'status' => 'expired', | |
| 574 | + 'removed' => false, | |
| 575 | + 'addon_deleted' => false, | |
| 576 | + ]; | |
| 577 | + } | |
| 578 | + | |
| 512 | 579 | $removed = false; |
| 513 | 580 | $addon_deleted = false; |
| 514 | 581 | |
| 515 | 582 | if( $is_terminal ) { |
| @@ -616,11 +683,13 @@ | ||
| 616 | 683 | |
| 617 | 684 | foreach( $keys_to_validate as $license_key => $product_id ) { |
| 618 | 685 | if( ! isset( $batch_results[ $license_key ] ) ) continue; |
| 619 | 686 | |
| 620 | - $result = $batch_results[ $license_key ]; | |
| 621 | - $license = $licenses[ $product_id ]; | |
| 622 | - | |
| 687 | + $result = $batch_results[ $license_key ]; | |
| 688 | + $license = $licenses[ $product_id ]; | |
| 689 | + $product_id = $this->sync_product_key( $product_id, $result ); | |
| 690 | + if( $product_id === '' ) continue; | |
| 691 | + | |
| 623 | 692 | if( ! empty( $result['success'] ) && ! empty( $result['data'] ) ) { |
| 624 | 693 | // License is valid — update local data |
| 625 | 694 | $this->save( $product_id, $result['data'] ); |
| 626 | 695 | } else { |