1
0
forked from BRT/arc

Add retry limits for "Подтверждаю" and "Оплатить" taps in PayByPhoneScript and PayByCardScript to prevent infinite loops and improve error handling with detailed logs.

This commit is contained in:
slava 2026-05-19 23:15:44 +05:00
parent feb839daf2
commit 14e46cac84
2 changed files with 74 additions and 6 deletions

View File

@ -413,7 +413,16 @@ void PayByCardScript::makePayment(
// 10. Ждём результат — "Платеж выполнен" или ошибку.
// Промежуточный статус "Выполняется" может висеть до ~1.5 мин, пока банк
// подтверждает перевод, поэтому общий таймаут — 60 итераций × 2 сек = 2 мин.
// Лимиты повторных тапов: если первый тап "Подтверждаю" не сработал,
// bottom sheet дисмиссится, виден "Оплатить" → перетапываем; следующий
// sheet → пробуем другую Y. Без лимита получали бесконечный
// "Оплатить ↔ Подтверждаю" со случайными попаданиями в системные экраны
// (security/launcher).
bool success = earlySuccess;
int confirmRetries = 0;
int oplatitRetries = 0;
const int maxConfirmRetries = 2;
const int maxOplatitRetries = 2;
for (int attempt = 0; !earlySuccess && attempt < 60; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(deviceId, width, height);
@ -458,14 +467,29 @@ void PayByCardScript::makePayment(
QString::fromUtf8("Подтверждение платежа"), true);
if (!retryConfirm.isEmpty() &&
retryConfirm.contentDesc.contains(QString::fromUtf8("Подтверждаю"))) {
qDebug() << "[Dushanbe::PayByCard] Confirm sheet still visible, re-tapping 'Подтверждаю'";
if (confirmRetries >= maxConfirmRetries) {
m_error = "Confirm tap missed " + QString::number(confirmRetries)
+ " times (sheet still visible)";
qWarning() << "[Dushanbe::PayByCard]" << m_error;
AppLogger::log("dushanbe/PayByCard", deviceId, m_error,
AdbUtils::captureScreenshotBytes(deviceId), "CREATE_TRANSACTION");
return;
}
++confirmRetries;
qDebug() << "[Dushanbe::PayByCard] Confirm sheet still visible, re-tap"
<< confirmRetries << "/" << maxConfirmRetries;
const QByteArray screen = AdbUtils::captureScreenshotBytes(deviceId);
QPoint tap = OcrUtils::findTextCenter(screen,
QString::fromUtf8("Подтверждаю"),
retryConfirm.y1(), retryConfirm.y2());
if (tap.x() < 0) {
const int tapY = retryConfirm.y2() - qMax(150, height / 14);
// Варьируем Y: первый retry — выше fallback на 100px,
// второй — ещё на 100px выше. Compose-кнопка действия
// обычно на y2 - 150..350; перебираем диапазон.
const int extra = 100 * confirmRetries;
const int tapY = retryConfirm.y2() - qMax(150 + extra, height / 14 + extra);
tap = QPoint(retryConfirm.x(), tapY);
qDebug() << "[Dushanbe::PayByCard] OCR miss, fallback tap at" << tap;
}
AdbUtils::makeTap(deviceId, tap.x(), tap.y());
QThread::msleep(4000);
@ -482,7 +506,17 @@ void PayByCardScript::makePayment(
retryOplatit = xmlScreenParser.findButtonNode(QString::fromUtf8("Оплатить"), false);
}
if (!retryOplatit.isEmpty() && retryOplatit.clickable) {
qDebug() << "[Dushanbe::PayByCard] Sheet dismissed, re-tapping 'Оплатить' to restart confirm flow";
if (oplatitRetries >= maxOplatitRetries) {
m_error = "Sheet dismissed and re-tap exhausted ("
+ QString::number(oplatitRetries) + " retries)";
qWarning() << "[Dushanbe::PayByCard]" << m_error;
AppLogger::log("dushanbe/PayByCard", deviceId, m_error,
AdbUtils::captureScreenshotBytes(deviceId), "CREATE_TRANSACTION");
return;
}
++oplatitRetries;
qDebug() << "[Dushanbe::PayByCard] Sheet dismissed, re-tap 'Оплатить'"
<< oplatitRetries << "/" << maxOplatitRetries;
AdbUtils::makeTap(deviceId, retryOplatit.x(), retryOplatit.y());
QThread::msleep(3000);
continue;

View File

@ -409,7 +409,16 @@ void PayByPhoneScript::makePayment(
// 10. Ждём результат — "Платеж выполнен" или ошибку.
// Промежуточный статус "Выполняется" может висеть до ~1.5 мин, пока банк
// подтверждает перевод, поэтому общий таймаут — 60 итераций × 2 сек = 2 мин.
// Лимиты повторных тапов: если первый тап "Подтверждаю" не сработал,
// bottom sheet дисмиссится, виден "Оплатить" → перетапываем; следующий
// sheet → пробуем другую Y. Без лимита получали бесконечный
// "Оплатить ↔ Подтверждаю" со случайными попаданиями в системные экраны
// (security/launcher).
bool success = earlySuccess;
int confirmRetries = 0;
int oplatitRetries = 0;
const int maxConfirmRetries = 2;
const int maxOplatitRetries = 2;
for (int attempt = 0; !earlySuccess && attempt < 60; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(deviceId, width, height);
@ -450,14 +459,29 @@ void PayByPhoneScript::makePayment(
QString::fromUtf8("Подтверждение платежа"), true);
if (!retryConfirm.isEmpty() &&
retryConfirm.contentDesc.contains(QString::fromUtf8("Подтверждаю"))) {
qDebug() << "[Dushanbe::PayByPhone] Confirm sheet still visible, re-tapping 'Подтверждаю'";
if (confirmRetries >= maxConfirmRetries) {
m_error = "Confirm tap missed " + QString::number(confirmRetries)
+ " times (sheet still visible)";
qWarning() << "[Dushanbe::PayByPhone]" << m_error;
AppLogger::log("dushanbe/PayByPhone", deviceId, m_error,
AdbUtils::captureScreenshotBytes(deviceId), "CREATE_TRANSACTION");
return;
}
++confirmRetries;
qDebug() << "[Dushanbe::PayByPhone] Confirm sheet still visible, re-tap"
<< confirmRetries << "/" << maxConfirmRetries;
const QByteArray screen = AdbUtils::captureScreenshotBytes(deviceId);
QPoint tap = OcrUtils::findTextCenter(screen,
QString::fromUtf8("Подтверждаю"),
retryConfirm.y1(), retryConfirm.y2());
if (tap.x() < 0) {
const int tapY = retryConfirm.y2() - qMax(150, height / 14);
// Варьируем Y: первый retry — выше fallback на 100px,
// второй — ещё на 100px выше. Compose-кнопка действия
// обычно на y2 - 150..350; перебираем диапазон.
const int extra = 100 * confirmRetries;
const int tapY = retryConfirm.y2() - qMax(150 + extra, height / 14 + extra);
tap = QPoint(retryConfirm.x(), tapY);
qDebug() << "[Dushanbe::PayByPhone] OCR miss, fallback tap at" << tap;
}
AdbUtils::makeTap(deviceId, tap.x(), tap.y());
QThread::msleep(4000);
@ -474,7 +498,17 @@ void PayByPhoneScript::makePayment(
retryOplatit = xmlScreenParser.findButtonNode(QString::fromUtf8("Оплатить"), false);
}
if (!retryOplatit.isEmpty() && retryOplatit.clickable) {
qDebug() << "[Dushanbe::PayByPhone] Sheet dismissed, re-tapping 'Оплатить' to restart confirm flow";
if (oplatitRetries >= maxOplatitRetries) {
m_error = "Sheet dismissed and re-tap exhausted ("
+ QString::number(oplatitRetries) + " retries)";
qWarning() << "[Dushanbe::PayByPhone]" << m_error;
AppLogger::log("dushanbe/PayByPhone", deviceId, m_error,
AdbUtils::captureScreenshotBytes(deviceId), "CREATE_TRANSACTION");
return;
}
++oplatitRetries;
qDebug() << "[Dushanbe::PayByPhone] Sheet dismissed, re-tap 'Оплатить'"
<< oplatitRetries << "/" << maxOplatitRetries;
AdbUtils::makeTap(deviceId, retryOplatit.x(), retryOplatit.y());
QThread::msleep(3000);
continue;