1
0
forked from BRT/arc
arc/android/rshb/PayByPhoneScript.cpp
trnsmkot e32dd21827 Add event handling and improved account management
Introduced a new "events" table and corresponding EventDAO for managing event data. Enhanced account handling with device ID and partial card number support. Added a NetworkService method to fetch payments from an API and store them as events.
2025-05-21 20:18:53 +07:00

286 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "PayByPhoneScript.h"
#include <QRandomGenerator>
#include <QThread>
#include <utility>
#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;
payBankName = m_bankName == "Россельхозбанк" ? "Россельхозбанк" : "Другой банк через СБП";
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;
}
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));
Node payByPhoneScreenTitle = xmlScreenParser.findTextNode("Перевод по телефону");
Node continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false);
if (!payByPhoneScreenTitle.isEmpty() && payByPhoneScreenTitle.y() < 300 && !continuePayBtn.isEmpty()) {
Node sumBtn = xmlScreenParser.findButtonNode("0", false);
if (!sumBtn.isEmpty()) {
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);
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 = "Не смогли найти 'Перевод по телефону'";
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<Node> 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 (!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);
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()) {
if (!TransactionDAO::updateTransactionStatus(transaction.id, TransactionStatus::Complete)) {
qDebug() << "Not updated: " << transaction.id;
}
// TODO обновляем данные
break;
}
QThread::msleep(1000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
}
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::getApplicationsByDeviceId(m_account.deviceId, m_account.appName);
if (device.id.isEmpty() || app.id.isEmpty()) {
qDebug() << "Device or app not found....";
return;
}
if (runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) {
if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) {
qDebug() << "======== 1. All products";
QList<QPair<AccountInfo, Node> > 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;
}
}
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;
}
} else {
m_error = "Не смогли дойти до домашней страницы....";
qDebug() << m_error;
}
emit finishedWithResult(m_error);
}