BANKS FAQ : Импорт данных из внешних провайдеров

В данном разделе описано как работает подсистема импорта данных из внешних провайдеров через CASHOFF API. Описание содержит следующие составляющие:

  • добавление связи CASHOFF с провайдером через профиль
  • авторизация профиля в провайдере
  • процесс импорта данных
  • получение загруженных данных из CASHOFF

Цикл работы с профилями при импорте данных

Глобально цикл состоит из следующих шагов:

  1. Добавление нового профиля. При добавлении указывается пользователь и провайдер.
  2. Запуск обновления данных. В рамках обновления данных из ЛК провайдера данные будут импортированы в CASHOFF.
  3. Авторизация в ЛК провайдера. Это первый, обязательный, этап обновления. На этом шаге либо клиенту нужно будет предоставить данные для авторизации (логин/пароль/смс и т.п.), либо CASHOFF использует сохраненные у него данные авторизации/сессию для восстановления подключения к провайдеру.
  4. Импорт данных. Выполняется после успешной авторизации

CASHOFF позволяет выполнить шаги 1-3 двумя разными способами: полностью через серверное API или через готовые визуальные модули.

После успешного добавления и первичного импорта данных можно повторно обновлять профиль выполняя шаги 2-4. 

Сам процесс авторизации и импорта данных происходит асинхронно: соответствующие API методы только запускают выполнение действия, взаимодействие с провайдером же происходит в фоновом режиме.

Результат выполнения действий отслеживается через статус профиля (поле status), а прогресс через поля update_current_stage и update_progress. Основной способ отслеживания - нотификаций, в частности событие profile.updated, которое описано ниже в разделе "Уведомление о изменении профиля".

Загруженные на шаге 4 данные доступны на постоянной основе и могут в любое время получены через API. К примеру для списка продуктов это запрос GET /accounts, а операций - GET /transactions. Запрашивать можно и в момент импорта, однако данные в таком случае будут только частично обновлены.

Запуск импорта через серверное API

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

Запуск импорта через визуальные модули

CASHOFF предоставляет готовый визуальный модуль profile-update, через который клиент может выбрать провайдера для подключения, авторизоваться и наблюдать процесс импорта данных. Приложению остается только забрать данные (шаг 4). Подробнее о использовании визуальных модулей написано в разделе Встраиваемые UI модули.

Данный модуль последовательно выполняет шаги 1-3, но при этом начать можно с любого шага. Например можно самостоятельно реализовать в приложении выбор провайдера, а визуальный модуль использовать только для процесса обновления и авторизации.

Так же его можно использовать исключительно для авторизации: показывать модуль при статусе профиля updating. Такой подход позволяет избавить приложение от необходимости работать с авторизационными данными клиента от провайдеров.

Данный модуль можно использовать как образец реализации интерфейса подключения и обновления профиля.

В итоге приложению остается только отслеживать завершение обновления профиля (серверными нотификациями или с помощью сообщений на клиенте) и получить данные из CASHOFF. Повторные обновления можно производить так же при помощью этого модуля.

Добавление нового профиля

Добавление профиля происходит запросом POST /profiles/add, в котором нужно указать идентификатор провайдера:

Запрос
{
    "provider_key": "sber"
}

Запрос в таком виде только создаст новый профиль, но не выполнит шаги 2 и 3.

Эти действия выполняются отдельными запросами, POST /profiles/{profile_id}/update и POST /profiles/{profile_id}/session/auth соответственно, но их можно выполнить и разом при создании:

Запрос
{
    "provider_key": "sber",
	"start_update": true,
    "auth_data": {
        "login": "VApTr56Ui9",
        "password": "8Apq0Ohy1"
    }
}

В данном запросе атрибут start_update запустит обновление, а auth_data предоставит данные для первого шага авторизации. Пример ответа:

Ответ
{
    "profile": {
        "id": 1291399,
        "user_id": 1330891,
        "provider": {
            "id": 72,
            "key": "sber",
            "name": "Сбербанк",
            "type": "bank",
            "logo": "https://cashoff.ru/static/build/cashoff/v-7c7f6a8a477c1b3e3d20a0ca10147158/img/logo/fi/sber_36.svg"
        },
        "session": {
            "key": "ac1db988825748b595526852e02c2712",
            "status": "ok"
        },
        "status": "updating",
        "created": "2019-03-28T08:52:23Z",
        "changed": "2019-03-28T08:52:23Z"
    },
	"update_status": "ok"
}

Переданные при создании профиля данные авторизации (auth_data) используются только если одновременно запускается обновление (start_update). Если обновление не запускается, то будет сохранен только логин (для контроля уникальности профиля у пользователя), а пароль не перенесется к моменту следующего запуска обновления.

CASHOFF не позволяет одному и тому же пользователю добавлять несколько одинаковых профилей (профили с одинаковым провайдером и логином). При повторном добавлении будет возвращена ошибка profile_exists, а в атрибуте extra.existing_profile_id можно будет найти идентификатор существующего дублирующего профиля. Подобная же ошибка будет при запросе POST /profiles/{profile_id}/session/auth, если изначально запрос создания профиля не включал логин, а тут он пришел и нашелся дублирующий профиль.

Запуск обновления профиля

Обновление запускается с помощью запроса POST /profiles/{profile_id}/update. Ответ придет с полем status, в котором будет указано что обновление либо успешно запущено (ok), либо код причины невозможности запуска.

Обновление состоит из двух ключевых фаз: 

  • Авторизация. Сначала идет попытка подключения по сохраненным данным (есть еще активная сессия). Если это не получается, то запускается сам процесс авторизации
  • Импорт данных. Данная фаза возможна только после успешной авторизации. 

Авторизация в ЛК провайдера

В процессе авторизации CASHOFF с помощью предоставляемых клиентом данных пытается авторизоваться в ЛК провайдера. В авторизации есть ряд ключевых моментов, которые нужно учитывать:

  1. Последовательность шагов авторизации не фиксирована: многие ЛК поддерживают вход как с смс, так и без, могут запрашивать проверочные вопросы, а так же выдавать дополнительные шаги в зависимости от введенных данных. Как результат набор шагов и их количество могут меняться в зависимости от пользователя, даже в рамках одного профиля.
  2. Пользователь может ошибаться при вводе данных авторизации, по этому каждый шаг может завершиться ошибкой введенных данных (например не правильный пароль). Такую ошибку необходимо показать пользователю и для продолжения обновления он должен будет повторить ввод данных текущего шага.

С учетом этих моментов общая схема авторизации выглядит следующим образом:

  1. CASHOFF проверяет статус сохраненной у него сессии в ЛК провайдера.
    1. Сессия "жива" (по ней можно получить данные): авторизация завершена, переход к шагу 6.
    2. Сессия не "жива": начинается непосредственно процесс авторизации.
  2. CASHOFF определяет текущий шаг авторизации и указывает в профиле какой шаг нужно выполнить сейчас (session.auth_step).
  3. Приложение показывает пользователю форму из session.auth_step. может само выбрать провайдера
  4. Пользователь вводит данные в форму, приложение отправляет форму в CASHOFF.
  5. CASHOFF применяет данные для авторизации в ЛК. Далее может быть один из следующих исходов:
    1. Шаг выполнен успешно, больше шагов нет: авторизация завершена, переход к шагу 6.
    2. Шаг выполнен успешно, но есть следующий: профиль получает новую форму (session.auth_step) и с ней повторяет шаги 3-5.
    3. Шаг выполнен неуспешно: форма (session.auth_step) остается та же, приложение показывает пользователю ошибку и он повторяет ввод данных (шаг 4).
  6. Авторизация завершена, начинается импорт данных.

Таким образом авторизация представляет собой цикл из шагов 2-5, который предваряется проверкой статуса имеющейся сессии и завершается переходом в импорт данных.

Данные на шаге 5 передаются в CASHOFF с помощью запроса POST /profiles/{profile_id}/session/auth и должны соответствовать текущему шагу (session.auth_step):

Запрос
{
    "auth_data": {
        "sms": "1234"
    }
}

Т.к. авторизация является частью процесса обновления, то каждый раз при отправке данных профиль будет переходить в статус updating, взаимодействовать фоново с провайдером и по результату переходить в какой-то из статусов: auth, error, ok

Завершение фазы авторизации можно отследить по тому, что update_current_stage <> auth.

В CASHOFF есть ограничение в 15 минут на выполнение шага 4: если с момента установки текущего шага авторизации не придет в течении 15 минут  запрос с данным для него, то процесс авторизации (вместе с обновлением) будет сброшен и обновление нужно будет начинать заново. Любая отправка данных, даже не позволившая пройти шаг, приведет к обновлению таймера. По этому суммарно время авторизации не ограничено этими 15 минутами - это ограничение только на попытку (на стороне CASHOFF, провайдеры всё равно могут накладывать свои ограничения).

Описания шагов авторизации

Каждый шаг авторизации провайдера представляет собой форму с набором полей. Типовая форма первого шага выглядит следующим образом:

Запрос
{
	"key": "initial",
	"fields": [
		{
			"key": "login",
			"type": "text",
			"label": "Логин"
		},
		{
			"key": "password",
			"type": "password",
			"label": "Пароль"
		}
	]
}

У данной формы ключ initial, и в ней содержится поля: login и password. По ней необходимо вывести пользователю форму с двумя полями, "Логин" и "Пароль". Поля формы с type=password должны маскировать вводимые в них данные.

Если в форме указан атрибут help, то его содержимое нужно вывести пользователю над полями - это вспомогательный текст содержащий 1-2 предложения, который поясняет особенности ввода данных на данной форме.

Если заполнен атрибут session.status_message, то его текст нужно вывести в качестве ошибки формы.

Результат ввода по этой форме, будучи отправленным в cashoff, выглядит следующим образом:

Запрос
{
    "auth_data": {
        "login": "1234",
        "password": "qwerty"
    }
}

Импорт данных

После успешной авторизации обновление переходит к импорту данных. Сигналом окончания обновления будет переход профиля в статус ok (успешное завершение) или fatal (обновление прервалось ошибкой). Во втором случае текст ошибки будет доступен в поле session.status_message - это готовое сообщение для пользователя.

Набор данных зависит от типа провайдера, подробнее о списке загружаемых данных в зависимости от типа провайдера написано в разделе Провайдеры.

В CASHOFF обновление данных выполняется где возможно инкрементно: при каждом обновлении запрашиваются только данные за те даты, за которые они еще не загружены. 

Указание выполняемых при импорте данных действий

При запуске обновления можно указать, какие данные нужно импортировать - за это отвечает атрибут update_stages (есть как в методе запуска обновления, так и в методе создания профиля). В нём перечисляются шаги, которые нужно выполнить в рамках обновления, а так же можно на некоторых шагах затребовать выполнение дополнительных действий. Атрибут может заполняться в двух форматах: сокращенном (список идентификаторов шагов) и развернутом (для каждого шага можно указать доп. действия). Пример сокращенного:

Запрос
["auth", "accounts"]

(запрошена авторизация и загрузка списка продуктов). Пример развернутого:

Запрос
{
	"auth": {},
	"accounts": {
		"payment_rub": true
	}
}

(то же самое, только для продуктов запрошено определение того, с какого продукта можно совершать рублевые платежи через CASHOFF). 

При указании списка шагов шаг auth можно опустить, т.к. он выполняется автоматически при необходимости. Однако его необходимо указать, если цель обновления - только обновить авторизацию:

Запрос
["auth"]

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

Если атрибут не заполнен, то шаги по умолчанию проставляются согласно типу провайдера.

На каком конкретно этапе обновления сейчас находится профиль можно узнать через поле update_current_stage. К примеру в случае авторизации там будет auth, а в случае загрузки списка продуктов - accounts.

Ошибки в процессе обновления данных

Импорт данных может завершиться ошибкой, вызванной как парсером, так и провайдером. В таких случаях профиль получает статус status=error, а текст ошибки будет в поле session.status_message; код причины ошибки можно найти в атрибуте last_attempt_error.

При ошибке обновление прерывается. Далее можно либо сбросить текущий статус (POST /profiles/{profile_id}/reset), либо запустить обновление заново (POST /profiles/{profile_id}/update) (по сути это сброс + запуск).

Сброс очистит текст ошибки и переведет авторизацию на первый шаг. При сбросе уже загруженные данные останутся не тронутыми, он касается только непосредственно состояния профиля.

Уведомления об изменении профиля

В CASHOFF предусмотрено событие profile.updated, уведомления по которому отправляется при изменении профиля. Изменение включают в себя изменение профиля (статуса и текущего этапа обновления), состояния сессии

Уведомления не отправляются при изменении загруженных по профилю данных, в частности продуктов, операций и чеков. Данное событие является основным способом отслеживания процесса импорта данных.

В теле уведомления profile.updated приходит полная информация по профилю, аналогичная выдаче запроса GET /profiles/{profile_id}.

Ниже представлены примеры уведомлений по различным этапам импорта данных

Требуется ввод данных авторизации (смс)

{
  "event""profile.updated",
  "data": {
    "id"1289652,
    "user_id"1330891,
    "provider": {
      "id"123,
      "key""pyaterochka",
      "name""Пятерочка",
      "type""retail_store",
      "logo""https://cashoff.ru/static/build/cashoff/v-7c7f6a8a477c1b3e3d20a0ca10147158/img/logo/fi/pyaterochka_36.svg"
    },
    "session": {
      "key""4001dc087366468a961022bdb8542ffb",
      "status""ok",
      "auth_step": {
        "key""2",
        "help""На Ваш мобильный телефон выслано SMS-сообщение с паролем для входа.Пожалуйста, введите полученный пароль.",
        "fields": [
          {
            "key""sms",
            "type""text",
            "label""Смс-Код"
          }
        ]
      }
    },
    "status""auth",
    "created""2019-03-27T15:15:28Z",
    "changed""2019-03-27T15:22:01Z"
  }
}

Обновление успешно завершено

{
  "event""profile.updated",
  "data": {
    "id"1289652,
    "user_id"1330891,
    "provider": {
      "id"123,
      "key""pyaterochka",
      "name""Пятерочка",
      "type""retail_store",
      "logo""https://cashoff.ru/static/build/cashoff/v-7c7f6a8a477c1b3e3d20a0ca10147158/img/logo/fi/pyaterochka_36.svg"
    },
    "session": {
      "key""4001dc087366468a961022bdb8542ffb",
      "status""ok"
    },
    "status""ok",
    "created""2019-03-27T15:15:28Z",
    "changed""2019-03-27T15:24:46Z",
    "accounts_updated_at""2019-03-27T15:24:46Z",
    "update_done_at""2019-03-27T15:24:46Z",
    "update_started_at""2019-03-27T15:22:49Z"
  }
}

Неверные данные авторизации

{
  "event""profile.updated",
  "data": {
    "id"1289757,
    "user_id"1330891,
    "provider": {
      "id"123,
      "key""pyaterochka",
      "name""Пятерочка",
      "type""retail_store",
      "logo""https://cashoff.ru/static/build/cashoff/v-7c7f6a8a477c1b3e3d20a0ca10147158/img/logo/fi/pyaterochka_36.svg"
    },
    "session": {
      "key""f1f4a828b6cb403d9c23367785cd9e18",
      "status""error",
      "status_message""Неверный логин и/или пароль",
      "auth_step": {
        "key""1",
        "help""Введите номер карты лояльности или номер телефона и пароль",
        "fields": [
          {
            "key""login",
            "type""text",
            "label""Номер Карты Или Телефона"
          },
          {
            "key""password",
            "type""password",
            "label""Пароль"
          }
        ]
      }
    },
    "status""auth",
    "created""2019-03-27T15:51:59Z",
    "changed""2019-03-27T15:53:37Z"
  }
}

Ошибка обновления профиля

{
  "event""profile.updated",
  "data": {
    "id"1291399,
    "user_id"1330891,
    "provider": {
        "id"72,
        "key""mkb",
        "name""Московский Кредитный Банк",
        "type""bank",
        "logo""https://cashoff.ru/static/build/cashoff/v-7c7f6a8a477c1b3e3d20a0ca10147158/img/logo/fi/mkb_36.svg"
    },
    "session": {
        "key""ac1db988825748b595526852e02c2712",
        "status""error",
        "status_message""Произошла ошибка при взаимодействии с системой \"Московский Кредитный Банк\". Попробуйте повторить попытку позже."
    },
    "status""error",
    "created""2019-03-28T08:52:23Z",
    "changed""2019-03-28T08:53:46Z",
    "update_started_at""2019-03-28T08:53:42Z"
}