交易邮件 API 集成指南:注册验证、密码重置与通知系统

交易邮件(Transactional Email)是由用户操作触发的个性化邮件,如注册验证、密码重置、订单确认等。与营销邮件不同,交易邮件是用户"预期"收到的,因此打开率更高(通常在 70-90%),对送达率的要求也更高。

一、交易邮件 vs 营销邮件

特性 交易邮件 营销邮件
触发方式 用户操作触发 定时批量发送
个性化程度 高度个性化 中度个性化
打开率 70-90% 20-30%
发送频率 按需 定期
退订要求 不应包含营销退订链接 必须包含退订链接
送达要求 必须送达 尽量送达

二、常见交易邮件类型

2.1 注册验证

// 注册验证邮件 - 生成验证令牌
const crypto = require('crypto');

function generateVerificationToken(userId) {
  const token = crypto.randomBytes(32).toString('hex');
  const expiresAt = Date.now() + 24 * 60 * 60 * 1000; // 24 小时有效
  
  // 保存到数据库
  saveToken({ userId, token, expiresAt, type: 'email_verification' });
  
  return token;
}

// 发送验证邮件
async function sendVerificationEmail(user) {
  const token = generateVerificationToken(user.id);
  const verifyUrl = `https://yourdomain.com/verify-email?token=${token}`;
  
  await emailService.send({
    to: user.email,
    template: 'email-verification',
    data: {
      username: user.name,
      verifyUrl,
      expiresIn: '24 小时',
    },
  });
}

2.2 密码重置

// 密码重置流程
async function requestPasswordReset(email) {
  const user = await findUserByEmail(email);
  if (!user) {
    // 不暴露用户是否存在(安全考虑)
    return { success: true };
  }
  
  const token = crypto.randomBytes(32).toString('hex');
  const expiresAt = Date.now() + 60 * 60 * 1000; // 1 小时有效
  
  await saveToken({ userId: user.id, token, expiresAt, type: 'password_reset' });
  
  const resetUrl = `https://yourdomain.com/reset-password?token=${token}`;
  
  await emailService.send({
    to: email,
    template: 'password-reset',
    data: {
      username: user.name,
      resetUrl,
      expiresIn: '1 小时',
      ipAddress: request.ip,
    },
  });
  
  return { success: true };
}

2.3 订单通知

// 订单确认邮件
async function sendOrderConfirmation(order) {
  await emailService.send({
    to: order.customerEmail,
    template: 'order-confirmation',
    data: {
      orderNumber: order.number,
      customerName: order.customerName,
      items: order.items.map(item => ({
        name: item.name,
        quantity: item.quantity,
        price: formatPrice(item.price),
      })),
      total: formatPrice(order.total),
      estimatedDelivery: order.estimatedDelivery,
      trackingUrl: order.trackingUrl,
    },
  });
}

// 发货通知
async function sendShippingNotification(order) {
  await emailService.send({
    to: order.customerEmail,
    template: 'shipping-notification',
    data: {
      orderNumber: order.number,
      trackingNumber: order.trackingNumber,
      trackingUrl: order.trackingUrl,
      carrier: order.carrier,
      estimatedDelivery: order.estimatedDelivery,
    },
  });
}

三、邮件模板管理

3.1 模板设计原则

  1. 每个场景一个模板:注册验证、密码重置、订单确认使用不同模板
  2. 模板参数化:使用占位符,避免硬编码
  3. 多语言支持:模板应支持国际化
  4. 响应式设计:适配移动端阅读
  5. 纯文本版本:同时提供 HTML 和纯文本版本

3.2 React Email 模板(Resend)

// Email 模板 - React Email
import { Html, Body, Container, Text, Link, Heading } from '@react-email/components';

export default function VerificationEmail({ username, verifyUrl }) {
  return (
    <Html>
      <Body style={{ fontFamily: 'Arial, sans-serif' }}>
        <Container>
          <Heading>验证您的邮箱</Heading>
          <Text>你好 {username},</Text>
          <Text>感谢您注册!请点击下方按钮验证您的邮箱地址:</Text>
          <Link href={verifyUrl} style={{
            display: 'inline-block',
            padding: '12px 24px',
            background: '#0070f3',
            color: 'white',
            borderRadius: '5px',
          }}>
            验证邮箱
          </Link>
          <Text style={{ color: '#666', fontSize: '12px' }}>
            此链接 24 小时内有效。如果您没有注册,请忽略此邮件。
          </Text>
        </Container>
      </Body>
    </Html>
  );
}

四、系统设计

4.1 邮件队列架构

架构设计:
1. 应用服务 → 消息队列(Redis/RabbitMQ)→ 邮件 Worker → 邮件 API
                                                      ↓
                                              日志与监控

优点:
- 异步处理,不阻塞用户请求
- 重试机制,失败自动重试
- 限速控制,避免触发服务商限制
- 可水平扩展 Worker 数量

4.2 邮件发送服务

// 邮件服务抽象层
class EmailService {
  constructor(provider) {
    this.provider = provider; // SendGrid / Mailgun / Resend
  }
  
  async send({ to, template, data }) {
    const email = await this.buildEmail(template, data);
    
    try {
      const result = await this.provider.send({
        to,
        from: '[email protected]',
        subject: email.subject,
        html: email.html,
        text: email.text,
        tags: [template], // 用于追踪
      });
      
      await this.logSuccess({ to, template, messageId: result.id });
      return result;
    } catch (error) {
      await this.logError({ to, template, error: error.message });
      throw error;
    }
  }
}

五、安全与防滥用

5.1 防止邮件轰炸

// 速率限制
const rateLimit = new Map();

function checkRateLimit(email, type) {
  const key = `${email}:${type}`;
  const now = Date.now();
  const windowMs = 60 * 1000; // 1 分钟窗口
  const maxAttempts = 3;     // 最多 3 次
  
  const attempts = rateLimit.get(key) || [];
  const recentAttempts = attempts.filter(t => now - t < windowMs);
  
  if (recentAttempts.length >= maxAttempts) {
    return false; // 超出限制
  }
  
  recentAttempts.push(now);
  rateLimit.set(key, recentAttempts);
  return true;
}

5.2 令牌安全

  • 使用安全的随机数生成器(crypto.randomBytes
  • 设置合理的过期时间
  • 令牌使用后立即失效
  • 记录失败尝试次数

六、监控与告警

6.1 关键监控指标

指标 告警阈值 说明
发送失败率 > 3% 邮件被拒绝或超时
延迟 > 5 分钟 邮件发送到送达的延迟
退信率 > 2% 邮箱地址无效
队列积压 > 1000 邮件队列堆积

6.2 日志记录

// 邮件日志结构
{
  messageId: 'abc123',
  to: '[email protected]',
  template: 'password-reset',
  status: 'delivered',  // sent | delivered | opened | bounced | failed
  timestamp: '2026-07-18T10:30:00Z',
  latency: 350,          // 毫秒
  provider: 'sendgrid',
  error: null,
}

七、总结

交易邮件是网站与用户沟通的命脉。设计交易邮件系统时,可靠性和送达率是首要考量。使用成熟的第三方邮件服务(Resend/SendGrid/Mailgun)是关键基础设施决策,配合邮件队列和重试机制确保可靠性。模板使用参数化设计,方便维护和国际化。最后,不要忘记监控——一个没人发现的邮件发送故障,比服务器宕机更隐蔽但后果同样严重。