| @@ -5,8 +5,9 @@ | ||
| 5 | 5 | use FluentCart\App\App; |
| 6 | 6 | use FluentCart\App\Helpers\AddressHelper; |
| 7 | 7 | use FluentCart\App\Helpers\Status; |
| 8 | 8 | use FluentCart\App\Models\Customer; |
| 9 | +use FluentCart\App\Services\CustomerIdentity\EmailVerificationService; | |
| 9 | 10 | use FluentCart\App\Services\Renderer\CheckoutFieldsSchema; |
| 10 | 11 | use FluentCart\Framework\Database\Orm\Builder; |
| 11 | 12 | use FluentCart\Framework\Database\Orm\Collection; |
| 12 | 13 | use FluentCart\Framework\Support\Arr; |
| @@ -423,22 +424,26 @@ | ||
| 423 | 424 | } |
| 424 | 425 | |
| 425 | 426 | $currentUser = get_user_by('ID', get_current_user_id()); |
| 426 | 427 | |
| 427 | - // Reading the current customer must not claim a record by email. | |
| 428 | + // With verification enabled, reading the current customer must not claim a record by email. | |
| 428 | 429 | $existingCustomer = Customer::query()->where('user_id', $currentUser->ID) |
| 429 | 430 | ->orderBy('id', 'ASC') |
| 430 | 431 | ->with(['billing_address', 'shipping_address']) |
| 431 | 432 | ->first(); |
| 432 | 433 | |
| 434 | + if (!$existingCustomer && !EmailVerificationService::isEnabled()) { | |
| 435 | + $existingCustomer = static::claimUnlinkedCustomerByEmail($currentUser); | |
| 436 | + } | |
| 437 | + | |
| 433 | 438 | if ($existingCustomer) { |
| 434 | 439 | static::$currentCustomerRuntimeCache = $existingCustomer; |
| 435 | 440 | return $existingCustomer; |
| 436 | 441 | } |
| 437 | 442 | |
| 438 | - if (!$createIfNotExists || Customer::query()->where('email', $currentUser->user_email)->exists()) { | |
| 439 | - // Do not create a duplicate or expose an unclaimed customer to a getter. | |
| 440 | - // Email confirmation links the existing row before dashboard access. | |
| 443 | + if (!$createIfNotExists || (EmailVerificationService::isEnabled() && Customer::query()->where('email', $currentUser->user_email)->exists())) { | |
| 444 | + // With verification enabled, confirmation links an existing guest row. | |
| 445 | + // Otherwise an explicitly requested profile is separate from guest history. | |
| 441 | 446 | return null; |
| 442 | 447 | } |
| 443 | 448 | |
| 444 | 449 | $userId = $currentUser->ID; |
| @@ -463,8 +468,32 @@ | ||
| 463 | 468 | ->first(); |
| 464 | 469 | |
| 465 | 470 | return static::$currentCustomerRuntimeCache; |
| 466 | 471 | |
| 472 | + } | |
| 473 | + | |
| 474 | + private static function claimUnlinkedCustomerByEmail(\WP_User $user): ?Customer | |
| 475 | + { | |
| 476 | + if (!$user->user_email) { | |
| 477 | + return null; | |
| 478 | + } | |
| 479 | + | |
| 480 | + $unlinked = Customer::query()->where('email', $user->user_email) | |
| 481 | + ->unclaimed() | |
| 482 | + ->orderBy('id', 'ASC') | |
| 483 | + ->first(); | |
| 484 | + | |
| 485 | + if (!$unlinked) { | |
| 486 | + return null; | |
| 487 | + } | |
| 488 | + | |
| 489 | + Customer::query()->where('id', $unlinked->id)->unclaimed()->update(['user_id' => $user->ID]); | |
| 490 | + | |
| 491 | + // A concurrent request for the same account may have won the update. | |
| 492 | + return Customer::query()->where('user_id', $user->ID) | |
| 493 | + ->orderBy('id', 'ASC') | |
| 494 | + ->with(['billing_address', 'shipping_address']) | |
| 495 | + ->first(); | |
| 467 | 496 | } |
| 468 | 497 | |
| 469 | 498 | private static function resolveCustomerName(array $data): array |
| 470 | 499 | { |