Реализация импорта контактов (Google/Outlook) на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация импорта контактов (Google/Outlook) на сайте
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Реализация импорта контактов (Google/Outlook) на сайте

Импорт контактов из Google или Outlook на сайт — типичная задача для CRM-систем, HR-порталов, платформ нетворкинга. Пользователь авторизуется через OAuth, даёт разрешение на чтение контактов, сайт забирает список и предлагает выбрать, кого пригласить или синхронизировать. Технически Google и Outlook — принципиально разные API, объединённые одним UI-флоу.

Google Contacts API: настройка OAuth

В Google Cloud Console: создать проект → включить «People API» → создать OAuth 2.0 Client ID (тип: Web application) → добавить redirect URI.

Нужные скоупы:

  • https://www.googleapis.com/auth/contacts.readonly — чтение контактов
  • https://www.googleapis.com/auth/contacts.other.readonly — контакты из «Другие контакты»
use Google\Client as GoogleClient;

class GoogleContactsService
{
    private GoogleClient $client;

    public function __construct()
    {
        $this->client = new GoogleClient();
        $this->client->setClientId(config('services.google.client_id'));
        $this->client->setClientSecret(config('services.google.client_secret'));
        $this->client->setRedirectUri(config('services.google.redirect'));
        $this->client->addScope('https://www.googleapis.com/auth/contacts.readonly');
        $this->client->setAccessType('offline'); // получаем refresh_token
    }

    public function getAuthUrl(): string
    {
        return $this->client->createAuthUrl();
    }

    public function handleCallback(string $code): array
    {
        $token = $this->client->fetchAccessTokenWithAuthCode($code);
        // Сохраняем токен для пользователя
        return $token;
    }
}

Получение контактов из Google People API

public function getContacts(array $accessToken): array
{
    $this->client->setAccessToken($accessToken);

    if ($this->client->isAccessTokenExpired() && isset($accessToken['refresh_token'])) {
        $this->client->fetchAccessTokenWithRefreshToken($accessToken['refresh_token']);
    }

    $service  = new \Google\Service\PeopleService($this->client);
    $contacts = [];
    $pageToken = null;

    do {
        $params = [
            'personFields' => 'names,emailAddresses,phoneNumbers',
            'pageSize'     => 1000,
        ];
        if ($pageToken) {
            $params['pageToken'] = $pageToken;
        }

        $result = $service->people_connections->listPeopleConnections('people/me', $params);

        foreach ($result->getConnections() ?? [] as $person) {
            $name   = $person->getNames()[0] ?? null;
            $email  = $person->getEmailAddresses()[0] ?? null;
            $phone  = $person->getPhoneNumbers()[0] ?? null;

            if (!$email) continue; // пропускаем без email

            $contacts[] = [
                'name'  => $name?->getDisplayName() ?? '',
                'email' => $email->getValue(),
                'phone' => $phone?->getValue() ?? '',
            ];
        }

        $pageToken = $result->getNextPageToken();
    } while ($pageToken);

    return $contacts;
}

Пагинация обязательна: у пользователя может быть 5000+ контактов, API возвращает максимум 1000 за запрос.

Microsoft Graph API: Outlook/Office 365 контакты

Регистрация приложения в Azure AD → «App registrations» → «New registration». Нужные разрешения: Contacts.Read (Delegated).

use Microsoft\Graph\Graph;
use Microsoft\Graph\Model\Contact;

class OutlookContactsService
{
    public function getAuthUrl(): string
    {
        $params = http_build_query([
            'client_id'     => config('services.microsoft.client_id'),
            'response_type' => 'code',
            'redirect_uri'  => config('services.microsoft.redirect'),
            'scope'         => 'offline_access Contacts.Read',
            'response_mode' => 'query',
        ]);

        return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?{$params}";
    }

    public function getToken(string $code): array
    {
        $response = Http::asForm()->post(
            'https://login.microsoftonline.com/common/oauth2/v2.0/token',
            [
                'client_id'     => config('services.microsoft.client_id'),
                'client_secret' => config('services.microsoft.client_secret'),
                'code'          => $code,
                'redirect_uri'  => config('services.microsoft.redirect'),
                'grant_type'    => 'authorization_code',
            ]
        );

        return $response->json();
    }

    public function getContacts(string $accessToken): array
    {
        $graph    = new Graph();
        $graph->setAccessToken($accessToken);

        $contacts = [];
        $url      = '/me/contacts?$select=displayName,emailAddresses,mobilePhone&$top=100';

        do {
            $result  = $graph->createRequest('GET', $url)->execute();
            $data    = $result->getBody();

            foreach ($data['value'] as $contact) {
                $email = $contact['emailAddresses'][0]['address'] ?? null;
                if (!$email) continue;

                $contacts[] = [
                    'name'  => $contact['displayName'] ?? '',
                    'email' => $email,
                    'phone' => $contact['mobilePhone'] ?? '',
                ];
            }

            $url = $data['@odata.nextLink'] ?? null;
            // Убираем базовый URL для Graph SDK
            if ($url) {
                $url = str_replace('https://graph.microsoft.com/v1.0', '', $url);
            }
        } while ($url);

        return $contacts;
    }
}

UI: выбор контактов для импорта

После получения списка пользователь выбирает, каких контактов импортировать:

function ContactImportModal({ contacts, onImport }) {
    const [selected, setSelected] = useState(new Set());

    const toggle = (email) => {
        setSelected(prev => {
            const next = new Set(prev);
            next.has(email) ? next.delete(email) : next.add(email);
            return next;
        });
    };

    return (
        <div>
            <div className="actions">
                <button onClick={() => setSelected(new Set(contacts.map(c => c.email)))}>
                    Выбрать все ({contacts.length})
                </button>
            </div>
            <ul>
                {contacts.map(contact => (
                    <li key={contact.email}>
                        <label>
                            <input
                                type="checkbox"
                                checked={selected.has(contact.email)}
                                onChange={() => toggle(contact.email)}
                            />
                            {contact.name} — {contact.email}
                        </label>
                    </li>
                ))}
            </ul>
            <button onClick={() => onImport([...selected])}>
                Импортировать выбранных ({selected.size})
            </button>
        </div>
    );
}

Хранение токенов

Токены доступа нельзя хранить в сессии — они должны быть в БД, зашифрованы:

// Миграция
$table->text('google_access_token')->nullable();
$table->text('google_refresh_token')->nullable();
$table->timestamp('google_token_expires_at')->nullable();

// В модели User — автоматическое шифрование
protected $casts = [
    'google_access_token'  => 'encrypted',
    'google_refresh_token' => 'encrypted',
];

Сроки

Импорт из одного провайдера (Google или Outlook) с UI выбора контактов, OAuth-флоу и сохранением в базу: 2–3 рабочих дня. Оба провайдера одновременно с обновлением токенов и повторной синхронизацией: 4–5 рабочих дней.