feat: Implement OAuth license activation flow

- 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
This commit is contained in:
Dwindi Ramadhana
2026-01-31 22:22:22 +07:00
parent d80f34c8b9
commit a0b5f8496d
23 changed files with 3218 additions and 806 deletions

View File

@@ -53,6 +53,9 @@ class EmailRenderer
$to = $this->get_recipient_email($recipient_type, $data);
if (!$to) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailRenderer] Failed to get recipient email for event: ' . $event_id);
}
return null;
}
@@ -125,14 +128,48 @@ class EmailRenderer
}
// Customer
if ($data instanceof WC_Order) {
if ($data instanceof \WC_Order) {
return $data->get_billing_email();
}
if ($data instanceof WC_Customer) {
if ($data instanceof \WC_Customer) {
return $data->get_email();
}
if ($data instanceof \WP_User) {
return $data->user_email;
}
// Handle array data (e.g. subscription notifications)
if (is_array($data)) {
// Check for customer object in array
if (isset($data['customer'])) {
if ($data['customer'] instanceof \WP_User) {
return $data['customer']->user_email;
}
if ($data['customer'] instanceof \WC_Customer) {
return $data['customer']->get_email();
}
}
// Check for direct email in data
if (isset($data['email']) && is_email($data['email'])) {
return $data['email'];
}
// Check for user_id
if (isset($data['user_id'])) {
$user = get_user_by('id', $data['user_id']);
if ($user) {
return $user->user_email;
}
}
}
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailRenderer] Could not determine recipient email for type: ' . $recipient_type);
}
return null;
}
@@ -292,6 +329,46 @@ class EmailRenderer
]);
}
// Subscription variables (passed as array)
if (is_array($data) && isset($data['subscription'])) {
$sub = $data['subscription'];
// subscription object usually has: id, user_id, product_id, status, ...
$sub_variables = [
'subscription_id' => $sub->id ?? '',
'subscription_status' => isset($sub->status) ? ucfirst($sub->status) : '',
'billing_period' => isset($sub->billing_period) ? ucfirst($sub->billing_period) : '',
'recurring_amount' => isset($sub->recurring_amount) ? wc_price($sub->recurring_amount) : '',
'next_payment_date' => isset($sub->next_payment_date) ? date('F j, Y', strtotime($sub->next_payment_date)) : 'N/A',
'end_date' => isset($sub->end_date) ? date('F j, Y', strtotime($sub->end_date)) : 'N/A',
'cancel_reason' => $data['reason'] ?? '',
'failed_count' => $data['failed_count'] ?? 0,
'payment_link' => $data['payment_link'] ?? '',
];
// Get product name if not already set
if (!isset($variables['product_name']) && isset($data['product']) && $data['product'] instanceof \WC_Product) {
$sub_variables['product_name'] = $data['product']->get_name();
$sub_variables['product_url'] = get_permalink($data['product']->get_id());
}
// Get customer details if not already set
if (!isset($variables['customer_name']) && isset($data['customer']) && $data['customer'] instanceof \WP_User) {
$user = $data['customer'];
$sub_variables['customer_name'] = $user->display_name;
$sub_variables['customer_email'] = $user->user_email;
}
$variables = array_merge($variables, $sub_variables);
} else if (is_array($data) && isset($data['customer']) && $data['customer'] instanceof \WP_User) {
// Basic user data passed in array without subscription (e.g. generalized notification)
$user = $data['customer'];
$variables = array_merge($variables, [
'customer_name' => $user->display_name,
'customer_email' => $user->user_email,
]);
}
// Merge extra data
$variables = array_merge($variables, $extra_data);