Handle declined transactions and enhance payment scripts:
- Add unique `bank_transaction_id` generation for declined transactions in `EventHandler`.
- Extend timeout and iteration logic to better handle intermediate statuses ("Выполняется") in `PayByPhoneScript` and `PayByCardScript`.
- Update
This commit is contained in:
parent
d58100083f
commit
c144305240
@ -50,28 +50,27 @@ void GetProfileInfoScript::doStart() {
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
}
|
||||
|
||||
// Парсим баланс с домашнего экрана. Если баланс не подгрузился (==0),
|
||||
// тапаем кнопку обновления и перечитываем дамп. Лоадера у refresh нет,
|
||||
// Всегда тапаем кнопку обновления — баланс на экране может быть закэширован
|
||||
// и не отражать актуальное состояние счёта. Лоадера у refresh нет,
|
||||
// поэтому ждём фиксированно.
|
||||
double parsedBalance = xmlScreenParser.parseBalanceFromHomeScreen();
|
||||
qDebug() << "[Dushanbe::GetProfileInfo] Parsed balance:" << parsedBalance;
|
||||
Node refreshBtn = xmlScreenParser.findRefreshButtonOnHomeScreen();
|
||||
if (refreshBtn.x() > 0) {
|
||||
qDebug() << "[Dushanbe::GetProfileInfo] Tapping refresh to get up-to-date balance";
|
||||
AdbUtils::makeTap(m_deviceId, refreshBtn.x(), refreshBtn.y());
|
||||
QThread::msleep(3000);
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
} else {
|
||||
qWarning() << "[Dushanbe::GetProfileInfo] Refresh button not found, parsing as-is";
|
||||
}
|
||||
|
||||
const double parsedBalance = xmlScreenParser.parseBalanceFromHomeScreen();
|
||||
qDebug() << "[Dushanbe::GetProfileInfo] Parsed balance after refresh:" << parsedBalance;
|
||||
if (parsedBalance <= 0.0) {
|
||||
Node refreshBtn = xmlScreenParser.findRefreshButtonOnHomeScreen();
|
||||
if (refreshBtn.x() > 0) {
|
||||
qDebug() << "[Dushanbe::GetProfileInfo] Balance is 0, tapping refresh and retrying";
|
||||
AdbUtils::makeTap(m_deviceId, refreshBtn.x(), refreshBtn.y());
|
||||
QThread::msleep(3000);
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
parsedBalance = xmlScreenParser.parseBalanceFromHomeScreen();
|
||||
qDebug() << "[Dushanbe::GetProfileInfo] Parsed balance after refresh:" << parsedBalance;
|
||||
}
|
||||
if (parsedBalance <= 0.0) {
|
||||
m_error = "Balance is zero after refresh: " + m_deviceId + " " + m_appCode;
|
||||
qWarning() << m_error;
|
||||
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
||||
emit finishedWithResult(m_error);
|
||||
return;
|
||||
}
|
||||
m_error = "Balance is zero after refresh: " + m_deviceId + " " + m_appCode;
|
||||
qWarning() << m_error;
|
||||
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
||||
emit finishedWithResult(m_error);
|
||||
return;
|
||||
}
|
||||
BankProfileDAO::updateAmount(app.id, parsedBalance);
|
||||
|
||||
|
||||
@ -142,13 +142,14 @@ void PayByCardScript::doStart() {
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
|
||||
}
|
||||
|
||||
makePayment(device.adbSerial, app.pinCode, device.screenWidth, device.screenHeight);
|
||||
makePayment(device.adbSerial, app.pinCode, app.phone, device.screenWidth, device.screenHeight);
|
||||
emit finishedWithResult(m_error);
|
||||
}
|
||||
|
||||
void PayByCardScript::makePayment(
|
||||
const QString &deviceId,
|
||||
const QString &pinCode,
|
||||
const QString &bankProfilePhone,
|
||||
const int width,
|
||||
const int height
|
||||
) {
|
||||
@ -386,9 +387,11 @@ void PayByCardScript::makePayment(
|
||||
return;
|
||||
}
|
||||
|
||||
// 10. Ждём результат — "Платеж выполнен" или ошибку
|
||||
// 10. Ждём результат — "Платеж выполнен" или ошибку.
|
||||
// Промежуточный статус "Выполняется" может висеть до ~1.5 мин, пока банк
|
||||
// подтверждает перевод, поэтому общий таймаут — 60 итераций × 2 сек = 2 мин.
|
||||
bool success = false;
|
||||
for (int attempt = 0; attempt < 15; ++attempt) {
|
||||
for (int attempt = 0; attempt < 60; ++attempt) {
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||||
closeGooglePlayBannerIfVisible(deviceId, width, height);
|
||||
|
||||
@ -417,6 +420,35 @@ void PayByCardScript::makePayment(
|
||||
return;
|
||||
}
|
||||
|
||||
// Промежуточный статус — банк ещё обрабатывает перевод
|
||||
if (!xmlScreenParser.findNodeByContentDesc(
|
||||
QString::fromUtf8("Выполняется"), true).isEmpty()) {
|
||||
qDebug() << "[Dushanbe::PayByCard] Status 'Выполняется', waiting for completion, attempt"
|
||||
<< attempt + 1;
|
||||
QThread::msleep(2000);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Если bottom sheet "Подтверждение платежа" всё ещё виден — первый тап
|
||||
// не сработал (мимо OCR-координат), повторяем его той же логикой.
|
||||
Node retryConfirm = xmlScreenParser.findNodeByContentDesc(
|
||||
QString::fromUtf8("Подтверждение платежа"), true);
|
||||
if (!retryConfirm.isEmpty() &&
|
||||
retryConfirm.contentDesc.contains(QString::fromUtf8("Подтверждаю"))) {
|
||||
qDebug() << "[Dushanbe::PayByCard] Confirm sheet still visible, re-tapping 'Подтверждаю'";
|
||||
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);
|
||||
tap = QPoint(retryConfirm.x(), tapY);
|
||||
}
|
||||
AdbUtils::makeTap(deviceId, tap.x(), tap.y());
|
||||
QThread::msleep(4000);
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << "[Dushanbe::PayByCard] Waiting for result, attempt" << attempt + 1;
|
||||
QThread::msleep(2000);
|
||||
}
|
||||
@ -460,10 +492,10 @@ void PayByCardScript::makePayment(
|
||||
qDebug() << "[Dushanbe::PayByCard] Result: date=" << bankTime << "fee=" << fee;
|
||||
}
|
||||
|
||||
// Генерируем ID транзакции из даты+времени (без перехода в историю)
|
||||
const QString operationId = bankTime.isEmpty()
|
||||
? QString("dushanbe_") + QDateTime::currentDateTimeUtc().toString("dd.MM.yyyy_HH:mm:ss")
|
||||
: QString("dushanbe_") + QString(bankTime).replace(' ', '_');
|
||||
// bank_transaction_id: phone аккаунта + unix timestamp (без перехода в историю).
|
||||
// Уникальность за счёт <bankProfilePhone, секунда>.
|
||||
const QString operationId = QString("dushanbe_") + bankProfilePhone + "_"
|
||||
+ QString::number(QDateTime::currentSecsSinceEpoch());
|
||||
qDebug() << "[Dushanbe::PayByCard] Generated operationId:" << operationId;
|
||||
|
||||
// Сохраняем транзакцию
|
||||
|
||||
@ -28,6 +28,7 @@ private:
|
||||
void makePayment(
|
||||
const QString &deviceId,
|
||||
const QString &pinCode,
|
||||
const QString &bankProfilePhone,
|
||||
int width,
|
||||
int height
|
||||
);
|
||||
|
||||
@ -139,13 +139,14 @@ void PayByPhoneScript::doStart() {
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
|
||||
}
|
||||
|
||||
makePayment(device.adbSerial, app.pinCode, device.screenWidth, device.screenHeight);
|
||||
makePayment(device.adbSerial, app.pinCode, app.phone, device.screenWidth, device.screenHeight);
|
||||
emit finishedWithResult(m_error);
|
||||
}
|
||||
|
||||
void PayByPhoneScript::makePayment(
|
||||
const QString &deviceId,
|
||||
const QString &pinCode,
|
||||
const QString &bankProfilePhone,
|
||||
const int width,
|
||||
const int height
|
||||
) {
|
||||
@ -382,9 +383,11 @@ void PayByPhoneScript::makePayment(
|
||||
return;
|
||||
}
|
||||
|
||||
// 10. Ждём результат — "Платеж выполнен" или ошибку
|
||||
// 10. Ждём результат — "Платеж выполнен" или ошибку.
|
||||
// Промежуточный статус "Выполняется" может висеть до ~1.5 мин, пока банк
|
||||
// подтверждает перевод, поэтому общий таймаут — 60 итераций × 2 сек = 2 мин.
|
||||
bool success = false;
|
||||
for (int attempt = 0; attempt < 15; ++attempt) {
|
||||
for (int attempt = 0; attempt < 60; ++attempt) {
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||||
closeGooglePlayBannerIfVisible(deviceId, width, height);
|
||||
|
||||
@ -410,6 +413,34 @@ void PayByPhoneScript::makePayment(
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xmlScreenParser.findNodeByContentDesc(
|
||||
QString::fromUtf8("Выполняется"), true).isEmpty()) {
|
||||
qDebug() << "[Dushanbe::PayByPhone] Status 'Выполняется', waiting for completion, attempt"
|
||||
<< attempt + 1;
|
||||
QThread::msleep(2000);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Если bottom sheet "Подтверждение платежа" всё ещё виден — первый тап
|
||||
// не сработал (мимо OCR-координат), повторяем его той же логикой.
|
||||
Node retryConfirm = xmlScreenParser.findNodeByContentDesc(
|
||||
QString::fromUtf8("Подтверждение платежа"), true);
|
||||
if (!retryConfirm.isEmpty() &&
|
||||
retryConfirm.contentDesc.contains(QString::fromUtf8("Подтверждаю"))) {
|
||||
qDebug() << "[Dushanbe::PayByPhone] Confirm sheet still visible, re-tapping 'Подтверждаю'";
|
||||
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);
|
||||
tap = QPoint(retryConfirm.x(), tapY);
|
||||
}
|
||||
AdbUtils::makeTap(deviceId, tap.x(), tap.y());
|
||||
QThread::msleep(4000);
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << "[Dushanbe::PayByPhone] Waiting for result, attempt" << attempt + 1;
|
||||
QThread::msleep(2000);
|
||||
}
|
||||
@ -452,10 +483,10 @@ void PayByPhoneScript::makePayment(
|
||||
qDebug() << "[Dushanbe::PayByPhone] Result: date=" << bankTime << "fee=" << fee;
|
||||
}
|
||||
|
||||
// Генерируем ID транзакции из даты+времени (без перехода в историю)
|
||||
const QString operationId = bankTime.isEmpty()
|
||||
? QString("dushanbe_") + QDateTime::currentDateTimeUtc().toString("dd.MM.yyyy_HH:mm:ss")
|
||||
: QString("dushanbe_") + QString(bankTime).replace(' ', '_');
|
||||
// bank_transaction_id: phone аккаунта + unix timestamp (без перехода в историю).
|
||||
// Уникальность за счёт <bankProfilePhone, секунда>.
|
||||
const QString operationId = QString("dushanbe_") + bankProfilePhone + "_"
|
||||
+ QString::number(QDateTime::currentSecsSinceEpoch());
|
||||
qDebug() << "[Dushanbe::PayByPhone] Generated operationId:" << operationId;
|
||||
|
||||
// Сохраняем транзакцию
|
||||
|
||||
@ -28,6 +28,7 @@ private:
|
||||
void makePayment(
|
||||
const QString &deviceId,
|
||||
const QString &pinCode,
|
||||
const QString &bankProfilePhone,
|
||||
int width,
|
||||
int height
|
||||
);
|
||||
|
||||
@ -297,6 +297,14 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Скрипт мог упасть до записи транзакции в БД — сервер всё равно
|
||||
// ждёт уникальный bank_transaction_id. Генерируем по той же схеме,
|
||||
// что и в success-ветке скриптов: <bankName>_<bankProfilePhone>_<unix>.
|
||||
if (declTxId.isEmpty()) {
|
||||
const BankProfileInfo declApp = BankProfileDAO::getApplication(declAcc.deviceId, event.bankName);
|
||||
declTxId = event.bankName + "_" + declApp.phone + "_"
|
||||
+ QString::number(QDateTime::currentSecsSinceEpoch());
|
||||
}
|
||||
|
||||
QJsonObject declined;
|
||||
declined["bank_transaction_id"] = declTxId;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user