Entries tagged php xml erstellen xmlwriter

Anleitung: Produkte bei eBay über API mit PHP SDK listen – Teil 1: XML Feed

Posted on 10. Oktober 2014 Comments

Dieser Blog Post ist Teil der Reihe Produkte bei eBay listen.


Wer eine große Menge an Produkten über eBay verkaufen möchte, kann entweder .csv Dateien ausfüllen oder die eBay API benutzen. Dafür stellt eBay offizielle SDKs für Java, .NET und Python zur Verfügung, wobei Python am vollständigsten ist. Der Entwickler David T. Sadler aka dts hat auf GitHub ein PHP SDK zur Verfügung gestellt welches viele Funktionen der API unterstüzt.

Für diesen Blogpost gehe ich davon aus, dass Produkte zu einem festen Preis (keine Auktionen) und in verschiedenen Varianten eines Typs (z.B. Farbe/Größe) verkauft werden sollen. Außerdem soll die BulkDataExchange API, ein Teil des LargeMerchantService, benutzt werden, um gleich mehrere tausend Produkte gleichzeitig listen zu können. Es soll weiterhin die sog. eBay Garantie erreicht werden, einer Art Stempel von eBay für Top-Seller mit den besten Konditionen für Kunden.

Diese Blogpost-Reihe wird folgende Themen behandeln

  • Erstellen eines XML Feeds
  • Anmelden für das Developer Programm und nutzen der Sandbox
  • Initiales Listen einer großen Anzahl von Produkten
  • Regelmäßiges Updaten der Verfügbarkeit (Quantity) und der Preise
  • Löschen von Produktlistings

1. Erstellen eines XML Feeds

1.1. Header

Wir wollen hier ganz klassisch eine .xml-Datei erstellen, die wir später per SDK hochladen. Die Reihenfolge der Tags ist irrelevant, ich habe sie so angeordnet, dass mir das manuelle Durchsehen der Datei einfach fällt. Wie das mit PHP geht, dazu später mehr, fangen wir mit dem Aufbau an:

<?xml version="1.0" encoding="UTF-8"?>
<BulkDataExchangeRequests>
<Header>
<Version>669</Version>
<SiteID>77</SiteID>
</Header>

Die Versionsnummer steht für die Version von BulkDataExchange, dieses Tag wird auch erst am Ende der Datei geschlossen. Die SiteID 77 steht für Deutschland. Andere Länder können in der API Dokumentation  nachgeschlagen werden.

<AddFixedPriceItemRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<ErrorLanguage>de_DE</ErrorLanguage>
<WarningLevel>Low</WarningLevel>
<Version>619</Version>

Für Produkte mit einem festen Preis wird hier der AddFixedPriceItemRequest verwendet. Damit wir erstmal eine Übersicht über die Fehler bekommen, können wir das WarningLevel auf Low stellen, sobald dort nichts mehr auftaucht, kann man das auch auch High stellen. Die API Version ist hier 619.

1.2. Item

Jetzt soll das Item definiert werden. Mit Item ist hier der übergeordnete/Eltern-/Parent-Artikel gemeint, eine Variation davon wird erst später definiert. Pro AddFixedPriceItemRequest gibt es nur ein Item, ein BulkDataExchangeRequest kann allerdings mehrere AddFixedPriceItemRequests enthalten. Für das Item gibt es eine umfangreiche Dokumentation, ich gehe hier die wichtigsten Tags durch:

<Country>DE</Country>
<Title>Supermarke Superjacke Unisex</Title>
<SubTitle>Diese Jacke ist einfach super!</SubTitle>
<InventoryTrackingMethod>SKU</InventoryTrackingMethod>
<SKU>2342</SKU>

Für Country werden (überwiegend) die zweistelligen Country Codes nach ISO 3166 benutzt. Der Title ist frei wählbar und darf bis zu 80 Zeichen lang sein. Am besten sollte hier schon bei der Ausgabe aus der Datenbank darauf geachtet werden und ggf. sinnvoll abgeschnitten werden. Der SubTitle ist ein kleiner grauer Untertitel, der in der eBay Suche unter dem eigentlichen Titel angezeigt wird. Achtung: er kostet pro Listing aber 1€ extra!

Es gibt mehrere Wege das Inventar bei eBay wiederzufinden, wir wählen hier SKU (Stock Keeping Unit, dt. Bestandseinheit) und geben sie auch gleich im Tag SKU mit an. Es ist natürlich auch möglich die Artikel über von eBay vergebene Item-ID zu verwalten, dafür muss InventoryTrackingMethod auf ItemID gesetzt werden, die Item-ID kommt dann mit der AddFixedPriceItemResponse, also der Antwort auf diesen Request. Ein weiteres Feld muss dann nicht ausgefüllt werden und das Tag SKU fällt weg.

<Description>Das ist eine wirklich super Jacke, sie hat die und die Eigenschaften.</Description>
<Currency>EUR</Currency>
<Location>Entenhausen</Location>
<DispatchTimeMax>1</DispatchTimeMax>
<ListingDuration>GTC</ListingDuration>
<ListingType>FixedPriceItem</ListingType>

In der Description soll der Artikel beschrieben werden, hier kann auch HTML verwendet werden, allerdings nur eingeschränkt. So funktionieren (bei eBay Deutschland) z.B. Links und Bilder, aber kein JavaScript oder iFrames. Die Currency ist der ISO 4217 Currency Code für die verwendete Währung. Dies ist auch die Währung für alle Variationen, da diese nicht in einer anderen Währung verkauft werden können. In der Location kann der Artikelstandort angebeben werden. Das ist natürlich bei mehreren Lagern problematisch, genau wie der nächste Tag.

Die DispatchTimeMax ist die maximale Anzahl an Tagen, bis der Artikel an den Versanddienstleister übergeben wird. Um den Stempel eBay-Garantie zu bekommen muss sie „1“ betragen. Sie sollte aber auch generell möglichst gering gehalten werden, um mehr Verkäufe zu erzielen, da Kunden prinzipiell eher kaufen, wenn der Artikel schnell geliefert werden kann. Diese Zeit wird automatisch mit der Versandzeit (s. unten) verrechnet, um das ungefähre Lieferdatum automatisch zu berechnen. Allerdings gibt es hier ein Problem: diese Zeit kann nur Item-Ebene angegeben werden, eine unterschiedliche Zeit – je nach Variation – kann in der Realität jedoch vorkommen. So könnte die Jacke in Größe S und Farbe schwarz in einem anderen Lager liegen als dieselbe Jacke in Größe M und Farbe rot. Oder sie könnte nachbestellt sein, also auf dem Weg ins Lager sein (wo die DispatchTime dann 1 Tag wäre), man möchte aber natürlich die Anzahl dieser Variation nicht auf 0 setzen, da sie ja verfügbar ist – nur eben nicht ganz so schnell. Von eBay gibt es hier leider wenig Hilfe bzw. Hinweise, wie mit der Situation umzugehen sei. Ein Hinweis auf eine ggf. längere Lieferzeit sollte also in so einem Fall zumindest in der Produktbeschreibung vorhanden sein, falls ein Kunde sich bei eBay beschwert. Aber: die Beschreibung kann nicht mehr geändert werden, sobald ein Kunde irgendeine Variation eines Artikels gekauft hat, man kann also nicht dynamisch nach Verfügbarkeit den Hinweis entfernen oder hinzufügen. Als work-around wäre z.B. auch denkbar eine Lieferzeit anhand der Item-Eigenschaften (z.B. Farbe und Größe) auf der eigenen Webseite zur Verfügung zu stellen, ggf. mit dynamischen Link in der Produktbeschreibung. So kommt es in der Regel zu weniger Support Tickets und zu glücklicheren Kunden 🙂

Die ListingDuration bei den meisten Artikeln dürfte auf GTC (Good ‚Till Cancelled, dt. gültig bis Widerruf), möglich sind hier auch Werte wie Days_14 oder Days_30, welche man z.B. für Aktionen und Abverkäufe nutzen kann. Die Valid Values variieren jedoch je nach Kategorie (s. unten) und können über den API Call getCategoryDetails abgefragt werden. Außerdem ist der Wert GTC in der Sandbox nicht möglich, dort sollte man Days_30 versuchen.

Warum man den ListingType hier noch einmal angeben muss erschließt sich mit nicht ganz, da diese Information ja im Container AddFixedPriceItem steht.

<ItemSpecifics>
<NameValueList>
<Name>Material</Name>
<Value>100% Baumwolle</Value>
</NameValueList>
<NameValueList>
<Name>Herstellernummer</Name>
<Value>1337</Value>
</NameValueList>
</ItemSpecifics>

Die ItemSpecifis sind Werte, die direkt über dem Artikel angezeigt werden. Dazu zählen der Artikelzustand (s. unten) und die hier angegebenen Felder. Mit dem API Call getCategorySpecifics kann man für eine Kategorie empfohlene Werte bekommen (alternativ keine angeben und dann im Backend den Artikel anklicken, dort wird man auch darauf hingewiesen), im Prinzip können die Felder jedoch mit freien Strings befüllt werden. Die vorgeschlagenen Werte dienen dazu, dass eBay Nutzer auf der Website die Suche sinnvoll eingrenzen können, z.B. nach Herstellernummer oder Marke.

<CategoryMappingAllowed>true</CategoryMappingAllowed>
<CategoryBasedAttributesPrefill>true</CategoryBasedAttributesPrefill>
<PrimaryCategory>
<CategoryID>109149</CategoryID>
</PrimaryCategory>
<SecondaryCategory>
<CategoryID>63862</CategoryID>
</SecondaryCategory>

PrimaryCategory ist die erste eBay Kategorie. Diese können z.B. aus dem Java Applet im CSV Manager oder aus der Liste entnommen werden. Am besten ist es, die Artikel so weit wie möglich „nach unten“ zu hängen. Außerdem ist diese Kategorie dafür verantwortlich, wie viel eBay Gebühren gezahlt werden. So sind Artikel im Bereich Sport z.B. günstiger als im Kleidungsbereich. Wenn man also Sportkleidung verkauft, wäre es günstiger, als erste Kategorie eine Unterkategorie des Sportbereichs anzugeben. In der zweiten Kategorie SecondaryCategory kann dann immer noch Kleidung angegeben werden. Für manche Produkte spielt dies aber auch keine Rolle, da viele Kategorien gleich behandelt werden.

Das Tag CategoryMappingAllowed erlaubt es eBay, bei einer Änderung der Kategoriestruktur, die alten Kategorien in die neuen Kategorien umzuwandeln (= zu „mappen“). So stellt man einen reibungslosen Ablauf bei automatisiertem Listing sicher. Andernfalls (also auf false) würde das Produkt nicht gelistet werden und es würde ein Fehler in der AddFixedPriceItemResponse auftauchen.

Mit CategoryBasedAttributesPrefill wird eBay erlaubt, aufgrund der Kategorie(n) (s. oben) schon einige Item spezifische Attribute (Item Specifics, s. oben) auszufüllen. Denkbar wäre z.B. dass alle Items in  der Kategorie Damenjacken in den Item Specifics schon den Gender Damen erhalten.

<PaymentMethods>CCAccepted</PaymentMethods>
<PaymentMethods>Moneybookers</PaymentMethods>
<PaymentMethods>PayPal</PaymentMethods>
<PayPalEmailAddress>paypal@firma.de</PayPalEmailAddress>

Die Bezahloptionen können einfach mit mehreren PaymentMethods Tags untereinander gereiht werden. Wichtig ist hierbei, dass die PayPalEmailAddress als Letztes kommt. Valid Values können in der API Dokumentation nachgeschaut werden, allerdings sind nicht alle Werte immer zulässig. Die E-Mail Adresse muss immer die sein, mit der sich zu erst bei PayPal angemeldet wurde, sonst gibt es eine Warning.

<ConditionID>1000</ConditionID>
<OutOfStockControl>true</OutOfStockControl>

Die ConditionID zeigt in den Item Specifics an, ob der Artikel neu oder gebraucht ist. Die Werte kann man über ein Look-Up Table nachschlagen. Wenn die Verfügbarkeit (Quantity) eines Artikels auf 0 sinkt, wird normalerweise das Listing beendet. Seit Version 823 (vom 5.8.2013) kann man mit dem Tag OutOfStockControl aber das Listing aktiv aber unterdrückt lassen, falls z.B. neue Ware bereits auf dem Weg ist. Dies erspart ein erneutes Listen und damit (außer bei Premium Shops) natürlich auch die Listingkosten.

<ReturnPolicy>
<RefundOption>EUSeller_ReturnRights</RefundOption>
<ReturnsAcceptedOption>ReturnsAccepted</ReturnsAcceptedOption>
<ReturnsWithinOption>Months_1</ReturnsWithinOption>
<Description>Zurückgeben ist kein Problem. Hier steht wie es geht.</Description>
<ShippingCostPaidByOption>Seller</ShippingCostPaidByOption>
</ReturnPolicy>

Die ReturnPolicy ist wichtig für die eBay Garantie. Die Valid Values für RefundOption können auch hier der API Dokumentation entnommen werden oder über den API Call getEbayDetails angefragt werden (daher stammt EUSeller_ReturnRights). Außerdem können diese Details auch im Backend hinterlegt werden. Um die eBay Garantie zu erreichen, sollte ReturnsWithinOption auf einen Monat (Months_1) gesetzt werden. Achtung: Das ist nicht gleichbedeutend mit Days_30. In der Description sollte ein kurzer Text (max. 5000 Zeichen) den Rückgabsprozess erklären. Natürlich könnte man ReturnsAcceptedOption auch auf ReturnsNotAccepted (den einzig anderen Valid Value aus der API Doku) setzen, das wirkt sich aber sicherlich schlecht auf die Verkaufszahlen aus. Schließlich gibt es mit dem ShippingOptionPaidByOption Tag noch ein paar Valid Values um anzugeben, unter welchen Umständen und wer die Rücksendekosten zu tragen hat.

<ShippingDetails>
<ShippingType>Flat</ShippingType>
<ShippingServiceOptions>
<ShippingServicePriority>1</ShippingServicePriority>
<ShippingService>DE_DHLPaket</ShippingService>
<ShippingServiceCost currency="EUR">0.0</ShippingServiceCost>
</ShippingServiceOptions>
</ShippingDetails>

Die ShippingDetails geben die Versand Optionen an. Auch hier gibt es Voreinstellungen im Verkäufer Backend. In diesem Beispiel bietet man kostenlosen Versand an, eine Vorraussetzung für die eBay Garantie. Die Valid Values für ShippingService stehen in einer ziemlich langen Liste in der API Dokumentation. Aufgrund dieser Angabe und der DispatchTimeMax (s. oben) wird der vorläufige Lieferzeitpunkt automatisch errechnet und im Listing angezeigt. Man kann mehrere ShippingServiceOptions definieren und diesen Optionen jeweils eine Priorität in der Auflistung geben, die ShippingServicePriority. Je höher die Zahl desto weiter hinten steht die Option.

<PictureDetails>
<PictureURL>http://eigener-server.de/images/main-bild-2342.jpg</PictureURL>
</PictureDetails>

Als letztes kann noch ein Bild spezifiziert werden. Dieses Bild kann keiner Variante zugeordnet werden („main“-Bild), daher ist es häufig ein Bild, was alle Variationen in einem Bild zeigt. Kann kann Bilder theoretisch auch bei Item und Variante zeigen, allerdings taucht es dann in der Item Ansicht doppelt auf. Ich gehe hier davon aus, dass die Bilder selbst gehostet werden. Man kann hier für auch den eBay Picture Service (EPS) nutzen, dazu mehr in der eBay Hilfe.

Die Verfügbarkeit (Tag Quantity) und den Preis (Tag StartPrice) müssen auf Item Ebene nicht angegeben werden, wenn sie in den Variationen definiert werden. Gibt man den Wert trotzdem an, wird er ignoriert.

1.3. Variationen

Die Variationen werden alle in dem überordneten Tag Variations (mit s!) angegeben. Wir eröffnen also die Variationenlistung mit diesem Tag

<Variations>

Als erstes empfehle ich erst einmal die Variation Specifics (das Pendant zu Item Specifics) im VariationSpecificsSet aufzuzählen, also alle möglichen Werte für die verschiedenen Dimensionen in denen es dieses Produkt gibt. Bei Kleidung könnten dies z.B. die folgenden Werte sein:

<VariationSpecificsSet>
<NameValueList>
<Name>Farbe</Name>
<Value>schwarz</Value>
<Value>rot</Value>
</NameValueList>
<NameValueList>
<Name>Größe</Name>
<Value>S</Value>
<Value>M</Value>
<Value>L</Value>
</NameValueList>
</VariationSpecificsSet>

Jetzt erst folgt die erste Variation (ohne s). Hier dürfen in den VariationSpecifics nur Werte angegeben werden, die auch oben spezifiziert wurden. Nicht alle Händler sind außerdem dazu berechtigt den OriginalRetailPrice (dt. unverbindliche Preisempfehlung UVP) anzugeben, am besten kontaktiert man hier den eBay Support. Die SKU ist hier eine spezielle SKU für ein Produkt in Abhängigkeit von den Dimensionen (z.B. Farbe und Größe). Sollte eine solche SKU nicht existieren, wäre eine Idee, sie aus Parent-SKU und Kürzeln für die Dimensionen zusammenzubauen (hier: 2342 => ParentID, rS => Farbe: rot, Größe: S). Die Quantity ist die verfügbare Anzahl in den angegebenen Dimensionen und obwohl der Tag StartPrice heisst, ist hier auch bei FixedPriceItems der Verkaufspreis gemeint.

<Variation>
<SKU>2342-rS</SKU>
<Quantity>123</Quantity>
<StartPrice>99.00</StartPrice>
<DiscountPriceInfo>
<OriginalRetailPrice>129.00</OriginalRetailPrice>
</DiscountPriceInfo>
<VariationSpecifics>
<NameValueList>
<Name>Farbe</Name>
<Value>rot</Value>
</NameValueList>
<NameValueList>
<Name>Größe</Name>
<Value>S</Value>
</NameValueList>
</VariationSpecifics>
</Variation>

Die Bilder für die verschiedenen Variationen werden widererwartend nicht im Variation Tag angegeben, sondern anhand der vorher definierten Dimensionen im Pictures Tag gemappt. Dabei reicht eine Dimension aus. Bei Kleidung z.B. wäre hier die Farbe relevant, die Größe spielt keine Rolle.

<Pictures>
<VariationSpecificName>Farbe</VariationSpecificName>
<VariationSpecificPictureSet>
<VariationSpecificValue>rot</VariationSpecificValue>
<PictureURL>http://eigener-server/images/variationen/bild-2342-rot.jpg</PictureURL>
</VariationSpecificPictureSet>
</Pictures>

Das war es auch schon fast. Jetzt müssen nur noch alle offenen Tags geschlossen werden.

</Variations>
</Item>
</AddFixedPriceItemRequest>

Jetzt können noch weitere AddFixedPriceItemRequest angehängt werden, bis die Datei 20.000 Zeilen lang ist. Dann muss auch der BulkDataExchangeRequest geschlossen werden.

</BulkDataExchangeRequests>

Wie man diese Datei in PHP erstellt, wird im zweiten Teil erklärt.