# SMTP relay

SMTP relay cho phép mọi ứng dụng biết gửi thư qua SMTP dùng Hạ Tầng: WordPress, Laravel, Django, Rails, Spring Boot, một server Postfix, thậm chí máy in. Thư đi qua đúng các bước kiểm tra như `POST /emails`, nên cũng hiện ở **Email → Email** với trạng thái, sự kiện và webhook.

## Trước khi bắt đầu

- Một tên miền đã xác minh cho địa chỉ `From`. Xem [Tên miền](https://hatang.dev/vi/docs/guides/email/domains.md).
- API key có phạm vi `email:send`. Key này chính là mật khẩu SMTP.

## Thông tin kết nối

| Thiết lập | Giá trị |
|---|---|
| Host | `smtp.hatang.dev` |
| Cổng | `5870` |
| Mã hoá | STARTTLS (kết nối bắt đầu ở dạng thường rồi nâng cấp bằng lệnh `STARTTLS`) |
| Tên đăng nhập | `hatang` |
| Mật khẩu | API key của bạn, `ht_...` |
| Xác thực | `PLAIN` hoặc `LOGIN` |

Các giá trị này cũng có ở **Cài đặt email → Thông tin kết nối** trong dashboard.

> **Caution: Cổng 5870 dùng STARTTLS, không phải TLS ngầm định**
>
> Đừng bật "SSL", "implicit TLS" hay `secure: true` cho cổng 5870: client sẽ bắt tay TLS trước khi server chào và bị treo cho tới khi hết thời gian chờ. Hãy chọn STARTTLS, có ứng dụng gọi là "TLS". Server từ chối xác thực cho tới khi kết nối đã được nâng cấp bằng `STARTTLS`, nên API key không bao giờ đi qua mạng ở dạng rõ.

## Hạ Tầng đọc thư thế nào {#hatang-đọc-thư-thế-nào}

| Phần của thư | Hạ Tầng xử lý ra sao |
|---|---|
| Header `From` | Người gửi. Tên miền phải đã xác minh và thuộc doanh nghiệp của API key. Không có header `From` thì dùng envelope sender (`MAIL FROM`). |
| Header `To` và `Cc` | Người nhận `to` và `cc`. |
| Người nhận trong envelope (`RCPT TO`) không có trong `To` hay `Cc` | Gửi dưới dạng `bcc`. |
| `Reply-To`, `Subject`, phần HTML và văn bản, tệp đính kèm, ảnh nhúng | Giữ nguyên. |
| `Message-ID` | Giữ nguyên, nên các chuỗi thư trong hệ thống của bạn vẫn khớp. |
| Header bắt đầu bằng `X-` | Chuyển tiếp như header riêng, giống trường `headers` của API. Các header riêng khác bị bỏ. |
| `X-Tags` | Được đọc thành tag và không chuyển tiếp. Giá trị là một mảng JSON: `X-Tags: [{"name":"category","value":"welcome"}]`. Giá trị không phải JSON hợp lệ bị bỏ qua. |

> **Tip: Luôn điền `To`**
>
> Nếu thư chỉ có người nhận Bcc, Hạ Tầng chuyển người đầu tiên sang `To`, nơi mọi người nhận khác đều thấy. Hãy đặt ít nhất một địa chỉ trong header `To`.

## Giới hạn

| Giới hạn | Giá trị |
|---|---|
| Người nhận mỗi thư | 50 người nhận trong envelope (`RCPT TO`) |
| Kích thước thư | Thư trên 40 MB bị từ chối. |
| Đăng nhập sai | Sau 10 lần đăng nhập sai từ một địa chỉ IP, mọi lần đăng nhập từ địa chỉ đó bị từ chối cho tới khi đủ 15 phút kể từ lần sai cuối cùng. |
| Hạn mức ngày | Dùng chung với API: mỗi thư tính là một email. |

## Mã phản hồi SMTP

| Phản hồi | Mã mở rộng | Ý nghĩa | Cần làm gì |
|---|---|---|---|
| `250` | `2.0.0` | Đã nhận. Phần chữ kết thúc bằng `queued as <id email>`. | Không cần làm gì. |
| `530` | `5.7.0` | Cần đăng nhập. | Đăng nhập trước lệnh `MAIL FROM`. |
| `535` | `5.7.8` | API key sai hoặc đã bị thu hồi. | Kiểm tra mật khẩu. |
| `421` | `4.7.0` | Quá nhiều lần đăng nhập sai từ địa chỉ IP của bạn. | Sửa mật khẩu và chờ rồi mới thử lại. |
| `452` | `5.5.3` | Quá 50 người nhận. | Tách thư ra. |
| `550` | `5.7.1` | Tên miền của `From` chưa xác minh hoặc không thuộc doanh nghiệp, API key bị giới hạn vào tên miền khác, hoặc doanh nghiệp đang tạm dừng gửi. Phần chữ nói rõ lý do. | Xác minh tên miền hoặc sửa địa chỉ `From`. |
| `553` | `5.6.0` | Thư bị từ chối vì không hợp lệ, ví dụ header `From` sai. | Sửa thư. Gửi lại y nguyên sẽ lại lỗi. |
| `451` | `4.3.0` | Lỗi tạm thời, kể cả khi đã hết hạn mức ngày. | Thử lại sau. Các mail server như Postfix tự thử lại. |

## Kiểm tra kết nối

Kiểm tra STARTTLS hoạt động:

```bash
openssl s_client -starttls smtp -connect smtp.hatang.dev:5870 -crlf -quiet
```

Gửi thư thử bằng [swaks](https://github.com/jetmore/swaks):

```bash
swaks --server smtp.hatang.dev:5870 --tls \
  --auth PLAIN --auth-user hatang --auth-password "$HATANG_API_KEY" \
  --from hello@shop.vn --to ban@example.vn \
  --header "Subject: Thử SMTP" --body "Chạy rồi."
```

Hoặc bằng thư viện chuẩn của Python:

```python
import os
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "Shop <hello@shop.vn>"
msg["To"] = "ban@example.vn"
msg["Subject"] = "Thử SMTP"
msg["X-Tags"] = '[{"name":"category","value":"test"}]'
msg.set_content("Chạy rồi.")

with smtplib.SMTP("smtp.hatang.dev", 5870) as smtp:
    smtp.starttls()
    smtp.login("hatang", os.environ["HATANG_API_KEY"])
    smtp.send_message(msg)
```

## Cấu hình mẫu

### Nodemailer

```js
import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
  host: "smtp.hatang.dev",
  port: 5870,
  secure: false, // bắt buộc là false: cổng 5870 dùng STARTTLS
  requireTLS: true, // không gửi nếu không nâng cấp được STARTTLS
  auth: { user: "hatang", pass: process.env.HATANG_API_KEY },
});

const info = await transporter.sendMail({
  from: "Shop <hello@shop.vn>",
  to: "lan@example.vn",
  subject: "Chào mừng bạn đến với Shop",
  html: "<p>Chào mừng!</p>",
  text: "Chào mừng!",
  headers: { "X-Tags": JSON.stringify([{ name: "category", value: "welcome" }]) },
});
console.log(info.response); // 250 2.0.0 Ok: queued as <id email>
```

`secure: true` nghĩa là TLS ngầm định và không chạy trên cổng 5870.

### Laravel

Trong `.env`:

```bash
MAIL_MAILER=smtp
MAIL_HOST=smtp.hatang.dev
MAIL_PORT=5870
MAIL_USERNAME=hatang
MAIL_PASSWORD=ht_123...
MAIL_FROM_ADDRESS=hello@shop.vn
MAIL_FROM_NAME="Shop"
```

Mailer của Laravel tự nâng cấp kết nối bằng STARTTLS khi server hỗ trợ, và `smtp.hatang.dev` luôn hỗ trợ. Để trống `MAIL_SCHEME` hoặc đặt là `smtp`; đừng dùng `smtps`. Với Laravel 10 trở về trước, đặt `MAIL_ENCRYPTION=tls`.

### Django

Trong `settings.py`:

```python
import os

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.hatang.dev"
EMAIL_PORT = 5870
EMAIL_HOST_USER = "hatang"
EMAIL_HOST_PASSWORD = os.environ["HATANG_API_KEY"]
EMAIL_USE_TLS = True   # STARTTLS
EMAIL_USE_SSL = False  # TLS ngầm định: phải tắt
DEFAULT_FROM_EMAIL = "Shop <hello@shop.vn>"
```

### Ruby on Rails

Trong `config/environments/production.rb`:

```ruby
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address: "smtp.hatang.dev",
  port: 5870,
  user_name: "hatang",
  password: ENV.fetch("HATANG_API_KEY"),
  authentication: :plain,
  enable_starttls_auto: true
}
```

### Spring Boot

Trong `application.properties`:

```text
spring.mail.host=smtp.hatang.dev
spring.mail.port=5870
spring.mail.username=hatang
spring.mail.password=${HATANG_API_KEY}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
```

### WordPress (WP Mail SMTP)

Cài plugin [WP Mail SMTP](https://wordpress.org/plugins/wp-mail-smtp/), mở **WP Mail SMTP → Settings** và chọn mailer **Other SMTP**:

| Thiết lập | Giá trị |
|---|---|
| From Email | Một địa chỉ trên tên miền đã xác minh, ví dụ `hello@shop.vn`. Bật **Force From Email**. |
| SMTP Host | `smtp.hatang.dev` |
| Encryption | **TLS** (trong WP Mail SMTP, đây là STARTTLS) |
| SMTP Port | `5870` |
| Auto TLS | Bật |
| Authentication | Bật |
| SMTP Username | `hatang` |
| SMTP Password | API key của bạn |

Để không lưu key trong database, khai báo trong `wp-config.php`:

```php
define( 'WPMS_ON', true );
define( 'WPMS_MAILER', 'smtp' );
define( 'WPMS_SMTP_HOST', 'smtp.hatang.dev' );
define( 'WPMS_SMTP_PORT', 5870 );
define( 'WPMS_SSL', 'tls' );
define( 'WPMS_SMTP_AUTH', true );
define( 'WPMS_SMTP_USER', 'hatang' );
define( 'WPMS_SMTP_PASS', 'ht_123...' );
define( 'WPMS_MAIL_FROM', 'hello@shop.vn' );
define( 'WPMS_MAIL_FROM_FORCE', true );
```

Sau đó gửi thử ở **WP Mail SMTP → Tools → Email Test**.

### PHPMailer

```php
<?php
use PHPMailer\PHPMailer\PHPMailer;

$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.hatang.dev';
$mail->Port = 5870;
$mail->SMTPAuth = true;
$mail->Username = 'hatang';
$mail->Password = getenv('HATANG_API_KEY');
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->CharSet = 'UTF-8';

$mail->setFrom('hello@shop.vn', 'Shop');
$mail->addAddress('lan@example.vn');
$mail->Subject = 'Chào mừng bạn đến với Shop';
$mail->isHTML(true);
$mail->Body = '<p>Chào mừng!</p>';
$mail->AltBody = 'Chào mừng!';
$mail->addCustomHeader('X-Tags', '[{"name":"category","value":"welcome"}]');
$mail->send();
```

### Postfix relayhost

Để mọi thư từ một server đi qua Hạ Tầng, đặt Hạ Tầng làm relay host trong `/etc/postfix/main.cf`:

```text
relayhost = [smtp.hatang.dev]:5870
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
```

Ghi thông tin đăng nhập vào `/etc/postfix/sasl_passwd`:

```text
[smtp.hatang.dev]:5870 hatang:ht_123...
```

Sau đó khoá quyền tệp và nạp lại:

```bash
sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
sudo systemctl reload postfix
```

Chỉ thư có địa chỉ `From` trên tên miền đã xác minh mới được nhận. Hãy đổi người gửi của thư hệ thống (ví dụ bằng `smtp_generic_maps`) để `root@localhost` thành một địa chỉ trên tên miền của bạn.

## Bước tiếp theo

- [Gửi email](https://hatang.dev/vi/docs/guides/email/sending.md): REST API, có gửi theo lô, hẹn giờ và idempotency.
- [Theo dõi mở và nhấp](https://hatang.dev/vi/docs/guides/email/tracking.md): theo dõi cũng áp dụng cho thư gửi qua SMTP.
- [Danh sách chặn và thư trả lại](https://hatang.dev/vi/docs/guides/email/suppressions-and-bounces.md)

Source: https://hatang.dev/vi/docs/guides/email/smtp
