Webhook Postmark support: management of bounces, spam and unsubscription

I finished coding Webhook Postmark support.

Event webhooks (Bounce, Spam plain, SubscriptionChange) from Postmark to Mautic are supported with this change.

I’m using this code in production with Mautic 4.4.2 and it works like a charm.

I share here the portions of code to modify, I hope it will be useful to someone:

in file: /mautic/app/bundles/EmailBundle/Config/config.php

Replace:

'mautic.transport.postmark' => [
                'class' => 'Mautic\EmailBundle\Swiftmailer\Transport\PostmarkTransport',
                'serviceAlias' => 'swiftmailer.mailer.transport.%s',
                'methodCalls' => [
                    'setUsername' => ['%mautic.mailer_user%'],
                    'setPassword' => ['%mautic.mailer_password%'],
                ],
            ],

By:

'mautic.transport.postmark' => [
                'class' => 'Mautic\EmailBundle\Swiftmailer\Transport\PostmarkTransport',
                'serviceAlias' => 'swiftmailer.mailer.transport.%s',
                'arguments' => [
                    'mautic.email.model.transport_callback',
                    '%mautic.mailer_mailjet_sandbox%',
                    '%mautic.mailer_mailjet_sandbox_default_mail%',
                ],
                'methodCalls' => [
                    'setUsername' => ['%mautic.mailer_user%'],
                    'setPassword' => ['%mautic.mailer_password%'],
                ],
            ],

Add:

    'mailer_postmark_sandbox'              => false,
    'mailer_postmark_sandbox_default_mail' => null,

After:

    'mailer_mailjet_sandbox'              => false,
    'mailer_mailjet_sandbox_default_mail' => null,

** Replace all code in file: /mautic/app/bundles/EmailBundle/Swiftmailer/Transport/PostmarkTransport.php

By:

<?php

namespace Mautic\EmailBundle\Swiftmailer\Transport;

use Mautic\EmailBundle\Model\TransportCallback;
use Mautic\LeadBundle\Entity\DoNotContact;
use Symfony\Component\HttpFoundation\Request;

/**
 * Class PostmarkTransport.
 */
class PostmarkTransport extends \Swift_SmtpTransport  implements CallbackTransportInterface
{
    /**
     * @var bool
     */
    private $sandboxMode;

    /**
     * @var string
     */
    private $sandboxMail;

    /**
     * @var TransportCallback
     */
    private $transportCallback;

    /**
     * {@inheritdoc}
     */
    public function __construct(TransportCallback $transportCallback, $sandboxMode = false, $sandboxMail = '')
    {
        parent::__construct('smtp.postmarkapp.com', 587, 'tls');
        $this->setAuthMode('login');

        $this->setSandboxMode($sandboxMode);
        $this->setSandboxMail($sandboxMail);

        $this->transportCallback = $transportCallback;
    }

    /**
     * @param null $failedRecipients
     *
     * @return int|void
     *
     * @throws \Exception
     */
    public function send(\Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
    {
        // add leadIdHash to track this email
        if (isset($message->leadIdHash)) {
            // contact leadidHeash and email to be sure not applying email stat to bcc

            $message->getHeaders()->removeAll('X-MJ-CUSTOMID');

            $message->getHeaders()->addTextHeader('X-MJ-CUSTOMID', $message->leadIdHash.'-'.key($message->getTo()));
        }

        if ($this->isSandboxMode()) {
            $message->setSubject(key($message->getTo()).' - '.$message->getSubject());
            $message->setTo($this->getSandboxMail());
        }

        return parent::send($message, $failedRecipients);
    }


    /**
     * Returns a "transport" string to match the URL path /mailer/{transport}/callback.
     *
     * @return mixed
     */
    public function getCallbackPath()
    {
        return 'postmark';
    }

    /**
     * Handle response.
     *
     * @return mixed
     */
    public function processCallbackRequest(Request $request)
    {
        $postData = json_decode($request->getContent(), true);

        if (is_array($postData)) {

            if (!in_array($postData['RecordType'], ['Bounce', 'SpamComplaint', 'SubscriptionChange'])) {
                return false;
            }

            if ('Bounce' === $postData['RecordType']) {
                $reason = $postData['Name'].': '.$postData['Description'];
                $type   = DoNotContact::BOUNCED;
                    echo "bounce";
            } elseif ('SpamComplaint' === $postData['RecordType']) {
                $reason = $postData['Name'].': '.$postData['Description'];
                $type   = DoNotContact::UNSUBSCRIBED;
            } elseif ('SubscriptionChange' === $postData['RecordType']) {
                $reason = 'User unsubscribed';
                $type   = DoNotContact::UNSUBSCRIBED;
            } else {
                return false;
            }

            $this->transportCallback->addFailureByAddress($postData['Email'], $reason, $type);

        } else {
            // respone must be an array
            return null;
        }
    }

    /**
     * @return bool
     */
    private function isSandboxMode()
    {
        return $this->sandboxMode;
    }

    /**
     * @param bool $sandboxMode
     */
    private function setSandboxMode($sandboxMode)
    {
        $this->sandboxMode = $sandboxMode;
    }

    /**
     * @return string
     */
    private function getSandboxMail()
    {
        return $this->sandboxMail;
    }

    /**
     * @param string $sandboxMail
     */
    private function setSandboxMail($sandboxMail)
    {
        $this->sandboxMail = $sandboxMail;
    }
}
1 Like