# cXML

> **cXML** – Eingang für B2B-Einkaufsnetzwerke (Ariba, Coupa, SAP). Kein Zusatz-Addon erforderlich.

## Überblick

cXML (Commerce XML) ist der Quasi-Standard für B2B-Einkaufsnetzwerke. Große Einkäufer wie SAP Ariba, Coupa oder Jaggaer senden Bestellungen als XML-Dokumente über HTTPS an Lieferanten. Orderport empfängt diese Dokumente, validiert die Authentifizierung und transformiert sie in Ihr Zielformat.

Die Integration unterstützt **nur den Eingang** – cXML-Antwortformate zu erzeugen (`ConfirmationRequest`, `ShipNoticeRequest`) ist nicht Teil dieser Integration.

## Kompatibilität

### Unterstützte Versionen

| Bereich | Version |
|---|---|
| cXML-Spezifikation | 1.2.014 und neuer |
| Request-Typ | `OrderRequest` |
| Content-Type | `application/xml` oder `text/xml` |
| DTD | `cXML.dtd` (externe Entities werden blockiert – XXE-Schutz) |

### Bekannte Einschränkungen

- **Keine Bestellbestätigungen.** Orderport antwortet ausschließlich mit einem cXML-Response (`Status code="200"`) und ohne `ConfirmationRequest`. Bestellbestätigungen aus Ihrem ERP zurück ans Netzwerk zu übertragen, ist nicht implementiert.
- **Keine ShipNotice / Invoice.** Versand- und Rechnungsdokumente gehen nicht über cXML zurück.
- **OrderType `update` und `delete`** werden angenommen, aber nicht deduplizert – jede Nachricht wird neu verarbeitet. Nur `new` unterliegt der Duplikatserkennung.
- **PunchOutOrderMessage** (interaktive Katalog-Session) wird nicht unterstützt, nur `OrderRequest`.
- **Digital Signatures** (`Signature`-Blöcke nach XML-DSig) werden nicht geprüft, nur SharedSecret.

## Authentifizierung

cXML transportiert das Passwort im XML-Body, nicht im HTTP-Header. Das Element `Sender/Credential/SharedSecret` enthält das Geheimnis.

- **Auth-Typ in Orderport**: `cXML SharedSecret`
- Orderport verwirft Requests, bei denen `From.Identity`, `To.Identity` oder `SharedSecret` nicht mit einer aktiven Credential der Pipeline übereinstimmen.
- SharedSecret wird in Orderport als SHA-256-Hash gespeichert, nie im Klartext.

## Einrichten in Orderport

### Pipeline anlegen

1. **Pipelines → Neue Pipeline → Experten-Wizard** (oder **Simple Wizard** mit Preset *Ariba* / *Coupa* / *SAP*)
2. Eingabeformat: **cXML**, Ausgabeformat: **openTrans** oder **JSON**
3. Auto-Mapping vorschlagen lassen

### Credential anlegen

Auf der Pipeline-Detailseite **Credentials → Hinzufügen**:

- **From-Domain** / **From-Identity**: Absender-Kennung im Netzwerk (z. B. Ariba ANID des Einkäufers)
- **To-Domain** / **To-Identity**: Ihre eigene Kennung
- **SharedSecret**: wird automatisch generiert oder manuell eingegeben. **Einmalig anzeigen** und im Einkaufssystem hinterlegen.
- **Label**: z. B. "Einkäufer XY Produktion"

Pro Pipeline sind mehrere Credentials möglich – eine pro Einkäufer.

## Einrichten im Quellsystem

Der konkrete Weg hängt vom Netzwerk ab:

- **SAP Ariba**: *Customer Relationship* → *Default Order Routing Method* auf **cXML**; Order-URL und SharedSecret eintragen.
- **Coupa**: *Supplier Information Portal* → *Integrations* → Order-Delivery auf **cXML**.
- **SAP Business Network**: *Configuration* → *Outbound Messages* → cXML-OrderRequest.

Die URL, die Sie dort hinterlegen, ist Ihre Orderport-Webhook-URL.

## Technische Details

### Eingangs-Payload (Auszug)

```xml
<cXML>
  <Header>
    <From><Credential domain="NetworkId">
      <Identity>buyer-anid-123</Identity>
    </Credential></From>
    <Sender><Credential domain="NetworkId">
      <Identity>sender-anid</Identity>
      <SharedSecret>***</SharedSecret>
    </Credential></Sender>
  </Header>
  <Request>
    <OrderRequest>
      <OrderRequestHeader orderID="PO-2024-001"
        orderDate="2024-03-15T14:30:00+01:00" type="new">
        <Total><Money currency="EUR">1500.00</Money></Total>
        <ShipTo><Address>
          <Name>Muster GmbH Lager</Name>
          <PostalAddress>
            <Street>Industriestr. 42</Street>
            <City>München</City>
            <PostalCode>80331</PostalCode>
            <Country isoCountryCode="DE"/>
          </PostalAddress>
        </Address></ShipTo>
      </OrderRequestHeader>
      <ItemOut quantity="10" lineNumber="1">
        <ItemID><SupplierPartID>WIDGET-001</SupplierPartID></ItemID>
        <ItemDetail>
          <UnitPrice><Money currency="EUR">150.00</Money></UnitPrice>
          <Description>Premium Widget Blau</Description>
          <UnitOfMeasure>EA</UnitOfMeasure>
        </ItemDetail>
      </ItemOut>
    </OrderRequest>
  </Request>
</cXML>
```

### Verfügbare Quellfelder

| Pfad | Bedeutung |
|---|---|
| `OrderRequestHeader.OrderID` | Bestellnummer |
| `OrderRequestHeader.OrderDate` | Bestelldatum |
| `OrderRequestHeader.OrderType` | `new`, `update`, `delete` |
| `Total.Amount` / `Total.Currency` | Gesamtbetrag + Währung |
| `ShipTo.Name` / `Street` / `City` / `PostalCode` / `CountryCode` | Lieferadresse |
| `BillTo.Name` / `Street` / `City` / `PostalCode` / `CountryCode` | Rechnungsadresse |
| `From.Domain` / `From.Identity` | Absender aus `Header/From/Credential` |
| `To.Domain` / `To.Identity` | Empfänger aus `Header/To/Credential` |
| `Items[].LineNumber` | Positionsnummer |
| `Items[].Quantity` | Menge |
| `Items[].SupplierPartID` | Artikelnummer |
| `Items[].Description` | Beschreibung |
| `Items[].UnitPrice` / `Items[].Currency` | Einzelpreis |
| `Items[].UnitOfMeasure` | Mengeneinheit (z. B. EA, PCE, KGM) |

### Meta-Felder

| Pfad | Bedeutung |
|---|---|
| `Meta.ItemCount` | Anzahl Positionen |
| `Meta.TotalLineAmount` | Σ Menge × Einzelpreis (berechnet) |
| `Meta.ReceivedAt` | Zeitstempel Bestelleingang (ISO 8601) |
| `Meta.ReceivedDate` | Datum Bestelleingang (Y-m-d) |
| `Meta.ReceivedTime` | Uhrzeit Bestelleingang (H:i:s) |
| `Meta.PipelineName` | Name der empfangenden Pipeline |

### Response-Format

Auf erfolgreiche Requests antwortet Orderport mit HTTP 200 und einem cXML-Response-Dokument:

```xml
<cXML payloadID="{uuid}@orderport.app" timestamp="2024-03-15T14:30:00+01:00">
  <Response>
    <Status code="200" text="OK">Bestellung empfangen</Status>
  </Response>
</cXML>
```

Fehlercodes im Response-Status: `401` (Auth fehlgeschlagen), `403` (Pipeline inaktiv), `406` (ungültiges Format), `409` (Duplikat), `500` (Serverfehler).

### XXE-Schutz

Externe Entity-Referenzen sind beim Parsen deaktiviert (Schutz gegen XXE-Angriffe). Kein XSLT-Processing aus Kundendaten.

### Endpoint

Zwei URL-Varianten sind beide gültig:

- `https://orderport.app/api/v1/webhook/{token}` – Unified Webhook
- `https://orderport.app/api/v1/cxml/{token}` – cXML-Alias (gleiche Logik, beides wird unterstützt)

## Fehlerbehebung

| Symptom | Ursache | Lösung |
|---|---|---|
| 401 mit `Status code="401"` | SharedSecret in Netzwerk und Orderport unterschiedlich | Secret aus Orderport-Credential neu generieren, im Netzwerk hinterlegen |
| 406 Not Acceptable | Falsches Content-Type-Header | Netzwerk sendet `application/xml` oder `text/xml` – nicht `application/cxml` |
| 409 bei Retry | Duplikatserkennung hat die `OrderID` erkannt | Kein Handlungsbedarf – Orderport antwortet idempotent, das Netzwerk wertet 200/409 beides als Erfolg |
| `From.Identity` leer | Ariba nutzt nur `To` und `Sender`, nicht `From` | Credential so anlegen, dass `From.Identity` leer bleibt; Orderport gleicht nur die besetzten Felder ab |

## Nächste Schritte

- [Feldmapping](/docs/feldmapping)
- [Zugangsdaten](/docs/credentials)
- [Duplikatserkennung](/docs/duplikatserkennung)
