Отправка писем в LPTracker по API с почтового ящика

Отправка писем в LPTracker по API с почтового ящика

Все чаще и чаще предприниматели для увеличения конверсии своего бизнеса используют готовые сторонние сервисы, одним из таких является LPTracker CRM ( CRM — система управления взаимоотношениями с клиентами ).

Права как и у всех систем она имеет свои недостатки, одной из которых невозможность напрямую выгружать с почтовых ящиков через протоколы POP3/IMAP, но к счастью у них хорошо реализована API, с которой очень просто взаимодействовать. Правда и не без своих нюансов, о которым будет сказано позднее.

 

Принцип взаимодействия

Есть сайт сгенерированный на конструкторе Tilda, На нем отображается email для связи и пару форм, при заполнении и отправке которых отсылается письма на тот же контактный почтовый адрес. Все пришедшие письма хранятся на хостинге ( в почтовом клиенте ). 

Будет реализована проверка новых писем на почтовом сервере, после чего будет происходить отправка писем в систему LPTracker.

 

Сбор писем в одном месте

Для упрощения работы с выгрузкой новых писем на основном контактном email необходимо установить отправку копий пришедших писем на новый ящик — lpt.temp@exemple.com.  

Так же необходимо создать сам почтовый ящик через панель предоставляемой хостингом, в моем случае это ISP.

Теперь все пришедшие письма будут дополнительно копироваться на новый почтовый ящик.

 

Хранение данных в базе данных (БД)

Теперь необходимо создать базу данных, где будет храниться все письма и связанные идентификаторы с ними из LPTracker.

Для этого, если используете ISP менеджер, переходим «Инструменты»->»phpMyAdmin», и заходим в web-рожицу БД. 

Создаем новую базу данные в которой создаем две таблицы:

  • tr_mails — хранит в себе сами письма
  • tr_users — хранит в себе контактные данные
CREATE TABLE IF NOT EXISTS `tr_mails` (
     `mail_id` int(11) NOT NULL AUTO_INCREMENT,
     `user_id` int(11) NOT NULL,
     `mail_subject` varchar(250) DEFAULT NULL,
     `mail_lead` int(10) unsigned DEFAULT NULL,
     `mail_reply` int(11) DEFAULT NULL,
     `mail_html` text,
     `mail_files` text,
     `mail_obj` text,
     `mail_api` int(11) unsigned NOT NULL DEFAULT '0',
     `timeadd` datetime DEFAULT NULL,
 PRIMARY KEY (`mail_id`),
   KEY `user_id` (`user_id`),
   KEY `mail_subject` (`mail_subject`),
   KEY `timeadd` (`timeadd`),
   KEY `mail_reply` (`mail_reply`),
   KEY `mail_api` (`mail_api`),
   KEY `mail_lead` (`mail_lead`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `tr_users` (
   `user_id` int(11) NOT NULL AUTO_INCREMENT,
   `user_name` varchar(250) DEFAULT NULL,
   `user_email` varchar(250) NOT NULL,
   `timeadd` datetime DEFAULT NULL,
   `timeupdate` datetime DEFAULT NULL,
 PRIMARY KEY (`user_id`),
 UNIQUE KEY `user_email` (`user_email`),
   KEY `user_name` (`user_name`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
ALTER TABLE `tr_mails`
   ADD CONSTRAINT `tr_mails_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `tr_users` (`user_id`),
   ADD CONSTRAINT `tr_mails_ibfk_2` FOREIGN KEY (`mail_reply`) REFERENCES `tr_mails` (`mail_id`);

Так же, последней командой создаем связь между таблицами.

Подготовительные работы закончены, теперь осталось только писать логику.

 

Выгрузка писем

Для того что бы распределить нагрузку и функциональность будет создано два контроллера, которые будут отвечать за свой этап взаимодействия:

  • imap.php — контроллер получения новых писем с почтового клиента, удаления и запись их в БД
  • api.php — проверка новых писем в БД, проверка/запись клиентов в системе LPTracker, после чего отправка писем, после чего обновление БД присваивая уникальный идентификатор CRM

Для корректной работы файл конфигураций будет вынесен в отдельный файл config.php с содержимым:

//доменное имя сервера
 $domenName = 'exemple.com';
 //конфиг LPTracker
 $lpConf = [
   'login' => '...', //имя пользователя от аккаунта CRM
   'password' => '...', //пароль от аккаунта CRM
   'service' => 'MailImapForTracker', //имя приложения
 ];
 //конфиг почтового клиента
 $mailConf = [
   'login' => '...', // логин для авторизации
   'password' => '...', //пароль для авторизации
   'service' => '...', // сервер почты, к примеру "hosting.ru:993/imap/ssl"
 ];
 //конфиг БД
 $dbConf = [
   'hostname' => 'localhost', //расположение сервера
   'database' => '...', //имя базы данных
   'username' => '...', //имя пользователя для подключения
   'password' => '...', //пароль для подключения
   'dbcollat' => 'utf8', //кодировка
 ];

Теперь два основных файла, imap.php

header("Content-Type: text/html; charset=utf-8");
 
require_once('config.php');
require_once('core/db.php');
require_once('core/PhpImap/__autoload.php');
 
//функция записи нового пользователя
function insert_user($data = array()){
    return  !empty($data) ? \DB::insert("
              INSERT IGNORE INTO `tr_users` 
                (`user_name`, `user_email`, `timeadd`, `timeupdate`) 
              VALUES (?, ?, ?, ?)", $data
    ) : null;
}
//функция записи нового письма
function insert_mail($data = array()){
    return !empty($data) ? \DB::insert("
              INSERT IGNORE INTO `tr_mails` 
                (`user_id`, `mail_subject`, `mail_html`, `mail_obj`, `mail_files`, `timeadd`) 
              VALUES (?, ?, ?, ?, ?, ?)", $data
    ) : null;
}
//доавляем новое письмо как реплику
function insert_mail_reply($data = array()){
    return !empty($data) ? \DB::insert("
              INSERT IGNORE INTO `tr_mails` 
                (`user_id`, `mail_subject`, `mail_reply`, `mail_html`, `mail_obj`, `mail_files`, `timeadd`) 
              VALUES (?, ?, ?, ?, ?, ?, ?)", $data
    ) : null;
}
//создать дерево директорий
function mkdir_r($dirName, $rights=0777){
    $dirs = explode('/', $dirName);
    $dir='';
    foreach ($dirs as $part) {
        $dir .= $part.'/';
        if (!is_dir(__DIR__.'/'.$dir) && strlen(__DIR__.'/'.$dir)>0)
            mkdir(__DIR__.'/'.$dir, $rights);
    }
    return __DIR__.'/'.$dir;
}
//удалить пустую директорию
function rrmdir($dir) {
    if (is_dir($dir)) {
        $objects = scandir($dir);
        $clear = true;
        foreach ($objects as $object) {
            if (
                $object != "." AND
                $object != ".."
            ) {
                $clear = false;
            }
        }
        if($clear){
            return @rmdir($dir);
        }
    }
    return false;
}
//проверка вывода ошибок
try{
//формирование папки для хранения файлов
    $dirTempArr = [ date("Ym"), date("d"), date("His") ];
//создание временной папки для хранения файлов
    $dirTemp = mkdir_r( "files/" . implode('/', $dirTempArr) );
    $mess = '';
    // подключение к почтовому клиенту
    $mailbox = new PhpImap\Mailbox('{'.$mailConf['service'].'}INBOX', $mailConf['login'], $mailConf['password'], $dirTemp);
    // подсчек общее количество писем, получение индефикаторов
    $mailsIds = $mailbox->searchMailbox();
    if( !empty($mailsIds) ) {
        //подключаемся к базе
        \DB::getInstance($dbConf);
        //получаем все письма
        $mail = array();
        foreach ($mailsIds as $id){
            //получили письмо
            $mail = $mailbox->getMail($id);
            //основные параметры
            $mess = array(
                'name' => trim( $mail->fromName ),
                'mail' => trim( $mail->fromAddress ),
                'html' => strip_tags( trim( $mail->textPlain ) ? trim( $mail->textPlain ) : trim( $mail->textHtml ) ),
                'date' => trim( $mail->date ),
                'files' => serialize( $mail->getAttachments() ),
                'obj' => serialize($mail),
                'subject' => trim($mail->subject),
            );
            //провери историю
            if( mb_substr($mess['subject'], 0, 3) == "Re:" )
            { // ответ
                $dbResult = \DB::select("
                    SELECT u.*, 
                      m.mail_id, m.mail_subject, m.mail_reply, m.mail_html, m.timeadd
                    FROM tr_users AS u
                      LEFT JOIN tr_mails AS m ON u.user_id=m.user_id AND m.mail_subject=?
                    WHERE u.user_email=? AND u.user_name=?", array(
                    trim(mb_substr($mess['subject'], 3)),
                    $mess['mail'],
                    $mess['name'],
                ));
                if( !empty($dbResult) ){
                    if(isset($dbResult[0], $dbResult[0]->mail_id)){
                        insert_mail_reply(array(
                            $dbResult[0]->user_id,
                            $mess['subject'],
                            $dbResult[0]->mail_id,
                            $mess['html'],
                            $mess['obj'],
                            $mess['files'],
                            $mess['date'],
                        ));
                    }else{
                        //нет сообщения, добавляем новую реплику
                        insert_mail(array(
                            $dbResult[0]->user_id,
                            $mess['subject'],
                            $mess['html'],
                            $mess['obj'],
                            $mess['files'],
                            $mess['date'],
                        ));
                    }
                }else{
                    //пользователя не существует, добавляем пользователя
                    $usrUid = insert_user(array(
                        $mess['name'],
                        $mess['mail'],
                        $mess['date'],
                        $mess['date'],
                    ));
                    if($usrUid){
                        //добавляем новую реплику
                        insert_mail(array(
                            $usrUid,
                            $mess['subject'],
                            $mess['html'],
                            $mess['obj'],
                            $mess['files'],
                            $mess['date'],
                        ));
                    }
                }
            }
            else
            { // новое сообщение
                $dbResult = \DB::select("
                    SELECT *
                    FROM tr_users AS u
                    WHERE u.user_email=? AND u.user_name=?", array(
                    $mess['mail'],
                    $mess['name'],
                ));
                if( !empty($dbResult) ){
                    //нет сообщения, добавляем новую реплику
                    insert_mail(array(
                        $dbResult[0]->user_id,
                        $mess['subject'],
                        $mess['html'],
                        $mess['obj'],
                        $mess['files'],
                        $mess['date'],
                    ));
                }else{
                    //пользователя не существует, добавляем пользователя
                    $usrUid = insert_user(array(
                        $mess['name'],
                        $mess['mail'],
                        $mess['date'],
                        $mess['date'],
                    ));
                    if($usrUid){
                        //добавляем новую реплику
                        insert_mail(array(
                            $usrUid,
                            $mess['subject'],
                            $mess['html'],
                            $mess['obj'],
                            $mess['files'],
                            $mess['date'],
                        ));
                    }
                }
            }
            $mailbox->deleteMail($id);
        }
    }else{
        $mess = 'Писем нет 🙂 ';
    }
    //удаляем папку если файлы не созданы
    for ( $i=count($dirTempArr); $i; $i-- ){
        if( !rrmdir($dirTemp) ){
            break;
        }
        if( $i and isset($dirTempArr[$i-1]) ){
            $dirTemp = str_replace('/'.$dirTempArr[$i-1], '', $dirTemp);
        }
    }
    exit($mess);
}catch (\Exception $e){
    print_r($e->getMessage());
}

и api.php

header("Content-Type: text/html; charset=utf-8");
 
require_once('config.php');
require_once('core/db.php');
require_once('core/PhpImap/__autoload.php');
require_once ('vendor/autoload.php');
 
use LPTracker\LPTracker;
 
//подключаемся к базе
\DB::getInstance($dbConf);
 
$dbMails = \DB::select("
   SELECT u.*,
     m.mail_id, m.mail_subject, m.mail_reply, m.mail_html, m.mail_api, m.mail_files, m.timeadd, m.mail_obj
   FROM tr_mails AS m
     LEFT JOIN tr_users AS u ON u.user_id=m.user_id
   WHERE m.mail_api = ? 
   LIMIT 5", array(
    0,
));
//проверка новых писем
if(empty($dbMails)){
    exit("Писем нет");
}
//авторизируемся в системе
$api = new LPTracker($lpConf);
try{
    //проверка токена
    if( empty($api) or !$api->getToken() ) {
        $api->logout();
        exit("Токен не получен");
    }
    //Получить список проектов
    $projects = $api->getProjectList();
    if( empty($projects) ) {
        $api->logout();
        exit("Нет проектов");
    }
    //проход по проектам
    foreach ($projects as $project){
        if( $project->getId() == 51595 ){
            //проход по письмам
            foreach ($dbMails as $mail){
                //проверка заполненности данных
                if(!$mail->mail_html){
                    $mail->mail_obj = unserialize($mail->mail_obj);
                    $mail->mail_html = strip_tags(isset($mail->mail_obj->textHtml) ? $mail->mail_obj->textHtml : '');
                    $mail->mail_obj = null;
                }
                //ищем контакт
                $contact = $api->searchContacts(
                    $project->getId(),
                    array(
                        'email' => $mail->user_email
                    )
                );
                //пользователя нет
                if(empty($contact)){
                    //создаем пользователя
                    $contact = $api->createContact(
                        $project,
                        array(
                            array(
                                "type" => "email",
                                "data" => $mail->user_email
                            )
                        ),
                        array(
                            "name" => $mail->user_name
                        )
                    );
                }else{
                    $contact = $contact[0];
                }
                $lead = 1;
                $comment = 1;
                //получени контакт
                if( !empty($contact) ){
                    //получаем лиды контакта
                    $lead = $api->contactLeads(
                        $contact
                    );
                    //проверка существование лида
                    if( empty($lead) ){
                        //создаем лид
                        $lead = $api->createLead(
                            $contact,
                            array(
                                'name' => $mail->mail_subject,
                                'source' => 'email',
                            )
                        );
                    }else{
                        $leadRes = null;
                        foreach ( $lead as $l ){
                            if( $l instanceof \LPTracker\models\Lead ){
                                foreach ( $l->getCustoms() as $n ){
                                    if( $n instanceof \LPTracker\models\Custom and $n->getType() == 'funnel' ){
                                        $leadRes = $l;
                                    }
                                }
                            }
                        }
                        $lead = ($leadRes) ?
                            $leadRes :
                            $api->createLead(
                                $contact,
                                array(
                                    'name' => $mail->mail_subject,
                                    'source' => 'email',
                                )
                            );
                    }
                    //проверка нужности добавления файла
                    $listFiles = [];
                    $files = [];
                    if( $mail->mail_files ){
                        $mail->mail_files = unserialize($mail->mail_files);
                        if(!empty($mail->mail_files)){
                            foreach ($mail->mail_files as $file){
                                if($file and isset($file->filePath) and $file->filePath and is_string($file->filePath)){
                                    preg_match ( '/[\S]+'.$domenName.'\/([\S]*)$/i' , $file->filePath , $matches);
                                    if( isset($matches[1]) and $matches[1]){
                                        $listFiles[] = 'http://'.$domenName.'/'.$matches[1];
                                    }
                                    //добавляем файл к лиду
                                    $res = $api->addFileToLead(
                                        $lead,
                                        $file->filePath
                                    );
                                    if( $res ){
                                        $files[] = $res;
                                    }
                                }
                            }
                        }
                    }
                    $listFiles = isset($listFiles[0]) ? ' ||=> Файлы: '.implode(' | ', $listFiles) : '';
 
                    //добавляем комментарий к лиду
                    $comment = $api->addCommentToLead(
                        $lead,
                        $mail->mail_html . $listFiles
                    );
                }
                //обновляем входящее сообщение
                \DB::update("
               UPDATE tr_mails 
               SET mail_api = ?, mail_lead = ? 
               WHERE mail_id = ?",
                    array(
                        ($comment===1)? $comment : $comment->getId(),
                        ($lead===1)? $lead : $lead->getId(),
                        (isset($mail->mail_id) and $mail->mail_id)?
                            $mail->mail_id : 1,
                    ));//*/
            }
        }
    }
}catch (\Exception $e){
    print_r($e->getMessage());
}
@$api->logout();

Теперь дополнительные файлы.

Создаем папку «core» и в ней файл для работы с БД «db.php»:

<?php
interface DBInterface{
    //старт файла
    public function __construct();
    //запрещаем клонирование объекта
    public function __clone();
    //запрещаем восстановление
    public function __wakeup();
    //инициализация обьекта
    public static function getInstance( array $config = array() );
    //запрос с возвратом ответа
    public static function select(string $query, array $val = null): array;
    //добавление материала
    public static function insert(string $query, array $val = null): int;
    //обновление материала
    public static function update(string $query, array $val = null): int;
    //удаление материала
    public static function delete(string $query, array $val = null): int;
    //удаление материала
    public static function error(): string;
    //Закрытие соединения
    public function __destruct();
}
class DB implements DBInterface{
    //сам бьект
    protected static $_instance = [
        'pdo' => null,
        'error' => null,
        'config' => null,
    ];
    //старт файла
    public function __construct(){}
    //запрещаем клонирование объекта
    public function __clone() {}
    //запрещаем восстановление
    public function __wakeup() {}
    public static function getInstance( array $config = array() ) {
        $error = (!isset(
            $config['hostname'],
            $config['database'],
            $config['username'],
            $config['password'],
            $config['dbcollat'])
        ) ? "Error connection to database." : false;
        //инициалезируем обьект
        if (self::$_instance === null) self::$_instance = new self;
        self::$_instance['error'] = $error;
        self::$_instance['config'] = $config;
        //проверка ошибок
        if( !self::$_instance['error'] ){
            //подключение к БД
            try{
                //драйвер подключения
                $dbconn = "mysql:host=$config[hostname];dbname=$config[database]";
                //процесс подключения
                self::$_instance['pdo'] = new PDO( $dbconn, $config['username'], $config['password'] );
                //установка параметров
                self::$_instance['pdo']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                //переводим кодировку
                self::$_instance['pdo']->exec("SET NAMES '$config[dbcollat]'");
            }catch(PDOException $e){ //вывод ошибки подключения
                self::$_instance['error'] = $e->getMessage();
            }
        }
        //возвращаем подключение
        return self::$_instance;
    }
    //запрос с возвратом ответа
    private static function query(string $query, array $val): PDOStatement {
        $stmt = null;
        try{
            //проверка наличия ошибок
            if(self::$_instance['error']){
                return null;
            }
            //проверка передаваемых параметров
            if(!$val){
                $stmt = self::$_instance['pdo']->query($query);
                $stmt->execute();
            }else{
                $stmt = self::$_instance['pdo']->prepare($query);
                $stmt->execute($val);
            }
        }catch(PDOException $e){ }
        //возврат результата
        return $stmt;
    }
    //запрос с возвратом ответа
    public static function select(string $query = '', array $val = null): array{
        //отправка результата
        $query = self::query($query, $val);
        //проверка ответа
        if( $query === null ){
            return array();
        }
        //возврат результата
        return (isset($query->errorInfo()[0]) and $query->errorInfo()[0] == 00000) ?
            $query->fetchAll(PDO::FETCH_OBJ) : array();
    }
    //добавление материала
    public static function insert(string $query = '', array $val = null): int {
        //отправка результата
        $query = self::query($query, $val);
        //проверка ответа
        if( $query === null ){
            return 0;
        }
        //возврат результата
        return self::$_instance['pdo']->lastInsertId();
    }
    //обновление материала
    public static function update(string $query = '', array $val = null): int {
        //отправка результата
        $query = self::query($query, $val);
        //проверка ответа
        if( $query === null ){
            return 0;
        }
        //возврат результата
        return (isset($query->errorInfo()[0]) and $query->errorInfo()[0] == 00000)?
            $query->rowCount() : 0;
    }
    //удаление материала
    public static function delete(string $query = '', array $val = null): int {
        //отправка результата
        $query = self::query($query, $val);
        //проверка ответа
        if( $query === null ){
            return 0;
        }
        //возврат результата
        return (isset($query->errorInfo()[0]) and $query->errorInfo()[0] == 00000)?
            $query->rowCount() : 0;
    }
    //удаление материала
    public static function error(): string {
        return self::$_instance['error'] ?? '';
    }
    //Закрытие соединения
    public function __destruct(){
        self::$_instance['pdo'] = null;
    }
}

Следующим модулем нам необходимо подключить библиотеку «php-imap» с github скачав архив по ссылке или через консоль (терминал) клонировать через команду git так, что бы папка «PhpImap» лежала в корне директории «core» для возможности собирать письма с почтового клиента по протоколу IMAP:

cd core/ && git clone git@github.com:tarsy-club/php-imap.git

и последним штрихом надо добавить официальную SDK от LPTracker для работы с API через композер в корень проекта:

composer require lptracker/php-sdk

Добавляем планировщик на оба контроллера с периодичностью каждые 2 минуты запуска «2/ * * * *»

Проект готов, теперь он будет при получании новых писем на email отсылать его копию на временную почту , откуда по крону будут получены новые письма и записаны в базу, вторым кроном будут выбраны из базы новые письма и по их данным будет произведен поиск через SDK в LPTracker совпадающих контактов, в случае если контакты не будут найдены они будут созданы. И для каждого контакта если не будет активной заявки, будет создана новая и в комментариях написано само тело сообщения.

 

Нюанс работы API

Но без ложки дектя не обойдется.

На момент написания статьи в официальном SDK LPTracker нет загрузка файлов и вызываемой функции «addFileToLead» не существует, поэтому на этом месте в случае передачи файлов будет выводиться ошибка, поэтому либо необходимо из файла «api.php» все что связанно с загрузкой файлов, либо отредактировать официальную библиотеку ( можно и создать собственный модуль для взаимодействия с API, но это будет плохим тоном и займет больше времени в разработке и дальнейшей поддержке).

Для работы с файлами первым делом надо создать модель для самих файлов.

Переходим в директорию «vendor/lptracker/php-sdk/src/LPTracker/» (дальнейшая разработка будет проходить относительно данной папки) и в папке «models» создаем файл «File.php» с содержимым:

<?php
 namespace LPTracker\models;
 use LPTracker\exceptions\LPTrackerSDKException;
 /**
 * Class Lead
 * @package LPTracker\models
 */
 class File extends Model
 {
 /**
 * @var integer
 */
 protected $id;
 /**
 * @var integer
 */
 protected $leadId;
 /**
 * @var string
 */
 protected $name;
 /**
 * @var string
 */
 protected $mime;
 /**
 * @var string
 */
 protected $data;
 /**
 * @var string
 */
 protected $file;
 /**
 * @var \DateTime
 */
 protected $createdAt;
 /**
 * @var array
 */
 protected $options = [];
 /**
 * Lead constructor.
 *
 * @param array $fileData
 */
 public function __construct(array $fileData = [])
 {
 if ( ! empty($fileData['id'])) {
 $this->id = intval($fileData['id']);
 }
 if ( ! empty($fileData['lead_id'])) {
 $this->leadId = intval($fileData['lead_id']);
 }
 if ( ! empty($fileData['name'])) {
 $this->name = $fileData['name'];
 }
 if ( ! empty($fileData['mime'])) {
 $this->mime = $fileData['mime'];
 }
 if ( ! empty($fileData['data'])) {
 $this->data = $fileData['data'];
 }
 if ( ! empty($fileData['file'])) {
 $this->setFile($fileData['file']);
 }
if ( ! empty($fileData['file_date'])) {
 $date = \DateTime::createFromFormat('d.m.Y H:i', $fileData['file_date']);
 $this->setCreatedAt($date);
 }
 if ( ! empty($fileData['params']) && is_array($fileData['params'])) {
 $this->options['params'] = $fileData['params'];
 }
 }
 /**
 * @param bool $toSave
 *
 * @return array
 */
 public function toArray($toSave = false)
 {
 $result = [];
 if ( ! empty($this->id)) {
 $result['id'] = $this->getId();
 }
 if ( ! empty($this->name)) {
 $result['name'] = $this->getName();
 }
 if ( ! empty($this->mime)) {
 $result['mime'] = $this->getMime();
 }
 if ( ! empty($this->data)) {
 $result['data'] = $this->getData();
 }
 if ( ! empty($this->createdAt)) {
 $result['file_date'] = $this->getCreatedAt()->format('d.m.Y H:i');
 }
 foreach ($this->options as $key => $value) {
 $result[$key] = $value;
 }
return $result;
 }
 /**
 * @return bool
 * @throws LPTrackerSDKException
 */
 public function validate()
 {
 if (empty($this->leadId)) {
 throw new LPTrackerSDKException('Lead ID is required');
 }
 if ( intval($this->leadId) <= 0) {
 throw new LPTrackerSDKException('Invalid lead id');
 }
 return ( empty($this->name) or empty($this->mime) or empty($this->data) ) ? false : true;
 }
 /**
 * @return int
 */
 public function getId()
 {
 return intval($this->id);
 }
 /**
 * @return string
 */
 public function getName()
 {
 return $this->name;
 }
 /**
 * @param string $name
 *
 * @return $this
 */
 public function setName($name)
 {
 $this->name = $name;
return $this;
 }
 /**
 * @return string
 */
 public function getMime()
 {
 return $this->mime;
 }
 /**
 * @param string $mime
 *
 * @return $this
 */
 public function setMime($mime)
 {
 $this->mime = $mime;
return $this;
 }
 /**
 * @return string
 */
 public function getData()
 {
 return $this->data;
 }
 /**
 * @param string $data
 *
 * @return $this
 */
 public function setData($data)
 {
 $this->data = $data;
return $this;
 }
 /**
 * @param string $url
 *
 * @return $this
 */
 public function setFile($url)
 {
 $type = null;
 if( empty($this->data) and file_exists($url) and filesize($url) < 1077777 ){
 $type = mime_content_type($url);
 $this->data = 'data:' . $type . ';base64,' . base64_encode(file_get_contents($url));
 }
 //параметры
 if( !empty($this->data) ){
 $path_parts = pathinfo($url);
 $this->name = !empty($this->name) ?
 $this->name :
 ( $path_parts['filename'] ? $path_parts['filename'] : 'not name' );
$this->mime = $this->mime ?
 $this->mime : $type;
 }else{
 $this->name = null;
 $this->mime = null;
 }
return $this;
 }
 /**
 * @return \DateTime
 */
 public function getCreatedAt()
 {
 return $this->createdAt;
 }
 /**
 * @param \DateTime $createdAt
 *
 * @return $this
 */
 public function setCreatedAt($createdAt)
 {
 $this->createdAt = $createdAt;
return $this;
 }
 }

Теперь SDK знает о новой модели для файлов и знает как с ними работать, осталось только создать запуск метода «addFileToLead». 

Заходим в файл «LPTracker.php» и добавляем внутр класса «LPTracker» новый метод:

/**
 * @param $lead
 * @param $url
 *
 * @return File
 * @throws LPTrackerSDKException
 * @throws exceptions\LPTrackerResponseException
 * @throws exceptions\LPTrackerServerException
 */
 public function addFileToLead($lead, $url)
 {
    $lead = ($lead instanceof Lead) ? $lead->getId() : intval($lead);
    $file = new File([
      'lead_id' => $lead,
      'file' => $url
    ]);
    $res = null;
    if( $file->validate() ) {
      //создаем файл
      $response = LPTrackerRequest::sendRequest(
        "/lead/$lead/file",
        $file->toArray(true),
        'POST',
        $this->token,
        $this->address
      );
      $res = new File($response);//*/
    }
    return $res;
}

и в самом начале добавляем инициализацию модели

use LPTracker\models\File;

Все, код готов к тестированию.

Опубликовано:
Пожертвование
На развите проекта, продление хостинга и на корм бобику - Дикуше :)