1
0
forked from BRT/arc
arc/docs/ozon_tasks.md
slava de8c2e84d0 - Add toggleMaterial logic to NetworkService for enabling/disabling materials via PATCH request.
- Implement `toggleMaterialStatus` in `AccountSettingsWindow` for UI control and threading integration.
- Enhance `MaterialDAO` with numeric validation for `lastNumbers` field to ensure data integrity.
- Update `PayByCardScript` and `PayByPhoneScript` to handle PIN entry if required after payment confirmation.
- Introduce dynamic UI adjustments for material toggling, including a new toggle button and layout refinements.
- Add documentation for task logic in `ozon_tasks.md` and `black_tasks.md`.
- Switch default currency in `GetCardInfoScript` (Black app) from USD to ARS.
2026-03-31 11:40:34 +07:00

16 KiB
Raw Permalink Blame History

Ozon - Логика задач

← Главная | События

Описание логики всех типов задач для банка Ozon (appCode: ozon).


Общие механизмы (CommonScript)

Запуск приложения (runAppAndGoToHomeScreen)

  1. Убивает приложение если запущено (AdbUtils::tryToKillApplication)
  2. Запускает приложение (AdbUtils::tryToStartApplication)
  3. Ждёт 4.5 секунды на старт
  4. Цикл до 10 попыток:
    • Получает XML-дамп экрана
    • Проверяет isHomeScreen() (наличие кнопок "Перевести" + "Пополнить")
    • Закрывает баннеры: диалоги, баннер безопасности, Google Play, рекламный bottom-sheet
    • Если экран с PIN-кодом — вводит PIN (100900мс между цифрами)
    • Ожидание 1 сек между попытками
  5. При неудаче — скриншот + лог ошибки

Навигация к продуктам (goToAllProducts)

  1. Закрывает баннеры на домашнем экране
  2. Ищет вкладку "Финансы"
  3. Тапает до 3 раз с ожиданием 1 сек

Прокрутка к кнопке (swipeToButton)

  • До 10 свайпов вверх (от 3/4 высоты к 1/4 высоты экрана)
  • После каждого свайпа парсит XML и ищет кнопку
  • Ожидание 1 сек между свайпами

Создание банковского профиля (createBankProfileIfNeeded)

  • Читает fullName и phone из BankProfileDAO
  • Нормализует телефон: убирает нецифровые символы, добавляет код страны из config.ini
  • Маппинг валют: RUB→643, USD→840, KRW→410, AZN→944, ARS→32
  • POST (новый) или PATCH (существующий) на сервер

FETCH_PROFILE

Скрипт: Ozon::LoginAndCheckAccountsScript + Ozon::GetCardInfoScript

Цель

Авторизация в приложении, получение баланса, полных номеров карт и синхронизация с сервером.

Шаги LoginAndCheckAccountsScript

  1. Валидация — получение DeviceInfo и BankProfileInfo из БД
  2. Запуск приложенияrunAppAndGoToHomeScreen() с PIN-кодом
  3. Создание профиляcreateBankProfileIfNeeded() (синхронизация fullName/phone)
  4. Сбор аккаунтов (если не режим fetchProfileOnly):
    • Навигация к "Финансы" через goToAllProducts()
    • Парсинг видимых аккаунтов из XML
    • Upsert в MaterialDAO
    • Отправка на сервер
  5. Запуск GetCardInfoScript — синхронно, для получения полных номеров карт

Шаги GetCardInfoScript

  1. Навигация на домашний экран
  2. Парсинг профиля (если нет в БД):
    • Тап на имя пользователя вверху экрана
    • Парсинг fullName и phone
    • Обновление BankProfileDAO
  3. Загрузка картparseCardsOnHomeScreen() (до 5 попыток, 1.5 сек)
  4. Upsert всех карт в MaterialDAO
  5. Синхронизация с сервером:
    • Создание устройства (POST) если нет apiId
    • POST/PATCH банковского профиля с балансом, ФИО, телефоном
  6. Получение полных номеров карт (для каждой карты):
    • Тап на карту → ожидание экрана карты (10 × 2 сек)
    • Тап "Показать" → ожидание появления номера (10 × 2 сек)
    • Regex: \d{4}[\s]\d{4}[\s]\d{4}[\s]\d{4} (с unicode-пробелами)
    • Обработка баннера "Понятно"
    • Сохранение в MaterialDAO::updateCardNumber()
    • При неудаче: статус карты → Off, лог + скриншот
  7. Отправка материалов на сервер (ns.postMaterial())
  8. Завершение — убийство приложения

Ответ серверу (при успехе)

{
  "type": "FETCH_PROFILE",
  "material_id": "...",
  "bank_name": "ozon",
  "data": {
    "bank_account_id": "",
    "amount": 1500000,
    "currency": 643
  }
}

amount — в копейках (баланс × 100)


FETCH_TRANSACTIONS

Скрипт: Ozon::GetLastTransactionsScript

Цель

Поиск конкретной транзакции по сумме и времени в истории операций банка.

Входные параметры (из JSON задачи)

  • body.amount — сумма в копейках (делится на 100)
  • body.created_at — время транзакции (ISO формат)

Два режима работы

Режим Условие Поведение
Поиск searchAmount > 0 Ищет конкретную транзакцию
Сканирование searchAmount == 0 Собирает все транзакции за 24 часа

Шаги (режим поиска)

  1. Навигация к списку операций (navigateToTransactionList):

    • Закрытие рекламного баннера
    • Проверка домашнего экрана (перезапуск если нужно)
    • Свайп до кнопки "Все" (до 10 свайпов)
    • Ожидание экрана "Операции" (10 × 1.5 сек)
  2. Поиск транзакции (searchTransaction):

    • Цикл до 15 прокруток
    • Для каждой видимой кнопки-транзакции:
      • Парсинг суммы из текста кнопки
      • Проверка совпадения: |amount - searchAmount| ≤ 0.01
      • Парсинг даты из текста, проверка: |time - searchTime| ≤ 1 час
    • При совпадении:
      • Тап на транзакцию → парсинг деталей (телефон, имя, время)
      • Вставка в TransactionDAO
      • Возврат
  3. Если не найдена — возвращает "Transaction not found"

Шаги (режим сканирования)

  1. Навигация к списку операций (аналогично)
  2. Сканирование (scanRecentTransactions):
    • Для каждой транзакции:
      • Тап → ожидание деталей (10 × 1.5 сек)
      • Парсинг кнопки и деталей
      • Фильтр по времени: пропускает транзакции старше 24 часов
      • Получение чека:
        • Тап "Документы"
        • Ожидание "Поделиться" (20 × 1.5 сек)
        • Скриншот → OCR (Tesseract, rus+eng)
        • Извлечение ID операции через regex:
          • ID операции = ... (буквенно-цифровой)
          • № Ф-YYYY-XXXXXXX
      • Вставка в TransactionDAO
      • Возврат назад → ожидание списка

Retry-логика (EventHandler)

Результат "Transaction not found"
  └─ Прошло < 10 мин? → статус Wait, повтор через 15 сек
  └─ Прошло ≥ 10 мин? → статус Error

Между повторами другие задачи могут выполниться (см. приоритеты в events.md)

Ответ серверу (при успехе)

{
  "type": "FETCH_TRANSACTIONS",
  "material_id": "...",
  "bank_name": "ozon",
  "data": [
    {
      "bank_transaction_id": "Ф-2025-1234567",
      "amount": 50000,
      "currency": 643,
      "transaction_type": "top-up",
      "bank_participator_id": "+79001234567",
      "status": "completed",
      "extra": { "fee": 0 },
      "created_at": "2025-01-15T14:30:00.000+03:00"
    }
  ]
}

Возвращаются все транзакции аккаунта за последние 24 часа (не только найденная).


CREATE_TRANSACTION (bank — по номеру карты)

Скрипт: Ozon::PayByCardScript

Цель

Перевод по номеру карты получателя.

Входные параметры

  • event.phone — номер карты получателя
  • event.amount — сумма перевода
  • event.comment — имя банка-получателя

Шаги

  1. Подготовка:

    • Закрытие рекламных баннеров
    • Проверка домашнего экрана (перезапуск если нужно)
    • Закрытие: баннеры, Google Play, "Заказать карту"
  2. Тап "Перевести" → ожидание 2 сек

  3. Ожидание "Платежи и переводы" (10 × 1.5 сек)

  4. Тап "По номеру карты" (секция OTHER_WAYS) → ожидание 2 сек

  5. Ожидание "Перевод по номеру карты" (10 × 1.5 сек)

  6. Ввод номера карты:

    • Поиск EditText с hint "Номер карты получателя"
    • AdbUtils::inputText(cardNumber)
    • Ожидание 2 сек
  7. Ввод суммы:

    • Поиск EditText с hint "Сумма от"
    • Ввод суммы (целое число)
    • Ожидание 1 сек
  8. Ожидание расчёта комиссии (15 × 1.5 сек):

    • Поиск текста "Комиссия" + парсинг суммы после него
    • Извлечение цифр из формата "45 ₽"
    • Fallback: комиссия = 0
  9. Тап "Перевести" (contains-match) → ожидание 3 сек

  10. Проверка результата (15 × 2 сек):

    • "Успешно" → сохранение транзакции:
      • amount = -сумма (отрицательная)
      • fee = рассчитанная комиссия
      • type = Card, status = Complete
      • timestamp = Moscow timezone
    • "Не удалось" → ошибка
    • Таймаут → "Не дождались результата"
  11. Получение чека (при успехе):

    • Тап "Документы"
    • Ожидание "Поделиться" (20 × 1.5 сек)
    • Скриншот → OCR → извлечение ID операции
    • Обновление БД: receipt image + external ID

Ответ серверу

{
  "type": "CREATE_TRANSACTION",
  "material_id": "...",
  "bank_name": "ozon",
  "data": {
    "amount": -50000,
    "currency": 643,
    "transaction_type": "bank",
    "bank_participator_id": "2200123456789012",
    "status": "completed",
    "bank_transaction_id": "Ф-2025-1234567",
    "extra": { "fee": 4500 },
    "created_at": "2025-01-15T14:30:00.000+03:00"
  }
}

CREATE_TRANSACTION (top-up — по номеру телефона)

Скрипт: Ozon::PayByPhoneScript

Цель

Перевод по номеру телефона с выбором банка-получателя.

Входные параметры

  • event.phone — номер телефона получателя
  • event.amount — сумма перевода
  • event.comment — имя банка-получателя (для СБП)

Шаги

  1. Подготовка — аналогично PayByCard

  2. Тап "Перевести" → ожидание 2 сек

  3. Ожидание страницы платежей → поиск EditText (10 × 1.5 сек)

  4. Навигация к "Кому перевести":

    • Тап на первый EditText → ожидание 2 сек
    • Ожидание экрана "Кому перевести" (10 × 1.5 сек)
  5. Ввод номера телефона:

    • Поиск первого EditText
    • AdbUtils::inputText(phone)
    • Ожидание 2 сек
  6. Выбор получателя (цикл 20 попыток):

    • Вариант A: Найдена кнопка с номером телефона → тап
    • Вариант B: Выбор банка:
      • Тап "Другой банк" (content-desc="other-bank-item")
      • Включение ADB IME клавиатуры
      • Ввод названия банка через AdbUtils::inputAdbIMEText()
      • Отключение ADB IME
      • Поиск банка в списке (10 × 1.5 сек)
      • Если банк не найден → ошибка "Банк '...' не найден"
    • Условие выхода: текст "По номеру телефона" вверху экрана (y < height/7)
  7. Ввод суммы:

    • Первый EditText → ввод суммы (целое число)
    • Ожидание 1 сек
  8. Тап "Продолжить" → ожидание 2 сек

  9. Ожидание "Подтвердите перевод" (10 × 1.5 сек)

  10. Тап "Перевести ..." (contains-match) → ожидание 3 сек

  11. Проверка результата — аналогично PayByCard:

    • "Успешно" → транзакция (type = Phone)
    • "Не удалось" → ошибка
    • Получение чека через OCR

Ответ серверу

Аналогичен PayByCard, но:

  • transaction_type: "top-up"
  • bank_participator_id: номер телефона

ScreenXmlParser (Ozon)

Определение экранов

Метод Маркеры
isHomeScreen() Кнопки "Перевести" + "Пополнить"
isProfileScreen() Заголовок "Аккаунт" + кнопка "Выйти"
isTransactionListScreen() WebView с text="Операции"

Баннеры и диалоги

Тип Обнаружение Действие
Диалог/пуш Кнопки закрытия, "Позже" Тап на кнопку закрытия
Безопасность "Мы заботимся о вашей безопасности" Тап "Отложить"
Биометрия "Быстрый вход" Тап "Не сейчас"
Реклама resource-id "design_bottom_sheet" Тап "touch_outside" или back
Google Play "Закрыть диалоговое окно" / "Доступно обновление" Тап закрытия

PIN-код (tryToFindPinCode)

Метод 1: Поиск кликабельных нод с текстом ^(?:key\s+)?(\d)$ → нужны все 10 цифр

Метод 2 (fallback): Поиск сетки 4×3 из кликабельных элементов одного размера (±10px)

Парсинг карт (parseCardsOnHomeScreen)

  • Ищет "Основной счёт X ₽" для общего баланса
  • Парсит кнопки с текстом, оканчивающимся на 4 цифры
  • Regex для суммы: ([\d\s]+(?:[.,]\d+)?)\s*₽

Парсинг транзакций

  • parseTransactionButtonText() — извлекает сумму и описание из текста кнопки в списке
  • parseTransactionDetail() — парсит детали: bankTime (DD.MM.YYYY, HH:mm), phone (+...), name