| @@ -117,11 +117,30 @@ | ||
| 117 | 117 | $this->mailEmailsOfEvent('renewal_reminder_due', $data); |
| 118 | 118 | }, 999, 1); |
| 119 | 119 | |
| 120 | 120 | add_action('fluent_cart/renewal_reminder_overdue', function ($data) { |
| 121 | + // Set only by the deprecated bridge in RenewalReminderService::send(): | |
| 122 | + // the staged email (renewal_overdue_first/followup/final) already went | |
| 123 | + // out for this payload, so mailing here would duplicate it. Direct | |
| 124 | + // dispatches of this hook never carry the flag and still deliver. | |
| 125 | + if (!empty(Arr::get($data, 'staged_email_dispatched'))) { | |
| 126 | + return; | |
| 127 | + } | |
| 121 | 128 | $this->mailEmailsOfEvent('renewal_reminder_overdue', $data); |
| 122 | 129 | }, 999, 1); |
| 123 | 130 | |
| 131 | + add_action('fluent_cart/renewal_overdue_first', function ($data) { | |
| 132 | + $this->mailEmailsOfEvent('renewal_overdue_first', $data); | |
| 133 | + }, 999, 1); | |
| 134 | + | |
| 135 | + add_action('fluent_cart/renewal_overdue_followup', function ($data) { | |
| 136 | + $this->mailEmailsOfEvent('renewal_overdue_followup', $data); | |
| 137 | + }, 999, 1); | |
| 138 | + | |
| 139 | + add_action('fluent_cart/renewal_overdue_final', function ($data) { | |
| 140 | + $this->mailEmailsOfEvent('renewal_overdue_final', $data); | |
| 141 | + }, 999, 1); | |
| 142 | + | |
| 124 | 143 | add_action('fluent_cart/subscription_renewal_reminder', function ($data) { |
| 125 | 144 | $this->mailEmailsOfEvent('subscription_renewal_reminder', $data); |
| 126 | 145 | }, 999, 1); |
| 127 | 146 | |
| @@ -237,8 +256,16 @@ | ||
| 237 | 256 | $mailer->addAttachment($pdfPath); |
| 238 | 257 | } |
| 239 | 258 | } |
| 240 | 259 | |
| 260 | + $mailer = $this->applyMailerFilter($mailer, [ | |
| 261 | + 'event' => $event, | |
| 262 | + 'mail_name' => $mailName, | |
| 263 | + 'recipient' => Arr::get($notification, 'recipient'), | |
| 264 | + 'notification' => $notification, | |
| 265 | + 'data' => $data, | |
| 266 | + ]); | |
| 267 | + | |
| 241 | 268 | $mailer->send(true); |
| 242 | 269 | |
| 243 | 270 | // Clean up temp PDF file after sending |
| 244 | 271 | if ($pdfPath && file_exists($pdfPath)) { |
| @@ -273,13 +300,36 @@ | ||
| 273 | 300 | $mailer->addAttachment($pdfPath); |
| 274 | 301 | } |
| 275 | 302 | } |
| 276 | 303 | |
| 304 | + $mailer = $this->applyMailerFilter($mailer, [ | |
| 305 | + 'event' => Arr::get($notification, 'event', ''), | |
| 306 | + 'mail_name' => $emailName, | |
| 307 | + 'recipient' => Arr::get($notification, 'recipient'), | |
| 308 | + 'notification' => $notification, | |
| 309 | + 'data' => $data, | |
| 310 | + ]); | |
| 311 | + | |
| 277 | 312 | $mailer->send(true); |
| 278 | 313 | |
| 279 | 314 | if ($pdfPath && file_exists($pdfPath)) { |
| 280 | 315 | @unlink($pdfPath); |
| 281 | 316 | } |
| 317 | + } | |
| 318 | + | |
| 319 | + /** | |
| 320 | + * Let third-party code adjust the fully prepared Mailer (recipients, | |
| 321 | + * subject, body, attachments already set) immediately before it sends. | |
| 322 | + * | |
| 323 | + * @param Mailer $mailer | |
| 324 | + * @param array $context event, mail_name, recipient, notification, data | |
| 325 | + * @return Mailer | |
| 326 | + */ | |
| 327 | + private function applyMailerFilter(Mailer $mailer, array $context): Mailer | |
| 328 | + { | |
| 329 | + $filtered = apply_filters('fluent_cart/email_notification/mailer', $mailer, $context); | |
| 330 | + | |
| 331 | + return $filtered instanceof Mailer ? $filtered : $mailer; | |
| 282 | 332 | } |
| 283 | 333 | |
| 284 | 334 | public function getEmailFooter(): string |
| 285 | 335 | { |