Integration eines Online-Shops auf 1C-Bitrix mit Mindbox

Um Loyalitätssysteme zu entwickeln, wenden sich Online-Shops den Marketing-Automatisierungsplattformen Customer Data Platform (CDP) zu. Gleichzeitig müssen Sie für eine erfolgreiche Integration manchmal mehr Daten speichern, als in der API-Dokumentation angegeben.



Wir werden Ihnen sagen, welche Daten wir benötigen, um einen Speicher auf 1C-Bitrix in die Mindbox-Plattform zu integrieren, wie Sie sie mithilfe der API und des SDK erhalten können und wie Sie einen kombinierten Ansatz mit asynchronem Datenversand verwenden.







Mithilfe von Kundendatenplattform-Diensten „erkennen“ Einzelhändler das Porträt ihres Kunden, einschließlich Verhaltensdaten. Diese Informationen werden sicher in CDP gespeichert und unterstützen Einzelhändler bei Marketingkampagnen und Analysen.



Wenn ein Kunde einen Fernseher oder ein anderes Produkt in den Warenkorb legt, speichert CDP diese Daten. Auf dieser Grundlage können Einzelhändler ihre Interaktionen mit Benutzern erweitern, beispielsweise Empfehlungen und Rabatte für ähnliche Produkte anbieten.



Einer unserer Kunden - eine Kette von Elektronikgeschäften - entschied sich für die Verbindung zur Mindbox CDP-Plattform und wandte sich an uns, um Hilfe bei der Integration zu erhalten. Wir haben die Integration für wichtige Benutzerszenarien durchgeführt: Autorisierung, Hinzufügen zum Warenkorb, Zahlung usw.



Hintergrund



Online-Shops können auf zwei Arten eine Verbindung zu Mindbox herstellen: über die API oder das JavaScript SDK (die Unterschiede werden später erläutert).



Um den besten Weg zu wählen, haben wir uns der Mindbox-Dokumentation zugewandt. Wenn nicht genügend Informationen vorhanden waren, haben wir dem Manager Fragen gestellt. Wir haben festgestellt, dass unsere Zusammenarbeit mit einer Phase schnellen Wachstums der Mindbox-Plattform zusammenfiel: Die durchschnittliche tägliche Belastung der Mindbox-API-Aufrufe verdoppelte sich (bis zu 120.000 Anfragen pro Minute, zu Spitzenzeiten - bis zu 250.000 ). Dies bedeutete, dass während des Black Friday und anderer Verkäufe aufgrund einer zusätzlichen Erhöhung der Auslastung das Risiko bestand, dass der CDP-Dienst nicht verfügbar war und keine Daten von dem in ihn integrierten Online-Shop erhielt.



Mindbox reagierte schnell auf dieses Problem und begann, die Architektur und Infrastruktur seiner IT-Systeme zu verbessern, um einen vierfachen Sicherheitsfaktor zu erreichen. Wir mussten wiederum sicherstellen, dass die Kaufdaten reibungslos an Mindbox gesendet wurden. Dies erforderte die Wahl der zuverlässigsten Integrationsmethode.



Mindbox-Integrationsmethoden



Wie oben erwähnt, schlägt Mindbox vor, eine API oder ein JavaScript-SDK zum Herstellen einer Verbindung zu verwenden. Als nächstes werden wir ihre Funktionen betrachten.



  • JavaScript SDK



Die Bibliothek ist ein "Wrapper" über die vom Dienst bereitgestellte API. Die Vorteile sind die einfache Integration und die Möglichkeit der asynchronen Datenübertragung. Am besten geeignet für Fälle, in denen nur die Webplattform unterstützt werden muss.



Einschränkungen: Möglicher Datenverlust, wenn Mindbox zum Zeitpunkt des Sendens nicht verfügbar ist. Plattformskripte werden nicht geladen, wenn auf der Seite des Online-Shops js-Fehler auftreten.



  • API-Integration



Die Integration des Geschäfts in Mindbox kann über die API erfolgen. Diese Methode reduziert die Abhängigkeit von JavaScript und eignet sich auch zum Einrichten der asynchronen Datenübermittlung.



Einschränkungen: Wir waren mit der Tatsache konfrontiert, dass einige Cookie-Daten nicht empfangen wurden, nämlich die eindeutige Benutzer-ID auf dem Gerät (mindboxDeviceUUID). Es muss in den meisten Mindbox-Vorgängen übergeben werden, um Benutzerinformationen zu verknüpfen.



In der Dokumentation sind diese Cookies nicht für alle Vorgänge erforderlich. Um eine unterbrechungsfreie Datenübertragung zu erreichen, haben wir dieses Problem mit dem Manager von Mindbox besprochen. Wir haben festgestellt, dass es für maximale Zuverlässigkeit ratsam ist, immer ein Cookie zu senden. Sie müssen jedoch das JavaScript SDK verwenden, um Cookies zu erhalten.



Kombinierte Methode



Wir haben die oben beschriebenen Integrationsmethoden in Betracht gezogen, aber in ihrer reinen Form waren sie für unser Projekt nicht geeignet. Um die geschäftlichen Probleme des Einzelhändlers zu lösen und ein Loyalitätssystem aufzubauen, musste ein vollständiger Datensatz über Benutzeraktionen, einschließlich der Kennung aus dem Cookie, an Mindbox übertragen werden. Gleichzeitig wollten wir die Abhängigkeit von JavaScript und das Risiko eines Datenverlusts verringern, wenn Mindbox vorübergehend nicht verfügbar ist.



Daher haben wir uns der dritten kombinierten Methode zugewandt: Wir arbeiten sowohl mit der API als auch mit dem JavaScript-SDK mithilfe unseres Warteschlangenmoduls.



Mit dem Javascript SDK identifizieren wir den Benutzer auf der Site (mindboxDeviceUUID). Dann bilden wir auf der Serverseite eine Anfrage mit allen erforderlichen Daten und stellen sie in die Warteschlange. Anforderungen in der Warteschlange über die API werden an den Mindbox-Dienst gesendet. Wenn die Antwort Nein lautet, wird die Anforderung erneut in die Warteschlange gestellt. Daher sendet Mindbox beim Senden von Daten einen vollständigen Satz der erforderlichen Informationen.



Im folgenden Beispiel können Sie mit der Sender- Klasse eine Anforderung erfassen und senden, indem Sie die Antwort zunächst verarbeiten. Die Klasse verwendet Daten aus dem Befehl selbst (Anforderungs- / Antworttyp, Geräte-UUID usw.) und aus den Moduleinstellungen (Parameter für die Arbeit mit der API, Token usw.).



<?php
declare(strict_types=1);

namespace Simbirsoft\MindBox;

use Bitrix\Main\Web\Uri;
use Bitrix\Main\Web\HttpClient;
use Simbirsoft\Base\Converters\ConverterFactory;
use Simbirsoft\MindBox\Contracts\SendableCommand;

class Sender
{
    /** @var Response   */
    protected $response;
    /** @var SendableCommand  */
    protected $command;

    /**
     * Sender constructor.
     *
     * @param SendableCommand $command
     */
    public function __construct(SendableCommand $command)
    {
        $this->command = $command;
    }

    /**
     *    .
     *
     * @return array
     */
    protected function getHeaders(): array
    {
        return [
            'Accept'        => Type\ContentType::REQUEST[$this->command->getRequestType()],
            'Content-Type'  => Type\ContentType::RESPONSE[$this->command->getResponseType()],
            'Authorization' => 'Mindbox secretKey="'. Options::get('secretKey') .'"',
            'User-Agent'    => $this->command->getHttpInfo('HTTP_USER_AGENT'),
            'X-Customer-IP' => $this->command->getHttpInfo('REMOTE_ADDR'),
        ];
    }

    /**
     *   .
     *
     * @return string
     */
    protected function getUrl(): string
    {
        $uriParts = [
            Options::get('apiUrl'),
            $this->command->getOperationType(),
        ];
        $uriParams = [
            'operation'  => $this->command->getOperation(),
            'endpointId' => Options::get('endpointId'),
        ];

        $deviceUUID = $this->command->getHttpInfo('deviceUUID');
        if (!empty($deviceUUID)) {
            $uriParams['deviceUUID'] = $deviceUUID;
        }

        return (new Uri(implode('/', $uriParts)))
            ->addParams($uriParams)
            ->getUri();
    }

    /**
     *  .
     *
     * @return bool
     */
    public function send(): bool
    {
        $httpClient = new HttpClient();

        $headers = $this->getHeaders();
        foreach ($headers as $name => $value) {
            $httpClient->setHeader($name, $value, false);
        }

        $encodedData = null;
        $request = $this->command->getRequestData();
        if (!empty($request)) {
            $converter = ConverterFactory::factory($this->command->getRequestType());
            $encodedData = $converter->encode($request);
        }

        $url = $this->getUrl();
        if ($httpClient->query($this->command->getMethod(), $url, $encodedData)) {
            $converter = ConverterFactory::factory($this->command->getResponseType());
            $response = $converter->decode($httpClient->getResult());
            $this->response = new Response($response);
            return true;
        }
        return false;
    }

    /**
     * @return Response
     */
    public function getResponse(): Response
    {
        return $this->response;
    }
}


Das Merkmal Sendable enthält alle möglichen Befehlseinstellungen zum Senden einer Anfrage an Mindbox, einschließlich vordefinierter, wie z. B. die Art der Anfrage / Antwort, die Anforderungsmethode und den Parameter sync / async. Es enthält auch Methoden, die allen Befehlen gemeinsam sind.



<?php
declare(strict_types=1);

namespace Simbirsoft\MindBox\Traits;

use RuntimeException;
use Bitrix\Main\Context;
use Simbirsoft\MindBox\Type;
use Simbirsoft\MindBox\Sender;
use Simbirsoft\MindBox\Response;
use Bitrix\Main\Localization\Loc;
use Simbirsoft\MindBox\Contracts\SendableCommand;

Loc::loadMessages($_SERVER['DOCUMENT_ROOT'] .'/local/modules/simbirsoft.base/lib/Contracts/Command.php');

trait Sendable
{
    /** @var string   (GET/POST) */
    protected $method = Type\OperationMethod::POST;
    /** @var string   (sync/async) */
    protected $operationType = Type\OperationType::ASYNC;
    /** @var string   (json/xml) */
    protected $requestType = Type\ContentType::JSON;
    /** @var string   (json/xml) */
    protected $responseType = Type\ContentType::JSON;
    /** @var array   */
    protected $data = [];

    /**
     *  .
     * @return string
     */
    abstract public function getOperation(): string;

    /**
     *  .
     *
     * @return array
     */
    abstract public function getRequestData(): array;

    /**
     * HTTP  
     *
     * @return string
     */
    public function getMethod(): string
    {
        return $this->method;
    }

    /**
     *  
     *
     * @return string
     *
     * @noinspection PhpUnused
     */
    public function getOperationType(): string
    {
        return $this->operationType;
    }

    /**
     *  .
     *
     * @return string
     *
     * @noinspection PhpUnused
     */
    public function getRequestType(): string
    {
        return $this->requestType;
    }

    /**
     *  .
     *
     * @return string
     *
     * @noinspection PhpUnused
     */
    public function getResponseType(): string
    {
        return $this->responseType;
    }

    /**
     *   
     *
     * @return void
     */
    public function initHttpInfo(): void
    {
        $server = Context::getCurrent()->getServer();
        $request = Context::getCurrent()->getRequest();

        $this->data = [
            'X-Customer-IP' => $server->get('REMOTE_ADDR'),
            'User-Agent'    => $server->get('HTTP_USER_AGENT'),
            'deviceUUID'    => $request->getCookieRaw('mindboxDeviceUUID'),
        ];
    }

    /**
     *    
     *
     * @param string $key
     * @param string $default
     *
     * @return string
     *
     * @noinspection PhpUnused
     */
    public function getHttpInfo(string $key, string $default = ''): string
    {
        return $this->data[$key] ?? $default;
    }

    /**
     *  .
     *
     * @return void
     *
     * @throws RuntimeException
     */
    public function execute(): void
    {
        /** @var SendableCommand $thisCommand */
        $thisCommand = $this;
        $sender = new Sender($thisCommand);
        if ($sender->send()) {
            throw new RuntimeException(Loc::getMessage('BASE_COMMAND_NOT_EXECUTED'));
        }

        $response = $sender->getResponse();
        if (!$response->isSuccess()) {
            throw new RuntimeException(Loc::getMessage('BASE_COMMAND_NOT_EXECUTED'));
        }

        if (!$this->prepareResponse($response)) {
            throw new RuntimeException(Loc::getMessage('BASE_COMMAND_NOT_EXECUTED'));
        }
    }

    /**
     *   .
     *
     * @param Response $response
     *
     * @return bool
     */
    public function prepareResponse(Response $response): bool
    {
        // $body   = $response->getBody();
        // $status = $body['customer']['processingStatus'];
        /**
         *  :
         * AuthenticationSucceeded -   
         * AuthenticationFailed         -    
         * NotFound                          -    
         */
        return true;
    }
}


Betrachten Sie als Beispiel das Benutzerautorisierungsereignis. Im Authorization Event Handler fügen wir unserer Warteschlange ein Objekt der AuthorizationCommand-Klasse hinzu. In dieser Klasse findet die minimal erforderliche Vorbereitung von Informationen statt, da sich zum Zeitpunkt der Ausführung des Befehls die Daten in der Datenbank ändern können und Sie sie speichern müssen. Außerdem werden die entsprechenden Parameter für die Anforderung in Mindbox festgelegt. In diesem Fall ist dies der Name des Vorgangs (wir finden ihn im Mindbox-Administrationsbereich). Darüber hinaus können Sie den Typ der Anforderung / Antwort, die Anforderungsmethode und den Parameter sync / async gemäß dem Merkmal Sendable angeben.



<?php
declare(strict_types=1);

namespace Simbirsoft\MindBox\Commands;

use Simbirsoft\Queue\Traits\Queueable;
use Simbirsoft\MindBox\Traits\Sendable;
use Simbirsoft\Queue\Contracts\QueueableCommand;
use Simbirsoft\MindBox\Contracts\SendableCommand;

final class AuthorizationCommand implements QueueableCommand, SendableCommand
{
    use Queueable, Sendable;

    /** @var array   */
    protected $user;

    /**
     * AuthorizationCommand constructor.
     *
     * @param array $user
     */
    public function __construct(array $user)
    {
        $keys = ['ID', 'EMAIL', 'PERSONAL_MOBILE'];
        $this->user = array_intersect_key($user, array_flip($keys));

        $this->initHttpInfo();
    }

    /**
     *  .
     *
     * @return string
     */
    public function getOperation(): string
    {
        return 'AuthorizationOnWebsite';
    }

    /**
     *  .
     *
     * @return array
     */
    public function getRequestData(): array
    {
        return [
            'customer' => [
                'email' => $this->user['EMAIL'],
            ],
        ];
    }
}


Modul-Interaktionsschema



In unserem Projekt haben wir drei Module identifiziert:



  • Base



Speichert allgemeine Dienstprogrammklassen und Schnittstellen (z. B. Befehlsschnittstellen), die dann im gesamten Projekt verwendet werden können.



  • Warteschlangenmodul



Die Interaktion mit Mindbox wird über Befehle implementiert. Um sie nacheinander auszuführen, verwenden wir unser Warteschlangenmodul. Dieses Modul speichert eingehende Befehle und führt sie zu gegebener Zeit aus.



  • Mindbox-Integrationsmodul



Dieses Modul "fängt" Ereignisse auf der Site ab, z. B. Autorisierung, Registrierung, Erstellen einer Bestellung, Hinzufügen zum Warenkorb und andere, generiert dann Befehle und sendet sie an das Warteschlangenmodul.







Das Mindbox-Modul überwacht Ereignisse und zugehörige Informationen auf der Site, einschließlich Cookies, bildet daraus einen Befehl und stellt ihn in eine Warteschlange. Wenn das Warteschlangenmodul einen Befehl aus der Warteschlange abruft und ausführt, werden Daten gesendet. Wenn die Antwort von Mindbox negativ ist - der nicht erfolgreich ausgeführte Befehl wird an das Ende der Warteschlange verschoben, wenn er positiv ist - wird der erfolgreich ausgeführte Befehl aus der Warteschlange entfernt.



Mit der oben beschriebenen kombinierten Methode konnten wir somit eine reibungslose Datenübertragung zu Mindbox sicherstellen.



Zusammenfassen



In diesem Artikel haben wir untersucht, wie ein Online-Shop eine Verbindung zur Kundendatenplattform herstellen kann, um Loyalitätssysteme zu entwickeln.



In unserem Beispiel wurden zwei Hauptverbindungsmethoden in der Mindbox-Dokumentation beschrieben: über das Javascript SDK und über die API. Um die Zuverlässigkeit der Datenübertragung auch bei vorübergehender Nichtverfügbarkeit des CDP-Dienstes zu verbessern, haben wir eine dritte kombinierte Methode ausgewählt und implementiert: Verwendung von API und Javascript SDK mit asynchronem Datenversand.



Vielen Dank für Ihre Aufmerksamkeit! Wir hoffen, dieser Artikel war hilfreich für Sie.



All Articles