- Add LicenseConnect.tsx focused OAuth confirmation page in customer SPA - Add /licenses/oauth/validate and /licenses/oauth/confirm API endpoints - Update App.tsx to render license-connect outside BaseLayout (no header/footer) - Add license_activation_method field to product settings in Admin SPA - Create LICENSING_MODULE.md with comprehensive OAuth flow documentation - Update API_ROUTES.md with license module endpoints
229 lines
9.3 KiB
Markdown
229 lines
9.3 KiB
Markdown
# Email Notification System Audit
|
|
|
|
**Date:** January 29, 2026
|
|
**Status:** ✅ System Architecture Sound, Minor Issues Identified
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
The WooNooW email notification system is **well-architected** with proper async handling, template rendering, and event management. The main components work together correctly. However, some potential gaps and improvements were identified.
|
|
|
|
---
|
|
|
|
## System Architecture
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[WooCommerce Hooks] --> B[EmailManager]
|
|
B --> C{Is WooNooW Mode?}
|
|
C -->|Yes| D[EmailRenderer]
|
|
C -->|No| E[WC Default Emails]
|
|
D --> F[TemplateProvider]
|
|
F --> G[Get Template]
|
|
G --> H[Replace Variables]
|
|
H --> I[Parse Markdown/Cards]
|
|
I --> J[wp_mail]
|
|
J --> K[WooEmailOverride Intercepts]
|
|
K --> L[MailQueue::enqueue]
|
|
L --> M[Action Scheduler]
|
|
M --> N[MailQueue::sendNow]
|
|
N --> O[Actual wp_mail]
|
|
```
|
|
|
|
---
|
|
|
|
## Core Components
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| [EmailManager.php](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Notifications/EmailManager.php) | Hooks WC order events, disables WC emails, routes to renderer |
|
|
| [EmailRenderer.php](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Notifications/EmailRenderer.php) | Renders templates, replaces variables, parses markdown |
|
|
| [TemplateProvider.php](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Notifications/TemplateProvider.php) | Manages templates, defaults, variable definitions |
|
|
| [EventRegistry.php](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Notifications/EventRegistry.php) | Central registry of all notification events |
|
|
| [NotificationManager.php](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Notifications/NotificationManager.php) | Validates settings, dispatches to channels |
|
|
| [WooEmailOverride.php](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Mail/WooEmailOverride.php) | Intercepts wp_mail via `pre_wp_mail` filter |
|
|
| [MailQueue.php](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Mail/MailQueue.php) | Async queue via Action Scheduler |
|
|
|
|
---
|
|
|
|
## Email Flow Trace
|
|
|
|
### 1. Event Trigger
|
|
- WooCommerce fires hooks like `woocommerce_order_status_pending_to_processing`
|
|
- `EmailManager::init_hooks()` registers callbacks for these hooks
|
|
|
|
### 2. EmailManager Processing
|
|
```php
|
|
// In EmailManager.php
|
|
add_action('woocommerce_order_status_pending_to_processing', [$this, 'send_order_processing_email']);
|
|
```
|
|
- Checks if WooNooW mode enabled: `is_enabled()`
|
|
- Checks if event enabled: `is_event_enabled()`
|
|
- Calls `send_email($event_id, $recipient_type, $order)`
|
|
|
|
### 3. Email Rendering
|
|
- `EmailRenderer::render()` called
|
|
- Gets template from `TemplateProvider::get_template()`
|
|
- Gets variables from `get_variables()` (order, customer, product data)
|
|
- Replaces `{variable}` placeholders
|
|
- Parses `[card]` markdown syntax
|
|
- Wraps in HTML template from `templates/emails/base.html`
|
|
|
|
### 4. wp_mail Interception
|
|
- `wp_mail()` is called with rendered HTML
|
|
- `WooEmailOverride::interceptMail()` catches via `pre_wp_mail` filter
|
|
- Returns `true` to short-circuit synchronous send
|
|
|
|
### 5. Queue & Async Send
|
|
- `MailQueue::enqueue()` stores payload in `wp_options` (temp)
|
|
- Schedules `woonoow/mail/send` action via Action Scheduler
|
|
- `MailQueue::sendNow()` runs asynchronously:
|
|
- Retrieves payload from options
|
|
- Disables `WooEmailOverride` to prevent loop
|
|
- Calls actual `wp_mail()`
|
|
- Deletes temp option
|
|
|
|
---
|
|
|
|
## Findings
|
|
|
|
### ✅ Working Correctly
|
|
|
|
1. **Async Email Queue**: Properly prevents timeout issues
|
|
2. **Template System**: Variables replaced correctly
|
|
3. **Event Registry**: Single source of truth
|
|
4. **Subscription Events**: Registered via `woonoow_notification_events_registry` filter
|
|
5. **Global Toggle**: WooNooW vs WooCommerce mode works
|
|
6. **WC Email Disable**: Default emails properly disabled when WooNooW active
|
|
|
|
### ⚠️ Potential Issues
|
|
|
|
#### 1. Missing Subscription Variable Population in EmailRenderer
|
|
**Location:** [EmailRenderer.php:147-299](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Notifications/EmailRenderer.php#L147-L299)
|
|
|
|
**Issue:** `get_variables()` handles `WC_Order`, `WC_Product`, `WC_Customer` but NOT subscription objects. Subscription notifications pass data like:
|
|
```php
|
|
$data = [
|
|
'subscription' => $subscription, // Custom subscription object
|
|
'customer' => $user,
|
|
'product' => $product,
|
|
...
|
|
]
|
|
```
|
|
|
|
**Impact:** Subscription email variables like `{subscription_id}`, `{billing_period}`, `{next_payment_date}` may not be replaced.
|
|
|
|
**Recommendation:** Add subscription variable population in `EmailRenderer::get_variables()`.
|
|
|
|
---
|
|
|
|
#### 2. EmailRenderer Type Check for Subscription
|
|
**Location:** [EmailRenderer.php:121-137](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Notifications/EmailRenderer.php#L121-L137)
|
|
|
|
**Issue:** `get_recipient_email()` only checks for `WC_Order` and `WC_Customer`. For subscriptions, `$data` is an array, so recipient email extraction fails.
|
|
|
|
**Impact:** Subscription emails may not find recipient email.
|
|
|
|
**Recommendation:** Handle array data or subscription object in `get_recipient_email()`.
|
|
|
|
---
|
|
|
|
#### 3. SubscriptionModule Sends to NotificationManager, Not EmailManager
|
|
**Location:** [SubscriptionModule.php:529-531](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Modules/Subscription/SubscriptionModule.php#L529-L531)
|
|
|
|
**Code:**
|
|
```php
|
|
\WooNooW\Core\Notifications\NotificationManager::send($event_id, 'email', $data);
|
|
```
|
|
|
|
**Issue:** This goes through `NotificationManager`, which calls its own `send_email()` that uses `EmailRenderer::render()`. The `EmailRenderer::render()` method receives `$data['subscription']` but doesn't know how to handle it.
|
|
|
|
**Impact:** Subscription email rendering may fail silently.
|
|
|
|
---
|
|
|
|
#### 4. No Error Logging in Email Rendering Failures
|
|
**Location:** [EmailRenderer.php:48-57](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Core/Notifications/EmailRenderer.php#L48-L57)
|
|
|
|
**Issue:** When `get_template_settings()` returns null or `get_recipient_email()` returns null, the function returns null silently with only an empty debug log statement.
|
|
|
|
**Recommendation:** Add proper `error_log()` calls for debugging.
|
|
|
|
---
|
|
|
|
#### 5. Duplicate wp_mail Calls
|
|
**Location:** Multiple places call `wp_mail()` directly:
|
|
- `EmailManager::send_email()` (line 521)
|
|
- `EmailManager::send_password_reset_email()` (line 406)
|
|
- `NotificationManager::send_email()` (line 170)
|
|
- `NotificationsController` test endpoint (line 1013)
|
|
- `CampaignManager` (lines 275, 329)
|
|
- `NewsletterController` (line 203)
|
|
|
|
**Issue:** All these are intercepted by `WooEmailOverride`, which is correct. However, if `WooEmailOverride` is disabled (testing mode), all send synchronously.
|
|
|
|
**Status:** Working as designed.
|
|
|
|
---
|
|
|
|
## Subscription Email Gap Analysis
|
|
|
|
The subscription module has these events defined but needs variable population:
|
|
|
|
| Event | Variables Needed |
|
|
|-------|-----------------|
|
|
| `subscription_pending_cancellation` | subscription_id, product_name, end_date |
|
|
| `subscription_cancelled` | subscription_id, cancel_reason |
|
|
| `subscription_expired` | subscription_id, product_name |
|
|
| `subscription_paused` | subscription_id, product_name |
|
|
| `subscription_resumed` | subscription_id, product_name |
|
|
| `subscription_renewal_failed` | subscription_id, failed_count, payment_link |
|
|
| `subscription_renewal_payment_due` | subscription_id, payment_link |
|
|
| `subscription_renewal_reminder` | subscription_id, next_payment_date |
|
|
|
|
**Required Fix:** Add subscription data handling to `EmailRenderer::get_variables()`.
|
|
|
|
---
|
|
|
|
## Recommendations
|
|
|
|
### High Priority
|
|
|
|
1. **Fix `EmailRenderer::get_variables()`** - Add handling for subscription data arrays
|
|
2. **Fix `EmailRenderer::get_recipient_email()`** - Handle array data with customer key
|
|
|
|
### Medium Priority
|
|
|
|
3. **Add error logging** - Replace empty debug conditions with actual logging
|
|
4. **Clean up debug conditions** - Many `if (defined('WP_DEBUG') && WP_DEBUG) {}` are empty
|
|
|
|
### Low Priority
|
|
|
|
5. **Consolidate email sending paths** - Consider routing all through one method
|
|
6. **Add email send failure tracking** - Log failed sends for troubleshooting
|
|
|
|
---
|
|
|
|
## Test Scripts Available
|
|
|
|
| Script | Purpose |
|
|
|--------|---------|
|
|
| `check-settings.php` | Diagnose notification settings |
|
|
| `test-email-flow.php` | Interactive email testing dashboard |
|
|
| `test-email-direct.php` | Direct wp_mail testing |
|
|
|
|
---
|
|
|
|
## Documentation
|
|
|
|
Comprehensive docs exist:
|
|
- [NOTIFICATION_SYSTEM.md](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/NOTIFICATION_SYSTEM.md)
|
|
- [EMAIL_DEBUGGING_GUIDE.md](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/EMAIL_DEBUGGING_GUIDE.md)
|
|
|
|
---
|
|
|
|
## Conclusion
|
|
|
|
The email notification system is **production-ready** for order-related notifications. The main gap is **subscription email variable population**, which requires updates to `EmailRenderer.php` to properly handle subscription data and extract variables.
|