Integração de uma loja online em 1C-Bitrix com Mindbox

Para desenvolver sistemas de fidelidade, as lojas online estão se voltando para plataformas de automação de marketing, Customer Data Platform (CDP). Ao mesmo tempo, às vezes, para uma integração bem-sucedida, você precisa salvar mais dados do que o indicado na documentação da API.



Diremos quais dados precisamos para integrar uma loja no 1C-Bitrix com a plataforma Mindbox, como você pode obtê-los usando a API e SDK e como usar uma abordagem combinada com envio de dados assíncronos.







Com a ajuda dos serviços da Plataforma de Dados do Cliente, os varejistas “reconhecem” o perfil de seu cliente, incluindo dados comportamentais. Essas informações são armazenadas com segurança no CDP e ajudam os varejistas com campanhas de marketing e análises.



Quando um cliente adiciona uma TV ou qualquer outro produto ao carrinho, o CDP armazena esses dados. Com base neles, os varejistas podem expandir suas interações com os usuários, por exemplo, oferecer recomendações e descontos em produtos semelhantes.



Um de nossos clientes - uma rede de lojas de eletrônicos - decidiu se conectar à plataforma Mindbox CDP e nos procurou para obter ajuda na integração. Realizamos integração para cenários-chave de usuário: autorização, adição ao carrinho, pagamento, etc.



fundo



As lojas online podem se conectar ao Mindbox de duas maneiras principais: usando a API ou o SDK do JavaScript (explicaremos as diferenças posteriormente).



Para escolher o melhor caminho, recorremos à documentação do Mindbox e, caso não haja informações suficientes, fazemos perguntas ao gerente. Verificamos que nossa cooperação coincidiu com um período de rápido crescimento da plataforma Mindbox: a carga média diária nas chamadas API do Mindbox dobrou (até 120 mil solicitações por minuto, no pico - até 250 mil). Isso significava que durante a Black Friday e outras vendas, devido a um aumento adicional na carga, havia o risco de o serviço CDP ficar indisponível e não receber dados da loja online que estava integrada a ele.



A Mindbox respondeu rapidamente a esse problema e começou a melhorar a arquitetura e a infraestrutura de seus sistemas de TI para atingir um fator de segurança quádruplo. Nós, por sua vez, precisávamos garantir que os dados de compra fossem enviados ao Mindbox sem problemas. Isso exigiu a escolha do método de integração mais confiável.



Métodos de integração Mindbox



Conforme observado acima, Mindbox sugere o uso de uma API ou JavaScript SDK para se conectar. A seguir, consideraremos seus recursos.



  • JavaScript SDK



A biblioteca é um "wrapper" da API fornecida pelo serviço. Suas vantagens são a facilidade de integração e a possibilidade de transferência assíncrona de dados. Mais adequado para casos em que apenas a plataforma da web precisa ser suportada.



Limitações: Potencial perda de dados se o Mindbox não estiver disponível no momento do envio. Os scripts da plataforma não serão carregados se houver erros js no lado da loja online.



  • Integração API



A integração da loja com o Mindbox pode ser feita por meio da API. Este método reduz a dependência de JavaScript e também é adequado para configurar o envio de dados assíncronos.



Limitações: Fomos confrontados com o facto de não termos recebido alguns dados de cookies, nomeadamente o identificador único do utilizador no dispositivo (mindboxDeviceUUID). Ele precisa ser passado na maioria das operações do Mindbox para colar as informações do usuário.



Na documentação, esses cookies não são necessários para todas as operações. E ainda, buscando a transmissão de dados ininterrupta, discutimos esse assunto com o gerente da Mindbox. Descobrimos que é aconselhável enviar sempre um cookie para a máxima confiabilidade. No entanto, você precisa usar o SDK do JavaScript para receber cookies.



Método combinado



Examinamos os métodos de integração acima, mas em sua forma pura eles não eram adequados para nosso projeto. Para resolver os problemas de negócios do varejista e construir um sistema de fidelidade, foi necessário transferir para o Mindbox um conjunto completo de dados sobre as ações do usuário, incluindo o identificador do cookie. Ao mesmo tempo, buscamos reduzir a dependência do JavaScript e o risco de perda de dados se o Mindbox estiver temporariamente indisponível.



Portanto, nos voltamos para o terceiro método combinado: trabalhamos com a API e o SDK do JavaScript usando nosso módulo de fila.



Usando o SDK Javascript, identificamos o usuário no site (mindboxDeviceUUID). Então, no lado do servidor, fazemos uma solicitação com todos os dados necessários e colocamos na fila. Solicitações enfileiradas via API são enviadas ao serviço Mindbox. Se a resposta for não, a solicitação é enfileirada novamente. Assim, ao enviar dados, Mindbox recebe um conjunto completo de informações necessárias.



No exemplo abaixo, a classe Sender permite coletar e enviar uma solicitação fazendo o processamento inicial da resposta. A classe usa dados do próprio comando (tipo de solicitação / resposta, deviceUUID, etc.) e das configurações do módulo (parâmetros para trabalhar com API, tokens, etc.).



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


O atributo Sendable contém todas as configurações de comando possíveis para enviar uma solicitação ao Mindbox, incluindo as predefinidas, como o tipo de solicitação / resposta, o método de solicitação e o parâmetro sync / async. Ele também contém métodos comuns a todos os comandos.



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


Como exemplo, considere o evento de autorização do usuário. No manipulador de eventos de autorização, adicionamos um objeto da classe AuthorizationCommand à nossa fila. Nesta aula ocorre a preparação mínima necessária das informações, pois no momento em que o comando é executado, os dados do banco de dados podem mudar, sendo necessário salvá-los. Além disso, os parâmetros correspondentes para a solicitação no Mindbox são definidos, neste caso, este é o nome da operação (o encontraremos no painel de administração do Mindbox). Além disso, você pode especificar o tipo de solicitação / resposta, o método de solicitação e o parâmetro sync / async de acordo com o atributo Sendable.



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


Esquema de interação do módulo



Em nosso projeto, identificamos três módulos:



  • Base



Armazena classes de utilitários comuns e interfaces (como interfaces de comando) que podem ser usadas em todo o projeto.



  • Módulo de fila



A interação com o Mindbox é implementada por meio de comandos. Para executá-los sequencialmente, usamos nosso módulo de fila. Este módulo salva os comandos recebidos e os executa quando chega a hora.



  • Módulo de integração Mindbox



Esse módulo "captura" eventos do site, como autorização, cadastro, criação de pedido, adição ao carrinho e outros, e a seguir gera comandos e os envia para o módulo de filas.







O módulo Mindbox monitora eventos e informações relacionadas no site, incluindo cookies, forma um comando a partir deles e o coloca em uma fila. Quando o módulo de fila recupera um comando da fila e o executa, os dados são enviados. Se a resposta do Mindbox for negativa, o comando executado sem sucesso é movido para o final da fila; se positivo, o comando executado com sucesso é removido da fila.



Assim, usando o método combinado descrito acima, fomos capazes de garantir uma transferência de dados tranquila para o Mindbox.



Resumindo



Neste artigo, examinamos as maneiras pelas quais uma loja online pode se conectar à plataforma de dados do cliente para desenvolver sistemas de fidelidade.



Em nosso exemplo, dois métodos principais de conexão foram descritos na documentação do Mindbox: via Javascript SDK e via API. Para melhorar a fiabilidade da transmissão de dados, mesmo em caso de indisponibilidade temporária do serviço CDP, escolhemos e implementamos um terceiro método combinado: utilizando API e Javascript SDK, com envio assíncrono de dados.



Obrigado pela atenção! Esperamos que este artigo tenha sido útil para você.



All Articles