Skip to content

Commit

Permalink
Merge pull request #325 from liberu-billing/sweep/Enhance-Email-Notif…
Browse files Browse the repository at this point in the history
…ication-and-Billing-Services-with-Robust-Email-Handling

Enhance Email Notification and Billing Services with Robust Email Handling
  • Loading branch information
curtisdelicata authored Dec 25, 2024
2 parents 87fb429 + 7ce77fe commit e640d10
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 3 deletions.
61 changes: 61 additions & 0 deletions app/Jobs/SendEmailNotification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@


<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Mail\Mailable;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;

class SendEmailNotification implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

protected $mailable;
protected $recipient;

public $tries = 3;
public $backoff = 300; // 5 minutes

public function __construct(Mailable $mailable, string $recipient)
{
$this->mailable = $mailable;
$this->recipient = $recipient;
}

public function handle()
{
try {
Mail::to($this->recipient)->send($this->mailable);

Log::info('Queued email sent successfully', [
'recipient' => $this->recipient,
'mailable_class' => get_class($this->mailable)
]);
} catch (\Exception $e) {
Log::error('Failed to send queued email', [
'recipient' => $this->recipient,
'mailable_class' => get_class($this->mailable),
'error' => $e->getMessage(),
'attempt' => $this->attempts()
]);

throw $e;
}
}

public function failed(\Throwable $exception)
{
Log::error('Email job failed permanently', [
'recipient' => $this->recipient,
'mailable_class' => get_class($this->mailable),
'error' => $exception->getMessage()
]);
}
}
43 changes: 43 additions & 0 deletions app/Mail/PaymentConfirmation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@


<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use App\Models\Payment;

class PaymentConfirmation extends Mailable
{
use Queueable, SerializesModels;

public $payment;

public function __construct(Payment $payment)
{
$this->payment = $payment;
}

public function envelope(): Envelope
{
return new Envelope(
subject: 'Payment Confirmation - Invoice #' . $this->payment->invoice->invoice_number,
);
}

public function content(): Content
{
return new Content(
view: 'emails.payment-confirmation',
);
}

public function attachments(): array
{
return [];
}
}
30 changes: 27 additions & 3 deletions app/Services/BillingService.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
use App\Models\UsageRecord;
use App\Services\PaymentGatewayService;
use App\Services\PricingService;
use App\Services\EmailNotificationService;
use App\Mail\OverdueInvoiceReminder;
use App\Mail\PaymentConfirmation;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use App\Mail\OverdueInvoiceReminder;
use Illuminate\Support\Facades\Log;

class BillingService
{
Expand All @@ -26,17 +29,38 @@ class BillingService
protected $pricingService;


protected $emailService;

public function __construct(
ServiceProvisioningService $serviceProvisioningService,
CurrencyService $currencyService,
PaymentPlanService $paymentPlanService = null,
PaymentGatewayService $paymentGatewayService = null
PricingService $pricingService = null
PaymentGatewayService $paymentGatewayService = null,
PricingService $pricingService = null,
EmailNotificationService $emailService = null
) {
$this->serviceProvisioningService = $serviceProvisioningService;
$this->currencyService = $currencyService;
$this->paymentPlanService = $paymentPlanService ?? new PaymentPlanService($this);
$this->paymentGatewayService = $paymentGatewayService ?? new PaymentGatewayService();
$this->pricingService = $pricingService ?? new PricingService();
$this->emailService = $emailService ?? new EmailNotificationService();
}

protected function sendOverdueReminderEmail(Invoice $invoice)
{
$customer = $invoice->customer;
$reminder = new OverdueInvoiceReminder($invoice);

return $this->emailService->send($reminder, $customer->email);
}

protected function sendPaymentConfirmation(Payment $payment)
{
$customer = $payment->invoice->customer;
$confirmation = new PaymentConfirmation($payment);

return $this->emailService->send($confirmation, $customer->email);
}

public function createSubscription(Customer $customer, SubscriptionPlan $plan, string $billingCycle)
Expand Down
80 changes: 80 additions & 0 deletions app/Services/EmailNotificationService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@


<?php

namespace App\Services;

use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;
use Illuminate\Mail\Mailable;
use Illuminate\Support\Facades\Queue;
use App\Jobs\SendEmailNotification;

class EmailNotificationService
{
protected $maxRetries = 3;
protected $retryDelay = 300; // 5 minutes

public function send(Mailable $mailable, string $recipient)
{
try {
Queue::push(new SendEmailNotification($mailable, $recipient));

Log::info('Email notification queued', [
'recipient' => $recipient,
'mailable_class' => get_class($mailable)
]);

return true;
} catch (\Exception $e) {
Log::error('Failed to queue email notification', [
'recipient' => $recipient,
'mailable_class' => get_class($mailable),
'error' => $e->getMessage()
]);

return false;
}
}

public function sendNow(Mailable $mailable, string $recipient)
{
$attempts = 0;

while ($attempts < $this->maxRetries) {
try {
Mail::to($recipient)->send($mailable);

Log::info('Email notification sent successfully', [
'recipient' => $recipient,
'mailable_class' => get_class($mailable),
'attempt' => $attempts + 1
]);

return true;
} catch (\Exception $e) {
$attempts++;

Log::warning('Email sending failed, retrying...', [
'recipient' => $recipient,
'mailable_class' => get_class($mailable),
'attempt' => $attempts,
'error' => $e->getMessage()
]);

if ($attempts >= $this->maxRetries) {
Log::error('Email sending failed after max retries', [
'recipient' => $recipient,
'mailable_class' => get_class($mailable),
'error' => $e->getMessage()
]);
return false;
}

sleep($this->retryDelay);
}
}

return false;
}
}

0 comments on commit e640d10

Please sign in to comment.