#include "PayByPhoneScript.h" #include #include #include #include "DatabaseManager.h" #include "adb/AdbUtils.h" #include "dao/AccountDAO.h" #include "dao/ApplicationDAO.h" #include "dao/DeviceDAO.h" #include "dao/TransactionDAO.h" #include "db/ApplicationInfo.h" #include "db/DeviceInfo.h" PayByPhoneScript::PayByPhoneScript( AccountInfo account, QString phone, QString bankName, const double amount, QObject *parent ) : CommonScript(parent), m_account(std::move(account)), m_phone(std::move(phone)), m_bankName(std::move(bankName)), m_amount(amount) { } PayByPhoneScript::~PayByPhoneScript() = default; void PayByPhoneScript::makePayment( const QString &deviceId, const QString &pinCode, int width, int height ) { // Оплата со страницы карты xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); Node payBtn = xmlScreenParser.findButtonNode("Оплатить", false); if (!payBtn.isEmpty()) { AdbUtils::makeTap(deviceId, payBtn.x(), payBtn.y()); QThread::msleep(1000); } else { m_error = "Кнопка 'Оплатить' не нашлась"; qDebug() << m_error; return; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); // TODO - По номеру карты В РСХБ и другие банки Node payTitle = xmlScreenParser.findTextNode("Платежи и переводы"); if (!payTitle.isEmpty() && payTitle.y() < 300) { Node payByPhoneBtn = xmlScreenParser.findButtonNode("По номеру телефона В РСХБ и через", false); if (!payByPhoneBtn.isEmpty()) { AdbUtils::makeTap(deviceId, payByPhoneBtn.x(), payByPhoneBtn.y()); QThread::msleep(1000); } else { m_error = "Не смогли найти пункт 'По номеру телефона В РСХБ и через'"; qDebug() << m_error; return; } } else { m_error = "Не смогли перейти на экран 'Платежи и переводы'"; qDebug() << m_error; return; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); Node payByPhoneTitle = xmlScreenParser.findTextNode("Перевод по телефону"); Node continueBtn = xmlScreenParser.findButtonNode("Продолжить", false); if (!payByPhoneTitle.isEmpty() && payByPhoneTitle.y() < 300 && continueBtn.isEmpty()) { Node inputPhone = xmlScreenParser.findFirstEditText(); AdbUtils::makeTap(deviceId, inputPhone.x(), inputPhone.y()); QThread::msleep(500); AdbUtils::inputText(deviceId, m_phone); QThread::msleep(1000); } else { m_error = "Не смогли найти пункт 'Перевод по телефону'"; qDebug() << m_error; return; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); QString payBankName = m_bankName == QStringLiteral("Россельхозбанк") ? "Россельхозбанк" : "Другой банк через СБП"; Node payOtherBankBtn = xmlScreenParser.findButtonNode(payBankName, false); if (!payOtherBankBtn.isEmpty()) { AdbUtils::makeTap(deviceId, payOtherBankBtn.x(), payOtherBankBtn.y()); QThread::msleep(1000); } else { m_error = "Не смогли нажать на первичный выбор банка"; qDebug() << m_error; return; } if (m_bankName != QStringLiteral("Россельхозбанк")) { xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); for (int i = 0; i < 5; ++i) { Node allBanksTitle = xmlScreenParser.findTextNode("Банки участники"); if (!allBanksTitle.isEmpty() && allBanksTitle.y() < 300) { Node inputBankName = xmlScreenParser.findFirstEditText(); AdbUtils::makeTap(deviceId, inputBankName.x(), inputBankName.y()); QThread::msleep(500); AdbUtils::enableAdbIMEKeyboard(deviceId); AdbUtils::inputAdbIMEText(deviceId, m_bankName); AdbUtils::disableAdbIMEKeyboard(deviceId); QThread::msleep(1000); break; } else { qDebug() << "--- allBanksTitle not found, i: " << i; } QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); for (int i = 0; i < 5; ++i) { Node bankNameListItem = xmlScreenParser.findTextNode(m_bankName); if (!bankNameListItem.isEmpty()) { AdbUtils::makeTap(deviceId, bankNameListItem.x(), bankNameListItem.y()); QThread::msleep(1000); break; } else { qDebug() << "--- bankNameListItem not found, i: " << i; } QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); const bool isRshbBank = m_bankName == QStringLiteral("Россельхозбанк"); QString payByPhoneScreenTitleText = isRshbBank ? "Перевод клиенту РСХБ" : "Перевод по телефону"; Node payByPhoneScreenTitle = xmlScreenParser.findTextNode(payByPhoneScreenTitleText); Node continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false); if (isRshbBank || !payByPhoneScreenTitle.isEmpty() && payByPhoneScreenTitle.y() < 300 && !continuePayBtn.isEmpty()) { Node sumBtn = xmlScreenParser.findButtonNode("0", false); if (isRshbBank || !sumBtn.isEmpty()) { if (!isRshbBank) { AdbUtils::makeTap(deviceId, sumBtn.x(), sumBtn.y()); QThread::msleep(500); } AdbUtils::inputText(deviceId, QString::number(m_amount)); QThread::msleep(500); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); Node readyBtn = xmlScreenParser.findTextNode("Готово"); if (!readyBtn.isEmpty()) { AdbUtils::makeTap(deviceId, readyBtn.x(), readyBtn.y()); QThread::msleep(500); if (isRshbBank) { QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false); } AdbUtils::makeTap(deviceId, continuePayBtn.x(), continuePayBtn.y()); QThread::msleep(1000); } else { m_error = "Не смогли нажать 'Готово'"; qDebug() << m_error; return; } } else { m_error = "Не смогли нажать 'Продолжить'"; qDebug() << m_error; return; } } else { m_error = "Не смогли найти заголовок: " + payByPhoneScreenTitleText; qDebug() << m_error; return; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); bool inputedPinCode = false; for (int i = 0; i < 5; ++i) { // TODO кнопка содержит "Перевести 10 ₽" если будет от 1000 то скорее всвего будет "Перевести 1 000 ₽", // TODO не ясно как форматирование будет, поэтому пока на начало зашьем Node makePayBtn = xmlScreenParser.findButtonNode("Перевести ", true); if (!makePayBtn.isEmpty()) { AdbUtils::makeTap(deviceId, makePayBtn.x(), makePayBtn.y()); QThread::msleep(500); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); if (const QList pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите код доступа"); pincode.size() == 4) { inputPinCode(deviceId, pincode); inputedPinCode = true; // После ввода пин-кода, ждем 3с QThread::msleep(3000); } break; } else { qWarning() << "--- makePayBtn not found, i: " << i; } QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } if (!isRshbBank && !inputedPinCode) { m_error = "Не смогли ввести пин-код!'"; qDebug() << m_error; return; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); for (int i = 0; i < 10; ++i) { Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false); if (!moreDetailBtn.isEmpty()) { AdbUtils::makeTap(deviceId, moreDetailBtn.x(), moreDetailBtn.y()); QThread::msleep(200); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); // TODO считываем все данные по переводу TransactionInfo transaction = xmlScreenParser.parseTransactionInfo(width, height); if (isRshbBank) { transaction.bankName = m_bankName; } transaction.accountId = m_account.id; transaction.amount = -m_amount; transaction.type = TransactionType::Phone; transaction.id = TransactionDAO::insertTransaction(transaction); if (transaction.id == -1) { // FIXME alert!! qDebug() << "Transaction has not inserted..."; } // ждем 10 секунд смену статуса for (int k = 0; k < 10; ++k) { Node paymentCompleted = xmlScreenParser.findTextNode("Исполнен", 0, 0, width, height / 2); if (!paymentCompleted.isEmpty()) { transaction.status = TransactionStatus::Complete; if (!TransactionDAO::updateTransactionStatus(transaction.id, TransactionStatus::Complete)) { qDebug() << "Not updated: " << transaction.id; } // TODO обновляем данные break; } QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } postNewTransactionData(m_account, transaction); break; } else { qWarning() << "--- moreDetailBtn not found, i: " << i; } QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter)); } Node homeBtn = xmlScreenParser.findButtonNode("Вернуться на главную", false); AdbUtils::makeTap(deviceId, homeBtn.x(), homeBtn.y()); QThread::msleep(1000); } void PayByPhoneScript::doStart() { const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appName); if (device.id.isEmpty() || app.id == -1) { m_error = "Device or app not found...."; qDebug() << m_error; emit finishedWithResult(m_error); return; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); if (!xmlScreenParser.isHomeScreen()) { if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { m_error = "Не смогли дойти до домашней страницы...."; qDebug() << m_error; emit finishedWithResult(m_error); return; } } if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { qDebug() << "======== 1. All products"; QList > accounts = xmlScreenParser.parseAccountsInfo(); for (auto &[account, node]: accounts) { account.deviceId = m_account.deviceId; account.appName = m_account.appName; if (!AccountDAO::upsertAccount(account)) { qDebug() << "Not updated: " << account.lastNumbers; } } postAccountsData(accounts); for (auto &[account, node]: accounts) { // FIXME будем считать что числа последние везде уникальные if (m_account.lastNumbers == account.lastNumbers) { qDebug() << "======== 2. Tap by card"; AdbUtils::makeTap(device.id, node.x(), node.y()); QThread::msleep(1000); break; } } qDebug() << "======== 3. Make payment"; makePayment(device.id, app.pinCode, device.screenWidth, device.screenHeight); } else { m_error = "Не смогли открыть 'Все продукты'"; qDebug() << m_error; } emit finishedWithResult(m_error); }