PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.1
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / app / Services / Email / EmailNotifications.php

EmailNotifications.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.1, at app/Services/Email/EmailNotifications.php

828 lines 43.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Email;
4
5 use FluentCart\App\Models\Meta;
6 use FluentCart\App\Services\Cache;
7 use FluentCart\App\Services\TemplateService;
8 use FluentCart\Framework\Support\Arr;
9
10 class EmailNotifications
11 {
12
13 const EMAIL_RECIPIENT_MAP = [
14 'customer' => '{{order.customer.email}}',
15 'user' => '{{user.user_email}}',
16 'subscriber' => '{{order.customer.email}}'
17 ];
18
19 const META_KEY = 'email_notifications_config';
20
21 public static function getNotifications(): array
22 {
23 $settings = static::getDefaultNotifications();
24 $settings = apply_filters('fluent_cart/email_notifications', $settings);
25 $config = Arr::get(static::cachedSettings(), 'notification_config', []);
26
27 foreach ($settings as $key => &$setting) {
28 $setting['name'] = $key;
29
30 if (empty($setting['group_label'])) {
31 $setting['group_label'] = __('Other Actions', 'fluent-cart');
32 }
33
34 $keyConfig = Arr::get($config, $key, []);
35 if (!$keyConfig) {
36 continue;
37 }
38
39 $setting['settings'] = wp_parse_args($keyConfig, $setting['settings']);
40
41 // Backward compat: migrate old attach_pdf to attach_pdf_template
42 if (empty($setting['settings']['attach_pdf_template']) && !empty($setting['settings']['attach_pdf']) && $setting['settings']['attach_pdf'] === 'yes') {
43 $setting['settings']['attach_pdf_template'] = 'order_receipt';
44 }
45 unset($setting['settings']['attach_pdf']);
46 }
47
48 return $settings;
49 }
50
51 public static function getDefaultNotifications(): array
52 {
53
54 /**
55 * Order Placed (Offline Payment) -> fluent_cart/order_placed_offline (customer + admin)
56 * Order Paid -> fluent_cart/order_paid (customer + admin)
57 * Order Renewal -> fluent_cart/subscription_renewed (customer + admin)
58 * Order Refunded -> fluent_cart/order_fully_refunded (customer + admin)
59 * Order Shipping Status Changed => fluent_cart/shipping_status_changed (customer + admin)
60 * Reminder Events:
61 * - fluent_cart/renewal_reminder_due
62 * - fluent_cart/renewal_reminder_overdue
63 * - fluent_cart/subscription_renewal_reminder
64 * - fluent_cart/subscription_trial_end_reminder
65 */
66
67 return [
68 'order_paid_admin' => [
69 'event' => 'order_paid_done',
70 'group' => 'order',
71 'group_label' => __('Order Actions', 'fluent-cart'),
72 'title' => __('Send mail to admin after New Order Paid', 'fluent-cart'),
73 'description' => __('This email will be sent to the admin after an order is placed.', 'fluent-cart'),
74 'recipient' => 'admin',
75 'smartcode_groups' => [],
76 'template_path' => 'order.paid.admin',
77 'is_async' => false,
78 'pre_header' => __('You got a new order on your shop. Congratulations! Checkout all the details in this email. You can also go to FluentCart Dashboard to view the order details and manage it. Thank you for using FluentCart.', 'fluent-cart'),
79 'settings' => [
80 'active' => 'yes',
81 'subject' => __('New Sales On {{settings.store_name}}', 'fluent-cart'),
82 'is_default_body' => 'yes',
83 'email_body' => '',
84 'attach_pdf_template' => '',
85 ]
86 ],
87 'order_paid_customer' => [
88 'event' => 'order_paid',
89 'group' => 'order',
90 'group_label' => __('Order Actions', 'fluent-cart'),
91 'title' => __('Purchase receipt to customer', 'fluent-cart'),
92 'description' => __('This email will be sent to the customer after an order is placed.', 'fluent-cart'),
93 'recipient' => 'customer',
94 'smartcode_groups' => [],
95 'template_path' => 'order.paid.customer',
96 'is_async' => false,
97 'settings' => [
98 'active' => 'yes',
99 'subject' => __('Purchase Receipt #{{order.invoice_no}}', 'fluent-cart'),
100 'is_default_body' => 'yes',
101 'email_body' => '',
102 'attach_pdf_template' => '',
103 ]
104 ],
105 'subscription_renewal_customer' => [
106 'event' => 'subscription_renewed',
107 'group' => 'subscription',
108 'group_label' => __('Subscription Actions', 'fluent-cart'),
109 'title' => __('Send mail to customer after a subscription renewed', 'fluent-cart'),
110 'description' => __('This email will be sent to the customer after a renewal payment made', 'fluent-cart'),
111 'recipient' => 'customer',
112 'smartcode_groups' => [],
113 'template_path' => 'subscription.renewal.customer',
114 'is_async' => false,
115 'settings' => [
116 'active' => 'yes',
117 'subject' => __('Renewal Confirmation on {{settings.store_name}}', 'fluent-cart'),
118 'is_default_body' => 'yes',
119 'email_body' => '',
120 'attach_pdf_template' => '',
121 ]
122 ],
123 'subscription_renewal_admin' => [
124 'event' => 'subscription_renewed',
125 'group' => 'subscription',
126 'group_label' => __('Subscription Actions', 'fluent-cart'),
127 'title' => __('Send mail to admin after a subscription renewed', 'fluent-cart'),
128 'description' => __('This email will be sent to the admin after a renewal payment made', 'fluent-cart'),
129 'recipient' => 'admin',
130 'smartcode_groups' => [],
131 'template_path' => 'subscription.renewal.admin',
132 'pre_header' => __('You got a new Renewal on your shop. Congratulations! Checkout all the details in this email. You can also go to FluentCart Dashboard to view the order details and manage it. Thank you for using FluentCart.', 'fluent-cart'),
133 'is_async' => false,
134 'settings' => [
135 'active' => 'yes',
136 'subject' => __('New Renewal On {{settings.store_name}}', 'fluent-cart'),
137 'is_default_body' => 'yes',
138 'email_body' => '',
139 'attach_pdf_template' => '',
140 ]
141 ],
142 'subscription_renewal_failed_customer' => [
143 'event' => 'subscription_renewal_failed',
144 'group' => 'subscription',
145 'group_label' => __('Subscription Actions', 'fluent-cart'),
146 'title' => __('Notify customer when an automatic renewal charge fails', 'fluent-cart'),
147 'description' => __('This email will be sent to the customer when their saved payment method could not be charged for a subscription renewal.', 'fluent-cart'),
148 'recipient' => 'customer',
149 'smartcode_groups' => [],
150 'template_path' => 'subscription.renewal_failed.customer',
151 'is_async' => false,
152 'settings' => [
153 'active' => 'yes',
154 'subject' => __('Automatic Payment Failed for Renewal #{{order.invoice_no}}', 'fluent-cart'),
155 'is_default_body' => 'yes',
156 'email_body' => '',
157 ]
158 ],
159 'subscription_renewal_failed_admin' => [
160 'event' => 'subscription_renewal_failed',
161 'group' => 'subscription',
162 'group_label' => __('Subscription Actions', 'fluent-cart'),
163 'title' => __('Notify admin when an automatic renewal charge fails', 'fluent-cart'),
164 'description' => __('This email will be sent to the admin when a saved payment method could not be charged for a subscription renewal.', 'fluent-cart'),
165 'recipient' => 'admin',
166 'smartcode_groups' => [],
167 'template_path' => 'subscription.renewal_failed.admin',
168 'pre_header' => __('An automatic subscription renewal charge failed. Review the order in the FluentCart Dashboard.', 'fluent-cart'),
169 'is_async' => false,
170 'settings' => [
171 'active' => 'no',
172 'subject' => __('Automatic Charge Failed for Renewal #{{order.invoice_no}}', 'fluent-cart'),
173 'is_default_body' => 'yes',
174 'email_body' => '',
175 ]
176 ],
177 'subscription_canceled_customer' => [
178 'event' => 'subscription_canceled',
179 'group' => 'subscription',
180 'group_label' => __('Subscription Actions', 'fluent-cart'),
181 'title' => __('Send mail to customer when a subscription is canceled', 'fluent-cart'),
182 'description' => __('This email will be sent to the customer when their subscription is canceled.', 'fluent-cart'),
183 'recipient' => 'customer',
184 'smartcode_groups' => [],
185 'template_path' => 'subscription.canceled.customer',
186 'is_async' => false,
187 'settings' => [
188 'active' => 'yes',
189 'subject' => __('Subscription Canceled on {{settings.store_name}}', 'fluent-cart'),
190 'is_default_body' => 'yes',
191 'email_body' => '',
192 ]
193 ],
194 'subscription_canceled_admin' => [
195 'event' => 'subscription_canceled',
196 'group' => 'subscription',
197 'group_label' => __('Subscription Actions', 'fluent-cart'),
198 'title' => __('Send mail to admin when a subscription is canceled', 'fluent-cart'),
199 'description' => __('This email will be sent to the admin when a subscription is canceled.', 'fluent-cart'),
200 'recipient' => 'admin',
201 'smartcode_groups' => [],
202 'template_path' => 'subscription.canceled.admin',
203 'pre_header' => __('A subscription has been canceled. Review the details in this email or visit FluentCart Dashboard to manage the subscription.', 'fluent-cart'),
204 'is_async' => false,
205 'settings' => [
206 'active' => 'yes',
207 'subject' => __('Subscription Canceled - {{order.customer.full_name}}', 'fluent-cart'),
208 'is_default_body' => 'yes',
209 'email_body' => '',
210 ]
211 ],
212 'subscription_period_skipped_customer' => [
213 'event' => 'subscription_period_skipped',
214 'group' => 'subscription',
215 'group_label' => __('Subscription Actions', 'fluent-cart'),
216 'title' => __('Notify customer when a billing period is skipped', 'fluent-cart'),
217 'description' => __('This email will be sent to the customer when an admin skips their next billing period. The internal reason and actor are never included. Off by default.', 'fluent-cart'),
218 'recipient' => 'customer',
219 'smartcode_groups' => [],
220 'template_path' => 'subscription.period_skipped.customer',
221 'is_async' => false,
222 'settings' => [
223 'active' => 'no',
224 'subject' => __('Your next payment has been skipped on {{settings.store_name}}', 'fluent-cart'),
225 'is_default_body' => 'yes',
226 'email_body' => '',
227 ]
228 ],
229 'order_refunded_admin' => [
230 'event' => 'order_refunded',
231 'group' => 'order',
232 'group_label' => __('Order Actions', 'fluent-cart'),
233 'title' => __('Send mail to admin after a refund.', 'fluent-cart'),
234 'description' => __('This email will be sent to the admin after an order is refunded (partial / full).', 'fluent-cart'),
235 'recipient' => 'admin',
236 'smartcode_groups' => [],
237 'template_path' => 'order.refunded.admin',
238 'is_async' => false,
239 'settings' => [
240 'active' => 'no',
241 'subject' => __('Refund sent to {{order.customer.full_name}}', 'fluent-cart'),
242 'is_default_body' => 'yes',
243 'email_body' => '',
244 'attach_pdf_template' => '',
245 ]
246 ],
247 'order_refunded_customer' => [
248 'event' => 'order_refunded',
249 'group' => 'order',
250 'group_label' => __('Order Actions', 'fluent-cart'),
251 'title' => __('Send mail to customer after a refund.', 'fluent-cart'),
252 'description' => __('This email will be sent to the customer after an order is refunded (partial / full).', 'fluent-cart'),
253 'recipient' => 'customer',
254 'smartcode_groups' => [],
255 'template_path' => 'order.refunded.customer',
256 'is_async' => false,
257 'settings' => [
258 'active' => 'yes',
259 'subject' => __('Refund Confirmation from {{settings.store_name}}', 'fluent-cart'),
260 'is_default_body' => 'yes',
261 'email_body' => '',
262 'attach_pdf_template' => '',
263 ]
264 ],
265 'order_shipped_customer' => [
266 'event' => 'shipping_status_changed_to_shipped',
267 'group' => 'order',
268 'group_label' => __('Order Actions', 'fluent-cart'),
269 'title' => __('Send mail to customer when shipping status changed to shipped.', 'fluent-cart'),
270 'description' => __('This email will be sent to the customer after an order is marked as shipped', 'fluent-cart'),
271 'recipient' => 'customer',
272 'smartcode_groups' => [],
273 'template_path' => 'order.shipped.customer',
274 'is_async' => false,
275 'settings' => [
276 'active' => 'yes',
277 'subject' => __('Order has been shipped #{{order.invoice_no}} 📦', 'fluent-cart'),
278 'is_default_body' => 'yes',
279 'email_body' => '',
280 'attach_pdf_template' => '',
281 ]
282 ],
283 'order_delivered_customer' => [
284 'event' => 'shipping_status_changed_to_delivered',
285 'group' => 'order',
286 'group_label' => __('Order Actions', 'fluent-cart'),
287 'title' => __('Send mail to customer when shipping status changed to delivered.', 'fluent-cart'),
288 'description' => __('This email will be sent to the customer after an order is marked as delivered', 'fluent-cart'),
289 'recipient' => 'customer',
290 'smartcode_groups' => [],
291 'template_path' => 'order.delivered.customer',
292 'is_async' => false,
293 'settings' => [
294 'active' => 'yes',
295 'subject' => __('Order has been delivered #{{order.invoice_no}}', 'fluent-cart'),
296 'is_default_body' => 'yes',
297 'email_body' => '',
298 'attach_pdf_template' => '',
299 ]
300 ],
301 'order_placed_admin' => [
302 'event' => 'order_placed_offline',
303 'group' => 'order',
304 'group_label' => __('Order Actions', 'fluent-cart'),
305 'title' => __('Send mail to admin after New Order Placed (Offline Payment)', 'fluent-cart'),
306 'description' => __('This email will be sent to the admin when an order is placed using offline payment method.', 'fluent-cart'),
307 'recipient' => 'admin',
308 'smartcode_groups' => [],
309 'template_path' => 'order.placed.admin',
310 'is_async' => false,
311 'manage_toggle' => 'no',
312 'toggle_label' => __('Auto-enabled for offline payments', 'fluent-cart'),
313 'pre_header' => __('You have a new order on your shop placed with offline payment. Please review the order details in this email. You can also go to FluentCart Dashboard to view the order details and manage it. Thank you for using FluentCart.', 'fluent-cart'),
314 'settings' => [
315 'subject' => __('New Order on {{settings.store_name}} (Offline Payment)', 'fluent-cart'),
316 'is_default_body' => 'yes',
317 'email_body' => '',
318 'attach_pdf_template' => '',
319 ]
320 ],
321 'order_placed_customer' => [
322 'event' => 'order_placed_offline',
323 'group' => 'order',
324 'group_label' => __('Order Actions', 'fluent-cart'),
325 'title' => __('Order confirmation to customer (Offline Payment)', 'fluent-cart'),
326 'description' => __('This email will be sent to the customer when an order is placed using offline payment method.', 'fluent-cart'),
327 'recipient' => 'customer',
328 'smartcode_groups' => [],
329 'template_path' => 'order.placed.customer',
330 'is_async' => false,
331 'manage_toggle' => 'no',
332 'toggle_label' => __('Auto-enabled for offline payments', 'fluent-cart'),
333 'settings' => [
334 'subject' => __('Order Confirmation #{{order.invoice_no}} (Offline Payment)', 'fluent-cart'),
335 'is_default_body' => 'yes',
336 'email_body' => '',
337 'attach_pdf_template' => '',
338 ]
339 ],
340 'renewal_created_admin' => [
341 'event' => 'renewal_created',
342 'group' => 'manual_subscription',
343 'group_label' => __('Store-Managed Renewals', 'fluent-cart'),
344 'title' => __('Notify admin when a renewal order is created', 'fluent-cart'),
345 'description' => __('This email will be sent to the admin when a renewal order is generated for a store-managed subscription.', 'fluent-cart'),
346 'recipient' => 'admin',
347 'smartcode_groups' => [],
348 'template_path' => 'renewal.created.admin',
349 'pre_header' => 'A new renewal order has been created for a store-managed subscription. Review it in the FluentCart Dashboard.',
350 'is_async' => false,
351 'settings' => [
352 'active' => 'yes',
353 'subject' => __('New Renewal Order Created on {{settings.store_name}}', 'fluent-cart'),
354 'is_default_body' => 'yes',
355 'email_body' => '',
356 ]
357 ],
358 'renewal_created_customer' => [
359 'event' => 'renewal_created',
360 'group' => 'manual_subscription',
361 'group_label' => __('Store-Managed Renewals', 'fluent-cart'),
362 'title' => __('Send the renewal order to the customer when a renewal order is created', 'fluent-cart'),
363 'description' => __('This email will be sent to the customer when a renewal order is generated for a store-managed subscription.', 'fluent-cart'),
364 'recipient' => 'customer',
365 'smartcode_groups' => [],
366 'template_path' => 'renewal.created.customer',
367 'is_async' => false,
368 'settings' => [
369 'active' => 'yes',
370 'subject' => __('Your Subscription Renewal #{{order.invoice_no}}', 'fluent-cart'),
371 'is_default_body' => 'yes',
372 'email_body' => '',
373 ]
374 ],
375 'system_upcoming_charge_customer' => [
376 'event' => 'system_upcoming_charge',
377 'group' => 'system_subscription',
378 'group_label' => __('Store-Managed Renewals · Auto-Charge', 'fluent-cart'),
379 'title' => __('Notify customer before an automatic renewal charge', 'fluent-cart'),
380 'description' => __('This email tells the customer the amount and date of the upcoming automatic charge to their saved payment method, when the renewal order is created ahead of the due date.', 'fluent-cart'),
381 'recipient' => 'customer',
382 'smartcode_groups' => [],
383 'template_path' => 'subscription.upcoming_charge.customer',
384 'is_async' => false,
385 'settings' => [
386 'active' => 'yes',
387 'subject' => __('Upcoming automatic payment for your subscription', 'fluent-cart'),
388 'is_default_body' => 'yes',
389 'email_body' => '',
390 ]
391 ],
392 'system_charge_failed_customer' => [
393 'event' => 'system_charge_failed',
394 'group' => 'system_subscription',
395 'group_label' => __('Store-Managed Renewals · Auto-Charge', 'fluent-cart'),
396 'title' => __('Notify customer when an automatic renewal charge fails', 'fluent-cart'),
397 'description' => __('This email will be sent to the customer when the saved payment method could not be charged for a renewal order. It includes the failure reason and a Pay Now link.', 'fluent-cart'),
398 'recipient' => 'customer',
399 'smartcode_groups' => [],
400 'template_path' => 'subscription.charge_failed.customer',
401 'is_async' => false,
402 'settings' => [
403 'active' => 'yes',
404 'subject' => __('Automatic Payment Failed for Renewal #{{order.invoice_no}}', 'fluent-cart'),
405 'is_default_body' => 'yes',
406 'email_body' => '',
407 ]
408 ],
409 'system_charge_failed_admin' => [
410 'event' => 'system_charge_failed',
411 'group' => 'system_subscription',
412 'group_label' => __('Store-Managed Renewals · Auto-Charge', 'fluent-cart'),
413 'title' => __('Notify admin when an automatic renewal charge fails', 'fluent-cart'),
414 'description' => __('This email will be sent to the admin when a saved payment method could not be charged for a renewal order.', 'fluent-cart'),
415 'recipient' => 'admin',
416 'smartcode_groups' => [],
417 'template_path' => 'subscription.charge_failed.admin',
418 'pre_header' => 'An automatic subscription renewal charge failed. Review the renewal order in the FluentCart Dashboard.',
419 'is_async' => false,
420 'settings' => [
421 'active' => 'no',
422 'subject' => __('Automatic Charge Failed for Renewal #{{order.invoice_no}}', 'fluent-cart'),
423 'is_default_body' => 'yes',
424 'email_body' => '',
425 ]
426 ],
427 'renewal_reminder_due_admin' => [
428 'event' => 'renewal_reminder_due',
429 'group' => 'manual_subscription',
430 'group_label' => __('Store-Managed Renewals', 'fluent-cart'),
431 'title' => __('Renewal due reminder copy to admin', 'fluent-cart'),
432 'description' => __('This email will be sent to admin when a due reminder is sent to customer.', 'fluent-cart'),
433 'recipient' => 'admin',
434 'smartcode_groups' => [],
435 'template_path' => 'order.reminder.due.admin',
436 'pre_header' => 'A renewal due reminder was sent to a customer. Review the order details in this email or visit FluentCart Dashboard.',
437 'is_async' => false,
438 'settings' => [
439 'active' => 'no',
440 'subject' => __('Renewal Reminder Sent #{{order.invoice_no}}', 'fluent-cart'),
441 'is_default_body' => 'yes',
442 'email_body' => '',
443 ]
444 ],
445 'renewal_reminder_due_customer' => [
446 'event' => 'renewal_reminder_due',
447 'group' => 'manual_subscription',
448 'group_label' => __('Store-Managed Renewals', 'fluent-cart'),
449 'title' => __('Renewal due reminder to customer', 'fluent-cart'),
450 'description' => __('This email will be sent before/at renewal due date when payment is pending.', 'fluent-cart'),
451 'recipient' => 'customer',
452 'smartcode_groups' => [],
453 'template_path' => 'order.reminder.due.customer',
454 'is_async' => false,
455 'settings' => [
456 'active' => 'yes',
457 'subject' => __('Payment Reminder #{{order.invoice_no}}', 'fluent-cart'),
458 'is_default_body' => 'yes',
459 'email_body' => '',
460 ]
461 ],
462 'renewal_reminder_overdue_customer' => [
463 'event' => 'renewal_reminder_overdue',
464 'group' => 'scheduler',
465 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'),
466 'title' => __('Payment reminder to customer', 'fluent-cart'),
467 'description' => __('This email will be sent to remind customer about pending payment.', 'fluent-cart'),
468 'recipient' => 'customer',
469 'smartcode_groups' => [],
470 'template_path' => 'order.reminder.overdue.customer',
471 'is_async' => false,
472 'manage_toggle' => 'no',
473 'toggle_label' => __('On demand', 'fluent-cart'),
474 'settings' => [
475 'subject' => __('Payment Reminder - Order #{{order.id}}', 'fluent-cart'),
476 'is_default_body' => 'yes',
477 'email_body' => '',
478 ]
479 ],
480 'subscription_past_due_admin' => [
481 'event' => 'subscription_past_due',
482 'group' => 'subscription',
483 'group_label' => __('Subscription Actions', 'fluent-cart'),
484 'title' => __('Notify admin when a subscription is past due', 'fluent-cart'),
485 'description' => __('This email will be sent to the admin when a subscription is marked as past due.', 'fluent-cart'),
486 'recipient' => 'admin',
487 'smartcode_groups' => [],
488 'template_path' => 'subscription.past_due.admin',
489 'pre_header' => 'A subscription payment is past due. Review the subscription details in this email or visit FluentCart Dashboard.',
490 'is_async' => false,
491 'settings' => [
492 'active' => 'no',
493 'subject' => __('Subscription Past Due #{{order.invoice_no}}', 'fluent-cart'),
494 'is_default_body' => 'yes',
495 'email_body' => '',
496 ]
497 ],
498 'subscription_past_due_customer' => [
499 'event' => 'subscription_past_due',
500 'group' => 'subscription',
501 'group_label' => __('Subscription Actions', 'fluent-cart'),
502 'title' => __('Notify customer when subscription is past due', 'fluent-cart'),
503 'description' => __('This email will be sent to the customer when their subscription is marked as past due.', 'fluent-cart'),
504 'recipient' => 'customer',
505 'smartcode_groups' => [],
506 'template_path' => 'subscription.past_due.customer',
507 'is_async' => false,
508 'settings' => [
509 'active' => 'yes',
510 'subject' => __('Your Subscription is Past Due on {{settings.store_name}}', 'fluent-cart'),
511 'is_default_body' => 'yes',
512 'email_body' => '',
513 ]
514 ],
515 'subscription_renewal_reminder_admin' => [
516 'event' => 'subscription_renewal_reminder',
517 'group' => 'scheduler',
518 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'),
519 'title' => __('Upcoming renewal reminder copy to admin', 'fluent-cart'),
520 'description' => __('This email will be sent to admin when an upcoming renewal reminder is sent.', 'fluent-cart'),
521 'recipient' => 'admin',
522 'smartcode_groups' => [],
523 'template_path' => 'subscription.reminder.admin',
524 'pre_header' => __('A subscription renewal reminder was sent to a customer. Review subscription details from FluentCart Dashboard.', 'fluent-cart'),
525 'is_async' => false,
526 'settings' => [
527 'active' => 'no',
528 'subject' => __('Renewal Reminder Sent for {{order.customer.full_name}}', 'fluent-cart'),
529 'is_default_body' => 'yes',
530 'email_body' => '',
531 ]
532 ],
533 'subscription_renewal_reminder_customer' => [
534 'event' => 'subscription_renewal_reminder',
535 'group' => 'scheduler',
536 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'),
537 'title' => __('Upcoming renewal reminder to customer', 'fluent-cart'),
538 'description' => __('This email will be sent before subscription auto-renewal date.', 'fluent-cart'),
539 'recipient' => 'customer',
540 'smartcode_groups' => [],
541 'template_path' => 'subscription.reminder.customer',
542 'is_async' => false,
543 'settings' => [
544 'active' => 'yes',
545 'subject' => __('Upcoming Renewal Reminder from {{settings.store_name}}', 'fluent-cart'),
546 'is_default_body' => 'yes',
547 'email_body' => '',
548 ]
549 ],
550 'subscription_trial_end_reminder_admin' => [
551 'event' => 'subscription_trial_end_reminder',
552 'group' => 'scheduler',
553 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'),
554 'title' => __('Trial ending soon reminder copy to admin', 'fluent-cart'),
555 'description' => __('This email will be sent to admin when a trial ending reminder is sent to a customer.', 'fluent-cart'),
556 'recipient' => 'admin',
557 'smartcode_groups' => [],
558 'template_path' => 'subscription.trial_end.admin',
559 'pre_header' => __('A trial ending soon reminder was sent to a customer. Review subscription details from FluentCart Dashboard.', 'fluent-cart'),
560 'is_async' => false,
561 'settings' => [
562 'active' => 'no',
563 'subject' => __('Trial Ending Soon - {{order.customer.full_name}}', 'fluent-cart'),
564 'is_default_body' => 'yes',
565 'email_body' => '',
566 ]
567 ],
568 'subscription_trial_end_reminder_customer' => [
569 'event' => 'subscription_trial_end_reminder',
570 'group' => 'scheduler',
571 'group_label' => __('Scheduler / Reminder Actions', 'fluent-cart'),
572 'title' => __('Trial ending soon reminder to customer', 'fluent-cart'),
573 'description' => __('This email will be sent before a trial period ends and converts to a paid subscription.', 'fluent-cart'),
574 'recipient' => 'customer',
575 'smartcode_groups' => [],
576 'template_path' => 'subscription.trial_end.customer',
577 'is_async' => false,
578 'settings' => [
579 'active' => 'yes',
580 'subject' => __('Your Trial is Ending Soon - {{settings.store_name}}', 'fluent-cart'),
581 'is_default_body' => 'yes',
582 'email_body' => '',
583 ]
584 ],
585 ];
586
587 }
588
589 public static function getNotificationsOfEvent($event, $viewData): array
590 {
591 $notifications = static::getNotifications();
592 $notifications = array_filter($notifications, function ($notification) use ($event) {
593 return $notification['event'] === $event;
594 });
595
596 if (empty($notifications)) {
597 return [];
598 }
599
600 $emails = [];
601 $mailingSettings = static::getSettings();
602
603 foreach ($notifications as $key => $notification) {
604
605 $settings = Arr::get($notification, 'settings');
606
607 // Skip active check for on-demand notifications (offline payments, payment reminders)
608 if (in_array($notification['event'], ['order_placed_offline', 'renewal_reminder_overdue']) || Arr::get($settings, 'active') === 'yes') {
609 // Continue with normal flow
610 } else {
611 continue;
612 }
613 $recipient = Arr::get($notification, 'recipient');
614
615 // A notification that declares its own `to` carries its recipient
616 // with it, so the whitelist — which only describes the recipients
617 // core knows how to resolve — does not apply to it.
618 if (!Arr::get($notification, 'to') && !in_array($recipient, ['admin', 'customer', 'user', 'subscriber'])) {
619 continue;
620 }
621
622 $toEmail = static::resolveRecipientTemplate($notification, $mailingSettings);
623
624 if (empty($toEmail)) {
625 continue;
626 }
627
628 $emails[$key] = static::formatNotification($notification, $viewData);
629 }
630
631 return $emails;
632 }
633
634 public static function formatNotification($notification, $viewData): array
635 {
636 $settings = Arr::get($notification, 'settings');
637 $mailingSettings = static::getSettings();
638
639 $recipient = Arr::get($notification, 'recipient');
640 $toEmail = static::resolveRecipientTemplate($notification, $mailingSettings);
641
642
643 $isDefaultEmailBody = Arr::get($settings, 'is_default_body', 'yes') === 'yes' || empty(Arr::get($settings, 'email_body'));
644
645 $emailBody = $isDefaultEmailBody ?
646 TemplateService::getTemplateByPathName(Arr::get($notification, 'template_path'), $viewData) :
647 Arr::get($settings, 'email_body');
648
649 return [
650 'to' => $toEmail,
651 'body' => $emailBody,
652 'pre_header' => Arr::get($notification, 'pre_header', ''),
653 'is_async' => $notification['is_async'],
654 'subject' => Arr::get($settings, 'subject'),
655 'is_custom' => !$isDefaultEmailBody,
656 'settings' => $settings,
657 // Carried through so send-time consumers can tell one notification
658 // from another, and admin-bound mail from customer-bound — see the
659 // admin recipient filter in parseEmailContent().
660 'name' => Arr::get($notification, 'name', ''),
661 'event' => Arr::get($notification, 'event', ''),
662 'recipient' => $recipient,
663 ];
664 }
665
666 /**
667 * Resolve the recipient template for a notification.
668 *
669 * A notification may declare its own `to` — a smartcode template, or a
670 * literal address — which wins outright. Add-ons need this: they register
671 * notifications that are not order-bound, and the built-in customer
672 * recipient resolves to {{order.customer.email}}, which has nothing to read
673 * from without an order.
674 *
675 * Falling back to the recipient map keeps every core notification, and any
676 * add-on that does not declare `to`, on exactly the path it used before.
677 *
678 * The returned value is a template — parseEmailContent() runs it through
679 * the shortcode parser before use. A declared `to` is returned as given:
680 * wp_mail() accepts an array of recipients as well as a comma-separated
681 * string, so an array is passed through rather than flattened.
682 *
683 * @param array $notification
684 * @param array|null $mailingSettings resolved settings, passed in to avoid a repeat lookup in loops
685 * @return string|array
686 */
687 public static function resolveRecipientTemplate($notification, $mailingSettings = null)
688 {
689 $declaredTo = Arr::get($notification, 'to', '');
690 if (!empty($declaredTo)) {
691 return $declaredTo;
692 }
693
694 if ($mailingSettings === null) {
695 $mailingSettings = static::getSettings();
696 }
697
698 $recipient = Arr::get($notification, 'recipient');
699
700 if ($recipient === 'admin') {
701 return (string)Arr::get($mailingSettings, 'admin_email', '');
702 }
703
704 return (string)Arr::get(self::EMAIL_RECIPIENT_MAP, $recipient, '');
705 }
706
707 public static function getNotification($name)
708 {
709 $notifications = static::getNotifications();
710 return Arr::get($notifications, $name);
711 }
712
713 //returns only the notification settings
714 public static function getNotificationConfig($notificationName = null)
715 {
716 $configs = self::getNotifications();
717 if ($notificationName) {
718 return Arr::get($configs, $notificationName . '.settings', []);
719 }
720 return Arr::pluck($configs, 'settings', 'name');
721 }
722
723 public static function updateNotification($name, $data)
724 {
725 $updateableKeys = [
726 'active',
727 'subject',
728 'email_body',
729 'is_default_body',
730 'attach_pdf_template',
731 ];
732 $allConfig = static::getSettings();
733 $config = static::getNotificationConfig($name);
734
735 foreach ($updateableKeys as $key) {
736 $defaultValue = Arr::get($config, $key, '');
737 $config[$key] = Arr::get($data, $key, $defaultValue);
738 }
739
740 // When switching back to default body, clear the custom email body unless draft mode is enabled
741 if (Arr::get($config, 'is_default_body') === 'yes' && !apply_filters('fluent_cart/keep_email_body_draft', false, ['notification_name' => $name])) {
742 $config['email_body'] = '';
743 }
744
745 Arr::set($allConfig, 'notification_config.' . $name, $config);
746
747 $isUpdate = Meta::query()->updateOrCreate(
748 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
749 ['meta_key' => static::META_KEY, 'object_type' => 'email_notification'],
750 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
751 ['meta_value' => $allConfig]
752 );
753
754 if ($isUpdate) {
755 static::updateCache();
756 }
757
758 return static::getNotification($name);
759 }
760
761 public static function defaultSettings(): array
762 {
763 return [
764 'from_name' => '',
765 'from_email' => '',
766 'reply_to_name' => '',
767 'reply_to_email' => '',
768 'email_footer' => '',
769 'show_email_footer' => 'yes',
770 'admin_email' => '{{wp.admin_email}}',
771 ];
772 }
773
774 //returns all the settings
775 public static function getSettings($key = null)
776 {
777
778 $defaultSettings = static::defaultSettings();
779 $cachedSettings = static::cachedSettings();
780 $settings = wp_parse_args($cachedSettings, $defaultSettings);
781
782 $settings['notification_config'] = wp_parse_args(
783 Arr::get($cachedSettings, 'notification_config', []),
784 Arr::get($defaultSettings, 'notification_config', [])
785 );
786
787 if (!empty($key)) {
788 return Arr::get($settings, $key);
789 }
790
791 return $settings;
792 }
793
794 public static function cachedSettings()
795 {
796 return Cache::get(static::META_KEY, function () {
797 $config = Meta::query()->where('object_type', 'email_notification')
798 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
799 ->where('meta_key', static::META_KEY)->first();
800 return $config ? $config->meta_value : [];
801 });
802 }
803
804 public static function updateSettings($data)
805 {
806 $allConfig = static::getSettings();
807
808 foreach ($data as $key => $value) {
809 Arr::set($allConfig, $key, $value);
810 }
811
812 $config = Meta::query()->updateOrCreate(
813 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
814 ['meta_key' => static::META_KEY, 'object_type' => 'email_notification'],
815 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
816 ['meta_value' => $allConfig]
817 );
818
819 static::updateCache();
820 return $config;
821 }
822
823 private static function updateCache(): void
824 {
825 Cache::forget(static::META_KEY);
826 }
827 }
828