Amazon SES経由のバウンスメールをWebアプリで受け取る

こんにちは、[MYLOPS](https://mylops.jp/?utm_source=qiita&utm_medium=social&utm_campaign=200325&utm_content=q_tr)のエンジニアの後河内です。
当社では、WEBアプリのメールサービスに[Amazon SES](https://aws.amazon.com/jp/ses/)を利用することが多いです。
以前、こちらに、[Amazon SESとPostfixの連携でハマったポイント](https://qiita.com/ushirog/items/bd0e890e22b01c474352)といった記事も書きました。

さて、今回ですが、CakePHP2で作成したWEBアプリでSES経由でメール送っていたのですが、その際にバウンスメールになったものをWEBアプリ側で受け取りその結果を管理者画面に表示できないかという相談を受け調査しましたので、その調査結果などをまとめていきたい思います。

SESからのメールのバウンスをWEBアプリで扱うにはどうしたらいいのか調べたところ、Amazon SNS（[Amazon Simple Notification Service とは](https://docs.aws.amazon.com/ja_jp/sns/latest/dg/welcome.html)）を利用すれば実現できそうなことがわかりました。

まずSES側の設定でバウンス(Bounce)や苦情(Complaint)など、SNSのトピックを設定することができます。（[Amazon SNS の Amazon SES 通知の設定](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/configure-sns-notifications.html)）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99689/f11ffcc6-510a-2e53-89cc-0b46d6e8e152.png)


# バウンス情報を設定したEメールに通知する

SNSですがトピックのサブスクリプションで通知方法や通知先を設定できます。
通知方法ですか例えばEメールといった設定ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99689/806c469e-85d4-0085-cf5e-b41904e2d983.png)


Eメールを設定した場合、バウンスメールがあると以下のようなメールがバウンスメールの送り先に送られてきます。

```
{"notificationType":"Bounce","bounce":
{"bounceType":"Permanent","bounceSubType":"General","bouncedRecipients":
[{"emailAddress":"bounce@simulator.amazonses.com","action":"failed",
"status":"5.1.1","diagnosticCode":"smtp; 550 5.1.1 user unknown"}],
"timestamp":"2020-02-26T11:53:43.465Z","feedbackId":"0000000-00000-000-0000-000-000000-000000",
"remoteMtaIp":"X.XX.XXX.XXX","reportingMTA":"dsn; a48-37.smtp-out.amazonses.com"},
"mail":{"timestamp":"2020-02-26T11:53:42.000Z","source":"info@hogehoge.jp",
"sourceArn":"arn:aws:ses:us-east-1:0000000000000:identity/hogehoge.jp",
"sourceIp":"XX.XXX.XXX.XXX","sendingAccountId":"0000000000",
"messageId":"0000000-000000-000000-000000-000000-000000","destination":["bounce@simulator.amazonses.com"]}}
```

**"emailAddress":"bounce@simulator.amazonses.com"**

上記の部分がバウンスメールが戻ってきたメールアドレスになります

ちなみに今回バウンスメールのテストに使ったアドレスですが、AWSが準備しているバウンステストのメールアドレスを利用してます。（[Amazon SES での E メール送信のテスト - Amazon Simple Email Service](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-email-simulator.html)）


SNSを利用すればバウンスメールになったメールアドレス情報を特定のメールアドレスに送るといったことは非常に簡単にできました。
ただし今回の要件は、WEBアプリの画面に表示する必要がありました。
SNSの送信方式を探したところ、SNSのサブスクリプションにはEメール以外に、https(http)に情報をPOSTするといった機能もありましたのでこちらを利用することにしました。


# SNSでバウンス情報をPOSTする

バウンス情報をPOSTするには、SNSのサブスクリプトの登録で、プロトコルにHTTPSを選び、エンドポイントにPOST先のURLを設定するだけでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99689/8164673c-571a-fcd2-0764-7e074f0d9a07.png)


# POSTされたバウンス情報を受け取る

POSTされたバウンス情報は当初は以下のようなPHPのコードで受け取りを考えました。

```
$postData = file_get_contents('php://input');
$data = json_decode($postData);
```

CakePHP2を利用せずに、PHPのみでコーディングした場合は受け取りができたのですが、CakePHPのコントローラに上記のコードを書いても受け取りすることができませんでした。
受け取ったデータの中身を調べてみても**空（NULL）**となってしまいました。

# CakePHP2でREST API
CakePHP2でPOSTされた情報の受け取り方を調査したところCakePHP2でREST APIを実装すればPOSTしたデータを受け取ることができそうでした。

REST設定に参考にしたページ

- [REST - 2.x](https://book.cakephp.org/2/ja/development/rest.html)
- [CakePHP2 REST 設定](https://qiita.com/aokazu/items/bae64fe4decc2cd2c933)

実装方法ですがroutes.phpに以下の実装を追加しました。

```routes.php
Router::mapResources('hogehogeapis');
Router::parseExtensions();
```

そしてコントローラー（POSTされた情報を扱うコントローラ）に以下の部分を追加しました。


```hogehogeapisController.php
public function beforeFilter()
{
$this->response->header('Access-Control-Allow-Origin: *');
$this->response->header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
$this->response->header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
    }


if ($this->request->is('post')) {
$data = $this->request->input('json_decode',true);

```

上記の設定をしたところPOSTされたデータを受け取ることができました。

ちなみにバウンスメール情報が送られるようにするには、一度POST先としてSNSのPOST先としての認証をする必要があります。
認証するには下記の方法でやるようですが、最初にPOSTされたデータの中身を確認し、SubscribeURLに手動でアクセスという形で認証をおこないました。

[受信者が HTTP/S エンドポイントの場合のシステム間メッセージングに Amazon SNS を使用する](https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-http-https-endpoint-as-subscriber.html)

## バウンス情報からメールアドレス部分の抜き出し

HTTPSにPOSTされる形式ですがJSON形式になります。
JSON形式からメールの部分を抜き出すために以下のようなコード追加しました。

```hogehogeapisController.php
$data = $this->request->input('json_decode',true);
$bounce=json_decode($data['Message'],true);
	
$bounce['bounce']['bouncedRecipients'][0]['emailAddress'],true));
```

ちなみに上記のコードでは受け取ったデータのJSON形式の文字列を配列化してさらに、配列化してます。

このようにすることで、メールアドレスを受け取ることができました。
上記までいけば、DBに格納し管理画面に表示することができそうでした。


今回は調査して機能を実装するにあたっての方針を見つけらました。
実装していく中で新たな問題点等やノウハウが見つかりましたら、別の記事として書いていけたらなと思っております。