# Magento 2

> **Magento 2** – Ausgang via REST-API (`POST /rest/V1/orders`). Benötigtes Addon: `connector_magento2`.

## Überblick

Mit dieser Integration legt Orderport Bestellungen, die aus einem anderen Kanal stammen (z. B. Ariba, Coupa, EDIFACT oder ein JSON-Webhook), direkt als Magento-2.4-Bestellung an – inklusive Kunde, Liefer- und Rechnungsadresse, Positionen sowie Zahlungs- und Versandart.

Magento ist in Orderport ein **reines Zielsystem**: Es gibt **keinen Eingangs-Webhook von Magento**. Orderport empfängt die Bestellung über einen der üblichen Eingänge, transformiert sie und schreibt sie per REST-API in den Shop.

Typischer Einsatzfall:

- Sie nehmen Bestellungen aus einem B2B-Einkaufsportal (cXML) oder per EDIFACT entgegen und wollen sie automatisch als Bestellung in Ihrem Magento-Shop sehen – ohne ein eigenes Magento-Modul zu entwickeln und zu pflegen.

## Kompatibilität

### Unterstützte Versionen

| Bereich | Version |
|---|---|
| Magento | **2.4** (Magento Open Source / Adobe Commerce) |
| API | REST API (`/rest/V1/...`) |
| Auth | Integration Access Token (Bearer, permanent) |
| Endpoint | `POST /rest/V1/orders` |

### Bekannte Einschränkungen

- **Nur Ausgang.** Magento sendet keine Bestellungen an Orderport. Die Integration legt ausschließlich Bestellungen *in* Magento an.
- **Bestellungen werden als Gast angelegt.** Jede importierte Order wird mit `customer_is_guest = 1` erzeugt. Es findet **kein Matching auf bestehende Magento-Kunden** statt (anders als bei Shopware 6).
- **Artikel müssen in Magento existieren.** Positionen werden per **SKU** als `product_type: simple` übergeben. Magento weist die Bestellung zurück, wenn eine SKU nicht existiert – es gibt **keinen Fallback auf „custom line items"**.
- **Keine Versandkosten.** `shipping_amount` wird fest auf `0.00` gesetzt; Versandkosten aus dem Quellsystem werden nicht übernommen.
- **Eine Lieferadresse pro Bestellung** (kein Split-Shipping).
- **Ein Steuersatz pro Bestellung.** Es wird ein konfigurierbarer Standard-Steuersatz auf die gesamte Bestellung angewendet – keine positionsabhängigen Steuersätze.
- **Status-Updates/Stornierungen fließen nicht zurück.** Eine einmal angelegte Bestellung wird von Orderport nicht weiter aktualisiert.
- **Nur Einzelbestellungen.** Keine Batch-Imports mehrerer Orders in einem Request.

## Authentifizierung

Orderport authentifiziert sich mit einem **Integration Access Token** (Bearer) an der Magento-REST-API. Der Token ist permanent – es gibt keinen OAuth-Token-Tausch und kein Ablaufdatum. Sie erzeugen ihn einmalig im Magento-Admin (siehe [Einrichten im Shop](#einrichten-im-shop)).

> **Magento liefert vier Werte – Orderport braucht nur einen.** Beim Aktivieren einer Integration zeigt Magento *Consumer Key*, *Consumer Secret*, *Access Token* und *Access Token Secret* an – das sind die vollständigen OAuth-1.0a-Zugangsdaten für signierte Requests. Orderport nutzt stattdessen Token-Authentifizierung und schickt nur den **Access Token** als Bearer-Header (`Authorization: Bearer {access_token}`). Tragen Sie also ausschließlich den **Access Token** ein; Consumer Key/Secret und Access Token Secret werden nicht benötigt.

Der Token wird in Orderport **verschlüsselt** gespeichert (`Crypt`) und nur zur Laufzeit zum Versand verwendet.

## Einrichten in Orderport

### Pipeline anlegen (Ausgang nach Magento)

1. **Pipelines → Neue Pipeline → Simple Wizard → Ziel: Magento 2**
2. **Shop-URL** und **Integration Access Token** eingeben, dann **„Verbinden & Stores laden"**
3. Sobald die Verbindung steht, lädt Orderport die **Store Views** aus Magento (`/rest/V1/store/storeViews`)
4. Zieleinstellungen wählen bzw. bestätigen:
   - **Store View** – das Ziel, in dem die Bestellung angelegt wird
   - **Zahlungsart-Code** – Magento-Code, z. B. `checkmo`, `banktransfer` (Default: `checkmo`)
   - **Versandart-Code** – Format `carrier_method`, z. B. `flatrate_flatrate` (Default: `flatrate_flatrate`)
   - **Steuersatz (%)** – Standard-Steuersatz für die Bestellung (Default: `19`)
   - **State / Status** – Magento-Bestellzustand (Defaults: `new` / `pending`)
5. Quelle (Eingangsformat) und Mapping einrichten – die KI-Vorschläge greifen die wesentlichen Felder automatisch
6. Transport wird automatisch auf **REST API** mit Adapter `magento2` gesetzt (POST, `application/json`, Bearer-Token)

Den Experten-Wizard können Sie ebenfalls nutzen: Ausgabeformat **JSON**, Adapter **`magento2`** – die Magento-Felder werden dann im Transport-Schritt erfragt.

### Mapping

Orderport schreibt in eine kanonische JSON-Struktur, die der `magento2`-Adapter in das Magento-Order-Entity übersetzt. Die verfügbaren Zielfelder sind unten dokumentiert. Spezialfälle (z. B. Preis-Modus brutto vs. netto) lassen sich im Mapping-Editor anpassen.

## Einrichten im Shop

### Integration anlegen (Magento-Admin)

1. **System → Erweiterungen → Integrationen → Neue Integration hinzufügen**
2. Name vergeben und mit Ihrem Admin-Passwort bestätigen
3. Im Reiter **API** die Ressourcen-Zugriffe wählen – mindestens:
   - **Verkäufe → Vorgänge → Bestellungen → Erstellen** (Anlegen von Orders)
   - **Shops → Einstellungen → Alle Shops** bzw. Lesezugriff auf Store Views (für den Verbindungstest)
4. **Speichern** → die Integration erscheint in der Liste
5. **Aktivieren** klicken und die Berechtigungen bestätigen → Magento zeigt nun den **Access Token** an
6. Den **Access Token** kopieren und in Orderport als *Integration Access Token* hinterlegen

### Access Token als Bearer erlauben (Magento 2.4.4+)

Seit Magento **2.4.4** dürfen Integration Access Tokens standardmäßig **nicht** als eigenständige Bearer-Tokens verwendet werden. Solange diese Option aus ist, lehnt Magento **jeden** REST-Aufruf mit `401` und der irreführenden Meldung *„The consumer isn't authorized to access %resources."* ab – unabhängig davon, welche Ressourcen die Integration freigeschaltet hat.

1. **Stores → Configuration → Services → OAuth → Consumer Settings**
2. **Allow OAuth Access Tokens to be used as standalone Bearer tokens** auf **Yes / Ja** setzen
3. Speichern und ggf. den Cache leeren

> Dies ist eine globale OAuth-Einstellung, kein Berechtigungsproblem der Integration. Ohne sie funktioniert der Bearer-Token von Orderport nicht.

> Die im Wizard angegebenen Codes für **Zahlungs- und Versandart** müssen aktiven Magento-Methoden entsprechen (z. B. `checkmo` = *Scheck / Zahlungsanweisung*, `flatrate_flatrate` = *Pauschalbetrag*). Stimmt der Code nicht mit einer aktivierten Methode überein, weist Magento die Bestellung zurück.

## Technische Details

### Kanonische Eingangs-Struktur (Adapter-Input)

Das Mapping schreibt in diese JSON-Struktur; der `magento2`-Adapter konsumiert sie:

```json
{
  "order": {
    "number": "ORD-2024-001",
    "date": "2024-03-15",
    "currency": "EUR",
    "total": 1500.00
  },
  "customer": {
    "first_name": "Max",
    "last_name": "Mustermann",
    "email": "bestellung@muster.de",
    "phone": "+49 89 123456",
    "company": "Muster GmbH"
  },
  "shipping": {
    "first_name": "Max",
    "last_name": "Mustermann",
    "street": "Industriestr. 42",
    "zip": "80331",
    "city": "München",
    "country": "DE"
  },
  "items": [
    { "sku": "WIDGET-001", "name": "Premium Widget Blau", "quantity": 10, "price": 150.00 }
  ]
}
```

### Verfügbare Zielfelder

| Pfad | Bedeutung |
|---|---|
| `order.number` | Bestellnummer |
| `order.date` | Bestelldatum |
| `order.currency` | Währungscode (z. B. `EUR`) |
| `order.total` | Gesamtbetrag |
| `customer.first_name` / `customer.last_name` | Name des Kunden |
| `customer.email` | E-Mail (Fallback: `unknown@orderport.app`) |
| `customer.phone` | Telefon |
| `customer.company` | Firma |
| `shipping.first_name` / `.last_name` / `.company` | Empfänger der Lieferung |
| `shipping.street` / `.zip` / `.city` / `.country` | Lieferadresse (Land als ISO-2) |
| `shipping.phone` | Telefon Lieferadresse |
| `billing.*` | Rechnungsadresse (gleiche Felder wie `shipping.*`; fehlt sie, wird die Lieferadresse verwendet) |
| `items[].sku` | Artikelnummer (muss in Magento existieren) |
| `items[].name` | Artikelname |
| `items[].quantity` | Menge |
| `items[].price` | Einzelpreis |

### Ausgangs-Payload (Magento-Order-Entity)

Der `magento2`-Adapter erzeugt ein `{ "entity": { … } }`-Objekt für `POST /rest/V1/orders` mit u. a.:

- `store_id`, `state`, `status`, `*_currency_code`
- `customer_email`, `customer_firstname`, `customer_lastname`, `customer_is_guest: 1`
- `items[]` (`sku`, `name`, `qty_ordered`, `price`, `row_total`, `product_type: "simple"`)
- `subtotal`, `subtotal_incl_tax`, `tax_amount`, `grand_total`, `total_qty_ordered`
- `shipping_amount: 0.00`
- `billing_address` und `extension_attributes.shipping_assignments[].shipping` (Adresse + `method`)
- `payment.method`

### Preis-Modus und Steuer

Die Preise aus den Positionen werden **1:1 übernommen** – Magento rechnet nichts neu. Orderport leitet Zwischensumme, Steuerbetrag und Gesamtbetrag aus den Positionen und dem **Standard-Steuersatz** ab:

- **`price_mode: "net"` (Default)** – die Positionspreise gelten als Netto; der Bruttowert wird mit dem Steuersatz hochgerechnet, anschließend wird die Steuer wieder herausgerechnet.
- **`price_mode: "gross"`** – die Positionspreise gelten als Brutto und werden direkt verwendet (`price_incl_tax` / `row_total_incl_tax` werden gesetzt).

Bei einem Steuersatz von `0` entfällt die Steueraufteilung; Zwischensumme = Gesamtbetrag.

### Adapter-Konfiguration

Die folgenden Werte werden im Wizard erfragt und im Transport unter `config.magento2` abgelegt:

| Feld | Pflicht | Default | Quelle |
|---|---|---|---|
| `base_url` | ja | – | Eingabe im Wizard (öffentliche URL) |
| `access_token` | ja | – | Eingabe im Wizard (verschlüsselt gespeichert) |
| `store_id` | ja | `1` | Auswahl aus den geladenen Store Views |
| `payment_method` | ja | `checkmo` | Eingabe (Magento-Zahlungsart-Code) |
| `shipping_method` | ja | `flatrate_flatrate` | Eingabe (`carrier_method`) |
| `order_state` | nein | `new` | Eingabe |
| `order_status` | nein | `pending` | Eingabe |
| `default_tax_rate` | nein | `19.0` | Eingabe (in %) |
| `price_mode` | nein | `net` | Mapping/Config (`net` oder `gross`) |

> Hinweis: Die Shop-URL muss **öffentlich erreichbar** sein. URLs auf interne oder private Adressen werden beim Verbindungstest abgelehnt.

## Fehlerbehebung

| Symptom | Ursache | Lösung |
|---|---|---|
| Verbindungstest scheitert: „Magento API nicht erreichbar" | Shop-URL falsch, REST nicht erreichbar oder private/interne Adresse | Öffentliche Shop-URL prüfen (inkl. `https://`); interne Hostnamen sind nicht erlaubt |
| „The consumer isn't authorized to access %resources." / 401 (bei **jedem** Aufruf) | Integration Access Tokens sind seit Magento 2.4.4 standardmäßig **nicht** als Bearer erlaubt – kein ACL-Problem | **Stores → Configuration → Services → OAuth → Consumer Settings → „Allow OAuth Access Tokens to be used as standalone Bearer tokens"** auf **Ja** setzen (siehe [Einrichten im Shop](#einrichten-im-shop)) |
| „The consumer isn't authorized" / 401 (nur einzelne Aufrufe) | Token falsch oder Integration nicht aktiviert | Integration in Magento **aktivieren**, Access Token neu kopieren und in Orderport hinterlegen |
| Bestellung wird abgelehnt: Produkt/SKU existiert nicht | SKU stimmt nicht mit einem Magento-Artikel überein | SKU-Schreibweise prüfen (case-sensitive); Artikel muss in Magento angelegt sein |
| „Invalid payment method" o. Ä. | Zahlungs-/Versandart-Code stimmt nicht | Exakten, aktivierten Magento-Code verwenden (z. B. `checkmo`, `flatrate_flatrate`) |
| Falsche Steuer-/Gesamtbeträge | `default_tax_rate` oder `price_mode` passt nicht zum Quellsystem | Steuersatz prüfen; bei Brutto-Quellpreisen `price_mode: "gross"` im Mapping setzen |
| Bestellungen erscheinen als Gast ohne Kundenkonto | Erwartetes Verhalten (`customer_is_guest: 1`) | Kein Eingreifen nötig – Magento legt jede importierte Order als Gastbestellung an |

## Nächste Schritte

- [Feldmapping](/docs/feldmapping) – Felder verfeinern und transformieren
- [REST API](/docs/rest-api) – Transport-Details für den Ausgang
- [cXML](/docs/cxml) – häufige Quelle für Magento-Bestellungen
