diff --git a/src/modules/billing/providers/cryptobot.rs b/src/modules/billing/providers/cryptobot.rs index 1961b47..c484a0c 100644 --- a/src/modules/billing/providers/cryptobot.rs +++ b/src/modules/billing/providers/cryptobot.rs @@ -107,7 +107,16 @@ impl PaymentProvider for CryptoBotProvider { .map_err(|e| AppError::Internal(format!("Ошибка парсинга ответа CryptoBot: {e}")))?; if res["ok"].as_bool() != Some(true) { - return Ok(false); + // Ответ API "не ok" (сбой/рейт-лимит/недоступность CryptoBot) — не то + // же самое, что "инвойс не найден/не оплачен". Раньше это тоже + // схлопывалось в `Ok(false)`, а confirm_payment необратимо помечает + // инвойс `failed` на любое `false` — реальная оплата терялась + // навсегда при простом временном сбое CryptoBot API. `Err` оставляет + // инвойс `pending`, юзер может нажать "Я оплатил" ещё раз. + return Err(AppError::Internal(format!( + "CryptoBot getInvoices вернул ошибку: {}", + res["error"] + ))); } let Some(item) = res["result"]["items"].as_array().and_then(|a| a.first()) else { diff --git a/src/modules/billing/providers/ton.rs b/src/modules/billing/providers/ton.rs index 1a87391..9e5f34f 100644 --- a/src/modules/billing/providers/ton.rs +++ b/src/modules/billing/providers/ton.rs @@ -62,7 +62,18 @@ impl PaymentProvider for TonProvider { .map_err(|e| AppError::Internal(format!("Ошибка запроса к TON API: {}", e)))?; if !res.status().is_success() { - return Ok(false); // Транзакция не найдена или еще не подтверждена + // Неуспешный HTTP-статус (429/5xx/недоступность TonAPI) — это НЕ то + // же самое, что "транзакция не найдена/невалидна". Раньше оба случая + // схлопывались в `Ok(false)`, а confirm_payment на любое `false` + // необратимо помечает инвойс `failed` — реальный платёж, наткнувшийся + // на временный сбой TonAPI именно в момент подтверждения, терялся + // навсегда: `reconcile_pending_payments` подбирает только `pending`, + // не `failed`. Возвращаем `Err`, чтобы `confirm_payment` НЕ трогал + // статус инвойса и юзер/сверка могли повторить попытку позже. + return Err(AppError::Internal(format!( + "TonAPI недоступен или транзакция ещё не проиндексирована: HTTP {}", + res.status() + ))); } let data: Value = res