E-Rechnung für Shopify und WooCommerce: So wird jede B2B-Bestellung automatisch konform
Shopify und WooCommerce erzeugen PDF-Rechnungen — kein strukturiertes XML, keine EN 16931-Konformität. Für B2B-Kunden reicht das ab 2027 nicht mehr. Mit einem Webhook und rechnungsapi.de läuft die E-Rechnungserzeugung vollautomatisch.
Was Shopify und WooCommerce nicht können
Shopify und WooCommerce bieten von Haus aus keine konforme E-Rechnung nach EN 16931. Plugins können helfen — aber oft nur für ZUGFeRD Basic-WL (nicht zugelassen!) oder als manueller Export. Eine vollautomatische Lösung braucht einen Webhook an rechnungsapi.de.
Welche Shop-Betreiber sind betroffen?
Die E-Rechnungspflicht gilt nur für B2B (Geschäftskunden), nicht für B2C (Endverbraucher). Das klingt einfach — in der Praxis muss ein gemischter Shop aber zuverlässig zwischen beiden unterscheiden.
| Szenario | E-Rechnungspflicht ab 2027? |
|---|---|
| Online-Shop mit Privatkunden (B2C) | Nein |
| Online-Shop mit Geschäftskunden (B2B) | Ja |
| Gemischter Shop (B2B + B2C) | Ja, für B2B-Bestellungen |
| Kleinunternehmer §19 UStG | Empfangen ja, Senden nein |
Praktische Faustregel: Wenn Kunden mit Firmennamen + USt-ID bestellen → B2B → E-Rechnung Pflicht. Das USt-ID-Feld im Checkout ist damit nicht nur ein nettes Feature, sondern die Grundlage für die automatische B2B-Erkennung.
Was Shopify und WooCommerce können — und was nicht
S Shopify
- ~Sendet standardmäßig PDF-Bestellbestätigungen, keine Rechnungen
- ~Shopify-Apps (z.B. "Order Printer") erzeugen PDFs — kein XML
- ✗Kein nativer ZUGFeRD/XRechnung Support
- ✓Shopify empfiehlt selbst externe Services für E-Rechnung
W WooCommerce
- ~Plugin "Germanized Pro" kann ZUGFeRD — aber nur Basic-WL Profil
- ✗Basic-WL ist ab 2027 NICHT für die E-Rechnungspflicht zugelassen
- ~Plugin "WooRechnung" ähnlich eingeschränkt
- ✓Für konformes ZUGFeRD COMFORT/EN 16931: externer API-Aufruf nötig
Lösungsvergleich
| Lösung | Shopify | WooCommerce | Konform? |
|---|---|---|---|
| Standard PDF | ✓ | ✓ | ✗ |
| Germanized Pro ZUGFeRD | ✗ | Basic-WL | ✗ (Basic-WL nicht zulässig) |
| rechnungsapi.de via Webhook | ✓ | ✓ | ✓ ZUGFeRD COMFORT/EN 16931 |
Der Weg: Shopify Webhook → rechnungsapi.de
B2B-Bestellung eingeht
Kunde hat Firmenname + USt-ID hinterlegt.
Shopify orders/paid Webhook feuert
Shopify sendet das vollständige Order-Objekt an deine Webhook-URL.
Server empfängt & validiert Webhook
HMAC-Prüfung mit dem Shopify Webhook Secret sichert die Authentizität.
B2B-Erkennung: USt-ID vorhanden?
Nur Bestellungen mit hinterlegter USt-ID werden als E-Rechnung verarbeitet.
POST an rechnungsapi.de
Vollständiger JSON-Payload mit Seller, Buyer und Positionen — Antwort ist ein ZUGFeRD PDF.
PDF per E-Mail + Archiv
Rechnung an Kunden versenden und GoBD-konform archivieren (S3, DATEV, Google Drive).
Shopify Webhook Handler (Node.js)
// shopify-webhook.js
const express = require('express')
const crypto = require('crypto')
const fetch = require('node-fetch')
const app = express()
app.post('/webhook/shopify/orders-paid', express.raw({ type: 'application/json' }), async (req, res) => {
// HMAC-Validierung
const hmac = req.headers['x-shopify-hmac-sha256']
const hash = crypto.createHmac('sha256', process.env.SHOPIFY_WEBHOOK_SECRET)
.update(req.body).digest('base64')
if (hash !== hmac) return res.status(401).send('Unauthorized')
const order = JSON.parse(req.body)
// Nur B2B-Bestellungen (mit USt-ID) als E-Rechnung
const vatId = order.billing_address?.company &&
order.note_attributes?.find(a => a.name === 'vat_id')?.value
if (!vatId) {
console.log('B2C-Bestellung, keine E-Rechnung noetig:', order.name)
return res.json({ received: true })
}
const lineItems = order.line_items.map((item, i) => {
const netPrice = (item.price * (1 - (item.discount_allocations[0]?.amount || 0) / item.price / 100))
return {
invoiceLineIdentifier: String(i + 1),
invoiceLineNetAmount: (netPrice * item.quantity).toFixed(2),
itemName: item.name,
invoicedQuantity: String(item.quantity),
invoicedQuantityUnitOfMeasureCode: 'C62',
itemNetPrice: netPrice.toFixed(2),
invoiceLineVatInformation: { invoiceLineVatCategoryCode: 'S', invoiceLineVatRate: 19 }
}
})
const netTotal = lineItems.reduce((s, l) => s + parseFloat(l.invoiceLineNetAmount), 0)
const response = await fetch('https://api.rechnungsapi.de/api/v1/zugferd/createZugferdFromJson', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.RECHNUNGSAPI_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
invoice: {
invoiceNumber: order.name.replace('#', 'INV-'),
invoiceIssueDate: new Date().toISOString().split('T')[0],
invoiceTypeCode: '380',
invoiceCurrencyCode: 'EUR',
seller: {
sellerName: 'Ihr Shop GmbH',
sellerVatIdentifier: 'DE123456789',
sellerElectronicAddress: 'rechnungen@ihrshop.de',
sellerPostalAddress: {
sellerAddressLine1: 'Shopstrasse 1',
sellerCity: 'Hamburg',
sellerPostCode: '20095',
sellerCountryCode: 'DE'
}
},
buyer: {
buyerName: order.billing_address.company || order.billing_address.name,
buyerVatIdentifier: vatId,
buyerElectronicAddress: order.email,
buyerPostalAddress: {
buyerAddressLine1: order.billing_address.address1,
buyerCity: order.billing_address.city,
buyerPostCode: order.billing_address.zip,
buyerCountryCode: order.billing_address.country_code
}
},
invoiceLine: lineItems,
documentTotals: {
sumOfInvoiceLineNetAmount: netTotal.toFixed(2),
invoiceTotalAmountWithoutVat: netTotal.toFixed(2),
invoiceTotalVatAmount: (netTotal * 0.19).toFixed(2),
invoiceTotalAmountWithVat: (netTotal * 1.19).toFixed(2),
amountDueForPayment: (netTotal * 1.19).toFixed(2)
},
vatBreakdown: [{
vatCategoryTaxableAmount: netTotal.toFixed(2),
vatCategoryTaxAmount: (netTotal * 0.19).toFixed(2),
vatCategoryCode: 'S',
vatCategoryRate: 19
}]
}
})
})
const pdfBuffer = await response.buffer()
// E-Mail versenden oder in Archiv speichern
console.log('ZUGFeRD-Rechnung erzeugt fuer:', order.name)
res.json({ received: true })
})Den Webhook in Shopify einrichten: Admin → Einstellungen → Benachrichtigungen → Webhooks → "Bestellung bezahlt" → URL deines Servers eintragen.
WooCommerce Integration (PHP)
Bei WooCommerce greifst du direkt auf den woocommerce_payment_complete Hook zu. Der Code läuft serverseitig in der functions.php deines Themes oder in einem Custom Plugin — kein separater Server nötig.
<?php
// functions.php - WooCommerce Hook nach Zahlung
add_action('woocommerce_payment_complete', 'generate_e_rechnung', 10, 1);
function generate_e_rechnung($order_id) {
$order = wc_get_order($order_id);
// Nur B2B (mit USt-ID)
$vat_id = get_post_meta($order_id, '_billing_vat_id', true);
if (empty($vat_id)) return;
$line_items = [];
foreach ($order->get_items() as $i => $item) {
$net_price = $item->get_subtotal() / $item->get_quantity();
$line_items[] = [
'invoiceLineIdentifier' => (string)($i + 1),
'invoiceLineNetAmount' => number_format($item->get_subtotal(), 2, '.', ''),
'itemName' => $item->get_name(),
'invoicedQuantity' => (string)$item->get_quantity(),
'invoicedQuantityUnitOfMeasureCode' => 'C62',
'itemNetPrice' => number_format($net_price, 2, '.', ''),
'invoiceLineVatInformation' => [
'invoiceLineVatCategoryCode' => 'S',
'invoiceLineVatRate' => 19
]
];
}
$net_total = $order->get_subtotal();
$payload = [
'invoice' => [
'invoiceNumber' => 'INV-' . $order->get_order_number(),
'invoiceIssueDate' => date('Y-m-d'),
'invoiceTypeCode' => '380',
'invoiceCurrencyCode' => 'EUR',
'seller' => [
'sellerName' => get_bloginfo('name'),
'sellerVatIdentifier' => get_option('woocommerce_vat_id'),
'sellerElectronicAddress' => get_option('woocommerce_email'),
'sellerPostalAddress' => [
'sellerAddressLine1' => get_option('woocommerce_store_address'),
'sellerCity' => get_option('woocommerce_store_city'),
'sellerPostCode' => get_option('woocommerce_store_postcode'),
'sellerCountryCode' => 'DE'
]
],
'buyer' => [
'buyerName' => $order->get_billing_company() ?: $order->get_formatted_billing_full_name(),
'buyerVatIdentifier' => $vat_id,
'buyerElectronicAddress' => $order->get_billing_email(),
'buyerPostalAddress' => [
'buyerAddressLine1' => $order->get_billing_address_1(),
'buyerCity' => $order->get_billing_city(),
'buyerPostCode' => $order->get_billing_postcode(),
'buyerCountryCode' => $order->get_billing_country()
]
],
'invoiceLine' => $line_items,
'documentTotals' => [
'sumOfInvoiceLineNetAmount' => number_format($net_total, 2, '.', ''),
'invoiceTotalAmountWithoutVat' => number_format($net_total, 2, '.', ''),
'invoiceTotalVatAmount' => number_format($net_total * 0.19, 2, '.', ''),
'invoiceTotalAmountWithVat' => number_format($net_total * 1.19, 2, '.', ''),
'amountDueForPayment' => number_format($net_total * 1.19, 2, '.', '')
],
'vatBreakdown' => [[
'vatCategoryTaxableAmount' => number_format($net_total, 2, '.', ''),
'vatCategoryTaxAmount' => number_format($net_total * 0.19, 2, '.', ''),
'vatCategoryCode' => 'S',
'vatCategoryRate' => 19
]]
]
];
$response = wp_remote_post('https://api.rechnungsapi.de/api/v1/zugferd/createZugferdFromJson', [
'headers' => [
'Authorization' => 'Bearer ' . get_option('rechnungsapi_token'),
'Content-Type' => 'application/json'
],
'body' => json_encode($payload),
'timeout' => 30
]);
if (!is_wp_error($response)) {
$pdf = wp_remote_retrieve_body($response);
$upload_dir = wp_upload_dir();
file_put_contents($upload_dir['path'] . '/rechnung-' . $order_id . '.pdf', $pdf);
}
}Die USt-ID musst du im WooCommerce Checkout als Custom Field erfassen. Mit Germanized Pro oder dem WooCommerce EU VAT Plugin wird _billing_vat_id automatisch als Order Meta gespeichert.
Alternative: n8n oder Make.com — kein eigener Server nötig
Wer keinen eigenen Server betreiben möchte, kann n8n oder Make.com als Middleware einsetzen. Die Daten fließen dann per Webhook direkt vom Shop in das No-Code-Tool und von dort an rechnungsapi.de. Du willst dich nicht selbst darum kümmern? Wir richten den n8n-Workflow für dich ein — oder übernehmen die individuelle Implementierung komplett. Sprich uns einfach an.
Shopify Webhook
orders/paid Webhook triggert n8n HTTP Trigger Node in Echtzeit.
n8n verarbeitet
Daten werden gemappt, B2B-Erkennung per IF-Node, JSON-Payload zusammengebaut.
rechnungsapi.de
HTTP Request Node POST → ZUGFeRD PDF zurück → E-Mail oder Archiv.
Dauert ca. 30 Minuten, kein Code nötig. Die vollständige Anleitung für n8n, Make.com und Zapier findest du im Artikel XRechnung & ZUGFeRD per n8n, Make oder Zapier automatisieren.
Häufige Fehler — und wie ihr sie vermeidet
B2B-Erkennung fehlt
Problem: Nicht alle Bestellungen brauchen eine E-Rechnung — nur B2B-Kunden. Ohne Prüfung werden auch Privatkunden als B2B behandelt.
Lösung: USt-ID als Pflichtfeld für Firmenkunden im Checkout einrichten. Der Webhook prüft dann nur Bestellungen mit vorhandener USt-ID.
Falsches ZUGFeRD-Profil
Problem: Germanized Pro erzeugt ZUGFeRD Basic-WL. Dieses Profil ist ab 2027 NICHT als E-Rechnung im Sinne des §14 UStG anerkannt — trotz "ZUGFeRD"-Label.
Lösung: rechnungsapi.de erzeugt immer ZUGFeRD COMFORT/EN 16931 — das einzige Profil, das die gesetzlichen Anforderungen erfüllt.
Kein Archiv
Problem: E-Rechnungen müssen 10 Jahre GoBD-konform aufbewahrt werden. Ein einfaches Verzeichnis auf dem Server genügt nicht.
Lösung: PDF direkt nach Erzeugung in S3, Google Drive oder DATEV Unternehmen Online speichern — unveränderbar und revisionssicher.
Häufige Fragen
Muss ich alle Shopify-Bestellungen als E-Rechnung versenden?
+
Nein. Die E-Rechnungspflicht gilt nur für B2B-Transaktionen zwischen deutschen Unternehmen. Bestellungen von Privatpersonen (B2C) sind ausgenommen. Erkennungsmerkmal: Hat die Bestellung eine USt-ID hinterlegt? Dann ist es B2B.
Funktioniert das mit Shopify Plus?
+
Ja. Webhooks sind auf allen Shopify-Plänen verfügbar — Basic, Shopify, Advanced und Plus. Der orders/paid Webhook ist auf jedem Plan nutzbar. Shopify Plus bietet zusätzlich erweiterte Webhook-Verwaltung über die Admin API.
Was kostet die rechnungsapi.de API?
+
Die API ist nutzungsbasiert abgerechnet. Es gibt einen kostenlosen Test-Account ohne Kreditkarte. Details und aktuelle Preise findest du unter rechnungsapi.de/pricing.
Gibt es eine Shopify-App dafür?
+
Derzeit gibt es keine native Shopify-App für konforme E-Rechnungen nach EN 16931. Die Webhook-Integration ist schneller einzurichten als eine App-Installation und gibt dir volle Kontrolle über die Rechnungslogik.
Was ist mit internationalen B2B-Kunden (EU, nicht Deutschland)?
+
Die E-Rechnungspflicht gilt aktuell für B2B-Transaktionen zwischen deutschen Unternehmen (inländische Leistungen). Bei EU-Kunden aus anderen Ländern: ZUGFeRD schadet nicht und bereitet auf kommende EU-weite Pflichten (ViDA ab 2030) vor, ist aber noch keine gesetzliche Pflicht.
E-Rechnung-API für deinen Shop kostenlos testen
Kostenlosen Account anlegen, API-Key holen, Webhook konfigurieren — fertig. Keine Kreditkarte, keine Mindestlaufzeit. Dein Shop erzeugt innerhalb von Minuten konforme ZUGFeRD-Rechnungen für B2B-Bestellungen.