PHP code example of r0bdiabl0 / laravel-email-tracker

1. Go to this page and download the library: Download r0bdiabl0/laravel-email-tracker library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

r0bdiabl0 / laravel-email-tracker example snippets


// config/email-tracker.php
'providers' => [
    'ses' => [
        'enabled' => env('EMAIL_TRACKER_SES_ENABLED', true),
        'sns_validator' => true, // Validate SNS message signatures
    ],
    'resend' => [
        'enabled' => env('EMAIL_TRACKER_RESEND_ENABLED', false),
        'webhook_secret' => env('RESEND_WEBHOOK_SECRET'),
    ],
    'mailgun' => [
        'enabled' => env('EMAIL_TRACKER_MAILGUN_ENABLED', false),
        'webhook_signing_key' => env('MAILGUN_WEBHOOK_SIGNING_KEY'),
    ],
    // ... etc.
],

// config/mail.php
'mailers' => [
    // Resend API transport (recommended for full tracking)
    'resend' => [
        'transport' => 'resend',
        'key' => env('RESEND_API_KEY'),
    ],

    // Postal API transport (recommended for full tracking)
    'postal' => [
        'transport' => 'postal',
        'url' => env('POSTAL_URL'),
        'key' => env('POSTAL_API_KEY'),
    ],

    // SES, Mailgun, Postmark use Laravel/Symfony built-in transports
    // SendGrid uses SMTP
],

use R0bdiabl0\EmailTracker\Facades\EmailTracker;

// Uses the default provider (from EMAIL_TRACKER_DEFAULT_PROVIDER)
EmailTracker::enableAllTracking()
    ->to('[email protected]')
    ->send(new WelcomeMail($user));

// Override to use a specific provider for this send
// Automatically switches to the Resend transport
EmailTracker::provider('resend')
    ->enableAllTracking()
    ->to('[email protected]')
    ->send(new WelcomeMail($user));

// Use Mailgun for transactional emails
EmailTracker::provider('mailgun')
    ->enableAllTracking()
    ->to('[email protected]')
    ->send(new OrderConfirmation($order));

use R0bdiabl0\EmailTracker\Facades\EmailTracker;

// Enable all tracking (opens, links, bounces, complaints, deliveries)
// Note: This does NOT enable unsubscribe headers - use enableUnsubscribeHeaders() separately
EmailTracker::enableAllTracking()
    ->to('[email protected]')
    ->send(new WelcomeMail($user));

// With unsubscribe headers for bulk/marketing emails
EmailTracker::enableAllTracking()
    ->enableUnsubscribeHeaders()  // Add RFC 8058 List-Unsubscribe headers
    ->to('[email protected]')
    ->send(new MarketingMail($user));

// With batch grouping for campaigns
EmailTracker::enableAllTracking()
    ->setBatch('welcome-campaign-2024')
    ->to('[email protected]')
    ->send(new WelcomeMail($user));

// Enable specific tracking only
EmailTracker::enableOpenTracking()
    ->enableLinkTracking()
    ->to('[email protected]')
    ->send(new WelcomeMail($user));

// Specify provider explicitly
EmailTracker::provider('resend')
    ->enableAllTracking()
    ->to('[email protected]')
    ->send(new WelcomeMail($user));

use Illuminate\Mail\Mailable;
use R0bdiabl0\EmailTracker\Traits\TracksWithEmail;

class WelcomeMail extends Mailable
{
    use TracksWithEmail;

    public function build()
    {
        return $this->view('emails.welcome');
    }
}

// Static methods for quick sending
WelcomeMail::sendTracked('[email protected]', batch: 'welcome');
WelcomeMail::queueTracked(['[email protected]'], batch: 'welcome', queue: 'emails');

use Illuminate\Notifications\Notification;
use R0bdiabl0\EmailTracker\Notifications\EmailTrackerChannel;

class WelcomeNotification extends Notification
{
    public function via($notifiable): array
    {
        return [EmailTrackerChannel::class];
    }

    public function toEmailTracker($notifiable): Mailable
    {
        return new WelcomeMail($notifiable);
    }
}

// config/email-tracker.php
'routes' => [
    'middleware' => ['throttle:60,1'], // Rate limiting
],

->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'email-tracker/webhook/*',
    ]);
})

protected $except = [
    'email-tracker/webhook/*',
];

use R0bdiabl0\EmailTracker\Facades\EmailTracker;

EmailTracker::enableAllTracking()
    ->enableUnsubscribeHeaders()
    ->to('[email protected]')
    ->send(new NewsletterMail($user));

// config/email-tracker.php
'unsubscribe' => [
    // Enable one-click unsubscribe headers globally
    'enabled' => env('EMAIL_TRACKER_UNSUBSCRIBE_ENABLED', false),

    // Optional: Include a mailto: fallback (some older clients prefer this)
    'mailto' => env('EMAIL_TRACKER_UNSUBSCRIBE_MAILTO'),

    // Signature expiration in hours (0 = no expiration)
    'signature_expiration' => env('EMAIL_TRACKER_UNSUBSCRIBE_EXPIRATION', 0),

    // Redirect URL after unsubscribe (null = return JSON response)
    'redirect_url' => env('EMAIL_TRACKER_UNSUBSCRIBE_REDIRECT'),
],

use R0bdiabl0\EmailTracker\Contracts\SentEmailContract;
use R0bdiabl0\EmailTracker\Contracts\UnsubscribeUrlGenerator;

// In a service provider's register() method:
$this->app->bind(UnsubscribeUrlGenerator::class, function () {
    return new class implements UnsubscribeUrlGenerator
    {
        public function generate(SentEmailContract $email): string
        {
            // Return whatever persistent URL your application uses.
            return route('my.unsubscribe', ['token' => /* your token for $email */]);
        }
    };
});

use R0bdiabl0\EmailTracker\Events\EmailUnsubscribeEvent;

// In EventServiceProvider
protected $listen = [
    EmailUnsubscribeEvent::class => [
        \App\Listeners\HandleUnsubscribe::class,
    ],
];

namespace App\Listeners;

use R0bdiabl0\EmailTracker\Events\EmailUnsubscribeEvent;

class HandleUnsubscribe
{
    public function handle(EmailUnsubscribeEvent $event): void
    {
        $email = $event->email;
        $messageId = $event->messageId;
        $sentEmail = $event->sentEmail; // May be null

        // Update user preferences
        User::where('email', $email)
            ->update(['marketing_emails' => false]);

        // Or remove from specific mailing list based on batch
        if ($sentEmail && $sentEmail->batch) {
            MailingListSubscription::where('email', $email)
                ->where('list', $sentEmail->batch->name)
                ->delete();
        }

        Log::info("User unsubscribed", ['email' => $email]);
    }
}

// bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'email-tracker/webhook/*',  // Adjust if using custom EMAIL_TRACKER_ROUTE_PREFIX
        'email-tracker/unsubscribe',
    ]);
})

  // In your event listener
  if (RateLimiter::tooManyAttempts('unsubscribe:' . $event->email, 5)) {
      Log::warning('Unsubscribe rate limit exceeded', ['email' => $event->email]);
      return;
  }
  RateLimiter::hit('unsubscribe:' . $event->email, 3600);
  

use R0bdiabl0\EmailTracker\Events\EmailSentEvent;
use R0bdiabl0\EmailTracker\Events\EmailDeliveryEvent;
use R0bdiabl0\EmailTracker\Events\EmailBounceEvent;
use R0bdiabl0\EmailTracker\Events\EmailComplaintEvent;
use R0bdiabl0\EmailTracker\Events\EmailOpenEvent;
use R0bdiabl0\EmailTracker\Events\EmailLinkClickEvent;
use R0bdiabl0\EmailTracker\Events\EmailUnsubscribeEvent;

protected $listen = [
    EmailBounceEvent::class => [
        \App\Listeners\HandleEmailBounce::class,
    ],
    EmailComplaintEvent::class => [
        \App\Listeners\HandleEmailComplaint::class,
    ],
    EmailUnsubscribeEvent::class => [
        \App\Listeners\HandleUnsubscribe::class,
    ],
];

namespace App\Listeners;

use R0bdiabl0\EmailTracker\Events\EmailBounceEvent;

class HandleEmailBounce
{
    public function handle(EmailBounceEvent $event): void
    {
        $bounce = $event->emailBounce;
        $email = $bounce->email;
        $type = $bounce->type; // 'Permanent' or 'Transient'
        $metadata = $bounce->metadata; // Raw webhook payload

        if ($type === 'Permanent') {
            // Mark user as having invalid email
            User::where('email', $email)->update(['email_valid' => false]);
        }

        // Access provider-specific diagnostic information from metadata
        // For SES: $metadata['bounce']['bouncedRecipients'][0]['diagnosticCode']
        // For Mailgun: $metadata['event-data']['delivery-status']['message']
        // For SendGrid: $metadata['reason']
        $diagnosticCode = $this->extractDiagnosticCode($metadata, $bounce->provider);

        // Log for monitoring
        Log::warning("Email bounced", [
            'email' => $email,
            'type' => $type,
            'provider' => $bounce->provider,
            'diagnostic_code' => $diagnosticCode,
        ]);
    }

    private function extractDiagnosticCode(?array $metadata, string $provider): ?string
    {
        if (!$metadata) {
            return null;
        }

        return match ($provider) {
            'ses' => $metadata['bounce']['bouncedRecipients'][0]['diagnosticCode'] ?? null,
            'mailgun' => $metadata['event-data']['delivery-status']['message'] ?? null,
            'sendgrid' => $metadata['reason'] ?? null,
            'postmark' => $metadata['Description'] ?? null,
            default => null,
        };
    }
}

// config/email-tracker.php
'suppression' => [
    'skip_bounced' => true,    // Skip permanently bounced addresses
    'skip_complained' => true, // Skip addresses that filed complaints
],

use R0bdiabl0\EmailTracker\Services\EmailValidator;

// Check if email should be blocked
if (EmailValidator::shouldBlock('[email protected]')) {
    return; // Don't send
}

// Get specific counts
$bounceCount = EmailValidator::getBounceCount('[email protected]');
$hasComplaint = EmailValidator::hasComplaint('[email protected]');

// Filter a list of emails
$validEmails = EmailValidator::filterBlockedEmails($emailList);

// In your event listener - metadata is ALWAYS available
public function handle(EmailBounceEvent $event): void
{
    $metadata = $event->emailBounce->metadata; // Available regardless of store_metadata config
    $diagnosticCode = $metadata['bounce']['bouncedRecipients'][0]['diagnosticCode'] ?? null;

    // Process in real-time...
}

// Delete bounce/complaint records older than 90 days
EmailBounce::where('bounced_at', '<', now()->subDays(90))->delete();
EmailComplaint::where('complained_at', '<', now()->subDays(90))->delete();

use R0bdiabl0\EmailTracker\Models\SentEmail;
use R0bdiabl0\EmailTracker\Models\Batch;

// Get all bounced emails
$bounced = SentEmail::bounced()->get();

// Get all emails that received complaints
$complained = SentEmail::complained()->get();

// Get emails by provider
$sesEmails = SentEmail::forProvider('ses')->get();

// Get delivered emails
$delivered = SentEmail::delivered()->get();

// Get emails for a specific address
$userEmails = SentEmail::forEmail('[email protected]')->get();

// Get batch with all emails
$batch = Batch::where('name', 'campaign-2024')->with('sentEmails')->first();

// Check if specific email bounced
$email = SentEmail::where('message_id', $messageId)->first();
if ($email->wasBounced()) {
    // Handle bounce
}

// Still works!
SesMail::enableAllTracking()->to($email)->send($mailable);

use R0bdiabl0\EmailTrackerFilament\EmailTrackerFilamentPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            EmailTrackerFilamentPlugin::make(),
        ]);
}

namespace App\EmailProviders;

use Carbon\Carbon;
use Illuminate\Http\Request;
use R0bdiabl0\EmailTracker\DataTransferObjects\EmailEventData;
use R0bdiabl0\EmailTracker\Enums\EmailEventType;
use R0bdiabl0\EmailTracker\Providers\AbstractProvider;
use Symfony\Component\HttpFoundation\Response;

class CustomSmtpProvider extends AbstractProvider
{
    /**
     * Unique provider name (used in routes and database).
     */
    public function getName(): string
    {
        return 'custom-smtp';
    }

    /**
     * Handle incoming webhook from your email provider.
     */
    public function handleWebhook(Request $request, ?string $event = null): Response
    {
        $this->logRawPayload($request);

        // Validate webhook signature
        if (! $this->validateSignature($request)) {
            return response()->json(['error' => 'Invalid signature'], 403);
        }

        $payload = $request->all();
        $eventType = $payload['event_type'] ?? 'unknown';

        // Parse into standardized format
        $data = $this->parsePayload($payload);

        // Route to appropriate handler based on event type
        // The base class helpers expect EmailEventData objects
        return match ($eventType) {
            'bounce' => $this->processBounceEvent($data),
            'complaint' => $this->processComplaintEvent($data),
            'delivered' => $this->processDeliveryEvent($data),
            default => response()->json(['success' => true]),
        };
    }

    /**
     * Parse webhook payload into standardized EmailEventData.
     */
    public function parsePayload(array $payload): EmailEventData
    {
        return new EmailEventData(
            messageId: $payload['message_id'] ?? '',
            email: $payload['recipient'] ?? '',
            provider: $this->getName(),
            eventType: $this->mapEventType($payload['event_type'] ?? ''),
            timestamp: isset($payload['timestamp'])
                ? Carbon::parse($payload['timestamp'])
                : null,
            bounceType: $payload['bounce_type'] ?? null,
            metadata: $payload,
        );
    }

    /**
     * Validate webhook signature/authenticity.
     */
    public function validateSignature(Request $request): bool
    {
        $secret = $this->getConfig('webhook_secret');

        if (! $secret) {
            return true; // Skip validation if no secret configured
        }

        $signature = $request->header('X-Custom-Signature');
        $expectedSignature = hash_hmac('sha256', $request->getContent(), $secret);

        return hash_equals($expectedSignature, $signature ?? '');
    }

    /**
     * Map provider event types to EmailEventType enum.
     */
    protected function mapEventType(string $event): EmailEventType
    {
        return match ($event) {
            'bounce' => EmailEventType::Bounced,
            'complaint' => EmailEventType::Complained,
            'delivered' => EmailEventType::Delivered,
            'opened' => EmailEventType::Opened,
            'clicked' => EmailEventType::Clicked,
            default => EmailEventType::Sent,
        };
    }
}

use R0bdiabl0\EmailTracker\Facades\EmailTracker;
use App\EmailProviders\CustomSmtpProvider;

public function boot(): void
{
    EmailTracker::registerProvider('custom-smtp', CustomSmtpProvider::class);
}

// config/email-tracker.php
'providers' => [
    // ... built-in providers ...

    'custom-smtp' => [
        'enabled' => env('EMAIL_TRACKER_CUSTOM_SMTP_ENABLED', true),
        'webhook_secret' => env('EMAIL_TRACKER_CUSTOM_SMTP_SECRET'),
    ],
],

// Logging (respects EMAIL_TRACKER_DEBUG setting)
$this->logDebug('Processing event');
$this->logError('Failed to process');
$this->logInfo('Event received');
$this->logRawPayload($request); // Log full webhook payload

// Configuration access
$secret = $this->getConfig('webhook_secret');

// Event processing helpers - pass an EmailEventData object
// These create database records and fire events automatically
$data = $this->parsePayload($payload);  // You implement this
$this->processBounceEvent($data);       // Creates EmailBounce record
$this->processComplaintEvent($data);    // Creates EmailComplaint record
$this->processDeliveryEvent($data);     // Updates SentEmail.delivered_at

public function handleWebhook(Request $request, ?string $event = null): Response
{
    $this->logRawPayload($request);

    if (! $this->validateSignature($request)) {
        return response()->json(['error' => 'Invalid signature'], 403);
    }

    $payload = $request->all();
    $eventType = $payload['event_type'] ?? 'unknown';

    // Parse into standardized format
    $data = $this->parsePayload($payload);

    // Use base class event processors
    return match ($eventType) {
        'bounce' => $this->processBounceEvent($data),
        'complaint' => $this->processComplaintEvent($data),
        'delivered' => $this->processDeliveryEvent($data),
        default => response()->json(['success' => true]),
    };
}

// config/email-tracker.php
'models' => [
    'sent_email' => \App\Models\TrackedEmail::class,
    'email_bounce' => \App\Models\CustomBounce::class,
    // ...
],

namespace App\Models;

use R0bdiabl0\EmailTracker\Models\SentEmail as BaseSentEmail;

class TrackedEmail extends BaseSentEmail
{
    // Add custom methods or relationships
    public function user()
    {
        return $this->belongsTo(User::class, 'email', 'email');
    }
}
bash
php artisan email-tracker:install