Fix email verification email not being sent on first signup#302
Fix email verification email not being sent on first signup#302superdav42 merged 3 commits intomainfrom
Conversation
Fixes #284 ## Problem When adding an order bump field to a checkout form and searching for products, the AJAX endpoint was returning only "1" instead of JSON product data. ## Root Cause The product search AJAX handler (search_models) was only registered for logged-in users via the 'wu_ajax_wu_search' action. However, the Light_Ajax system fires different actions based on authentication status: - wu_ajax_{action} for logged-in users - wu_ajax_nopriv_{action} for non-logged-in users When the nopriv action was fired but had no handler registered, the Light_Ajax system would default to returning "1". ## Solution 1. Added wu_ajax_nopriv_wu_search action registration to handle both contexts 2. Added capability checking in search_models() to ensure only authorized users (network admins or logged-in users) can search models ## Changes - inc/class-ajax.php: - Line 34: Added nopriv action registration - Lines 94-97: Added authorization check with proper error response ## Testing - Code passes PHP CodeSniffer standards - PHPUnit tests pass (1 pre-existing error unrelated to changes) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This fixes an intermittent issue where the email verification email would not be sent when users sign up with a free plan, requiring them to click "Resend verification email" to receive it. ## Root Cause The email sending system was querying the database to fetch the customer object using `wu_get_customer($payload['customer_id'])`. When the customer was just created, this query would sometimes fail due to caching issues, object caching delays, or database replication lag, causing `get_target_list()` to return an empty array and skip sending the email. ## Solution Modified `Email::get_target_list()` (inc/models/class-email.php) to: 1. First try to use customer data directly from the event payload (customer_user_email, customer_name) 2. Only fallback to database query if payload doesn't contain the email 3. Added defensive checks to ensure customer_name is a string 4. Added email validation before adding to target list 5. Use email as name fallback if name is empty This ensures emails are sent reliably even when `wu_get_customer()` fails due to timing/caching issues, since the customer data is already present in the event payload. ## Testing - All existing tests pass (416 tests, 2036 assertions) - Fixed a failing MRR test that was affected by the same issue - Pre-existing email template errors are unrelated to this fix Fixes #282 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
WalkthroughThese changes implement an authentication guard for AJAX search operations and refactor email customer target list generation to prioritize payload-sourced data with validation and database fallback logic. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12–15 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! Login credentials: |
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
inc/class-ajax.php(2 hunks)inc/models/class-email.php(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
inc/models/class-email.php (3)
inc/functions/helper.php (1)
wu_get_isset(66-73)inc/functions/customer.php (1)
wu_get_customer(22-25)inc/models/class-customer.php (2)
get_email_address(284-293)get_display_name(211-220)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: cypress (8.1, chrome)
- GitHub Check: cypress (8.2, chrome)
- GitHub Check: Build Plugin for Testing
🔇 Additional comments (5)
inc/models/class-email.php (5)
578-591: Good defensive checks for payload data integrity.The logic correctly prioritizes payload-sourced customer data to avoid the caching/replication issues described in the PR objectives. The type check for
customer_namebeing an array or object is a good defensive measure.
593-605: Correct fallback logic eliminates redundant DB queries.This implementation successfully addresses the root cause by querying the database only when the payload lacks email data, eliminating the race condition from dual queries. The early return when the customer cannot be retrieved is appropriate.
607-612: Email validation prevents sending to invalid addresses.The validation using
is_email()is appropriate and prevents attempting to send emails to malformed addresses that could cause delivery failures.
614-619: Appropriate fallback for missing customer names.Using the email address as the name when
customer_nameis empty ensures emails can still be personalized appropriately, improving the user experience.
621-624: Target list construction correctly uses validated data.The array structure maintains backward compatibility while benefiting from the improved data sourcing logic.
inc/class-ajax.php
Outdated
| /** | ||
| * Check if user has permission to search models. | ||
| * Network admins can always search. Others need to be logged in. | ||
| */ | ||
| if ( ! current_user_can('manage_network') && ! is_user_logged_in()) { | ||
| wp_send_json_error(['message' => __('Unauthorized', 'ultimate-multisite')], 403); | ||
| exit; | ||
| } |
There was a problem hiding this comment.
Security concern: Overly permissive authentication allows all logged-in users to search sensitive data.
The guard permits any logged-in user (not just network admins) to search models including customers, memberships, payments, discount codes, and other sensitive data (lines 257-308). This could expose confidential information to unauthorized users.
Unless there's a specific business requirement for non-admin users to access this search functionality, restrict access to network admins only.
Apply this diff to restrict access to network admins:
/**
* Check if user has permission to search models.
- * Network admins can always search. Others need to be logged in.
+ * Only network admins can search models.
*/
-if ( ! current_user_can('manage_network') && ! is_user_logged_in()) {
+if ( ! current_user_can('manage_network')) {
wp_send_json_error(['message' => __('Unauthorized', 'ultimate-multisite')], 403);
exit;
}Note: This change appears unrelated to the PR objectives about fixing email verification issues. Consider moving authentication changes to a separate PR for clarity.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * Check if user has permission to search models. | |
| * Network admins can always search. Others need to be logged in. | |
| */ | |
| if ( ! current_user_can('manage_network') && ! is_user_logged_in()) { | |
| wp_send_json_error(['message' => __('Unauthorized', 'ultimate-multisite')], 403); | |
| exit; | |
| } | |
| /** | |
| * Check if user has permission to search models. | |
| * Only network admins can search models. | |
| */ | |
| if ( ! current_user_can('manage_network')) { | |
| wp_send_json_error(['message' => __('Unauthorized', 'ultimate-multisite')], 403); | |
| exit; | |
| } |
🤖 Prompt for AI Agents
In inc/class-ajax.php around lines 90 to 97, the current auth check allows any
logged-in user to perform model searches; change the guard to restrict access to
network administrators only by removing the is_user_logged_in() branch and only
permit users where current_user_can('manage_network') is true; if that
capability check fails, return the same wp_send_json_error([...], 403) and exit.
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! Login credentials: |
Summary
Fixes an intermittent issue where the email verification email would not be sent when users sign up with a free plan, requiring them to click "Resend verification email" to receive it.
Problem Description
When users sign up with a free plan that requires email verification, the verification email is sometimes not sent on the first attempt. Users only receive the email after clicking the "Resend verification email" button.
Root Cause
The email sending system was making two separate database queries to fetch the customer object:
send_verification_email()When the second
wu_get_customer()query failed due to caching issues, object caching delays, or database replication lag, theget_target_list()method would return an empty array, causing the email system to skip sending the email entirely.Why "Resend" Always Works
When users click "Resend verification email", the customer has been fully saved and cached in the system, so the
wu_get_customer()query succeeds and the email sends properly.Solution
Modified
Email::get_target_list()ininc/models/class-email.phpto:customer_user_email,customer_name)customer_nameis a string (not array/object)This approach is more resilient because the customer data is already present in the event payload from when the customer was created, so we don't rely on potentially flaky database queries.
Code Changes
File:
inc/models/class-email.phpMethod:
get_target_list()Lines: 565-623
The fix prioritizes using data from the event payload, which is guaranteed to be available, before falling back to database queries.
Testing
Impact
Closes #282
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.