From 58a7456087c273879720d51990fd421c56169346 Mon Sep 17 00:00:00 2001 From: paulwer Date: Fri, 15 Dec 2023 07:22:24 +0100 Subject: [PATCH] wip process normal mailgun webhooks --- app/Jobs/Mailgun/ProcessMailgunWebhook.php | 286 ++++++++++++--------- composer.json | 4 +- composer.lock | 146 +++++++++-- 3 files changed, 305 insertions(+), 131 deletions(-) diff --git a/app/Jobs/Mailgun/ProcessMailgunWebhook.php b/app/Jobs/Mailgun/ProcessMailgunWebhook.php index f5bf05f69352..296aef9f56a5 100644 --- a/app/Jobs/Mailgun/ProcessMailgunWebhook.php +++ b/app/Jobs/Mailgun/ProcessMailgunWebhook.php @@ -27,6 +27,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Mailgun\Mailgun; use Postmark\PostmarkClient; use Turbo124\Beacon\Facades\LightLogs; @@ -63,7 +64,7 @@ class ProcessMailgunWebhook implements ShouldQueue return SystemLog::query() ->where('company_id', $this->invitation->company_id) ->where('type_id', SystemLog::TYPE_WEBHOOK_RESPONSE) - ->whereJsonContains('log', ['MessageID' => $message_id]) + ->whereJsonContains('log', ['id' => $message_id]) ->orderBy('id', 'desc') ->first(); @@ -85,7 +86,7 @@ class ProcessMailgunWebhook implements ShouldQueue { MultiDB::findAndSetDbByCompanyKey($this->request['Tag']); - $this->invitation = $this->discoverInvitation($this->request['MessageID']); + $this->invitation = $this->discoverInvitation($this->request['message']['headers']['message-id']); if (!$this->invitation) { return; @@ -95,11 +96,12 @@ class ProcessMailgunWebhook implements ShouldQueue $this->invitation->email_error = $this->request['Details']; } - switch ($this->request['RecordType']) { + switch ($this->request['event'] ?? $this->request['severity']) { case 'delivered': return $this->processDelivery(); - case 'permanent_fail': - case 'temporary_fail': + case 'failed': + case 'permanent': + case 'temporary': return $this->processBounce(); case 'complained': return $this->processSpamComplaint(); @@ -112,40 +114,33 @@ class ProcessMailgunWebhook implements ShouldQueue } // { - // "Metadata": { - // "example": "value", - // "example_2": "value" + // "event": "opened", + // "id": "-laxIqj9QWubsjY_3pTq_g", + // "timestamp": 1377047343.042277, + // "log-level": "info", + // "recipient": "recipient@example.com", + // "geolocation": { + // "country": "US", + // "region": "Texas", + // "city": "Austin" // }, - // "RecordType": "Open", - // "FirstOpen": true, - // "Client": { - // "Name": "Chrome 35.0.1916.153", - // "Company": "Google", - // "Family": "Chrome" + // "tags": [], + // "campaigns": [], + // "user-variables": {}, + // "ip": "111.111.111.111", + // "client-info": { + // "client-type": "mobile browser", + // "client-os": "iOS", + // "device-type": "mobile", + // "client-name": "Mobile Safari", + // "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10B143", + // "bot": "" // }, - // "OS": { - // "Name": "OS X 10.7 Lion", - // "Company": "Apple Computer, Inc.", - // "Family": "OS X 10" + // "message": { + // "headers": { + // "message-id": "20130821005614.19826.35976@samples.mailgun.org" + // } // }, - // "Platform": "WebMail", - // "UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36", - // "ReadSeconds": 5, - // "Geo": { - // "CountryISOCode": "RS", - // "Country": "Serbia", - // "RegionISOCode": "VO", - // "Region": "Autonomna Pokrajina Vojvodina", - // "City": "Novi Sad", - // "Zip": "21000", - // "Coords": "45.2517,19.8369", - // "IP": "188.2.95.4" - // }, - // "MessageID": "00000000-0000-0000-0000-000000000000", - // "MessageStream": "outbound", - // "ReceivedAt": "2022-02-06T06:37:48Z", - // "Tag": "welcome-email", - // "Recipient": "john@example.com" // } private function processOpen() @@ -155,7 +150,7 @@ class ProcessMailgunWebhook implements ShouldQueue $data = array_merge($this->request, ['history' => $this->fetchMessage()]); - $sl = $this->getSystemLog($this->request['MessageID']); + $sl = $this->getSystemLog($this->request['message']['headers']['message-id']); if ($sl) { $this->updateSystemLog($sl, $data); @@ -173,18 +168,54 @@ class ProcessMailgunWebhook implements ShouldQueue } // { - // "RecordType": "Delivery", - // "ServerID": 23, - // "MessageStream": "outbound", - // "MessageID": "00000000-0000-0000-0000-000000000000", - // "Recipient": "john@example.com", - // "Tag": "welcome-email", - // "DeliveredAt": "2021-02-21T16:34:52Z", - // "Details": "Test delivery webhook details", - // "Metadata": { - // "example": "value", - // "example_2": "value" - // } + // "event": "delivered", + // "id": "hK7mQVt1QtqRiOfQXta4sw", + // "timestamp": 1529692199.626182, + // "log-level": "info", + // "envelope": { + // "transport": "smtp", + // "sender": "sender@example.org", + // "sending-ip": "123.123.123.123", + // "targets": "john@example.com" + // }, + // "flags": { + // "is-routed": false, + // "is-authenticated": false, + // "is-system-test": false, + // "is-test-mode": false + // }, + // "delivery-status": { + // "tls": true, + // "mx-host": "aspmx.l.example.com", + // "code": 250, + // "description": "", + // "session-seconds": 0.4367079734802246, + // "utf8": true, + // "attempt-no": 1, + // "message": "OK", + // "certificate-verified": true + // }, + // "message": { + // "headers": { + // "to": "team@example.org", + // "message-id": "20180622182958.1.48906CB188F1A454@exmple.org", + // "from": "sender@exmple.org", + // "subject": "Test Subject" + // }, + // "attachments": [], + // "size": 586 + // }, + // "storage": { + // "url": "https://storage-us-west1.api.mailgun.net/v3/domains/...", + // "region": "us-west1", + // "key": "AwABB...", + // "env": "production" + // }, + // "recipient": "john@example.com", + // "recipient-domain": "example.com", + // "campaigns": [], + // "tags": [], + // "user-variables": {} // } private function processDelivery() { @@ -193,7 +224,7 @@ class ProcessMailgunWebhook implements ShouldQueue $data = array_merge($this->request, ['history' => $this->fetchMessage()]); - $sl = $this->getSystemLog($this->request['MessageID']); + $sl = $this->getSystemLog($this->request['message']['headers']['message-id']); if ($sl) { $this->updateSystemLog($sl, $data); @@ -211,47 +242,66 @@ class ProcessMailgunWebhook implements ShouldQueue } // { - // "Metadata": { - // "example": "value", - // "example_2": "value" + // "event": "failed", || "temporary" || "permanent" + // "id": "pl271FzxTTmGRW8Uj3dUWw", + // "timestamp": 1529701969.818328, + // "log-level": "error", + // "severity": "permanent", + // "reason": "suppress-bounce", + // "envelope": { + // "sender": "john@example.org", + // "transport": "smtp", + // "targets": "joan@example.com" // }, - // "RecordType": "Bounce", - // "ID": 42, - // "Type": "HardBounce", - // "TypeCode": 1, - // "Name": "Hard bounce", - // "Tag": "Test", - // "MessageID": "00000000-0000-0000-0000-000000000000", - // "ServerID": 1234, - // "MessageStream": "outbound", - // "Description": "The server was unable to deliver your message (ex: unknown user, mailbox not found).", - // "Details": "Test bounce details", - // "Email": "john@example.com", - // "From": "sender@example.com", - // "BouncedAt": "2021-02-21T16:34:52Z", - // "DumpAvailable": true, - // "Inactive": true, - // "CanActivate": true, - // "Subject": "Test subject", - // "Content": "Test content" + // "flags": { + // "is-routed": false, + // "is-authenticated": true, + // "is-system-test": false, + // "is-test-mode": false + // }, + // "delivery-status": { + // "attempt-no": 1, + // "message": "", + // "code": 605, + // "description": "Not delivering to previously bounced address", + // "session-seconds": 0.0 + // }, + // "message": { + // "headers": { + // "to": "joan@example.com", + // "message-id": "20180622211249.1.2A6098970A380E12@example.org", + // "from": "john@example.org", + // "subject": "Test Subject" + // }, + // "attachments": [], + // "size": 867 + // }, + // "storage": { + // "url": "https://se.api.mailgun.net/v3/domains/example.org/messages/eyJwI...", + // "key": "eyJwI..." + // }, + // "recipient": "slava@mailgun.com", + // "recipient-domain": "mailgun.com", + // "campaigns": [], + // "tags": [], + // "user-variables": {} // } - private function processBounce() { $this->invitation->email_status = 'bounced'; $this->invitation->save(); $bounce = new EmailBounce( - $this->request['Tag'], - $this->request['From'], - $this->request['MessageID'] + $this->request['tags']->implode(','), + $this->request['message']['headers']['from'], + $this->request['message']['headers']['message-id'] ); LightLogs::create($bounce)->send(); $data = array_merge($this->request, ['history' => $this->fetchMessage()]); - $sl = $this->getSystemLog($this->request['MessageID']); + $sl = $this->getSystemLog($this->request['message']['headers']['message-id']); if ($sl) { $this->updateSystemLog($sl, $data); @@ -265,29 +315,33 @@ class ProcessMailgunWebhook implements ShouldQueue } // { - // "Metadata": { - // "example": "value", - // "example_2": "value" + // "event": "opened", + // "id": "-laxIqj9QWubsjY_3pTq_g", + // "timestamp": 1377047343.042277, + // "log-level": "info", + // "recipient": "recipient@example.com", + // "geolocation": { + // "country": "US", + // "region": "Texas", + // "city": "Austin" + // }, + // "tags": [], + // "campaigns": [], + // "user-variables": {}, + // "ip": "111.111.111.111", + // "client-info": { + // "client-type": "mobile browser", + // "client-os": "iOS", + // "device-type": "mobile", + // "client-name": "Mobile Safari", + // "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10B143", + // "bot": "" + // }, + // "message": { + // "headers": { + // "message-id": "20130821005614.19826.35976@samples.mailgun.org" + // } // }, - // "RecordType": "SpamComplaint", - // "ID": 42, - // "Type": "SpamComplaint", - // "TypeCode": 100001, - // "Name": "Spam complaint", - // "Tag": "Test", - // "MessageID": "00000000-0000-0000-0000-000000000000", - // "ServerID": 1234, - // "MessageStream": "outbound", - // "Description": "The subscriber explicitly marked this message as spam.", - // "Details": "Test spam complaint details", - // "Email": "john@example.com", - // "From": "sender@example.com", - // "BouncedAt": "2021-02-21T16:34:52Z", - // "DumpAvailable": true, - // "Inactive": true, - // "CanActivate": false, - // "Subject": "Test subject", - // "Content": "Test content" // } private function processSpamComplaint() { @@ -295,16 +349,16 @@ class ProcessMailgunWebhook implements ShouldQueue $this->invitation->save(); $spam = new EmailSpam( - $this->request['Tag'], + $this->request['tags'], $this->request['From'], - $this->request['MessageID'] + $this->request['message']['headers']['message-id'] ); LightLogs::create($spam)->send(); $data = array_merge($this->request, ['history' => $this->fetchMessage()]); - $sl = $this->getSystemLog($this->request['MessageID']); + $sl = $this->getSystemLog($this->request['message']['headers']['message-id']); if ($sl) { $this->updateSystemLog($sl, $data); @@ -370,28 +424,30 @@ class ProcessMailgunWebhook implements ShouldQueue private function fetchMessage(): array { - if (strlen($this->request['MessageID']) < 1) { + if (strlen($this->request['message']['headers']['message-id']) < 1) { return $this->default_response; } try { - $postmark = new PostmarkClient(config('services.postmark.token')); - $messageDetail = $postmark->getOutboundMessageDetails($this->request['MessageID']); + $mailgun = new Mailgun(config('services.mailgun.token'), config('services.mailgun.endpoint')); + $messageDetail = $mailgun->messages()->show($this->request['message']['headers']['message-id']); - $recipients = collect($messageDetail['recipients'])->flatten()->implode(','); - $subject = $messageDetail->subject ?? ''; + $recipients = collect($messageDetail->getRecipients())->flatten()->implode(','); + $subject = $messageDetail->getSubject() ?? ''; - $events = collect($messageDetail->messageevents)->map(function ($event) { + $events = collect($mailgun->events()->get(config('services.mailgun.domain'), [ + "message-id" => $this->request['message']['headers']['message-id'], + ])->getItems())->map(function ($event) { return [ - 'bounce_id' => $event?->Details?->BounceID ?? '', - 'recipient' => $event->Recipient ?? '', - 'status' => $event->Type ?? '', - 'delivery_message' => $event->Details->DeliveryMessage ?? $event->Details->Summary ?? '', - 'server' => $event->Details->DestinationServer ?? '', - 'server_ip' => $event->Details->DestinationIP ?? '', - 'date' => \Carbon\Carbon::parse($event->ReceivedAt)->format('Y-m-d H:i:s') ?? '', + 'bounce_id' => array_key_exists("id", $event) ? $event["id"] : '', + 'recipient' => array_key_exists("recipient", $event) ? $event["recipient"] : '', + 'status' => array_key_exists("delivery-status", $event) && array_key_exists("code", $event["delivery-status"]) ? $event["delivery-status"]["code"] : '', + 'delivery_message' => array_key_exists("delivery-status", $event) && array_key_exists("message", $event["delivery-status"]) ? $event["delivery-status"]["message"] : (array_key_exists("delivery-status", $event) && array_key_exists("description", $event["delivery-status"]) ? $event["delivery-status"]["description"] : ''), + 'server' => array_key_exists("delivery-status", $event) && array_key_exists("mx-host", $event["delivery-status"]) ? $event["delivery-status"]["mx-host"] : '', + 'server_ip' => array_key_exists("ip", $event) ? $event["ip"] : '', + 'date' => \Carbon\Carbon::parse($event["timestamp"])->format('Y-m-d H:i:s') ?? '', ]; })->toArray(); diff --git a/composer.json b/composer.json index 7a52c900c649..f34e970897f5 100644 --- a/composer.json +++ b/composer.json @@ -70,10 +70,12 @@ "league/fractal": "^0.20.0", "league/omnipay": "^3.1", "livewire/livewire": "^2.10", + "mailgun/mailgun-php": "^3.6", "microsoft/microsoft-graph": "^1.69", "mollie/mollie-api-php": "^2.36", "nelexa/zip": "^4.0", "nwidart/laravel-modules": "^10.0", + "nyholm/psr7": "^1.8", "omnipay/paypal": "^3.0", "payfast/payfast-php-sdk": "^1.1", "phpoffice/phpspreadsheet": "^1.29", @@ -92,7 +94,7 @@ "sprain/swiss-qr-bill": "^4.3", "square/square": "30.0.0.*", "stripe/stripe-php": "^12", - "symfony/http-client": "^6.0", + "symfony/http-client": "^7.0", "symfony/mailgun-mailer": "^6.1", "symfony/postmark-mailer": "^6.1", "turbo124/beacon": "^1.5", diff --git a/composer.lock b/composer.lock index 09d27aa2b06b..4c0450f960b0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ef5c36f71295ade916c3b7084642f0b4", + "content-hash": "a1427d5a8467c0c3dffac544c6da5f65", "packages": [ { "name": "afosto/yaac", @@ -6071,6 +6071,67 @@ ], "time": "2023-06-21T14:59:35+00:00" }, + { + "name": "mailgun/mailgun-php", + "version": "v3.6.3", + "source": { + "type": "git", + "url": "https://github.com/mailgun/mailgun-php.git", + "reference": "3dbdc2f220fa64e78e903477efa22858c72509be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mailgun/mailgun-php/zipball/3dbdc2f220fa64e78e903477efa22858c72509be", + "reference": "3dbdc2f220fa64e78e903477efa22858c72509be", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0", + "php-http/client-common": "^2.2.1", + "php-http/discovery": "^1.19", + "php-http/multipart-stream-builder": "^1.1.2", + "psr/http-client": "^1.0", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "nyholm/nsa": "^1.2.1", + "nyholm/psr7": "^1.3.1", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.7", + "symfony/http-client": "^5.4 || ^6.3" + }, + "suggest": { + "nyholm/psr7": "PSR-7 message implementation", + "symfony/http-client": "HTTP client" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Mailgun\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Travis Swientek", + "email": "travis@mailgunhq.com" + } + ], + "description": "The Mailgun SDK provides methods for all API functions.", + "support": { + "issues": "https://github.com/mailgun/mailgun-php/issues", + "source": "https://github.com/mailgun/mailgun-php/tree/v3.6.3" + }, + "time": "2023-12-01T10:04:01+00:00" + }, { "name": "markbaker/complex", "version": "3.0.2", @@ -8012,6 +8073,62 @@ "abandoned": "psr/http-factory", "time": "2023-04-14T14:16:17+00:00" }, + { + "name": "php-http/multipart-stream-builder", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/multipart-stream-builder.git", + "reference": "f5938fd135d9fa442cc297dc98481805acfe2b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/f5938fd135d9fa442cc297dc98481805acfe2b6a", + "reference": "f5938fd135d9fa442cc297dc98481805acfe2b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/discovery": "^1.15", + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "nyholm/psr7": "^1.0", + "php-http/message": "^1.5", + "php-http/message-factory": "^1.0.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Message\\MultipartStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "A builder class that help you create a multipart stream", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "multipart stream", + "stream" + ], + "support": { + "issues": "https://github.com/php-http/multipart-stream-builder/issues", + "source": "https://github.com/php-http/multipart-stream-builder/tree/1.3.0" + }, + "time": "2023-04-28T14:10:22+00:00" + }, { "name": "php-http/promise", "version": "1.2.1", @@ -11378,28 +11495,27 @@ }, { "name": "symfony/http-client", - "version": "v6.4.0", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "5c584530b77aa10ae216989ffc48b4bedc9c0b29" + "reference": "c3e90d09b3c45a5d47170e81a712d51c352cbc68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/5c584530b77aa10ae216989ffc48b4bedc9c0b29", - "reference": "5c584530b77aa10ae216989ffc48b4bedc9c0b29", + "url": "https://api.github.com/repos/symfony/http-client/zipball/c3e90d09b3c45a5d47170e81a712d51c352cbc68", + "reference": "c3e90d09b3c45a5d47170e81a712d51c352cbc68", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "^3", "symfony/service-contracts": "^2.5|^3" }, "conflict": { "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.3" + "symfony/http-foundation": "<6.4" }, "provide": { "php-http/async-client-implementation": "*", @@ -11416,11 +11532,11 @@ "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0" + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -11451,7 +11567,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.0" + "source": "https://github.com/symfony/http-client/tree/v7.0.0" }, "funding": [ { @@ -11467,7 +11583,7 @@ "type": "tidelift" } ], - "time": "2023-11-28T20:55:58+00:00" + "time": "2023-11-29T08:40:23+00:00" }, { "name": "symfony/http-client-contracts",