Integration of an online store on 1C-Bitrix with Mindbox

To develop loyalty systems, online stores are turning to marketing automation platforms, Customer Data Platform (CDP). At the same time, sometimes for successful integration it is necessary to save more data than specified in the API documentation.

We will tell you what data we need to integrate a store on 1C-Bitrix with the Mindbox platform, how it can be obtained using the API and SDK, and how to use a combined approach with asynchronous data sending.

With the help of Customer Data Platform services, retailers “recognize” the profile of their customer, including behavioral data. This information is stored securely in CDP and assists retailers with marketing campaigns and analytics.

When a customer adds a TV or any other item to the cart, CDP stores this data. Based on them, retailers can expand their interactions with users, for example, offer recommendations and discounts on similar products.

One of our clients – a chain of electronics stores – decided to connect to the CDP platform Mindbox and turned to us for help in integration. We carried out integration for key user scenarios: authorization, adding to the cart, payment, etc.

Background

Online stores can connect to Mindbox in two main ways: using the API or the JavaScript SDK (we’ll explain the differences later).

To choose the best way, we turned to the Mindbox documentation, and if there was not enough information, we asked the manager questions. We found out that our collaboration coincided with a period of rapid growth of the Mindbox platform: the average daily load on Mindbox API calls doubled (up to 120 thousand requests per minute, at the peak – up to 250 thousand). This meant that during Black Friday and other sales, due to an additional increase in load, there was a risk that the CDP service would be unavailable and would not receive data from the online store that was integrated with it.

Mindbox responded quickly to this problem and began to improve the architecture and infrastructure of its IT systems to achieve a fourfold safety factor. We, in turn, needed to ensure that purchase data was sent to Mindbox smoothly. This required choosing the most reliable integration method.

Mindbox integration methods

As noted above, Mindbox suggests using an API or JavaScript SDK to connect. Next, we will consider their features.

  • JavaScript SDK

Library – “wrapper” over the API provided by the service. Its advantages are ease of integration and the possibility of asynchronous data transfer. Ideal for those cases where only the web platform needs to be supported.

Limitations: data loss is possible if Mindbox is not available at the time of sending. Platform scripts will not be loaded if there are js errors on the side of the online store.

  • API Integration

The integration of the store with Mindbox can be done through the API. This method reduces dependence on JavaScript and is also suitable for setting up asynchronous data submission.

Limitations: we were faced with not receiving some cookie data, namely the unique identifier of the user on the device (mindboxDeviceUUID). It needs to be passed in most Mindbox operations to glue user information.

In the documentation, these cookies are not required for all operations. And yet, striving for uninterrupted data transfer, we discussed this issue with the manager of Mindbox. We have found that it is advisable to always send a cookie for maximum reliability. However, you need to use the JavaScript SDK to receive cookies.

Combined method

We have considered the methods of integration described above, but in their pure form they were not suitable for our project. To solve the retailer’s business problems and build a loyalty system, it was necessary to transfer to Mindbox a complete set of data about user actions, including the identifier from the cookie. At the same time, we aimed to reduce the dependence on JavaScript and the risk of data loss if Mindbox is temporarily unavailable.

Therefore, we turned to the third, combined method: we work with both the API and the JavaScript SDK using our queue module.

Using the Javascript SDK, we identify the user on the site (mindboxDeviceUUID). Then, on the server side, we form a request with all the necessary data and put it into the queue. Queued requests via API are sent to the Mindbox service. If the answer is no, the request is re-queued. Thus, when sending data, Mindbox receives a complete set of necessary information.

In the following example, the class Sender allows you to collect and send a request by performing the initial processing of the response. The class uses data from the command itself (request / response type, deviceUUID, etc.) and from the module settings (parameters for working with API, tokens, etc.).

command = $command;
    }

    /**
     * Сформировать массив заголовков запроса.
     *
     * @return array
     */
    protected function getHeaders(): array
    {
        return [
            'Accept'        => TypeContentType::REQUEST[$this->command->getRequestType()],
            'Content-Type'  => TypeContentType::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("https://habr.com/", $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;
    }
}

The Sendable trait contains all possible command settings for sending a request to Mindbox, including predefined ones, such as the type of request / response, the request method, and the sync / async parameter. It also contains methods common to all commands.

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;
    }
}

As an example, consider the user authorization event. In the authorization event handler, we add an object of the AuthorizationCommand class to our queue. In this class, the minimum necessary preparation of information takes place, since at the time of the command execution, the data in the database may change, and you need to save them. Also, the corresponding parameters for the request in Mindbox are set, in this case this is the name of the operation (we will find it in the Mindbox admin panel) Additionally, you can specify the type of request / response, the request method, and the sync / async parameter according to the Sendable trait.

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'],
            ],
        ];
    }
}

Module interaction scheme

In our project, we have identified three modules:

  • Base

Stores common utility classes and interfaces (such as command interfaces) that can then be used throughout the project.

  • Queue module

Interaction with Mindbox is implemented through commands. To execute them sequentially, we use our queue module. This module saves the incoming commands and executes them when the execution time comes.

  • Mindbox integration module

This module “catches” events on the site, such as authorization, registration, creating an order, adding to the cart and others, and then generates commands and sends them to the queues module.

The Mindbox module monitors events and related information on the site, including from cookies, forms a command from them and puts it into a queue. When the queue module retrieves a command from the queue and executes it, data is sent. If the answer from Mindbox is negative, the unsuccessfully executed command is moved to the end of the queue, if it is positive, the successfully executed command is removed from the queue.

Thus, using the combined method described above, we were able to ensure a smooth transfer of data to Mindbox.

Summing up

In this article, we looked at how an online store can connect to the Customer Data Platform to develop loyalty systems.

In our example, the Mindbox documentation described two main connection methods: through the Javascript SDK and through the API. To improve the reliability of data transmission, even in the case of temporary unavailability of the CDP service, we have chosen and implemented a third, combined method: using API and Javascript SDK, with asynchronous data sending.

Thanks for your attention! We hope this article was helpful to you.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *