juhasev/laravel-ses

允许您在通过 Laravel 和 Amazon SES 发送电子邮件时跟踪打开、投递、退回、投诉和点击链接

v5.0.0 2024-07-18 14:08 UTC

README

alt text

Laravel SES (AWS 简单电子邮件服务)

Laravel SES 是一个允许您获取通过 AWS SES(简单电子邮件服务)发送的电子邮件发送统计信息的包,包括投递、打开、退回、投诉和链接跟踪。此包最初由 Oliveready7 编写。不幸的是,原始作者已经停止维护此包,所以我决定创建这个分支,以便此包可以与当前的 Laravel 版本一起使用。最低要求是 PHP 7.3,Laravel 9 需要 PHP 8.x。

所有包都已更新到现代版本。我已经优化了原始的数据库存储以节省空间和适当的索引。此包与 Laravel 9.x 兼容。

Laravel SES 还支持 SMTP 错误代码,会抛出异常,例如当您超过速率限制时,您可以处理适当的退避。

Laravel 版本支持

  • 如果您正在使用 Laravel 11,请使用 v5.*
  • 如果您正在使用 Laravel 10,请使用 v4.*
  • 如果您正在使用 Laravel 9,请使用 v3.*
  • 如果您正在使用 Laravel 7 或 8,请使用 v1.1.5
  • 如果您正在使用 Laravel 6,请使用 v0.8.4

安装

通过 composer 安装

composer require juhasev/laravel-ses
composer require aws/aws-php-sns-message-validator (optional)

在 config/app.php 中确保加载服务提供者。这应该会自动完成。

Juhasev\LaravelSes\LaravelSesServiceProvider::class

Laravel 配置

确保您的 app/config/services.php 中有 SES 值已设置

'ses' => [
    'key' => your_ses_key,
    'secret' => your_ses_secret,
    'domain' => your_ses_domain,
    'region' => your_ses_region
],

确保您的邮件驱动程序位于 app/config/mail.php 中,设置为 'ses'

    'default' => env('MAIL_MAILER', 'ses')

发布公共资产

php artisan vendor:publish --tag=ses-assets --force

发布迁移

php artisan vendor:publish --tag=ses-migrations --force

发布包的配置(laravelses.php)

php artisan vendor:publish --tag=ses-config

路由

此包添加了 3 个公开路由到您的应用程序,AWS SNS 回调目标

/ses/notification/bounce
/ses/notification/complaint
/ses/notification/delivery

我们还添加了两个额外的公开路由来跟踪打开和链接点击

/ses/beacon
/ses/link

配置选项

  • aws_sns_validator - 包是否使用 AWS 的 SNS 验证器来验证传入的 SNS 请求。默认 = false
  • debug - 调试模式,记录所有 SNS 回调请求

https://github.com/aws/aws-php-sns-message-validator

AWS 配置

预阅读

如果您是首次使用 SES 通知,本文是一个很好的起点

https://docs.aws.amazon.com/sns/latest/dg/sns-http-https-endpoint-as-subscriber.html

IAM 用户和策略

您的应用程序 IAM 用户需要通过 SES 发送电子邮件并订阅 SNS 通知。这可以通过 AWS 控制面板完成,如上文章所述,或者使用以下 AWS CloudFormation 模板

AWS CloudFormation 策略示例

  ApplicationSNSPolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      Description: "Policy for sending subscribing to SNS bounce notifications"
      Path: "/"
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - sns:CreateTopic
              - sns:DeleteTopic
              - sns:Subscribe
              - sns:Unsubscribe
            Resource:
              - 'arn:aws:sns:*'

  ApplicationSESPolicy:
    Type: "AWS::IAM::ManagedPolicy"
    Properties:
      Description: "Policy for creating SES bounce notification"
      Path: "/"
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - ses:*
            Resource:
              - '*'

策略定义后,需要将其添加到配置的 IAM 用户。

  # AWS PHP API User
  APIUser:
    Type: "AWS::IAM::User"
    Properties:
      ManagedPolicyArns:
        - !Ref ApplicationSNSPolicy
        - !Ref ApplicationSESPolicy
      UserName: staging-user

运行设置

确保在您的 APP_URL(在 .env 中)设置正确,与您的发送域名匹配。如果您为多个域名发送电子邮件(即多租户应用程序),则可以使用此命令设置多个域名。

在继续之前,您需要准备好 SES 域

设置命令自动配置您的 SES 域以发送触发对您的 Laravel 应用程序进行回调的 SNS 通知。

php artisan sns:setup mydomain.com

注意:您不应尝试使用子域名 client.mydomain.com,目前 AWS 不支持此功能。

用法

要发送启用所有跟踪的电子邮件

SesMail::enableAllTracking()
    ->to('hello@example.com')
    ->send(new Mailable);

调用 enableAllTracking() 启用打开、拒绝、退回、投递、投诉和链接跟踪。

请注意,如果您在开启Open -tracking时尝试发送包含多个收件人的Mailable,将会抛出LaravelSesTooManyRecipients异常。

其他抛出的异常还包括

LaravelSesDailyQuotaExceededException::class
LaravelSesInvalidSenderAddressException::class
LaravelSesMaximumSendingRateExceeded::class
LaravelSesSendFailedException::class
LaravelSesTemporaryServiceFailureException::class
LaravelSesTooManyRecipientsException::class

您可以使用基类来捕获所有这些异常

try {
    SesMail::enableAllTracking()
        ->to('hello@example.com')
        ->send(new Mailable);
        
} catch (LaravelSesMaximumSendingRateExceeded $e) {

    // Implement back off logic

} catch (LaravelSesException $e) {
    
    $smtpCode = $e->getCode();
    $smtpErrorMessage = $e->getMessage();
    
    // Do something like back of if rate limit is reached.
)

You can, of course, disable and enable all the tracking options

```php
SesMail::disableAllTracking();
SesMail::disableOpenTracking();
SesMail::disableLinkTracking();
SesMail::disableBounceTracking();
SesMail::disableComplaintTracking();
SesMail::disableDeliveryTracking();

SesMail::enableAllTracking();
SesMail::enableOpenTracking();
SesMail::enableLinkTracking();
SesMail::enableBounceTracking();
SesMail::enableComplaintTracking();
SesMail::enableDeliveryTracking();

批量选项让您有机会将邮件分组,这样您就可以获取特定组的统计结果

SesMail::enableAllTracking()
    ->setBatch('welcome_emails')
    ->to('hello@example.com')
    ->send(new Mailable);

您还可以获取汇总统计数据

Stats::statsForEmail($email);

$stats = Stats::statsForBatch('welcome_emails');

print_r($stats)
[
    "sent" => 8,
    "deliveries" => 7,
    "opens" => 4,
    "bounces" => 1,
    "complaints" => 2,
    "clicks" => 3,
    "link_popularity" => [
        "https://welcome.page" => [
            "clicks" => 3
        ],
        "https://facebook.com/brand" => [
            "clicks" => 1
        ]
    ]
]

要通过仓库获取单个统计信息

EmailStatRepository::getBouncedCount($email);
EmailRepository::getBounces($email);

BatchStatRepository::getBouncedCount($batch);
BatchStatRepository::getDeliveredCount($batch);
BatchStatRepository::getComplaintsCount($batch);

您还可以直接使用模型,就像使用任何其他Eloquent模型一样

$sentEmails = SentEmail::whereEmail($email)->get();

$emailBounces = EmailBounce::whereEmail($email)->get();
$emailComplaints = EmailComplaint::whereEmail($email)->get();
$emailLink = EmailLink::whereEmail($email)->get();
$emailOpen = EmailOpen::whereEmail($email)->get();

如果您正在使用自定义模型,则可以使用ModelResolver()助手,如下所示

$sentEmail = ModelResolver::get('SentEmail')::take(100)->get();

监听事件

可以创建事件订阅者

<?php

namespace App\Listeners;

use App\Actions\ProcessSesEvent;
use Juhasev\LaravelSes\Factories\Events\SesBounceEvent;
use Juhasev\LaravelSes\Factories\Events\SesComplaintEvent;
use Juhasev\LaravelSes\Factories\Events\SesDeliveryEvent;
use Juhasev\LaravelSes\Factories\Events\SesOpenEvent;
use Juhasev\LaravelSes\Factories\Events\SesSentEvent;

class SesSentEventSubscriber
{
    /**
     * Subscribe to events
     *
     * @param $events
     */
    public function subscribe($events)
    {
        $events->listen(SesBounceEvent::class, SesSentEventSubscriber::class . '@bounce');
        $events->listen(SesComplaintEvent::class, SesSentEventSubscriber::class . '@complaint');
        $events->listen(SesDeliveryEvent::class,SesSentEventSubscriber::class . '@delivery');
        $events->listen(SesOpenEvent::class, SesSentEventSubscriber::class . '@open');
        $events->listen(SesLinkEvent::class, SesSentEventSubscriber::class . '@link');
    }

    /**
     * SES bounce event took place
     *
     * @param SesBounceEvent $event
     */
    public function bounce(SesBounceEvent $event)
    {
        // Do something
    }

    /**
     * SES complaint event took place
     *
     * @param SesComplaintEvent $event
     */
    public function complaint(SesComplaintEvent $event)
    {
        // Do something
    }

    /**
     * SES delivery event took place
     *
     * @param SesDeliveryEvent $event
     */
    public function delivery(SesDeliveryEvent $event)
    {
        // Do something
    }

    /**
     * SES Open open event took place
     *
     * @param SesOpenEvent $event
     */
    public function open(SesOpenEvent $event)
    {
        // Do something
    }
   /**
     * SES Open link event took place
     *
     * @param SesLinkEvent $event
     */
    public function link(SesLinkEvent $event)
    {
        // Do something
    }

}

为了使其工作,您需要在Laravel App/Providers/EventServiveProvider.php中注册EventSubscriber。

 protected $subscribe = [
    SesSentEventSubscriber::class
 ];

示例事件数据

print_r($event->data);
(
    [id] => 22
    [sent_email_id] => 49
    [type] => Permanent
    [bounced_at] => 2020-04-03 19:42:31
    [sent_email] => Array
        (
            [id] => 49
            [message_id] => 31b530dce8e2a282d12e5627e7109580@localhost
            [email] => bounce@simulator.amazonses.com
            [batch_id] => 7
            [sent_at] => 2020-04-03 19:42:31
            [delivered_at] => 
            [batch] => Array
                (
                    [id] => 7
                    [name] => fa04cbf2c2:Project:268
                    [created_at] => 2020-04-03 17:03:23
                    [updated_at] => 2020-04-03 17:03:23
                )

        )
    )
)

术语

发送 = 尝试发送的邮件数量

投递 = 已成功投递的邮件数量

打开 = 已打开的邮件数量

投诉 = 将邮件放入垃圾箱的人数

点击 = 至少点击了您邮件中一个链接的人数

链接流行度 = 邮件中每个链接的唯一点击次数,按点击次数从多到少排序

开发

将Laravel SES仓库克隆到您的项目中的/packages目录下

git clone https://github.com/juhasev/laravel-ses.git

设置Composer.json以解决您的开发文件夹中的类

 "autoload": {
    "psr-4": {
      "App\\": "app/",
      "Juhasev\\LaravelSes\\": "packages/juhasev/laravel-ses/src"
    }
  },

Composer require

require: {
    "juhasev/laravel-ses": "dev-master"
}

或者运行require

composer require juhasev/laravel-ses:dev-master

要运行单元测试,请执行

phpunit