El correo que envía una aplicación suele fallar en silencio. La función devuelve true, el mensaje se entrega a un proceso local, y nada le dice que lo rechazaron una hora después. El remedio es enviar por SMTP autenticado, donde un fallo es un error que usted ve.
No use mail()
- Informa de si el mensaje se entregó al siguiente eslabón, no de si llegó a destino.
- Envía con la identidad del usuario del servidor web, así que SPF y DKIM suelen fallar.
- No tiene autenticación, ni reintento, ni un registro que pueda leer.
Use SMTP
use PHPMailer\PHPMailer\PHPMailer;
$m = new PHPMailer(true);
$m->isSMTP();
$m->Host = 'mail.yourdomain.com';
$m->SMTPAuth = true;
$m->Username = 'noreply@yourdomain.com';
$m->Password = getenv('SMTP_PASS');
$m->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$m->Port = 465;
$m->setFrom('noreply@yourdomain.com', 'Your Shop');
$m->addReplyTo('support@yourdomain.com');
$m->addAddress($customerEmail);
$m->Subject = 'Your order';
$m->Body = $text;
$m->send();
Nunca ponga la contraseña en el código. Léala del entorno, y mantenga el archivo de entorno fuera de la raíz web y fuera de git.
El From tiene que ser su propio dominio
Poner en From la dirección del cliente para que funcionen las respuestas es el error clásico: entonces está enviando como gmail.com, y eso incumple su SPF y su DMARC de plano. Envíe con su dominio y ponga Reply-To con el de él.
Envíelo en segundo plano
Enviar durante la petición hace que el visitante espere a un servidor remoto y convierte una caída del correo en un pago roto. Póngalo en cola y envíelo desde un trabajador.
Y registre el resultado
$m->SMTPDebug = 2; // development only
// production: record the message ID and the accept/reject per recipient
Para todo lo transaccional -recibos, restablecimientos de contraseña- use un servicio de envío dedicado. La entregabilidad es todo su negocio, y separa sus recibos de sus boletines: vea correo transaccional frente a boletines.
