Giter VIP home page Giter VIP logo

laravel-postmark's Introduction

Postmark

Laravel Postmark

Latest Version on Packagist Software License Tests StyleCI Total Downloads

Postmark is the easiest and most reliable way to be sure your important transactional emails get to your customer's inbox.

Upgrading

Please see UPGRADE for details.

Installation

You can install the package via composer:

$ composer require coconutcraig/laravel-postmark

The package will automatically register itself.

Usage

Update your .env file by adding your server key and set your mail driver to postmark.

MAIL_MAILER=postmark
POSTMARK_TOKEN=YOUR-SERVER-KEY-HERE

That's it! The mail system continues to work the exact same way as before and you can switch out Postmark for any of the pre-packaged Laravel mail drivers (smtp, mailgun, log, etc...).

Remember, when using Postmark the sending address used in your emails must be a valid Sender Signature that you have already configured.

Postmark Templates

Notification

Postmark offers a fantastic templating service for you to utilize instead of maintaining your templates within your Laravel application. If you would like to take advantage of that, this package offers an extension on the base MailMessage provided out of the box with Laravel. Within a Laravel notification, you can do the following to start taking advantage of Postmark templates.

use CraigPaul\Mail\TemplatedMailMessage;

public function toMail($notifiable)
{
    return (new TemplatedMailMessage)
        ->identifier(8675309)
        ->include([
            'name' => 'Customer Name',
            'action_url' => 'https://example.com/login',
        ]);
}

Mailable

It is also possible to use templated notifications via an extension on the base Mailable provided out of the box with Laravel.

use CraigPaul\Mail\TemplatedMailable;
use Illuminate\Support\Facades\Mail;

$mailable = (new TemplatedMailable())
    ->identifier(8675309)
    ->include([
        'name' => 'Customer Name',
        'action_url' => 'https://example.com/login',
    ]);

Mail::to('[email protected]')->send($mailable);

You may also utilize an alias instead of the template identifier by using the ->alias() method in both cases.

Postmark Tags

If you rely on categorizing your outgoing emails using Tags in Postmark, you can simply add a header within your Mailable class's build method.

use Symfony\Component\Mailer\Header\TagHeader;
use Symfony\Component\Mime\Email;

public function build()
{
    $this->withSymfonyMessage(function (Email $message) {
        $message->getHeaders()->add(new TagHeader('value'))
    });
}

Postmark Metadata

Similar to tags, you can also include metadata by adding a header.

use Symfony\Component\Mailer\Header\MetadataHeader;
use Symfony\Component\Mime\Email;

public function build()
{
    $this->withSymfonyMessage(function (Email $message) {
        $message->getHeaders()->add(new MetadataHeader('field', 'value'));
        $message->getHeaders()->add(new MetadataHeader('another-field', 'another value'));
    });
}

In this case, the following object will be sent to Postmark as metadata.

{
    "field": "value",
    "another-field", "another value"
}

Postmark Servers

Out of the box, we determine the Postmark server you send to using a configuration variable set within the environment you have deployed to. This works for most use cases, but if you have the need or desire to determine the Postmark server at runtime, you can supply a header during the sending process.

use CraigPaul\Mail\PostmarkServerTokenHeader;
use Symfony\Component\Mime\Email;

public function build()
{
    $this->withSymfonyMessage(function (Email $message) {
        $message->getHeaders()->add(new PostmarkServerTokenHeader('POSTMARK_TOKEN'))
    });
}

Change log

Please see CHANGELOG for more information on what has changed recently.

Testing

$ composer test

Contributing

Please see CONTRIBUTING and CONDUCT for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

laravel-postmark's People

Contributors

coconutcraig avatar craigpaul avatar joostdebruijn avatar julesjanssen avatar mattyh88 avatar mbardelmeijer avatar mvdnbrk avatar sheldonreiff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

laravel-postmark's Issues

[3.x] Preparing for Symfony 6.0 & Laravel 9.0

Detailed description

The release of Laravel 9.0 is scheduled for January 2022 and it ships with Symfony 6.0. Swift Mailer is replaced by Symfony Mailer.

Relevant pages and PR's to have a look at:

Context

This project provide some features I like which are not present in the Symfony Postmark bridge, most notably sending mails via templates. It is not likely that the Symfony Postmark Bridge will implement this functionality in their project for obvious reasons, looking at a similar case for Mandrill (symfony/symfony#37612).

Possible implementation

The changes in Symfony 6.0 and Laravel 9.0 are not backwards compatible and require a serious rewrite of this project (for example run the tests in https://github.com/joostdebruijn/laravel-postmark/tree/3.x). It may be interesting to have a look at the Symfony Postmark Bridge (https://github.com/symfony/postmark-mailer) and extend it where necessary to achieve feature parity with the module at this moment. This reduces maintance on this module while keeping the extra functionality.

Of course I'm very happy to help, but before spending time on this I would like to know what the maintainer of this package thinks of the upcoming changes and the future of this project.

Your environment

Include as many relevant details about the environment you experienced the bug in and how to reproduce it.

  • Laravel Postmark Version: 3.0.0?
  • Laravel Version: >= 9.0.0
  • PHP Version: >= 8.0.0

Returning the Postmark Message ID

This is either a question or an enhancement request, or maybe both 😄

I would like to know the message ID of the email that was just sent.

I’m working on a site where I want our admin users to be able to see the delivery status of emails for each user. Using the webhook I can get a message ID, but I don’t have anyway of matching it up with the sent email.

Example

An email is sent to someone using Postmark, and the message ID is recoreded in the database. An admin on our site can see that the email was sent, but has not yet been delivered. When there’s a response, the message is updated, so that if it’s bounced (or other), the admin can manually get in contact with the person.

Context

Any developer that wants to take advantage of the tracking from start-to-finish would find this useful. Useful for important emails (such as legal, buying/selling, recruitment).

Possible implementation

Just looking at the code here and in Laravel, it looks like the response is either void or $this->numberOfRecipients($message), so I don’t know how messy it’ll be to get the message ID from the response.

My environment

  • Laravel Postmark Version: 2.6.0
  • Laravel Version: 5.7.28*
  • PHP Version: 7.3.10

* Yes I know it’s on my list.

Github actions

I think we can remove travis and move to Github actions.

Do we still need scrutinzer? Does this really add value?
We could move this to a static analyzer like phpstan or psalm (and run with a GH action).

What do you think @craigpaul ?

Laravel 7 uses MAIL_MAILER instead of MAIL_DRIVER

Detailed description

After updating my Laravel app to v7, postmark stopped working. I kept on receiving error code "550 5.7.1". It took me a long time to find where the catch was, as I moved my app to a fresh server at the same time.

It turned out to be a small detail in the .ENV file, as Laravel 7.0 uses MAIL_MAILER instead of MAIL_DRIVER to select the mailing protocol.

Possible implementation

Please update your documentation (see screenshot below) so that it mentions also the MAIL_MAILER for Laravel v7 (and higher) option for configuring your postmark driver.

image

[3.x] Better exceptions

Postmark has different response codes when you are sending messages.

For example a 422 response code is returned when you try to send to a recipient that has been marked as inactive.
With the current implementation all exceptions on Postmark's end leads to a GuzzleHttp\Exception\ClientException.

I am proposing a custom exception which you can catch.
In my use case I can ignore a 422 exception, while a 429 / Rate Limit Exceeded can be catched and I am able to try to send to message again later.

See https://postmarkapp.com/developer/api/overview#response-codes

Stale branches

Think we can remove these stale branches from the repo:

  • postmark-template-support
  • templates

Default branch

@craigpaul I suggest you should set the default branch to 2.x while we are working on the main branch for the upcoming 3.x release.

Otherwise people may refer to changes we have made which are not released yet.

Too much data being rendered in `template.blade.php` view.

When I attempted to use this library to send an email using a template, I noticed the email body was always empty. After tracing around the code, I found that the output from the postmark::template view (https://github.com/craigpaul/laravel-postmark/blob/5481b9ad178fddc919fa717c9ae25eb7cc6b72ce/resources/views/template.blade.php) was always empty and I never received any feedback in output (logs, console etc). Further digging and I discovered that the data was getting into the render function, however, the output was always empty.

After some more poking around I ended up in Illuminate\View\View::gatherData(). There is a line that adds "shared" data of __env and app. Removing these allows the template to render. It seems there is just too much data in these array fields.

To solve the problem, I paired down the data by changing the template from:

@json($__data)

to:

{
    "id": @json($id),
    "alias": @json($alias),
    "model": @json($model)
}

I am unclear why there is so much data, but, at least this solves the problem. For reference, I am extending PostmarkTemplateMailable and overloading the __construct() and build() methods which is being called via

Mail::to("[email protected]")
    ->send(new OrderReceived($order))
;

Here is my Mailable class for reference

<?php

namespace App\Mail;

use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use Coconuts\Mail\PostmarkTemplateMailable;

class OrderReceived extends PostmarkTemplateMailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    /**
     * The order instance.
     *
     * @var \App\Models\Order
     */
    protected $order;

    /**
     * Create a new message instance.
     *
     * @param  \App\Models\Order  $order
     * @return void
     */
    public function __construct(Order $order)
    {
        parent::__construct();
        $this->order = $order;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this
            ->afterCommit()
            ->id(12345)
            ->include([
                "product_url" => "product_url_Value",
                "product_name" => "product_name_Value",
                "name" => "name_Value",
                "action_url" => "action_url_Value",
                "support_email" => "support_email_Value",
                "company_name" => "company_name_Value",
                "company_address" => "company_address_Value",
            ])
        ;
    }
}

[3.x] No ability anymore to pass guzzle options in 3.x

Detailed description

Add ability back to override guzzle options like bigger timeouts in config/postmark or at other location.

Context

Sometimes you need bigger timeout or more control about guzzle. That's not possible anymore because of the changes in 3.x

Possible implementation

check for guzzle config like other implementations in mailmanager already do or like you did before in 2.x

Batch Email Sending

Postmark has an option for Batch Email sending.

It would be really good if you can add this feature to v3 so we can use Laravel 8 Mailer for our Newsletters.

Testing Against Multiple Versions

Now that we are supporting Laravel 5.5 - 5.7 on v2.3.*, I think it would be best if Travis CI was running tests against each version of Laravel with the appropriate PHP version. Right now it is only going to test against the latest version of laravel/framework on each specified PHP version.

Example Implementations / Ideas:

Travis Configuration for Laravel Package Testing
Testing Multiple Dependency Versions with Travis CI
Laravel Package Development: Testing Against Multiple Laravel Versions

catch postmark message id in an event listener when message is sent

I'm using this package to send my emails via Postmark (it already works), but I need to store the postmark message ID into DB.
I have an event listener like this, but headers only contains "from","to" and "subject"...

public function handle(MessageSent $event)
{
if (isset($event->data['booking'])) {
$booking = $event->data['booking'];
$messageId = $event->message->getHeaders()->get('x-pm-message-id')?->getValue() ?? $event->message->getHeaders()->get('message-id')->getName();
$booking->mailIdentifiers()->create(['message_identifier' => $messageId]);
}
}

On my postmarks' account looks like this:
image

Send templates based on Mailable class

Detailed description

It would be nice to be able to send mails with a template based on the Mailable class. Now it is only possible to send mails based on templates via Notifications.

Context

I don't want to use the more advanced Notifications-stuff, but I would like to use the Mail functionality with Postmark templates.

Possible implementation

It would be nice if a template and data could be provided in build() of a Mailable.

Your environment

Include as many relevant details about the environment you experienced the bug in and how to reproduce it.

  • Laravel Postmark Version: 2.10.0
  • Laravel Version: 8.45.0
  • PHP Version: 8.0.7

No hint path defined for [postmark]

error.txt
The error is :
No hint path defined for [postmark].

Integration Method using: MailMessage with notifiable in Laravel

Detailed description

I am trying to send an email from existing implementation of laravel notification using postmark email template method.

toMail() which extends by Notification
image

call from controller using user->notify(with Notification class)
image

Context

Why is this change important to you? How would you use it?

How can it benefit other users?

Possible implementation

Not obligatory, but suggest an idea for implementing addition or change.

In Local environment

Include as many relevant details about the environment you experienced the bug in and how to reproduce it.

  • Laravel Postmark Version: v2.11.2
  • Laravel Version: 6.20.44
  • PHP Version: 7.4.0

Typed property error on 'id' when using alias instead of identifier

Detailed description

I encoutered this bug when creating a new TemplatedMailMessage and using the 'alias' directive instead of using an identifier. My code looks like this:

$message = (new TemplatedMailMessage())
            ->from(
                config('mail.from.address'),
                config('mail.from.name'))
            ->alias($this->templateId)
            ->include($this->templateModel($notifiable));

In the 2.x versions of your package this worked. But in the 3.x version it throws this error:

Error: Typed property CraigPaul\Mail\TemplatedMailMessage::$id must not be accessed before initialization in /.../vendor/coconutcraig/laravel-postmark/src/TemplatedMailMessage.php:27

However, when adding the "identifier" with value '0' it does work:

$message = (new TemplatedMailMessage())
            ->from(
                config('mail.from.address'),
                config('mail.from.name'))
            ->identifier(0)
            ->alias($this->templateId)
            ->include($this->templateModel($notifiable));

Possible implementation

Change the id property on the TemplatedMailMessage class to this:

protected ?int $id = null;

Your environment

  • Laravel Postmark Version: 3.0.1
  • Laravel Version: 9.1.0
  • PHP Version: 8.0

Templated email keeps appending withTemplate

Detailed description

    public function getApiEndpoint(Swift_Mime_SimpleMessage $message)
    {
        if ($this->templated($message)) {
            $this->apiEndpoint .= '/withTemplate';
        }
        return $this->apiEndpoint;
    }

This keeps appending '/withTemplate' to the api endpoint statefully, resulting in requests like

POST https://api.postmarkapp.com/email/withTemplate/withTemplate/withTemplate/withTemplate/withTemplate/withTemplate/withTemplate resulted in a 404 Not Found response.

Your environment

  • Laravel Postmark Version: 2.7.0

MailMessage Template Laravel 7 Unexpected response

I'm trying to use the MailMessage class to send in postmark template, the configuration is correct but I receive a json as a message to my mailbox.

this is my implementation

image

this is my response

{"__env":{},"app":{"contextual":[]},"id":18999584,"alias":"user-c=
onfirmation","model":{"name":"Usuario","product_name":"Maquillate=
.com","action_url":"https://dev.maquillate.com/confirmation","=
login_url":"https://dev.maquillate.com/login","username":"Usua=
rio"},"__laravel_notification_id":"02bb8627-5d08-485f-9122-4e7091=
79f8c1","__laravel_notification":"App\Notifications\UserConfirm=
ation","__laravel_notification_queued":false,"message":{}}=0D=0A

image

Your environment

Include as many relevant details about the environment you experienced the bug in and how to reproduce it.

  • Laravel Postmark Version: 2.8
  • Laravel Version: 7.x
  • PHP Version: 7.4

$config['message_stream_id'] not defined?

$config['message_stream_id'] is not defined, and so it triggers a PHP error - maybe it should be $config['message_stream_id'] ?? '', or something similar. Am I meant to be defining message_stream_id somewhere? It feels like a default (i.e. not defining it) should be ok.

  • Laravel Postmark Version: 3.0.4
  • Laravel Version: 10.15.0
  • PHP Version: 8.1.19

[3.x] Upgrade Guide

We just tagged the first release candidate for Laravel 9.0 / Symfony 6.0 support, but before a full-fledged release can take place we will need to write up a thorough upgrade guide entry as this is a fairly big breaking change, hopefully, most of the changes are handled as part of the Laravel upgrade though.

Message stream Id on postmark Factory

Detailed description

I have a question We are using this package to send emails via postmark but when we try and send a email at a particular time it seems to be failing with Undefined array key "message_stream_id"

Context

[2023-07-26 08:22:48] staging.ERROR: Undefined array key "message_stream_id" {"exception":"[object] (ErrorException(code: 0): Undefined array key "message_stream_id" at /home/forge/staging.solidarity-snp.datakrag.co.za/releases/2023-07-24-17-47-51/vendor/coconutcraig/laravel-postmark/src/PostmarkServiceProvider.php:24)
[stacktrace]

  • Laravel Postmark Version: 3.0.4
  • Laravel Version: 9.52
  • PHP Version: 8.1

How to capture exceptions inside postmark api

I am getting exceptions in my log for certain emails when people try resetting their password.

(see below) - Is there a recommended way of capturing those or avoiding these altogether?

Using latest version 2.4 with laravel 5.8.8

Message
Client error: `POST https://api.postmarkapp.com/email` resulted in a `422 Unprocessable Entity` response:
{"ErrorCode":406,"Message":"You tried to send to a recipient that has been marked as inactive.\nFound inactive addresses (truncated...)
Level
ERROR
Exception
```{
   "class": "GuzzleHttp\\Exception\\ClientException",
   "message": "Client error: `POST https:\/\/api.postmarkapp.com\/email` resulted in a `422 Unprocessable Entity` response:\n{\"ErrorCode\":406,\"Message\":\"You tried to send to a recipient that has been marked as inactive.\\nFound inactive addresses (truncated...)\n",
   "code": 422,
   "file": "\/home\/forge\/www.nanochipid.com\/releases\/20190414015353\/vendor\/guzzlehttp\/guzzle\/src\/Exception\/RequestException.php:113",
   "trace": [
...

Postmark webhooks

Postmark can call a webhook on several events.

I am currently maintaining this package to handle these:
https://github.com/mvdnbrk/laravel-postmark-webhooks

Spatie has also a package which does this for their mailcoach app:
https://github.com/spatie/laravel-mailcoach-postmark-feedback

As part of sending an email you probably want to get notified, now these are separate packages.
Maybe we can integrate these together, one package is easier to maintain and these two complement each other.

POSTMARK_SECRET not set.

At this point if someone forgets to set the POSTMARK_SECRET an exception of GuzzleHttp\Exception\ClientException will be thrown.

Client error: 'POST https://api.postmarkapp.com/email' resulted in a '422 Unprocessable Entity'

We should prevent this from happening by checking if the POSTMARK_SECRET was set.

Setting a custom Message-ID in header for email threading

I have a question,

I am trying to post a "Message-ID" parameter with the headers to the Postmark API so I can thread emails, I also need to post a "In-Reference-To" header parameter as well as "References". This is what I'm currently doing in the build() function of the mailable:

    $this->withSwiftMessage(function (\Swift_Message $message) {
        $msgId = $message->getHeaders()->get('Message-ID');
        $msgId->setId('[email protected]');
        $message->getHeaders()->addTextHeader('X-PM-KeepID', 'true');
    });

When I dump the headers I can see the new Message-ID, but once it reaches Postmark, it doesn't change.

  • Laravel Version: 5.8
  • PHP Version: 7.3

Multiple Mailers with Different Streams

I'm trying to configure 2 mailers, one for postmark transactionals and another for broadcasts. in the following way, but when I try to send a notification using the "postmark-broadcast" it doesn't take use the message_stream_id of that mailer, instead uses the one from "postmark" mailer. Is this a bug, or am I doing something wrong?

'postmark' => [
  'transport' => 'postmark',
],
'postmark-broadcast' => [
  'transport' => 'postmark',
  'message_stream_id' => env('POSTMARK_BROADCAST_MESSAGE_STREAM', 'broadcast'),
],
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->mailer('postmark-broadcast')
            ->subject($this->company->name.": New Investor Update '".$this->investorUpdate->title."'")
            ->markdown('emails.new_investor_update', [
                'url' => route('investor-update', ['id' => $this->investorUpdate->hash, 'as' => $notifiable->routes['mail']]),
                'title' => $this->investorUpdate->title,
                'company' => $this->company->name,
                'creator' => $this->creator,
                'section_title' => $this->firstSection->title,
                'section_body' => $this->firstSection->text,
            ]);
    }

Ability to use different servers

Postmark uses the concept of servers.
Each server has its own api token.

At the moment there is only one way to set this api token, with POSTMARK_SECRET env variable.

If an app requires sending messages to different servers this can not be done easily.

We should add the ability to set the api token on the fly.

message_stream_id stopped working after last commit

Hello. In the last commit (called "Adds support for passing in Guzzle options to the Laravel HTTP client") following line has been added in src/PostmarkServiceProvider.php
$config = $this->app->make('config');

unfortunately this overwrites the value of existing $config parameter, therefore few lines further down, the "$config['message_stream_id']" is always null.
If you add dd($config) prior to line 22, you'll see the value of message_stream_id (if defined in mail.php). After line 23 it's undefined because the $config is overwritten.

https://github.com/craigpaul/laravel-postmark/blob/f7fa0aa150c65d513f58f14a693169d0eb2201e0/src/PostmarkServiceProvider.php

Could you please check? It's easily replicable.
Thanks!

Inline attachments not working

I am trying to add an inline attachment inside my blade email file.

$message->embed(public_path('img/mail/logo.png')) or $message->embed(asset('img/mail/logo.png')) but only what I got in final email is data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7

I also tried the official wildbit library recommended by Laravel doc (https://github.com/wildbit/swiftmailer-postmark) and that works fine. I need to continue using this package because I can get MessageID.

  • Laravel Postmark Version: v2.10.1
  • Laravel Version: 8.53.0
  • PHP Version: 7.4.16

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.