I believe most of AWS users will use Amazon Simple Email Service (SES) for system email sending.
Before you can use in production, by default it’s in sandbox mode, which you are required to pre-configure a few email addresses to receive email.
You’re required to describe your use case, what you will use for, how you will handle bounce back email, etc. in details.
E.g. You will be rejected if not provide enough information
Let’s get started
1. Configure in AWS console
Make sure you verify your domain.
In order to verify, you need to add a TXT record to your DNS management.
After domain, then add an email address (the from email)
Once added, make sure verify DKIM & MAIL FROM Domain, just add a few records to DNS management
When using SES, must have this mechanism to handle bounced email. Let say a spammer trick your system to keep blasting email to addresses that doesn’t exist, SES may block the service.
1.1. Create SNS topic
It’s something like a event listener, when some events happen, the listener will perform certain actions.
The action here means subscription, let’s create one
And here, I’m using webhook with HTTPS endpoint
Make sure the endpoint is available, SNS will ping it for confirmation
1.2. Configure SES to link with SNS
Go to Notifications, and edit
Set the topic to what you created just now
2. Email Response Handler
Remember, we’ve specified an endpoint to the topic subscription?
Let’s create the endpoint in routes/web.php. This will only be used by AWS
publicfunctionsesNotification(Request $request) { if ($request->method() !== 'POST') { returnresponse('405 (Accept POST Only)', 405)->header('Content-Type', 'text/plain'); }
try { // only process valid message from aws $messageRaw = Message::fromRawPostData(); $validator = newMessageValidator();
// Note : Please Uncheck for Enable raw message delivery to comply with AWS SNS Validator format if (!$validator->isValid($messageRaw)) { \Log::info('Invalid AWS Message');
/** * Helper method to extract email * e.g. John Smith <john@smith.com> * @param string $recipient * @return string */ privatefunctionextractEmail($recipient) { if (filter_var($recipient, FILTER_VALIDATE_EMAIL)) { return$recipient; } $pattern = '/[a-z0-9_\-\+\.]+@[a-z0-9\-]+\.([a-z]{2,4})(?:\.[a-z]{2})?/i'; if (preg_match($pattern, $recipient, $matches)) { return$matches[0]; } returnnull; } }
Here’s the sample output of $messageRaw
1 2 3 4 5 6 7 8 9 10 11 12
Array ( [Type] => Notification [MessageId] => 11111111-2222-3333-4444-555555555555 [TopicArn] => arn:aws:sns:ap-southeast-1:999999999999:ses-notification [Message] => {"notificationType":"Bounce","bounce":{"feedbackId":"aaaaaaaaaaaaaaaa-bbbbbbbb-cccc-dddd-eeee-ffffffffffff-000000","bounceType":"Permanent","bounceSubType":"OnAccountSuppressionList","bouncedRecipients":[{"emailAddress":"John <test123123123123123@gmail.com>","action":"failed","status":"5.1.1","diagnosticCode":"Amazon SES did not send the message to this address because it is on the suppression list for your account. For more information about removing addresses from the suppression list, see the Amazon SES Developer Guide at https://docs.aws.amazon.com/ses/latest/DeveloperGuide/sending-email-suppression-list.html"}],"timestamp":"2021-05-08T14:35:45.000Z","reportingMTA":"dns; amazonses.com"},"mail":{"timestamp":"2021-05-08T14:35:45.469Z","source":"noreply@yoursite.com","sourceArn":"arn:aws:ses:ap-southeast-1:999999999999:identity/noreply@yoursite.com","sourceIp":"202.202.202.202","sendingAccountId":"999999999999","messageId":"1111111111111111-22222222-3333-4444-5555-666666666666-000000","destination":["John <test123123123123123@gmail.com>"],"headersTruncated":false,"headers":[{"name":"Message-ID","value":"<99999999999999999999999999999999@swift.generated>"},{"name":"Date","value":"Sat, 08 May 2021 22:35:44 +0800"},{"name":"Subject","value":"YourSite: Test send mail"},{"name":"From","value":"YourSite <noreply@yoursite.com>"},{"name":"To","value":"John <test123123123123123@gmail.com>"},{"name":"MIME-Version","value":"1.0"},{"name":"Content-Type","value":"multipart/mixed; boundary=\"_=_swift_5555555555_77777777777777777777777777777777_=_\""}],"commonHeaders":{"from":["YourSite <noreply@yoursite.com>"],"date":"Sat, 08 May 2021 22:35:44 +0800","to":["John <test123123123123123@gmail.com>"],"messageId":"<99999999999999999999999999999999@swift.generated>","subject":"YourSite: Test send mail"}}} [Timestamp] => 2021-05-08T14:35:45.908Z [SignatureVersion] => 1 [Signature] => UUUUUUUUUUUUUUUUUUUUUUUUUUUUU+kkkkkkkkkkk/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC/zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz/eeeee/qqqqqqqq+BBB+nnnnnnnnnnnnnnnnnnnnnnnnnn/e/yyyyyyyyyyy+dddd+XXXXXXXXXXXXXXXXXXXXXXXXX/ww/rr+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH+g== [SigningCertURL] => https://sns.ap-southeast-1.amazonaws.com/SimpleNotificationService-99999999999999999999999999999999.pem [UnsubscribeURL] => https://sns.ap-southeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-southeast-1:999999999999:ses-notification:11111111-2222-3333-4444-555555555555 )
<?php publicfunctionvia($notifiable) { $channels = []; $bounced = BouncedEmail::find($notifiable->routeNotificationForMail()); if (empty($bounced)) { // only send if it's not in the bounced list $channels[] = 'mail'; }