WooCommerce – Beliebige Anhänge an E-Mails anfügen
Shops müssen oft dasselbe Dokument mit jeder Bestellung verschicken: AGB, Widerrufsformular, ein Pflegehinweis, eine Lizenz. WooCommerce hat genau dafür einen Filter, und er braucht etwa zehn Zeilen.
Der Filter
/**
* Dateien an WooCommerce-E-Mails anhängen.
*/
add_filter( 'woocommerce_email_attachments', 'shop_dateien_an_mails_haengen', 10, 3 );
function shop_dateien_an_mails_haengen( $attachments, $email_id, $object ) {
if ( ! is_a( $object, 'WC_Order' ) ) {
return $attachments;
}
$pfad = get_stylesheet_directory() . '/dokumente/';
$attachments[] = $pfad . 'agb.pdf';
$attachments[] = $pfad . 'widerrufsformular.pdf';
return $attachments;
}
$attachments ist eine Liste von absoluten Pfaden im Dateisystem — keine URLs. WooCommerce gibt sie an
wp_mail() weiter, und das liest die Dateien von der Platte. Ein Pfad, der mit https:// beginnt, wird still
verworfen.
Achte auf den Schrägstrich in '/dokumente/'. Wer get_stylesheet_directory() direkt an einen Dateinamen
hängt, erhält /var/www/wp-content/themes/meinthemeagb.pdf — einen Pfad, der nicht existiert. Die Mail geht
ohne die Datei und ohne Fehlermeldung hinaus. Das ist der häufigste Grund, warum dieser Code „nicht
funktioniert“.
Nur für bestimmte E-Mails
Ohne Prüfung reist die Datei mit jeder Bestell-Mail mit, bis hin zum Passwort-Zurücksetzen. Also die E-Mail-ID vergleichen:
add_filter( 'woocommerce_email_attachments', 'shop_dateien_an_mails_haengen', 10, 3 );
function shop_dateien_an_mails_haengen( $attachments, $email_id, $object ) {
$gewuenscht = array( 'customer_processing_order', 'customer_completed_order' );
if ( ! is_a( $object, 'WC_Order' ) || ! in_array( $email_id, $gewuenscht, true ) ) {
return $attachments;
}
$attachments[] = get_stylesheet_directory() . '/dokumente/agb.pdf';
return $attachments;
}
Die IDs der Standard-Mails stehen unten in den Fragen. In einem deutschen Shop gehören die Dokumente an die
Bestellbestätigung (customer_processing_order), weil das Gesetz sie zum Zeitpunkt der Bestellung verlangt und
nicht erst beim Versand des Pakets.
Warum die WC_Order-Prüfung wichtig ist
Derselbe Filter wird für die Bestandsmeldungen benutzt — geringer Bestand, kein Bestand, Nachbestellung. Dort
ist $object ein WC_Product oder eine Liste, keine Bestellung. Ohne den is_a()-Wächter landen Deine AGB
an der Bestandswarnung an den Shop-Betreiber, und jeder Aufruf einer Bestellmethode auf diesem Objekt wirft
einen schweren Fehler, der den Versand der Mail ganz abbricht.
Aktuelle WooCommerce-Versionen geben ein viertes Argument mit, das E-Mail-Objekt selbst. Hier genügen drei Parameter; das vierte nur anfordern, wenn Du es wirklich benutzt.
Eine fehlende Datei sichtbar machen
WooCommerce prüft nicht, ob die Dateien existieren. Zwei Zeilen mehr machen aus dem stillen Fehler einen Log-Eintrag:
foreach ( array( 'agb.pdf', 'widerrufsformular.pdf' ) as $datei ) {
$voll = get_stylesheet_directory() . '/dokumente/' . $datei;
if ( file_exists( $voll ) && is_readable( $voll ) ) {
$attachments[] = $voll;
continue;
}
wc_get_logger()->warning(
sprintf( 'Mail-Anhang fehlt: %s', $voll ),
array( 'source' => 'email-attachments' )
);
}
Der Eintrag erscheint unter WooCommerce → Status → Protokolle. Ohne ihn fällt die Lücke erst auf, wenn Wochen später jemand nach dem Dokument fragt.
Wo die Dateien liegen sollten
Das Theme-Verzeichnis ist bequem und hat zwei Nachteile: eine Datei in wp-content/themes/... kann jeder
abrufen, der die URL kennt, und ein Theme-Wechsel verliert sie. Für Dokumente, die ohnehin öffentlich sind —
AGB, Widerrufsformular — ist das in Ordnung. Alles Persönliche gehört außerhalb des Webwurzelordners oder in
ein Verzeichnis mit eigener .htaccess:
Require all denied
wp_mail() liest die Datei weiterhin, weil es im Dateisystem arbeitet und nicht über den Webserver.
Vor dem Scharfschalten
Eine Testbestellung im Shop aufgeben und die Mail prüfen, die wirklich ankommt, nicht die Vorschau. Dabei auf die Größe achten: einige PDFs mit mehreren Megabyte machen die Nachricht langsam und schieben sie bei manchen Anbietern in Richtung Spam-Ordner. Dokumente über etwa zwei Megabyte sind als Link auf eine Seite im Shop besser aufgehoben — dann kannst Du das Dokument auch austauschen, ohne etwas neu zu verschicken.
Häufige Fragen
Wohin kommt der Code?
In ein kleines eigenes Plugin oder in die functions.php eines Child-Themes. Nie in ein Eltern-Theme, weil das nächste Theme-Update ihn entfernt.
Welche E-Mail-IDs gibt es?
An den Shop gehen new_order, cancelled_order und failed_order. An die Kundschaft gehen customer_on_hold_order, customer_processing_order, customer_completed_order, customer_refunded_order, customer_invoice, customer_note, customer_new_account und customer_reset_password. Eine Teilerstattung benutzt ebenfalls customer_refunded_order, eine eigene ID dafür gibt es nicht. Plugins bringen eigene IDs mit, darum $email_id auslesen statt raten.
Die Mail kommt ohne Anhang an. Warum?
Fast immer stimmt der Pfad nicht, oder die Datei ist für den Benutzer des Webservers nicht lesbar. WooCommerce warnt nicht davor — die Mail geht ohne die Datei hinaus. Pfad mit file_exists() prüfen und bei Fehlen eine Zeile ins Log schreiben.
Kann ich eine Datei anhängen, die pro Bestellung entsteht, etwa eine Rechnung als PDF?
Ja. Die Datei zuerst erzeugen, in ein Verzeichnis außerhalb des Webwurzelordners oder in einen geschützten Uploads-Ordner schreiben, dann den absoluten Pfad anhängen. Das muss schnell gehen: der Code läuft während der Bestellung, ein langsamer Generator verzögert den Kassenvorgang.