Bestellte Artikel an externe API übermitteln

Hallo zusammen,

ich möchte Shopware 5 dazu bringen, im Fall einer Bestellung die bestellten Artikel an eine externe API zu melden.
Im Grunde, wie die Bestellbestätigung, nur als API-Aufruf. Die Hooks scheinen das richtige Mittel, allerdings finde ich dazu in der Doku (noch) keinen Ansatz.
Wer weiß, wie das Event heißt, wenn eine Bestellung ausgelöst wurde? Und in welchen Variablen stehen die bestellten Artikel-Nummern?

Vielen Dank für die Hilfe vorab.

 

Beste Grüße

Andreas

Hallo Andreas,

Du könntest den Event für das Senden der Bestellbestätigungsmail nutzen. Ich würde Dir empfehlen den Versand asynchron zu gestalten (entweder über einen internen Controller Aufruf oder über einen Cronjob). Die Übertragung ist dann entkoppelt vom Bestellvorgang selbst und es kann nicht vorkommen, dass der Bestellabschluss für einen Besucher hängt, wenn die API mal etwas länger braucht. Außerdem kannst Du bei Fehlern noch nicht übertragene Bestellungen erneut senden. Die Daten zur Bestellung würde ich direkt aus den jeweiligen Order-Ressourcen frisch auslesen und dann in den s_order_attributes ein neues Feld mit Status der Übertragung und ggf. ein weiteres in welchem die Response bzw. eine Fehlermeldung von der Übertragung gespeichert werden kann.

 

Viele Grüße

Marc

 

www.mndnext.de

Jetzt neu im Plugin Store: Plugin-Update Benachrichtigungen (MND Plugin Notification)

1 „Gefällt mir“

Nur so Gedanken:

Eben habe ich ein Plugin für den Checkout erstellt, was aber nur beim Bestellabschluss zum Einsatz kommt. Hierzu habe ich “Shopware_Controllers_Frontend_Checkout::finishAction::before” als Hook benutzt. Vielleicht ist das der gesuchte Anhaltspunkt. Alle Daten von der Bestellung liegen in der Session. Per CURL könnten diese dann an eine externe API gesendet werden. Um an die nötigen Informationen zu kommen habe ich folgendne Weg benutzt:

$this->session = Shopware()->Session();
$sOrderVariables = $this->session['sOrderVariables']->getArrayCopy()

Wie schon erwähnt, nur so als Gedanken.

 

1 „Gefällt mir“

Vielen für die beiden Anregungen. Ich habe mich für die Echtzeit entschieden und werde die Daten ausserhalb von Shopware verarbeiten. Nun zum Problem. Ich kriege aus Shopware keine Daten und verstehe nicht, warum das so ist.

Das Plugin läuft an der richtigen Stelle im Bestellprozess. Es ruft meine externe API auf und soll die bestellten Artikelnummern übermitteln. Neben den Artikelnummern bekomme ich noch die Id und den Token der API aus der Config. Das klappt. Allerdings finde ich scheinbar nicht das richtige Datenarray in dem ich die ‚articleNumber‘ finde. Die benötigten Daten kommen einfach nicht rüber… Warscheinlich ganz einfach, nur ich komme nicht drauf:
 

class Shopware_Plugins_Frontend_OrderRepeater_Bootstrap extends Shopware_Components_Plugin_Bootstrap {

    public function getVersion() {

		return "1.0.0";
    }

    public function getLabel() {
        return 'Rückmelder';
    }

    public function install() {

        $this->subscribeEvent(
            'Shopware_Controllers_Frontend_Checkout::finishAction::before',
            'repeatOrderDetails'
        );

		$this->createConfig();

        return true;
    }

    private function createConfig()
    {
        $form = $this->Form();
        $form->setElement('text', 'apiid', array('label' => 'API-Id'));
        $form->setElement('text', 'apitoken', array('label' => 'API-Token'));
        
    }

    public function uninstall() {
        return true;
    }
    

    public function repeatOrderDetails(Enlight_Event_EventArgs $args) {

	//	$this->session = Shopware()->Session();
	//	$sOrderVariables = $this->session['sOrderVariables']->getArrayCopy();		
		
		$sApiToken = $this->Config()->get('apitoken');
		$sApiId = $this->Config()->get('apiid');
		
	        $details = $args->get('details');			

		$ordernumbers = "";
		foreach ($details as $key) {
			if ($ordernumbers != "") $ordernumbers .= ",";
			$ordernumbers .= $details[$key]["articleNumber"]; 
		}
		
                ...
    }
    
}

 

1 „Gefällt mir“

Wie wäre es damit standardmäßig zu verfahren und regelmäßig über die API zu prüfen ob es neue Bestellungen gibt?

Noch ein paar Anmerkungen von mir:

  1. Das Shopware_Controllers_Frontend_Checkout::finishAction::before ist eine schlechte Idee. Wen der Endkunde die Finish-Seite neulädt (F5), wird das Event erneut getriggert. Am besten nutzt du das Event Shopware_Modules_Order_SaveOrder_ProcessDetails. Dort werden dir alle Bestellpositionen, die sOrder-Core-Klasse und die OrderId übergeben. An dieser Stelle ist die Bestellung auch schon sicher eingegangen und die der Datenbank gespeichert.

  2. Wie bereits von @mndnext‍ erwähnt, die Übertragung der Daten an die API solltest du nachträglich machen. Oder zumindest einen geringen Timeout-Wert wählen. Sollte es fehlschlagen -> Versuche es später erneut (evtl. via CronJob)

Viele Grüße

2 „Gefällt mir“

@NextMike die API habe ich als Fallback im Gepäck. Ich möchte zunächst sehen, wie performant die Echtzeit-Variante (bei z.B. umfangreichen Bestellungen) ist.

Es wird ein System aus verschiedenen Shops aufgebaut, bei denen die Shops auf Shopware einen Endpunkt bilden. Ein umfangreiches System wird die Datenhoheit im Backend halten, spezielle Daten über die API in die Shopware-Shops spielen und hätte gerne eine zeitnahe Rückmeldung, sobald ein Artikel verkauft ist.
 

@slimki.
Zu 1. Danke für den Hinweis und das Event. Klingt komisch, aber ich finde nichts. Keine Liste mit Events und welche Daten dann zur Verfügung stehen.
Ich werde der Sache nachgehen und gucken, wo es mich hinbringt.

Zu 2. Ja, ggf. später. Ich möchte tatsächlich sehen, wie performant wir Daten austauschen können. Ich möchte dazu unbedingt nur die relevanten Artikelnummern filtern und übertragen…

@KOBAB schrieb:

@slimki.
Zu 1. Danke für den Hinweis und das Event. Klingt komisch, aber ich finde nichts. Keine Liste mit Events und welche Daten dann zur Verfügung stehen.
Ich werde der Sache nachgehen und gucken, wo es mich hinbringt.

Ich hab dir das oben absichtlich verlinkt Wink. Klick’ einfach mal drauf, dann siehst du was dir übergeben wird (subject, details, orderId). Zugreifen kann man dann im Event-Listener z.B. via $arguments->get(‚details‘) (alternativ $arguments->getDetails()).

Viele Grüße 

1 „Gefällt mir“
public function repeatOrderDetails(Enlight_Event_EventArgs $args) {

    $orderid = $args->get('orderId');

}

Ich habe mich nun in das Event ‘Shopware_Modules_Order_SaveOrder_ProcessDetails’ eingeklinkt. In ‘repeatOrderDetails’ stelle ich Daten zusammen und rufe die externe API auf. Der Aufruf klappt und auch die Daten aus der Konfiguration werden verwendet. Ich bekomme allerdings keine anderen Daten zur Bestellung. Nicht mal die ‘orderId’. Ist alles leer. Hat jemand noch eine Idee, woran das liegen kann?

Mhh das müsste funktionieren… Hast du das Plugin nach der Änderung auch neuinstalliert? Du registrierst den Listener ja in deiner „install“-Methode. Die wird neu bei einer Neuinstallation gefeuert. Wenn ja, was steht in der s_core_subscribes-Datenbanktabelle? Steht da dein Event-Listener korrekt drin?

Viele Grüße

1 „Gefällt mir“

Hallo,

schön, dass Du Schritt 1 schon hast. Wegen dem Durchreichen an Deine API kann ich dir jedoch, entgegen allen bisher gebrachten Empfehlungen einen MessageQueue Server empfehlen. Konkret kann ich Dir da RabbitMQ empfehlen, damit kann man echt vieles machen: Prinzipiell folgt der Server dem Consumer/Producer Prinzip. Also Dein Producer ist in dem Fall die vom Kunden ausgelöste Bestellung. Du gibst dann per Socket Verbindung eine kurze Nachricht an den RabbitMQ Server. Dieser entscheidet daraufhin anhand der Exchange Queue, wie mit der Nachricht weiter verfahren wird, bzw. wer diese erhält. Hier ist man wirklich recht flexibel. Außerdem bietet RabbitMQ Ausfallsicherheit. Sollte also mal der Server abstürzen und es liegen noch Messages in der Queue, dann kann dieser problemlos nach einem Neustart die Arbeit wieder aufnehmen.

 

Ich verstehe aber nicht wofür Du überhaupt eine API brauchst, wenn Du die Daten nur zwischen verschiedenen Shopware Shop Instanzen durch reichen willst. Vielleicht ist Shopware Connect der bessere Weg für Dich? Oder eine simple Datenbank Replikation?

 

 

MFG

 

derwunner

1 „Gefällt mir“

@simkli:
Ja, ich deinstalliere das alte Plugin, lade das neue hoch und aktiviere es. Ich habe dabei hin und wieder auch Parameter an die API verändert, um zu sehen, dass immer die aktuelle Version läuft. Grundsätzlich klappt es ja, die API wird zum richtigen Zeitpunkt aufgerufen, die Konfiguration wird verwendet etc. Es kommen aber keine Daten: $args->get(‚whatsoever‘) ist leer. Ich vermute, dass evtl. die Installation ein Problem hat, z.B. der Enlight_Controller oder so.
An die DB komme ich nicht ran. (Ist auch nicht meine Shopware-Installation.)
 

@derwunner:

Klingt prima. Ich bin hier erst am Anfang. Noch bekomme ich keine Daten… Ich kann noch nicht sagen, wie performant das in Echtzeit sein wird.
Als Fallback hätte ich die Datenbank erweitern lassen und dann über die REST-API geschaut, ob es noch Daten gibt, die die Zentrale nicht kennt. Das Grundkonzept basiert allerdings auf der Kommunikation in Echtzeit.
 

Shopware ist ein Außenposten in dem Shop-Verbund. Ein anderes Shop-System wird die Datenhoheit halten, Daten aufbereiten und verarbeiten und u.a. auf Shopware-Shops verteilen. Daten rein klappt prima über die REST-API. Verkauft ein Shop einen Artikel, muss die Zentrale das zeitnah wissen. Die Shopware-Shops sollten hier ein Plugin bekommen, das verkaufte Artikel meldet.  Das Plugin läuft an der richtigen Stelle, meldet allerdings stets ohne Daten.

@KOBAB schrieb:

@simkli:
Ja, ich deinstalliere das alte Plugin, lade das neue hoch und aktiviere es. Ich habe dabei hin und wieder auch Parameter an die API verändert, um zu sehen, dass immer die aktuelle Version läuft. Grundsätzlich klappt es ja, die API wird zum richtigen Zeitpunkt aufgerufen, die Konfiguration wird verwendet etc. Es kommen aber keine Daten: $args->get(‚whatsoever‘) ist leer. Ich vermute, dass evtl. die Installation ein Problem hat, z.B. der Enlight_Controller oder so.
An die DB komme ich nicht ran. (Ist auch nicht meine Shopware-Installation.)
 

Da muss irgendwo der Fehler liegen. Leider kann man dir so nur schwer helfen. Du müsstest mal den gesamten Code posten. Woher weißt du denn das die Variablen leer sind? Wie gibst du das aus? Vielleicht sind sie gefüllt und werden nur nicht korrekt an die API übermittelt?

Um in die Datenbank zu schauen, kannst du auch das praktische Plugin von @shyim nutzen: https://github.com/shyim/adminer-for-shopware/releases

Viele Grüße

1 „Gefällt mir“
public function repeatOrderDetails(Enlight_Event_EventArgs $args) {
		
    $sApiToken = $this->Config()->get('apitoken');
    $sApiId = $this->Config()->get('apiid');
		
    $sDetails = $args->getDetails();
	    
    $ordernumber = $sDetails['orderId'];
    if ($ordernumber == "") $ordernumber = "empty";
		
    $ch = fopen("https://...../?id=$sApiId&token=$sApiToken&f=$ordernumber","r");
    $content = fread($ch,100);
    fclose($ch);

}

Auf der anderen Seite ist ein Skript, dass die $_GET vars nimmt, in eine Mail packt und an mich sendet.

@KOBAB schrieb:

public function repeatOrderDetails(Enlight_Event_EventArgs $args) {

$sApiToken = $this->Config()->get(‚apitoken‘);
$sApiId = $this->Config()->get(‚apiid‘);

$sDetails = $args->getDetails();

$ordernumber = $sDetails[‚orderId‘];
if ($ordernumber == „“) $ordernumber = „empty“;

$ch = fopen(„https://…/?id=$sApiId&token=$sApiToken&f=$ordernumber“,„r“;
$content = fread($ch,100);
fclose($ch);

}

Auf der anderen Seite ist ein Skript, dass die $_GET vars nimmt, in eine Mail packt und an mich sendet.

Da fehlt immer noch die Hauptsache. Außerdem ist das nur ein lesender Zugriff. Du möchtest ja was an die API senden und nicht etwas von der API empfangen. Außerdem empfiehlt sich für solche Zwecke GuzzleHttp: Guzzle, PHP HTTP client — Guzzle Documentation

Das ist bereits schon in Shopware vorhanden, Du musst es nur noch verwenden :wink:

Das ist wesentlich komfortabler, als alles per cURL zu machen. Darüber hinaus noch wesentlich weniger Fehleranfälliger als cURL. Kennst Du Dich einmal mit der Bibliothek aus, dann bist Du damit schneller als mit cURL.

 

 

MFG

 

derwunner

1 „Gefällt mir“

Welche Hauptsache meinst du?
Ich habe alles auf ein Minimum zurückgefahren, weil ich offenbar nichts aus dem Event rausbekomme. Jetzt ist es ein einfacher fopen, den ich zum Testen via Browser simulieren kann. Ich hätte gerne die bestellten Artikelnummern gesammelt und übertragen. Da das irgendwie nicht geht, versuche ich aktuell die orderId und bekomme auch hier nichts über die $args.
 

Ohne Witz. Es geht jetzt.

Auf einmal findet sich in $args->get(‚details‘) ein Array, das ich auseinander nehmen kann. Zumindest bei den letzten Test-Bestellungen in Folge.
Habt Dank für die Hilfe! Ich weiß nicht, warum es plötzich geht, aber ich rühre das nun nicht mehr an.
Anbei der komplette Code für die Nachwelt:
 

class Shopware_Plugins_Frontend_OrderRepeater_Bootstrap extends Shopware_Components_Plugin_Bootstrap {

    public function getVersion() {

        return '1.0.0';
    }

    public function getLabel() {

        return 'Order Repeater';
    }

    public function install() {

        $this->subscribeEvent(
            'Shopware_Modules_Order_SaveOrder_ProcessDetails',
            'OrderRepeater'
        );

	$this->createConfig();

        return true;
    }

    private function createConfig() {
        $form = $this->Form();
        $form->setElement('text', 'apiid', array('label' => 'API-Id'));
        $form->setElement('text', 'apitoken', array('label' => 'API-Token'));
        
    }

    public function uninstall() {
        return true;
    }
    

    public function OrderRepeater(Enlight_Event_EventArgs $args) {
		
	$sApiToken = $this->Config()->get('apitoken');
	$sApiId = $this->Config()->get('apiid');
		
	$OrderDetails = $args->get('details');
		
	$article = "";
	foreach($OrderDetails as $item){ 
			
		if ($article != "") $article .= ",";
		$article .= $item["ordernumber"]; 

	}
		
	$ch = fopen("https://...../?id=$sApiId&token=$sApiToken&f=$article","r");
	$content = fread($ch,100);
	fclose($ch);

    }

}

Ich werde mir „Guzzle“ noch ansehen und dann einige Tests unter Last mit vielen, vielen Artikeln machen.

Noch einmal vielen Dank für die Unterstützungen und Tipps! You rock!

1 „Gefällt mir“

Also, nimm bitte keinen Hook dafür, sondern einfach das entsprechende postDispatch Event. Dann bekommst Du auch alle Daten, die Du brauchst, übergeben. Also was ich meine ist, klinke Dich nach dem form submit Request ein, nachdem die Bestellung in der Datenbank gespeichert wurde => postDispatch.

Wenn Du nicht sicher bist, welche Parameter du empfangen kannst, dann schau in der Action nach, welche dort verarbeitet werden. Die hast Du auf jeden Fall.