mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-19 19:32:10 +00:00
Fix Telegram Stars rounding and formatting
This commit is contained in:
@@ -491,8 +491,14 @@ class Settings(BaseSettings):
|
||||
return bool(getattr(self, "LANGUAGE_SELECTION_ENABLED", True))
|
||||
|
||||
def format_price(self, price_kopeks: int) -> str:
|
||||
rubles = price_kopeks // 100
|
||||
return f"{rubles} ₽"
|
||||
sign = "-" if price_kopeks < 0 else ""
|
||||
rubles, kopeks = divmod(abs(price_kopeks), 100)
|
||||
|
||||
if kopeks:
|
||||
value = f"{sign}{rubles}.{kopeks:02d}".rstrip("0").rstrip(".")
|
||||
return f"{value} ₽"
|
||||
|
||||
return f"{sign}{rubles} ₽"
|
||||
|
||||
def get_reports_chat_id(self) -> Optional[str]:
|
||||
if self.ADMIN_REPORTS_CHAT_ID:
|
||||
|
||||
22
app/external/telegram_stars.py
vendored
22
app/external/telegram_stars.py
vendored
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from typing import Optional, Dict, Any
|
||||
from aiogram import Bot
|
||||
from aiogram.types import LabeledPrice, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
@@ -18,8 +19,9 @@ class TelegramStarsService:
|
||||
return settings.rubles_to_stars(rubles)
|
||||
|
||||
@staticmethod
|
||||
def calculate_rubles_from_stars(stars: int) -> float:
|
||||
return settings.stars_to_rubles(stars)
|
||||
def calculate_rubles_from_stars(stars: int) -> Decimal:
|
||||
rate = Decimal(str(settings.get_stars_rate()))
|
||||
return (Decimal(stars) * rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
|
||||
|
||||
async def create_invoice(
|
||||
self,
|
||||
@@ -31,10 +33,10 @@ class TelegramStarsService:
|
||||
start_parameter: Optional[str] = None
|
||||
) -> Optional[str]:
|
||||
try:
|
||||
amount_rubles = amount_kopeks / 100
|
||||
stars_amount = self.calculate_stars_from_rubles(amount_rubles)
|
||||
amount_rubles = Decimal(amount_kopeks) / Decimal(100)
|
||||
stars_amount = self.calculate_stars_from_rubles(float(amount_rubles))
|
||||
stars_rate = settings.get_stars_rate()
|
||||
|
||||
|
||||
invoice_link = await self.bot.create_invoice_link(
|
||||
title=title,
|
||||
description=description,
|
||||
@@ -46,7 +48,7 @@ class TelegramStarsService:
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Создан Stars invoice на {stars_amount} звезд (~{int(amount_rubles)}₽) "
|
||||
f"Создан Stars invoice на {stars_amount} звезд (~{settings.format_price(amount_kopeks)}) "
|
||||
f"для {chat_id}, курс: {stars_rate}₽/⭐"
|
||||
)
|
||||
return invoice_link
|
||||
@@ -65,8 +67,8 @@ class TelegramStarsService:
|
||||
keyboard: Optional[InlineKeyboardMarkup] = None
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
try:
|
||||
amount_rubles = amount_kopeks / 100
|
||||
stars_amount = self.calculate_stars_from_rubles(amount_rubles)
|
||||
amount_rubles = Decimal(amount_kopeks) / Decimal(100)
|
||||
stars_amount = self.calculate_stars_from_rubles(float(amount_rubles))
|
||||
stars_rate = settings.get_stars_rate()
|
||||
|
||||
message = await self.bot.send_invoice(
|
||||
@@ -82,12 +84,12 @@ class TelegramStarsService:
|
||||
|
||||
logger.info(
|
||||
f"Отправлен Stars invoice {message.message_id} на {stars_amount} звезд "
|
||||
f"(~{int(amount_rubles)}₽), курс: {stars_rate}₽/⭐"
|
||||
f"(~{settings.format_price(amount_kopeks)}), курс: {stars_rate}₽/⭐"
|
||||
)
|
||||
return {
|
||||
"message_id": message.message_id,
|
||||
"stars_amount": stars_amount,
|
||||
"rubles_amount": amount_rubles,
|
||||
"rubles_amount": float(amount_rubles),
|
||||
"payload": payload
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import logging
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from aiogram import Dispatcher, types, F
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import settings
|
||||
from app.database.models import User
|
||||
from app.services.payment_service import PaymentService
|
||||
from app.external.telegram_stars import TelegramStarsService
|
||||
@@ -114,6 +116,8 @@ async def handle_successful_payment(
|
||||
|
||||
if success:
|
||||
rubles_amount = TelegramStarsService.calculate_rubles_from_stars(payment.total_amount)
|
||||
amount_kopeks = int((rubles_amount * Decimal(100)).to_integral_value(rounding=ROUND_HALF_UP))
|
||||
amount_text = settings.format_price(amount_kopeks).replace(" ₽", "")
|
||||
|
||||
keyboard = await payment_service.build_topup_success_keyboard(user)
|
||||
|
||||
@@ -129,7 +133,7 @@ async def handle_successful_payment(
|
||||
"Спасибо за пополнение! 🚀",
|
||||
).format(
|
||||
stars_spent=payment.total_amount,
|
||||
amount=int(rubles_amount),
|
||||
amount=amount_text,
|
||||
transaction_id=transaction_id_short,
|
||||
),
|
||||
parse_mode="HTML",
|
||||
@@ -137,8 +141,10 @@ async def handle_successful_payment(
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"✅ Stars платеж успешно обработан: "
|
||||
f"пользователь {user.id}, {payment.total_amount} звезд → {int(rubles_amount)}₽"
|
||||
"✅ Stars платеж успешно обработан: пользователь %s, %s звезд → %s",
|
||||
user.id,
|
||||
payment.total_amount,
|
||||
settings.format_price(amount_kopeks),
|
||||
)
|
||||
else:
|
||||
logger.error(f"Ошибка обработки Stars платежа для пользователя {user.id}")
|
||||
|
||||
@@ -2,7 +2,7 @@ import logging
|
||||
import hashlib
|
||||
import hmac
|
||||
import uuid
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
from aiogram import Bot
|
||||
@@ -113,8 +113,8 @@ class PaymentService:
|
||||
raise ValueError("Bot instance required for Stars payments")
|
||||
|
||||
try:
|
||||
amount_rubles = amount_kopeks / 100
|
||||
stars_amount = TelegramStarsService.calculate_stars_from_rubles(amount_rubles)
|
||||
amount_rubles = Decimal(amount_kopeks) / Decimal(100)
|
||||
stars_amount = TelegramStarsService.calculate_stars_from_rubles(float(amount_rubles))
|
||||
|
||||
invoice_link = await self.bot.create_invoice_link(
|
||||
title="Пополнение баланса VPN",
|
||||
@@ -125,7 +125,11 @@ class PaymentService:
|
||||
prices=[LabeledPrice(label="Пополнение", amount=stars_amount)]
|
||||
)
|
||||
|
||||
logger.info(f"Создан Stars invoice на {stars_amount} звезд (~{int(amount_rubles)}₽)")
|
||||
logger.info(
|
||||
"Создан Stars invoice на %s звезд (~%s)",
|
||||
stars_amount,
|
||||
settings.format_price(amount_kopeks),
|
||||
)
|
||||
return invoice_link
|
||||
|
||||
except Exception as e:
|
||||
@@ -142,7 +146,7 @@ class PaymentService:
|
||||
) -> bool:
|
||||
try:
|
||||
rubles_amount = TelegramStarsService.calculate_rubles_from_stars(stars_amount)
|
||||
amount_kopeks = int(rubles_amount * 100)
|
||||
amount_kopeks = int((rubles_amount * Decimal(100)).to_integral_value(rounding=ROUND_HALF_UP))
|
||||
|
||||
transaction = await create_transaction(
|
||||
db=db,
|
||||
@@ -167,7 +171,9 @@ class PaymentService:
|
||||
|
||||
logger.info(f"💰 Баланс пользователя {user.telegram_id} изменен: {old_balance} → {user.balance_kopeks} (изменение: +{amount_kopeks})")
|
||||
|
||||
description_for_referral = f"Пополнение Stars: {int(rubles_amount)}₽ ({stars_amount} ⭐)"
|
||||
description_for_referral = (
|
||||
f"Пополнение Stars: {settings.format_price(amount_kopeks)} ({stars_amount} ⭐)"
|
||||
)
|
||||
logger.info(f"🔍 Проверка реферальной логики для описания: '{description_for_referral}'")
|
||||
|
||||
if any(word in description_for_referral.lower() for word in ["пополнение", "stars", "yookassa", "topup"]) and not any(word in description_for_referral.lower() for word in ["комиссия", "бонус"]):
|
||||
@@ -206,14 +212,18 @@ class PaymentService:
|
||||
reply_markup=keyboard,
|
||||
)
|
||||
logger.info(
|
||||
f"✅ Отправлено уведомление пользователю {user.telegram_id} о пополнении на {int(rubles_amount)}₽"
|
||||
"✅ Отправлено уведомление пользователю %s о пополнении на %s",
|
||||
user.telegram_id,
|
||||
settings.format_price(amount_kopeks),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка отправки уведомления о пополнении Stars: {e}")
|
||||
|
||||
logger.info(
|
||||
f"✅ Обработан Stars платеж: пользователь {user_id}, "
|
||||
f"{stars_amount} звезд → {int(rubles_amount)}₽"
|
||||
"✅ Обработан Stars платеж: пользователь %s, %s звезд → %s",
|
||||
user_id,
|
||||
stars_amount,
|
||||
settings.format_price(amount_kopeks),
|
||||
)
|
||||
return True
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user