From cf02dd1b4c464a02a3755189528bb9cfcfafbabc Mon Sep 17 00:00:00 2001 From: Daniele Rosario Date: Tue, 15 Apr 2025 18:50:58 +0200 Subject: [PATCH 1/4] wip --- README.md | 23 +++- composer.json | 8 +- src/Drivers/SesDriver.php | 239 ++++++++++++++++++++++++++++++++++++++ src/Enums/Provider.php | 1 + 4 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 src/Drivers/SesDriver.php diff --git a/README.md b/README.md index 92ef331..21ea0d4 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,20 @@ Add the API key of your email service provider to the `config/services.php` file 'webhook_signing_key' => env('MAILGUN_WEBHOOK_SIGNING_KEY'), 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 'scheme' => 'https', -] +], + +'ses' => [ + // You should already have these set up by Laravel's default installation + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + + // This one is package-specific + 'configuration_set_name' => env('AWS_SES_CONFIGURATION_SET', 'laravel-mails-ses-webhook'), + 'account_id' => env('AWS_ACCOUNT_ID', '') // Your AWS account id + 'scheme' => 'https' // 'http' or 'https', + 'verify_signature' => true +], ``` When done, run this command with the slug of your service provider: @@ -224,6 +237,14 @@ This is the contents of the published config file: ] ``` +### [Optional] Amazon SES + +When using Amazon SES, you also require the following dependencies + +```bash +composer require aws/aws-sdk-php aws/aws-php-sns-message-validator +``` + ## Usage ### Logging diff --git a/composer.json b/composer.json index 193bdc4..2f9320f 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,13 @@ "phpstan/extension-installer": "^1.4", "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.0", + "aws/aws-sdk-php": "^3.342", + "aws/aws-php-sns-message-validator": "^1.10" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "Required when using Amazon SES", + "aws/aws-sdk-php": "Required when using Amazon SES, also required by Laravel" }, "autoload": { "psr-4": { diff --git a/src/Drivers/SesDriver.php b/src/Drivers/SesDriver.php new file mode 100644 index 0000000..e7b05a7 --- /dev/null +++ b/src/Drivers/SesDriver.php @@ -0,0 +1,239 @@ +warn("Failed to create Ses webhook"); + $components->error("There is no Amazon SES Driver configured in your laravel application."); + return; + } + + $trackingConfig = (array)config('mails.logging.tracking'); + + // send - The call was successful and Amazon SES is attempting to deliver the email. + // reject - Amazon SES determined that the email contained a virus and rejected it. + // bounce - The recipient's mail server permanently rejected the email. This corresponds to a hard bounce. + // complaint - The recipient marked the email as spam. + // delivery - Amazon SES successfully delivered the email to the recipient's mail server. + // open - The recipient received the email and opened it in their email client. + // click - The recipient clicked one or more links in the email. + // renderingFailure - Amazon SES did not send the email because of a template rendering issue. + $events = []; + $eventTypes = []; + + if ((bool)$trackingConfig['opens']) { + $events[] = 'open'; + $eventTypes[] = 'Delivery'; + } + + if ((bool)$trackingConfig['clicks']) { + $events[] = 'click'; + $eventTypes[] = 'Delivery'; + } + + if ((bool)$trackingConfig['deliveries']) { + $events[] = 'delivery'; + $eventTypes[] = 'Delivery'; + } + + if ((bool)$trackingConfig['bounces']) { + $events[] = 'reject'; + $events[] = 'bounce'; + $events[] = 'renderingFailure'; + $eventTypes[] = 'Bounce'; + } + + if ((bool)$trackingConfig['complaints']) { + $events[] = 'complaint'; + $eventTypes[] = 'Complaint'; + } + + $sesClient = $sesTransport->ses(); + $configurationSet = config('services.ses.configuration_set_name', 'laravel-mails-ses-webhook'); + + try { + // 1. Create Configuration Set + $sesClient->createConfigurationSet([ + 'ConfigurationSet' => [ + 'Name' => $configurationSet, + ], + ]); + + + // 2. Create a SNS Topic + $config = config('services.sns', config('services.ses', [])); + $snsClient = $this->createSnsClient($config); + $result = $snsClient->createTopic([ + 'Name' => $configurationSet, + ]); + $topicArn = $result->get('TopicArn'); + + // 3. Give access to SES to publish notifications to the topic. + $snsClient->addPermission([ + 'AWSAccountId' => $config['account_id'] ?? '', + 'ActionName' => 'Publish', + 'Label' => 'ses-notification-policy', + 'TopicArn' => $topicArn, + ]); + + // 4. Set the channels + $eventTypes = array_unique($eventTypes); + foreach ($eventTypes as $eventType) { + $sesClient->setIdentityNotificationTopic([ + 'Identity' => config('services.ses.identity', config('mail.from.address')), + 'NotificationType' => $eventType, + 'SnsTopic' => $topicArn, + ]); + } + + // 5. Register SNS as the event destination + $sesClient->createConfigurationSetEventDestination([ + 'ConfigurationSetName' => $configurationSet, + 'EventDestination' => [ + 'Enabled' => true, + 'Name' => $configurationSet, + 'MatchingEventTypes' => $events, + 'SNSDestination' => [ + 'TopicARN' => $topicArn, + ] + ] + ]); + + // 5. Subscribe to the topic + $webhookUrl = URL::signedRoute('mails.webhook', ['provider' => Provider::SES]); + $scheme = config('services.ses.scheme', 'https'); + $snsClient->subscribe([ + 'Endpoint' => $webhookUrl, + 'TopicArn' => $topicArn, + 'Protocol' => $scheme + ]); + + } catch (\Throwable $e) { + report($e); + $components->warn("Failed to create Ses webhook"); + $components->error($e->getMessage()); + return; + } + + $components->info("Created SES Webhooks for: " . implode(", ", $eventTypes)); + } + + public function verifyWebhookSignature(array $payload): bool + { + dd($payload); + if (app()->runningUnitTests()) { + return true; + } + + if (empty($payload['signature']['timestamp']) || empty($payload['signature']['token']) || empty($payload['signature']['signature'])) { + return false; + } + + $hmac = hash_hmac('sha256', $payload['signature']['timestamp'] . $payload['signature']['token'], config('services.mailgun.webhook_signing_key')); + + if (function_exists('hash_equals')) { + return hash_equals($hmac, $payload['signature']['signature']); + } + + return $hmac === $payload['signature']['signature']; + } + + public function attachUuidToMail(MessageSending $event, string $uuid): MessageSending + { + $event->message->getHeaders()->addTextHeader('X-Mailgun-Variables', json_encode([config('mails.headers.uuid') => $uuid])); + + return $event; + } + + public function getUuidFromPayload(array $payload): ?string + { + return $payload['event-data']['user-variables'][$this->uuidHeaderName] ?? null; + } + + protected function getTimestampFromPayload(array $payload): string + { + return $payload['event-data']['timestamp']; + } + + public function eventMapping(): array + { + return [ + EventType::ACCEPTED->value => ['event-data.event' => 'accepted'], + EventType::CLICKED->value => ['event-data.event' => 'clicked'], + EventType::COMPLAINED->value => ['event-data.event' => 'complained'], + EventType::DELIVERED->value => ['event-data.event' => 'delivered'], + EventType::HARD_BOUNCED->value => ['event-data.event' => 'failed', 'event-data.severity' => 'permanent'], + EventType::OPENED->value => ['event-data.event' => 'opened'], + EventType::SOFT_BOUNCED->value => ['event-data.event' => 'failed', 'event-data.severity' => 'temporary'], + EventType::UNSUBSCRIBED->value => ['event-data.event' => 'unsubscribed'], + ]; + } + + public function dataMapping(): array + { + return [ + 'ip_address' => 'event-data.ip', + 'platform' => 'event-data.client-info.device-type', + 'os' => 'event-data.client-info.client-os', + 'browser' => 'event-data.client-info.client-name', + 'user_agent' => 'event-data.client-info.user-agent', + 'city' => 'event-data.geolocation.city', + 'country_code' => 'event-data.geolocation.country', + 'link' => 'event-data.url', + 'tag' => 'event-data.tags', + ]; + } + + public function unsuppressEmailAddress(string $address): Response + { + $client = Http::asJson() + ->withBasicAuth('api', config('services.mailgun.secret')) + ->baseUrl(config('services.mailgun.endpoint') . '/v3/'); + + return $client->delete(config('services.mailgun.domain') . '/unsubscribes/' . $address); + } + + protected function createSnsClient(array $config): SnsClient + { + $config = array_merge( + [ + 'version' => 'latest' + ], + $config + ); + + return new SnsClient($this->addSnsCredentials($config)); + } + + protected function addSnsCredentials(array $config): array + { + if (! empty($config['key']) && ! empty($config['secret'])) { + $config['credentials'] = Arr::only($config, ['key', 'secret']); + + if (! empty($config['token'])) { + $config['credentials']['token'] = $config['token']; + } + } + + return Arr::except($config, ['token']); + } +} diff --git a/src/Enums/Provider.php b/src/Enums/Provider.php index 27126dc..9ca554e 100644 --- a/src/Enums/Provider.php +++ b/src/Enums/Provider.php @@ -6,4 +6,5 @@ enum Provider: string { case POSTMARK = 'postmark'; case MAILGUN = 'mailgun'; + case SES = 'ses'; } From d629b16e089e60a9e40ab35ba662e42fb1d4a2fe Mon Sep 17 00:00:00 2001 From: Daniele Rosario Date: Tue, 15 Apr 2025 18:58:30 +0200 Subject: [PATCH 2/4] wip --- README.md | 6 +++--- src/Managers/MailProviderManager.php | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 21ea0d4..5443c68 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,9 @@ Add the API key of your email service provider to the `config/services.php` file // This one is package-specific 'configuration_set_name' => env('AWS_SES_CONFIGURATION_SET', 'laravel-mails-ses-webhook'), - 'account_id' => env('AWS_ACCOUNT_ID', '') // Your AWS account id - 'scheme' => 'https' // 'http' or 'https', - 'verify_signature' => true + 'account_id' => env('AWS_ACCOUNT_ID', ''), // Your AWS account id + 'scheme' => 'https', // 'http' or 'https', + 'verify_signature' => true, ], ``` diff --git a/src/Managers/MailProviderManager.php b/src/Managers/MailProviderManager.php index ba43c37..42d6ced 100644 --- a/src/Managers/MailProviderManager.php +++ b/src/Managers/MailProviderManager.php @@ -5,6 +5,7 @@ use Illuminate\Support\Manager; use Vormkracht10\Mails\Drivers\MailgunDriver; use Vormkracht10\Mails\Drivers\PostmarkDriver; +use Vormkracht10\Mails\Drivers\SesDriver; class MailProviderManager extends Manager { @@ -23,6 +24,11 @@ protected function createMailgunDriver(): MailgunDriver return new MailgunDriver; } + protected function createSesDriver(): SesDriver + { + return new SesDriver; + } + public function getDefaultDriver(): ?string { return null; From 9d0329846a59c962ca60d6396113940a20ad3fae Mon Sep 17 00:00:00 2001 From: Daniele Rosario Date: Wed, 16 Apr 2025 10:23:52 +0200 Subject: [PATCH 3/4] wip --- README.md | 2 + src/Controllers/WebhookController.php | 3 + src/Drivers/SesDriver.php | 156 +++++--- tests/SesTest.php | 534 ++++++++++++++++++++++++++ 4 files changed, 639 insertions(+), 56 deletions(-) create mode 100644 tests/SesTest.php diff --git a/README.md b/README.md index 5443c68..a658d2e 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,8 @@ When using Amazon SES, you also require the following dependencies composer require aws/aws-sdk-php aws/aws-php-sns-message-validator ``` +You aws ses user should also have the authorization to create SNS topics + ## Usage ### Logging diff --git a/src/Controllers/WebhookController.php b/src/Controllers/WebhookController.php index fff812c..04920ef 100644 --- a/src/Controllers/WebhookController.php +++ b/src/Controllers/WebhookController.php @@ -2,6 +2,7 @@ namespace Vormkracht10\Mails\Controllers; +use Aws\Sns\Message; use Illuminate\Http\Request; use Illuminate\Http\Response; use Vormkracht10\Mails\Enums\Provider; @@ -16,6 +17,8 @@ public function __invoke(Request $request, string $provider): Response return response('Unknown provider.', status: 400); } + file_put_contents(storage_path('logs/request.json'), json_encode($request->all(), JSON_PRETTY_PRINT)); + if (! MailProvider::with($provider)->verifyWebhookSignature($request->all())) { return response('Invalid signature.', status: 400); } diff --git a/src/Drivers/SesDriver.php b/src/Drivers/SesDriver.php index e7b05a7..6fb8477 100644 --- a/src/Drivers/SesDriver.php +++ b/src/Drivers/SesDriver.php @@ -2,7 +2,9 @@ namespace Vormkracht10\Mails\Drivers; -use Aws\Ses\SesClient; +use Aws\Exception\AwsException; +use Aws\Sns\Message; +use Aws\Sns\MessageValidator; use Aws\Sns\SnsClient; use Illuminate\Http\Client\Response; use Illuminate\Mail\Events\MessageSending; @@ -19,9 +21,8 @@ class SesDriver extends MailDriver implements MailDriverContract { public function registerWebhooks($components): void { - /** @var SesTransport|null $sesTransport */ - $sesTransport = Mail::driver('ses'); - if ($sesTransport) { + $mailer = Mail::driver('ses'); + if ($mailer === null) { $components->warn("Failed to create Ses webhook"); $components->error("There is no Amazon SES Driver configured in your laravel application."); return; @@ -67,17 +68,25 @@ public function registerWebhooks($components): void $eventTypes[] = 'Complaint'; } + /** @var SesTransport $sesTransport */ + $sesTransport = $mailer->getSymfonyTransport(); $sesClient = $sesTransport->ses(); - $configurationSet = config('services.ses.configuration_set_name', 'laravel-mails-ses-webhook'); + $configurationSet = config('services.ses.options.ConfigurationSetName', 'laravel-mails-ses-webhook'); try { - // 1. Create Configuration Set - $sesClient->createConfigurationSet([ - 'ConfigurationSet' => [ - 'Name' => $configurationSet, - ], - ]); - + // 1. Get or create the Configuration Set + try { + $sesClient->createConfigurationSet([ + 'ConfigurationSet' => [ + 'Name' => $configurationSet, + ], + ]); + } catch (AwsException $e) { + // Already exists, move on! + if ($e->getAwsErrorCode() !== 'ConfigurationSetAlreadyExists') { + throw $e; + } + } // 2. Create a SNS Topic $config = config('services.sns', config('services.ses', [])); @@ -89,8 +98,8 @@ public function registerWebhooks($components): void // 3. Give access to SES to publish notifications to the topic. $snsClient->addPermission([ - 'AWSAccountId' => $config['account_id'] ?? '', - 'ActionName' => 'Publish', + 'AWSAccountId' => [$config['account_id'] ?? ''], + 'ActionName' => ['Publish'], 'Label' => 'ses-notification-policy', 'TopicArn' => $topicArn, ]); @@ -98,11 +107,20 @@ public function registerWebhooks($components): void // 4. Set the channels $eventTypes = array_unique($eventTypes); foreach ($eventTypes as $eventType) { + $identity = config('services.ses.identity', config('mail.from.address')); + // get notified for the various types of events via SNS $sesClient->setIdentityNotificationTopic([ - 'Identity' => config('services.ses.identity', config('mail.from.address')), + 'Identity' => $identity, 'NotificationType' => $eventType, 'SnsTopic' => $topicArn, ]); + + // Force SNS to include the SES mail headers in the notification + $sesClient->setIdentityHeadersInNotificationsEnabled([ + 'Identity' => $identity, + 'NotificationType' => $eventType, + 'Enabled' => true, + ]); } // 5. Register SNS as the event destination @@ -110,7 +128,7 @@ public function registerWebhooks($components): void 'ConfigurationSetName' => $configurationSet, 'EventDestination' => [ 'Enabled' => true, - 'Name' => $configurationSet, + 'Name' => $configurationSet . '-' . uniqid(), 'MatchingEventTypes' => $events, 'SNSDestination' => [ 'TopicARN' => $topicArn, @@ -139,79 +157,105 @@ public function registerWebhooks($components): void public function verifyWebhookSignature(array $payload): bool { - dd($payload); if (app()->runningUnitTests()) { return true; } - if (empty($payload['signature']['timestamp']) || empty($payload['signature']['token']) || empty($payload['signature']['signature'])) { - return false; - } + // Weird SNS thing, you need to read the raw post body + $message = Message::fromRawPostData(); - $hmac = hash_hmac('sha256', $payload['signature']['timestamp'] . $payload['signature']['token'], config('services.mailgun.webhook_signing_key')); + $validator = (new MessageValidator(function ($url) { + return Http::timeout(10)->get($url)->body(); + })); - if (function_exists('hash_equals')) { - return hash_equals($hmac, $payload['signature']['signature']); + try { + $validator->validate($message); + if ($message['Type'] === 'SubscriptionConfirmation') { + Http::timeout(10)->get($message['SubscribeURL'])->throw(); + } + return true; + } catch (\Throwable $e) { + report($e); + return false; } - - return $hmac === $payload['signature']['signature']; } public function attachUuidToMail(MessageSending $event, string $uuid): MessageSending { - $event->message->getHeaders()->addTextHeader('X-Mailgun-Variables', json_encode([config('mails.headers.uuid') => $uuid])); + $event->message->getHeaders()->addTextHeader($this->uuidHeaderName, $uuid); return $event; } public function getUuidFromPayload(array $payload): ?string { - return $payload['event-data']['user-variables'][$this->uuidHeaderName] ?? null; + $message = Message::fromRawPostData(); + $headers = $message['Message']['mail']['headers'] ?? []; + $header = Arr::first($headers, function ($header) { + return $header['name'] === config('mails.headers.uuid'); + }); + + return $header['value'] ?? null; } protected function getTimestampFromPayload(array $payload): string { - return $payload['event-data']['timestamp']; + foreach (['click', 'open', 'bounce', 'complaint', 'delivery', 'mail'] as $event) { + if (isset($payload['Message'][$event]['timestamp'])) { + return $payload['Message'][$event]['timestamp']; + } + } + return $payload['Message']['Timestamp']; + } + + public function getDataFromPayload(array $payload): array + { + $message = Message::fromRawPostData(); + $payload = $message->toArray(); + + return collect($this->dataMapping()) + ->mapWithKeys(function ($values, $key) use ($payload) { + foreach ($values as $value) { + $value = data_get($payload, $value); + if ($value !== null) { + return [$key => $value]; + } + } + return null; + }) + ->filter() + ->merge([ + 'payload' => $payload, + 'type' => $this->getEventFromPayload($payload), + 'occurred_at' => $this->getTimestampFromPayload($payload), + ]) + ->toArray(); } public function eventMapping(): array { return [ - EventType::ACCEPTED->value => ['event-data.event' => 'accepted'], - EventType::CLICKED->value => ['event-data.event' => 'clicked'], - EventType::COMPLAINED->value => ['event-data.event' => 'complained'], - EventType::DELIVERED->value => ['event-data.event' => 'delivered'], - EventType::HARD_BOUNCED->value => ['event-data.event' => 'failed', 'event-data.severity' => 'permanent'], - EventType::OPENED->value => ['event-data.event' => 'opened'], - EventType::SOFT_BOUNCED->value => ['event-data.event' => 'failed', 'event-data.severity' => 'temporary'], - EventType::UNSUBSCRIBED->value => ['event-data.event' => 'unsubscribed'], + EventType::ACCEPTED->value => ['Message.eventType' => 'Send'], + EventType::CLICKED->value => ['Message.eventType' => 'Click'], + EventType::COMPLAINED->value => ['Message.eventType' => 'Complaint'], + EventType::DELIVERED->value => ['Message.eventType' => 'Delivery'], + EventType::OPENED->value => ['Message.eventType' => 'Open'], + EventType::HARD_BOUNCED->value => ['Message.eventType' => 'Bounce', 'Message.bounce.bounceType' => 'Permanent'], + EventType::SOFT_BOUNCED->value => ['Message.eventType' => 'Bounce', 'Message.bounce.bounceType' => 'Temporary'], ]; } public function dataMapping(): array { return [ - 'ip_address' => 'event-data.ip', - 'platform' => 'event-data.client-info.device-type', - 'os' => 'event-data.client-info.client-os', - 'browser' => 'event-data.client-info.client-name', - 'user_agent' => 'event-data.client-info.user-agent', - 'city' => 'event-data.geolocation.city', - 'country_code' => 'event-data.geolocation.country', - 'link' => 'event-data.url', - 'tag' => 'event-data.tags', + 'ip_address' => ['Message.click.ipAddress', 'Message.open.ipAddress'], + 'browser' => ['Message.mail.client-info.client-name'], + 'user_agent' => ['Message.click.userAgent', 'Message.open.userAgent','Message.complaint.userAgent'], + 'link' => ['Message.click.link'], + 'tag' => ['Message.click.linkTags'], ]; } - public function unsuppressEmailAddress(string $address): Response - { - $client = Http::asJson() - ->withBasicAuth('api', config('services.mailgun.secret')) - ->baseUrl(config('services.mailgun.endpoint') . '/v3/'); - - return $client->delete(config('services.mailgun.domain') . '/unsubscribes/' . $address); - } - protected function createSnsClient(array $config): SnsClient { $config = array_merge( @@ -226,10 +270,10 @@ protected function createSnsClient(array $config): SnsClient protected function addSnsCredentials(array $config): array { - if (! empty($config['key']) && ! empty($config['secret'])) { + if (!empty($config['key']) && !empty($config['secret'])) { $config['credentials'] = Arr::only($config, ['key', 'secret']); - if (! empty($config['token'])) { + if (!empty($config['token'])) { $config['credentials']['token'] = $config['token']; } } diff --git a/tests/SesTest.php b/tests/SesTest.php new file mode 100644 index 0000000..2b19069 --- /dev/null +++ b/tests/SesTest.php @@ -0,0 +1,534 @@ +to('mark@vormkracht10.nl') + ->from('local@computer.nl') + ->cc('cc@vk10.nl') + ->bcc('bcc@vk10.nl') + ->subject('Test') + ->text('Text') + ->html('

HTML

'); + }); + + $mail = MailModel::latest()->first(); + + $message = json_decode('{ + "eventType": "Delivery", + "mail": { + "timestamp": "2016-10-19T23:20:52.240Z", + "source": "sender@example.com", + "sourceArn": "arn:aws:ses:us-east-1:123456789012:identity/sender@example.com", + "sendingAccountId": "123456789012", + "messageId": "EXAMPLE7c191be45-e9aedb9a-02f9-4d12-a87d-dd0099a07f8a-000000", + "destination": [ + "recipient@example.com" + ], + "headersTruncated": false, + "headers": [ + { + "name": "From", + "value": "sender@example.com" + }, + { + "name": "To", + "value": "recipient@example.com" + }, + { + "name": "Subject", + "value": "Message sent from Amazon SES" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "Content-Type", + "value": "text/html; charset=UTF-8" + }, + { + "name": "Content-Transfer-Encoding", + "value": "7bit" + } + ], + "commonHeaders": { + "from": [ + "sender@example.com" + ], + "to": [ + "recipient@example.com" + ], + "messageId": "EXAMPLE7c191be45-e9aedb9a-02f9-4d12-a87d-dd0099a07f8a-000000", + "subject": "Message sent from Amazon SES" + }, + "tags": { + "ses:configuration-set": [ + "ConfigSet" + ], + "ses:source-ip": [ + "192.0.2.0" + ], + "ses:from-domain": [ + "example.com" + ], + "ses:caller-identity": [ + "ses_user" + ], + "ses:outgoing-ip": [ + "192.0.2.0" + ], + "myCustomTag1": [ + "myCustomTagValue1" + ], + "myCustomTag2": [ + "myCustomTagValue2" + ] + } + }, + "delivery": { + "timestamp": "2016-10-19T23:21:04.133Z", + "processingTimeMillis": 11893, + "recipients": [ + "recipient@example.com" + ], + "smtpResponse": "250 2.6.0 Message received", + "remoteMtaIp": "123.456.789.012", + "reportingMTA": "mta.example.com" + } +}', true); + + post(URL::signedRoute('mails.webhook', ['provider' => Provider::SES]), [ + 'Message' => $message, + 'signature' => 'secrethmacsignature', + ])->assertAccepted(); + + assertDatabaseHas((new MailEvent)->getTable(), [ + 'type' => EventType::DELIVERED->value, + ]); +}); + +it('can receive incoming accept webhook from mailgun', function () { + Mail::send([], [], function (Message $message) { + $message->to('mark@vormkracht10.nl') + ->from('local@computer.nl') + ->cc('cc@vk10.nl') + ->bcc('bcc@vk10.nl') + ->subject('Test') + ->text('Text') + ->html('

HTML

'); + }); + + $mail = MailModel::latest()->first(); + + post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ + 'signature' => [ + 'timestamp' => 1649408311, + 'token' => 'eventtoken', + 'signature' => 'secrethmacsignature', + ], + 'event-data' => [ + 'event' => 'accepted', + 'timestamp' => 1649408305, + 'id' => 'OTk6MTA1MDI6YWNjZXB0ZWQ6NTYyNTQ4NzY3', + 'recipient' => 'test@omnivery.com', + 'recipient-domain' => 'omnivery.com', + 'campaigns' => [], + 'tags' => ['accepted'], + 'user-variables' => [ + config('mails.headers.uuid') => $mail?->uuid, + ], + 'flags' => [ + 'is-system-test' => false, + 'is-test-mode' => false, + ], + 'envelope' => [ + 'sending-ip' => '123.123.123.123', + 'sender' => 'sender@omnivery.dev', + 'targets' => 'test@omnivery.com', + 'transport' => 'smtp', + ], + 'message' => [ + 'headers' => [ + 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', + 'subject' => 'Production test', + 'from' => '"Friendly Sender" ', + 'to' => 'test@omnivery.com', + 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', + ], + 'size' => 5637, + ], + ], + ])->assertAccepted(); + + assertDatabaseHas((new MailEvent)->getTable(), [ + 'type' => EventType::ACCEPTED->value, + ]); +}); + +it('can receive incoming hard bounce webhook from mailgun', function () { + Mail::send([], [], function (Message $message) { + $message->to('mark@vormkracht10.nl') + ->from('local@computer.nl') + ->cc('cc@vk10.nl') + ->bcc('bcc@vk10.nl') + ->subject('Test') + ->text('Text') + ->html('

HTML

'); + }); + + $mail = MailModel::latest()->first(); + + post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ + 'event-data' => [ + 'event' => 'failed', + 'severity' => 'permanent', + 'envelope' => [ + 'sender' => 'bounce-d9bee8ac-b0e7-11ec-8086-57d93b186f66@notify.omnivery.com', + 'sending-ip' => '185.136.201.130', + 'targets' => 'nosuchemail@omnivery.com', + 'transport' => 'smtp', + ], + 'recipient' => 'nosuchemail@omnivery.com', + 'message' => [ + 'size' => 5597, + 'headers' => [ + 'message-id' => 'd9bee8ac-b0e7-11ec-8086-57d93b186f66', + 'subject' => 'Test message subject', + 'date' => 'Thu, 31 Mar 2022 11:43:57 +0000', + 'to' => 'nosuchemail@omnivery.com', + 'from' => '"Friendly Sender" ', + ], + ], + 'delivery-status' => [ + 'code' => 550, + 'bounce-class' => 'bad-mailbox', + 'description' => '550 5.1.1 : Recipient address rejected: User unknown in virtual mailbox table', + 'mx-host' => 'mail.mailkit.eu', + 'tls' => true, + 'mx-ip' => '185.136.200.19', + 'message' => ': Recipient address rejected: User unknown in virtual mailbox table', + ], + 'id' => 'MTozOmhhcmRib3VuY2U6MTY0ODcyNzAzOQ==', + 'timestamp' => 1648727038.22387, + 'recipient-domain' => 'omnivery.com', + ], + 'signature' => [ + 'signature' => 'secrethmacsignature', + 'timestamp' => 1648727039, + 'token' => 'eventtoken', + ], + ])->assertAccepted(); + + assertDatabaseHas((new MailEvent)->getTable(), [ + 'type' => EventType::HARD_BOUNCED->value, + ]); +}); + +it('can receive incoming soft bounce webhook from mailgun', function () { + Mail::send([], [], function (Message $message) { + $message->to('mark@vormkracht10.nl') + ->from('local@computer.nl') + ->cc('cc@vk10.nl') + ->bcc('bcc@vk10.nl') + ->subject('Test') + ->text('Text') + ->html('

HTML

'); + }); + + $mail = MailModel::latest()->first(); + + post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ + 'event-data' => [ + 'event' => 'failed', + 'severity' => 'temporary', + 'envelope' => [ + 'sender' => 'bounce-d9bee8ac-b0e7-11ec-8086-57d93b186f66@notify.omnivery.com', + 'sending-ip' => '185.136.201.130', + 'targets' => 'nosuchemail@omnivery.com', + 'transport' => 'smtp', + ], + 'recipient' => 'nosuchemail@omnivery.com', + 'message' => [ + 'size' => 5597, + 'headers' => [ + 'message-id' => 'd9bee8ac-b0e7-11ec-8086-57d93b186f66', + 'subject' => 'Test message subject', + 'date' => 'Thu, 31 Mar 2022 11:43:57 +0000', + 'to' => 'nosuchemail@omnivery.com', + 'from' => '"Friendly Sender" ', + ], + ], + 'delivery-status' => [ + 'code' => 550, + 'bounce-class' => 'bad-mailbox', + 'description' => '550 5.1.1 : Recipient address rejected: User unknown in virtual mailbox table', + 'mx-host' => 'mail.mailkit.eu', + 'tls' => true, + 'mx-ip' => '185.136.200.19', + 'message' => ': Recipient address rejected: User unknown in virtual mailbox table', + ], + 'id' => 'MTozOmhhcmRib3VuY2U6MTY0ODcyNzAzOQ==', + 'timestamp' => 1648727038.22387, + 'recipient-domain' => 'omnivery.com', + ], + 'signature' => [ + 'signature' => 'secrethmacsignature', + 'timestamp' => 1648727039, + 'token' => 'eventtoken', + ], + ])->assertAccepted(); + + assertDatabaseHas((new MailEvent)->getTable(), [ + 'type' => EventType::SOFT_BOUNCED->value, + ]); +}); + +it('can receive incoming complaint webhook from mailgun', function () { + Mail::send([], [], function (Message $message) { + $message->to('mark@vormkracht10.nl') + ->from('local@computer.nl') + ->cc('cc@vk10.nl') + ->bcc('bcc@vk10.nl') + ->subject('Test') + ->text('Text') + ->html('

HTML

'); + }); + + $mail = MailModel::latest()->first(); + + post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ + 'signature' => [ + 'timestamp' => 1649408311, + 'token' => 'eventtoken', + 'signature' => 'secrethmacsignature', + ], + 'event-data' => [ + 'event' => 'complained', + 'timestamp' => 1649408305, + 'id' => 'OTk6MTA1MDI6Y29tcGxhaW5lZDo1NjI1NDg3Njc=', + 'recipient' => 'test@omnivery.com', + 'recipient-domain' => 'omnivery.com', + 'campaigns' => [], + 'tags' => ['complaint', 'feedback'], + 'user-variables' => [ + config('mails.headers.uuid') => $mail?->uuid, + ], + 'flags' => [ + 'is-system-test' => false, + 'is-test-mode' => false, + ], + 'complaint' => [ + 'complained-at' => 'Thu, 7 Apr 2022 13:34:17 +0000', + 'feedback-id' => 'feedback-id-12345', + 'user-agent' => 'Feedback Loop Processor', + ], + 'message' => [ + 'headers' => [ + 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', + 'subject' => 'Production test', + 'from' => '"Friendly Sender" ', + 'to' => 'test@omnivery.com', + 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', + ], + 'size' => 5637, + ], + ], + ])->assertAccepted(); + + assertDatabaseHas((new MailEvent)->getTable(), [ + 'type' => EventType::COMPLAINED->value, + ]); +}); + +it('can receive incoming open webhook from mailgun', function () { + Mail::send([], [], function (Message $message) { + $message->to('mark@vormkracht10.nl') + ->from('local@computer.nl') + ->cc('cc@vk10.nl') + ->bcc('bcc@vk10.nl') + ->subject('Test') + ->text('Text') + ->html('

HTML

'); + }); + + $mail = MailModel::latest()->first(); + + post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ + 'signature' => [ + 'signature' => 'secrethmacsignature', + 'token' => 'eventtoken', + 'timestamp' => 1649408311, + ], + 'event-data' => [ + 'recipient-domain' => 'omnivery.com', + 'timestamp' => 1649408305, + 'envelope' => [ + 'targets' => 'test@omnivery.com', + ], + 'message' => [ + 'headers' => [ + 'subject' => 'Production test', + 'to' => 'test@omnivery.com', + 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', + 'from' => '"Friendly Sender" ', + 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', + ], + ], + 'client-info' => [ + 'suspected-bot' => false, + 'device-type' => 'Personal computer', + 'client-name' => 'Thunderbird', + 'client-type' => 'Email client', + 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.7.0', + 'client-os' => 'Windows 10', + ], + 'ip' => '123.123.123.123', + 'recipient' => 'test@omnivery.com', + 'id' => 'OTk6MTA1MDI6b3BlbmVkOjE2NDk0MDgzMTE=', + 'event' => 'opened', + 'geolocation' => [ + 'country_code' => 'ES', + 'continent_name' => 'Europe', + 'country_name' => 'Spain', + 'continent_code' => 'EU', + 'city' => 'Puerto de la Omnivery', + ], + ], + ])->assertAccepted(); + + assertDatabaseHas((new MailEvent)->getTable(), [ + 'type' => EventType::OPENED->value, + ]); +}); + +it('can receive incoming click webhook from mailgun', function () { + Mail::send([], [], function (Message $message) { + $message->to('mark@vormkracht10.nl') + ->from('local@computer.nl') + ->cc('cc@vk10.nl') + ->bcc('bcc@vk10.nl') + ->subject('Test') + ->text('Text') + ->html('

HTML

'); + }); + + $mail = MailModel::latest()->first(); + + post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ + 'signature' => [ + 'timestamp' => 1649408311, + 'token' => 'eventtoken', + 'signature' => 'secrethmacsignature', + ], + 'event-data' => [ + 'event' => 'clicked', + 'timestamp' => 1649408305, + 'id' => 'OTk6MTA1MDI6Y2xpY2tlZDo1NjI1NDg3Njc=', + 'recipient' => 'test@omnivery.com', + 'recipient-domain' => 'omnivery.com', + 'campaigns' => [], + 'user-variables' => [ + config('mails.headers.uuid') => $mail?->uuid, + ], + 'flags' => [ + 'is-system-test' => false, + 'is-test-mode' => false, + ], + 'ip' => '123.123.123.123', + 'geolocation' => [ + 'country' => 'Spain', + 'region' => 'ES', + 'city' => 'Puerto de la Omnivery', + ], + 'url' => 'https://example.com', + 'client-info' => [ + 'client-name' => 'Chrome', + 'client-type' => 'browser', + 'device-type' => 'desktop', + 'client-os' => 'Windows', + ], + 'message' => [ + 'headers' => [ + 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', + 'subject' => 'Production test', + 'from' => '"Friendly Sender" ', + 'to' => 'test@omnivery.com', + 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', + ], + 'size' => 5637, + ], + ], + ])->assertAccepted(); + + assertDatabaseHas((new MailEvent)->getTable(), [ + 'type' => EventType::CLICKED->value, + 'link' => 'https://example.com', + ]); +}); + +it('can receive incoming unsubscribe webhook from mailgun', function () { + Mail::send([], [], function (Message $message) { + $message->to('mark@vormkracht10.nl') + ->from('local@computer.nl') + ->cc('cc@vk10.nl') + ->bcc('bcc@vk10.nl') + ->subject('Test') + ->text('Text') + ->html('

HTML

'); + }); + + $mail = MailModel::latest()->first(); + + post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ + 'signature' => [ + 'timestamp' => 1649408311, + 'token' => 'eventtoken', + 'signature' => 'secrethmacsignature', + ], + 'event-data' => [ + 'event' => 'unsubscribed', + 'timestamp' => 1649408305, + 'id' => 'OTk6MTA1MDI6dW5zdWJzY3JpYmVkOjU2MjU0ODc2Nw==', + 'recipient' => 'test@omnivery.com', + 'recipient-domain' => 'omnivery.com', + 'campaigns' => [], + 'tags' => ['unsubscribed'], + 'user-variables' => [ + config('mails.headers.uuid') => $mail?->uuid, + ], + 'flags' => [ + 'is-system-test' => false, + 'is-test-mode' => false, + ], + 'unsubscribe' => [ + 'mailing-list' => 'newsletter@omnivery.com', + 'ip' => '123.123.123.123', + ], + 'message' => [ + 'headers' => [ + 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', + 'subject' => 'Production test', + 'from' => '"Friendly Sender" ', + 'to' => 'test@omnivery.com', + 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', + ], + 'size' => 5637, + ], + ], + ])->assertAccepted(); + + assertDatabaseHas((new MailEvent)->getTable(), [ + 'type' => EventType::UNSUBSCRIBED->value, + ]); +}); From f8df09f62a86813cc58d0f4b1fed2d395cab82f9 Mon Sep 17 00:00:00 2001 From: Daniele Rosario Date: Wed, 16 Apr 2025 10:25:43 +0200 Subject: [PATCH 4/4] test --- tests/SesTest.php | 830 +++++++++++++++++++++++----------------------- 1 file changed, 415 insertions(+), 415 deletions(-) diff --git a/tests/SesTest.php b/tests/SesTest.php index 2b19069..ea823b4 100644 --- a/tests/SesTest.php +++ b/tests/SesTest.php @@ -117,418 +117,418 @@ 'type' => EventType::DELIVERED->value, ]); }); - -it('can receive incoming accept webhook from mailgun', function () { - Mail::send([], [], function (Message $message) { - $message->to('mark@vormkracht10.nl') - ->from('local@computer.nl') - ->cc('cc@vk10.nl') - ->bcc('bcc@vk10.nl') - ->subject('Test') - ->text('Text') - ->html('

HTML

'); - }); - - $mail = MailModel::latest()->first(); - - post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ - 'signature' => [ - 'timestamp' => 1649408311, - 'token' => 'eventtoken', - 'signature' => 'secrethmacsignature', - ], - 'event-data' => [ - 'event' => 'accepted', - 'timestamp' => 1649408305, - 'id' => 'OTk6MTA1MDI6YWNjZXB0ZWQ6NTYyNTQ4NzY3', - 'recipient' => 'test@omnivery.com', - 'recipient-domain' => 'omnivery.com', - 'campaigns' => [], - 'tags' => ['accepted'], - 'user-variables' => [ - config('mails.headers.uuid') => $mail?->uuid, - ], - 'flags' => [ - 'is-system-test' => false, - 'is-test-mode' => false, - ], - 'envelope' => [ - 'sending-ip' => '123.123.123.123', - 'sender' => 'sender@omnivery.dev', - 'targets' => 'test@omnivery.com', - 'transport' => 'smtp', - ], - 'message' => [ - 'headers' => [ - 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', - 'subject' => 'Production test', - 'from' => '"Friendly Sender" ', - 'to' => 'test@omnivery.com', - 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', - ], - 'size' => 5637, - ], - ], - ])->assertAccepted(); - - assertDatabaseHas((new MailEvent)->getTable(), [ - 'type' => EventType::ACCEPTED->value, - ]); -}); - -it('can receive incoming hard bounce webhook from mailgun', function () { - Mail::send([], [], function (Message $message) { - $message->to('mark@vormkracht10.nl') - ->from('local@computer.nl') - ->cc('cc@vk10.nl') - ->bcc('bcc@vk10.nl') - ->subject('Test') - ->text('Text') - ->html('

HTML

'); - }); - - $mail = MailModel::latest()->first(); - - post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ - 'event-data' => [ - 'event' => 'failed', - 'severity' => 'permanent', - 'envelope' => [ - 'sender' => 'bounce-d9bee8ac-b0e7-11ec-8086-57d93b186f66@notify.omnivery.com', - 'sending-ip' => '185.136.201.130', - 'targets' => 'nosuchemail@omnivery.com', - 'transport' => 'smtp', - ], - 'recipient' => 'nosuchemail@omnivery.com', - 'message' => [ - 'size' => 5597, - 'headers' => [ - 'message-id' => 'd9bee8ac-b0e7-11ec-8086-57d93b186f66', - 'subject' => 'Test message subject', - 'date' => 'Thu, 31 Mar 2022 11:43:57 +0000', - 'to' => 'nosuchemail@omnivery.com', - 'from' => '"Friendly Sender" ', - ], - ], - 'delivery-status' => [ - 'code' => 550, - 'bounce-class' => 'bad-mailbox', - 'description' => '550 5.1.1 : Recipient address rejected: User unknown in virtual mailbox table', - 'mx-host' => 'mail.mailkit.eu', - 'tls' => true, - 'mx-ip' => '185.136.200.19', - 'message' => ': Recipient address rejected: User unknown in virtual mailbox table', - ], - 'id' => 'MTozOmhhcmRib3VuY2U6MTY0ODcyNzAzOQ==', - 'timestamp' => 1648727038.22387, - 'recipient-domain' => 'omnivery.com', - ], - 'signature' => [ - 'signature' => 'secrethmacsignature', - 'timestamp' => 1648727039, - 'token' => 'eventtoken', - ], - ])->assertAccepted(); - - assertDatabaseHas((new MailEvent)->getTable(), [ - 'type' => EventType::HARD_BOUNCED->value, - ]); -}); - -it('can receive incoming soft bounce webhook from mailgun', function () { - Mail::send([], [], function (Message $message) { - $message->to('mark@vormkracht10.nl') - ->from('local@computer.nl') - ->cc('cc@vk10.nl') - ->bcc('bcc@vk10.nl') - ->subject('Test') - ->text('Text') - ->html('

HTML

'); - }); - - $mail = MailModel::latest()->first(); - - post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ - 'event-data' => [ - 'event' => 'failed', - 'severity' => 'temporary', - 'envelope' => [ - 'sender' => 'bounce-d9bee8ac-b0e7-11ec-8086-57d93b186f66@notify.omnivery.com', - 'sending-ip' => '185.136.201.130', - 'targets' => 'nosuchemail@omnivery.com', - 'transport' => 'smtp', - ], - 'recipient' => 'nosuchemail@omnivery.com', - 'message' => [ - 'size' => 5597, - 'headers' => [ - 'message-id' => 'd9bee8ac-b0e7-11ec-8086-57d93b186f66', - 'subject' => 'Test message subject', - 'date' => 'Thu, 31 Mar 2022 11:43:57 +0000', - 'to' => 'nosuchemail@omnivery.com', - 'from' => '"Friendly Sender" ', - ], - ], - 'delivery-status' => [ - 'code' => 550, - 'bounce-class' => 'bad-mailbox', - 'description' => '550 5.1.1 : Recipient address rejected: User unknown in virtual mailbox table', - 'mx-host' => 'mail.mailkit.eu', - 'tls' => true, - 'mx-ip' => '185.136.200.19', - 'message' => ': Recipient address rejected: User unknown in virtual mailbox table', - ], - 'id' => 'MTozOmhhcmRib3VuY2U6MTY0ODcyNzAzOQ==', - 'timestamp' => 1648727038.22387, - 'recipient-domain' => 'omnivery.com', - ], - 'signature' => [ - 'signature' => 'secrethmacsignature', - 'timestamp' => 1648727039, - 'token' => 'eventtoken', - ], - ])->assertAccepted(); - - assertDatabaseHas((new MailEvent)->getTable(), [ - 'type' => EventType::SOFT_BOUNCED->value, - ]); -}); - -it('can receive incoming complaint webhook from mailgun', function () { - Mail::send([], [], function (Message $message) { - $message->to('mark@vormkracht10.nl') - ->from('local@computer.nl') - ->cc('cc@vk10.nl') - ->bcc('bcc@vk10.nl') - ->subject('Test') - ->text('Text') - ->html('

HTML

'); - }); - - $mail = MailModel::latest()->first(); - - post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ - 'signature' => [ - 'timestamp' => 1649408311, - 'token' => 'eventtoken', - 'signature' => 'secrethmacsignature', - ], - 'event-data' => [ - 'event' => 'complained', - 'timestamp' => 1649408305, - 'id' => 'OTk6MTA1MDI6Y29tcGxhaW5lZDo1NjI1NDg3Njc=', - 'recipient' => 'test@omnivery.com', - 'recipient-domain' => 'omnivery.com', - 'campaigns' => [], - 'tags' => ['complaint', 'feedback'], - 'user-variables' => [ - config('mails.headers.uuid') => $mail?->uuid, - ], - 'flags' => [ - 'is-system-test' => false, - 'is-test-mode' => false, - ], - 'complaint' => [ - 'complained-at' => 'Thu, 7 Apr 2022 13:34:17 +0000', - 'feedback-id' => 'feedback-id-12345', - 'user-agent' => 'Feedback Loop Processor', - ], - 'message' => [ - 'headers' => [ - 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', - 'subject' => 'Production test', - 'from' => '"Friendly Sender" ', - 'to' => 'test@omnivery.com', - 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', - ], - 'size' => 5637, - ], - ], - ])->assertAccepted(); - - assertDatabaseHas((new MailEvent)->getTable(), [ - 'type' => EventType::COMPLAINED->value, - ]); -}); - -it('can receive incoming open webhook from mailgun', function () { - Mail::send([], [], function (Message $message) { - $message->to('mark@vormkracht10.nl') - ->from('local@computer.nl') - ->cc('cc@vk10.nl') - ->bcc('bcc@vk10.nl') - ->subject('Test') - ->text('Text') - ->html('

HTML

'); - }); - - $mail = MailModel::latest()->first(); - - post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ - 'signature' => [ - 'signature' => 'secrethmacsignature', - 'token' => 'eventtoken', - 'timestamp' => 1649408311, - ], - 'event-data' => [ - 'recipient-domain' => 'omnivery.com', - 'timestamp' => 1649408305, - 'envelope' => [ - 'targets' => 'test@omnivery.com', - ], - 'message' => [ - 'headers' => [ - 'subject' => 'Production test', - 'to' => 'test@omnivery.com', - 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', - 'from' => '"Friendly Sender" ', - 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', - ], - ], - 'client-info' => [ - 'suspected-bot' => false, - 'device-type' => 'Personal computer', - 'client-name' => 'Thunderbird', - 'client-type' => 'Email client', - 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.7.0', - 'client-os' => 'Windows 10', - ], - 'ip' => '123.123.123.123', - 'recipient' => 'test@omnivery.com', - 'id' => 'OTk6MTA1MDI6b3BlbmVkOjE2NDk0MDgzMTE=', - 'event' => 'opened', - 'geolocation' => [ - 'country_code' => 'ES', - 'continent_name' => 'Europe', - 'country_name' => 'Spain', - 'continent_code' => 'EU', - 'city' => 'Puerto de la Omnivery', - ], - ], - ])->assertAccepted(); - - assertDatabaseHas((new MailEvent)->getTable(), [ - 'type' => EventType::OPENED->value, - ]); -}); - -it('can receive incoming click webhook from mailgun', function () { - Mail::send([], [], function (Message $message) { - $message->to('mark@vormkracht10.nl') - ->from('local@computer.nl') - ->cc('cc@vk10.nl') - ->bcc('bcc@vk10.nl') - ->subject('Test') - ->text('Text') - ->html('

HTML

'); - }); - - $mail = MailModel::latest()->first(); - - post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ - 'signature' => [ - 'timestamp' => 1649408311, - 'token' => 'eventtoken', - 'signature' => 'secrethmacsignature', - ], - 'event-data' => [ - 'event' => 'clicked', - 'timestamp' => 1649408305, - 'id' => 'OTk6MTA1MDI6Y2xpY2tlZDo1NjI1NDg3Njc=', - 'recipient' => 'test@omnivery.com', - 'recipient-domain' => 'omnivery.com', - 'campaigns' => [], - 'user-variables' => [ - config('mails.headers.uuid') => $mail?->uuid, - ], - 'flags' => [ - 'is-system-test' => false, - 'is-test-mode' => false, - ], - 'ip' => '123.123.123.123', - 'geolocation' => [ - 'country' => 'Spain', - 'region' => 'ES', - 'city' => 'Puerto de la Omnivery', - ], - 'url' => 'https://example.com', - 'client-info' => [ - 'client-name' => 'Chrome', - 'client-type' => 'browser', - 'device-type' => 'desktop', - 'client-os' => 'Windows', - ], - 'message' => [ - 'headers' => [ - 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', - 'subject' => 'Production test', - 'from' => '"Friendly Sender" ', - 'to' => 'test@omnivery.com', - 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', - ], - 'size' => 5637, - ], - ], - ])->assertAccepted(); - - assertDatabaseHas((new MailEvent)->getTable(), [ - 'type' => EventType::CLICKED->value, - 'link' => 'https://example.com', - ]); -}); - -it('can receive incoming unsubscribe webhook from mailgun', function () { - Mail::send([], [], function (Message $message) { - $message->to('mark@vormkracht10.nl') - ->from('local@computer.nl') - ->cc('cc@vk10.nl') - ->bcc('bcc@vk10.nl') - ->subject('Test') - ->text('Text') - ->html('

HTML

'); - }); - - $mail = MailModel::latest()->first(); - - post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ - 'signature' => [ - 'timestamp' => 1649408311, - 'token' => 'eventtoken', - 'signature' => 'secrethmacsignature', - ], - 'event-data' => [ - 'event' => 'unsubscribed', - 'timestamp' => 1649408305, - 'id' => 'OTk6MTA1MDI6dW5zdWJzY3JpYmVkOjU2MjU0ODc2Nw==', - 'recipient' => 'test@omnivery.com', - 'recipient-domain' => 'omnivery.com', - 'campaigns' => [], - 'tags' => ['unsubscribed'], - 'user-variables' => [ - config('mails.headers.uuid') => $mail?->uuid, - ], - 'flags' => [ - 'is-system-test' => false, - 'is-test-mode' => false, - ], - 'unsubscribe' => [ - 'mailing-list' => 'newsletter@omnivery.com', - 'ip' => '123.123.123.123', - ], - 'message' => [ - 'headers' => [ - 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', - 'subject' => 'Production test', - 'from' => '"Friendly Sender" ', - 'to' => 'test@omnivery.com', - 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', - ], - 'size' => 5637, - ], - ], - ])->assertAccepted(); - - assertDatabaseHas((new MailEvent)->getTable(), [ - 'type' => EventType::UNSUBSCRIBED->value, - ]); -}); +// +//it('can receive incoming accept webhook from mailgun', function () { +// Mail::send([], [], function (Message $message) { +// $message->to('mark@vormkracht10.nl') +// ->from('local@computer.nl') +// ->cc('cc@vk10.nl') +// ->bcc('bcc@vk10.nl') +// ->subject('Test') +// ->text('Text') +// ->html('

HTML

'); +// }); +// +// $mail = MailModel::latest()->first(); +// +// post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ +// 'signature' => [ +// 'timestamp' => 1649408311, +// 'token' => 'eventtoken', +// 'signature' => 'secrethmacsignature', +// ], +// 'event-data' => [ +// 'event' => 'accepted', +// 'timestamp' => 1649408305, +// 'id' => 'OTk6MTA1MDI6YWNjZXB0ZWQ6NTYyNTQ4NzY3', +// 'recipient' => 'test@omnivery.com', +// 'recipient-domain' => 'omnivery.com', +// 'campaigns' => [], +// 'tags' => ['accepted'], +// 'user-variables' => [ +// config('mails.headers.uuid') => $mail?->uuid, +// ], +// 'flags' => [ +// 'is-system-test' => false, +// 'is-test-mode' => false, +// ], +// 'envelope' => [ +// 'sending-ip' => '123.123.123.123', +// 'sender' => 'sender@omnivery.dev', +// 'targets' => 'test@omnivery.com', +// 'transport' => 'smtp', +// ], +// 'message' => [ +// 'headers' => [ +// 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', +// 'subject' => 'Production test', +// 'from' => '"Friendly Sender" ', +// 'to' => 'test@omnivery.com', +// 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', +// ], +// 'size' => 5637, +// ], +// ], +// ])->assertAccepted(); +// +// assertDatabaseHas((new MailEvent)->getTable(), [ +// 'type' => EventType::ACCEPTED->value, +// ]); +//}); +// +//it('can receive incoming hard bounce webhook from mailgun', function () { +// Mail::send([], [], function (Message $message) { +// $message->to('mark@vormkracht10.nl') +// ->from('local@computer.nl') +// ->cc('cc@vk10.nl') +// ->bcc('bcc@vk10.nl') +// ->subject('Test') +// ->text('Text') +// ->html('

HTML

'); +// }); +// +// $mail = MailModel::latest()->first(); +// +// post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ +// 'event-data' => [ +// 'event' => 'failed', +// 'severity' => 'permanent', +// 'envelope' => [ +// 'sender' => 'bounce-d9bee8ac-b0e7-11ec-8086-57d93b186f66@notify.omnivery.com', +// 'sending-ip' => '185.136.201.130', +// 'targets' => 'nosuchemail@omnivery.com', +// 'transport' => 'smtp', +// ], +// 'recipient' => 'nosuchemail@omnivery.com', +// 'message' => [ +// 'size' => 5597, +// 'headers' => [ +// 'message-id' => 'd9bee8ac-b0e7-11ec-8086-57d93b186f66', +// 'subject' => 'Test message subject', +// 'date' => 'Thu, 31 Mar 2022 11:43:57 +0000', +// 'to' => 'nosuchemail@omnivery.com', +// 'from' => '"Friendly Sender" ', +// ], +// ], +// 'delivery-status' => [ +// 'code' => 550, +// 'bounce-class' => 'bad-mailbox', +// 'description' => '550 5.1.1 : Recipient address rejected: User unknown in virtual mailbox table', +// 'mx-host' => 'mail.mailkit.eu', +// 'tls' => true, +// 'mx-ip' => '185.136.200.19', +// 'message' => ': Recipient address rejected: User unknown in virtual mailbox table', +// ], +// 'id' => 'MTozOmhhcmRib3VuY2U6MTY0ODcyNzAzOQ==', +// 'timestamp' => 1648727038.22387, +// 'recipient-domain' => 'omnivery.com', +// ], +// 'signature' => [ +// 'signature' => 'secrethmacsignature', +// 'timestamp' => 1648727039, +// 'token' => 'eventtoken', +// ], +// ])->assertAccepted(); +// +// assertDatabaseHas((new MailEvent)->getTable(), [ +// 'type' => EventType::HARD_BOUNCED->value, +// ]); +//}); +// +//it('can receive incoming soft bounce webhook from mailgun', function () { +// Mail::send([], [], function (Message $message) { +// $message->to('mark@vormkracht10.nl') +// ->from('local@computer.nl') +// ->cc('cc@vk10.nl') +// ->bcc('bcc@vk10.nl') +// ->subject('Test') +// ->text('Text') +// ->html('

HTML

'); +// }); +// +// $mail = MailModel::latest()->first(); +// +// post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ +// 'event-data' => [ +// 'event' => 'failed', +// 'severity' => 'temporary', +// 'envelope' => [ +// 'sender' => 'bounce-d9bee8ac-b0e7-11ec-8086-57d93b186f66@notify.omnivery.com', +// 'sending-ip' => '185.136.201.130', +// 'targets' => 'nosuchemail@omnivery.com', +// 'transport' => 'smtp', +// ], +// 'recipient' => 'nosuchemail@omnivery.com', +// 'message' => [ +// 'size' => 5597, +// 'headers' => [ +// 'message-id' => 'd9bee8ac-b0e7-11ec-8086-57d93b186f66', +// 'subject' => 'Test message subject', +// 'date' => 'Thu, 31 Mar 2022 11:43:57 +0000', +// 'to' => 'nosuchemail@omnivery.com', +// 'from' => '"Friendly Sender" ', +// ], +// ], +// 'delivery-status' => [ +// 'code' => 550, +// 'bounce-class' => 'bad-mailbox', +// 'description' => '550 5.1.1 : Recipient address rejected: User unknown in virtual mailbox table', +// 'mx-host' => 'mail.mailkit.eu', +// 'tls' => true, +// 'mx-ip' => '185.136.200.19', +// 'message' => ': Recipient address rejected: User unknown in virtual mailbox table', +// ], +// 'id' => 'MTozOmhhcmRib3VuY2U6MTY0ODcyNzAzOQ==', +// 'timestamp' => 1648727038.22387, +// 'recipient-domain' => 'omnivery.com', +// ], +// 'signature' => [ +// 'signature' => 'secrethmacsignature', +// 'timestamp' => 1648727039, +// 'token' => 'eventtoken', +// ], +// ])->assertAccepted(); +// +// assertDatabaseHas((new MailEvent)->getTable(), [ +// 'type' => EventType::SOFT_BOUNCED->value, +// ]); +//}); +// +//it('can receive incoming complaint webhook from mailgun', function () { +// Mail::send([], [], function (Message $message) { +// $message->to('mark@vormkracht10.nl') +// ->from('local@computer.nl') +// ->cc('cc@vk10.nl') +// ->bcc('bcc@vk10.nl') +// ->subject('Test') +// ->text('Text') +// ->html('

HTML

'); +// }); +// +// $mail = MailModel::latest()->first(); +// +// post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ +// 'signature' => [ +// 'timestamp' => 1649408311, +// 'token' => 'eventtoken', +// 'signature' => 'secrethmacsignature', +// ], +// 'event-data' => [ +// 'event' => 'complained', +// 'timestamp' => 1649408305, +// 'id' => 'OTk6MTA1MDI6Y29tcGxhaW5lZDo1NjI1NDg3Njc=', +// 'recipient' => 'test@omnivery.com', +// 'recipient-domain' => 'omnivery.com', +// 'campaigns' => [], +// 'tags' => ['complaint', 'feedback'], +// 'user-variables' => [ +// config('mails.headers.uuid') => $mail?->uuid, +// ], +// 'flags' => [ +// 'is-system-test' => false, +// 'is-test-mode' => false, +// ], +// 'complaint' => [ +// 'complained-at' => 'Thu, 7 Apr 2022 13:34:17 +0000', +// 'feedback-id' => 'feedback-id-12345', +// 'user-agent' => 'Feedback Loop Processor', +// ], +// 'message' => [ +// 'headers' => [ +// 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', +// 'subject' => 'Production test', +// 'from' => '"Friendly Sender" ', +// 'to' => 'test@omnivery.com', +// 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', +// ], +// 'size' => 5637, +// ], +// ], +// ])->assertAccepted(); +// +// assertDatabaseHas((new MailEvent)->getTable(), [ +// 'type' => EventType::COMPLAINED->value, +// ]); +//}); +// +//it('can receive incoming open webhook from mailgun', function () { +// Mail::send([], [], function (Message $message) { +// $message->to('mark@vormkracht10.nl') +// ->from('local@computer.nl') +// ->cc('cc@vk10.nl') +// ->bcc('bcc@vk10.nl') +// ->subject('Test') +// ->text('Text') +// ->html('

HTML

'); +// }); +// +// $mail = MailModel::latest()->first(); +// +// post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ +// 'signature' => [ +// 'signature' => 'secrethmacsignature', +// 'token' => 'eventtoken', +// 'timestamp' => 1649408311, +// ], +// 'event-data' => [ +// 'recipient-domain' => 'omnivery.com', +// 'timestamp' => 1649408305, +// 'envelope' => [ +// 'targets' => 'test@omnivery.com', +// ], +// 'message' => [ +// 'headers' => [ +// 'subject' => 'Production test', +// 'to' => 'test@omnivery.com', +// 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', +// 'from' => '"Friendly Sender" ', +// 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', +// ], +// ], +// 'client-info' => [ +// 'suspected-bot' => false, +// 'device-type' => 'Personal computer', +// 'client-name' => 'Thunderbird', +// 'client-type' => 'Email client', +// 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.7.0', +// 'client-os' => 'Windows 10', +// ], +// 'ip' => '123.123.123.123', +// 'recipient' => 'test@omnivery.com', +// 'id' => 'OTk6MTA1MDI6b3BlbmVkOjE2NDk0MDgzMTE=', +// 'event' => 'opened', +// 'geolocation' => [ +// 'country_code' => 'ES', +// 'continent_name' => 'Europe', +// 'country_name' => 'Spain', +// 'continent_code' => 'EU', +// 'city' => 'Puerto de la Omnivery', +// ], +// ], +// ])->assertAccepted(); +// +// assertDatabaseHas((new MailEvent)->getTable(), [ +// 'type' => EventType::OPENED->value, +// ]); +//}); +// +//it('can receive incoming click webhook from mailgun', function () { +// Mail::send([], [], function (Message $message) { +// $message->to('mark@vormkracht10.nl') +// ->from('local@computer.nl') +// ->cc('cc@vk10.nl') +// ->bcc('bcc@vk10.nl') +// ->subject('Test') +// ->text('Text') +// ->html('

HTML

'); +// }); +// +// $mail = MailModel::latest()->first(); +// +// post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ +// 'signature' => [ +// 'timestamp' => 1649408311, +// 'token' => 'eventtoken', +// 'signature' => 'secrethmacsignature', +// ], +// 'event-data' => [ +// 'event' => 'clicked', +// 'timestamp' => 1649408305, +// 'id' => 'OTk6MTA1MDI6Y2xpY2tlZDo1NjI1NDg3Njc=', +// 'recipient' => 'test@omnivery.com', +// 'recipient-domain' => 'omnivery.com', +// 'campaigns' => [], +// 'user-variables' => [ +// config('mails.headers.uuid') => $mail?->uuid, +// ], +// 'flags' => [ +// 'is-system-test' => false, +// 'is-test-mode' => false, +// ], +// 'ip' => '123.123.123.123', +// 'geolocation' => [ +// 'country' => 'Spain', +// 'region' => 'ES', +// 'city' => 'Puerto de la Omnivery', +// ], +// 'url' => 'https://example.com', +// 'client-info' => [ +// 'client-name' => 'Chrome', +// 'client-type' => 'browser', +// 'device-type' => 'desktop', +// 'client-os' => 'Windows', +// ], +// 'message' => [ +// 'headers' => [ +// 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', +// 'subject' => 'Production test', +// 'from' => '"Friendly Sender" ', +// 'to' => 'test@omnivery.com', +// 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', +// ], +// 'size' => 5637, +// ], +// ], +// ])->assertAccepted(); +// +// assertDatabaseHas((new MailEvent)->getTable(), [ +// 'type' => EventType::CLICKED->value, +// 'link' => 'https://example.com', +// ]); +//}); +// +//it('can receive incoming unsubscribe webhook from mailgun', function () { +// Mail::send([], [], function (Message $message) { +// $message->to('mark@vormkracht10.nl') +// ->from('local@computer.nl') +// ->cc('cc@vk10.nl') +// ->bcc('bcc@vk10.nl') +// ->subject('Test') +// ->text('Text') +// ->html('

HTML

'); +// }); +// +// $mail = MailModel::latest()->first(); +// +// post(URL::signedRoute('mails.webhook', ['provider' => Provider::MAILGUN]), [ +// 'signature' => [ +// 'timestamp' => 1649408311, +// 'token' => 'eventtoken', +// 'signature' => 'secrethmacsignature', +// ], +// 'event-data' => [ +// 'event' => 'unsubscribed', +// 'timestamp' => 1649408305, +// 'id' => 'OTk6MTA1MDI6dW5zdWJzY3JpYmVkOjU2MjU0ODc2Nw==', +// 'recipient' => 'test@omnivery.com', +// 'recipient-domain' => 'omnivery.com', +// 'campaigns' => [], +// 'tags' => ['unsubscribed'], +// 'user-variables' => [ +// config('mails.headers.uuid') => $mail?->uuid, +// ], +// 'flags' => [ +// 'is-system-test' => false, +// 'is-test-mode' => false, +// ], +// 'unsubscribe' => [ +// 'mailing-list' => 'newsletter@omnivery.com', +// 'ip' => '123.123.123.123', +// ], +// 'message' => [ +// 'headers' => [ +// 'message-id' => '6d261932-b677-11ec-aa58-03210c12f2eb', +// 'subject' => 'Production test', +// 'from' => '"Friendly Sender" ', +// 'to' => 'test@omnivery.com', +// 'date' => 'Thu, 7 Apr 2022 13:34:17 +0000', +// ], +// 'size' => 5637, +// ], +// ], +// ])->assertAccepted(); +// +// assertDatabaseHas((new MailEvent)->getTable(), [ +// 'type' => EventType::UNSUBSCRIBED->value, +// ]); +//});