fix: bind HWID top-ups to subscription periods
This commit is contained in:
@@ -15,6 +15,7 @@ from .callbacks import (
|
||||
notify_service_unavailable,
|
||||
parse_payment_callback,
|
||||
payment_link_message_text,
|
||||
quote_hwid_callback_parts,
|
||||
render_link_or_fail,
|
||||
render_payment_link,
|
||||
safe_callback_answer,
|
||||
@@ -55,6 +56,7 @@ from .success import (
|
||||
PaymentSuccessOutcome,
|
||||
PaymentSuccessRequest,
|
||||
SuccessMessage,
|
||||
append_hwid_renewal_note,
|
||||
build_success_message,
|
||||
finalize_successful_payment,
|
||||
is_traffic_sale_base,
|
||||
@@ -82,6 +84,7 @@ __all__ = [
|
||||
"build_payment_description",
|
||||
"build_payment_record_payload",
|
||||
"build_success_message",
|
||||
"append_hwid_renewal_note",
|
||||
"coerce_payment_db_id",
|
||||
"create_base_payment_record",
|
||||
"create_webapp_payment_record",
|
||||
@@ -113,6 +116,7 @@ __all__ = [
|
||||
"payment_record_amounts",
|
||||
"payment_unavailable",
|
||||
"post_json_request",
|
||||
"quote_hwid_callback_parts",
|
||||
"render_link_or_fail",
|
||||
"render_payment_link",
|
||||
"resolve_inviter_name",
|
||||
|
||||
@@ -21,6 +21,8 @@ from .common import (
|
||||
format_human_units,
|
||||
mark_payment_failed_creation,
|
||||
sale_mode_base,
|
||||
sale_mode_is_hwid_devices,
|
||||
sale_mode_tariff_key,
|
||||
)
|
||||
|
||||
|
||||
@@ -112,6 +114,34 @@ def describe_payment(translator: Translator, parts: PaymentCallbackParts) -> str
|
||||
)
|
||||
|
||||
|
||||
async def quote_hwid_callback_parts(
|
||||
*,
|
||||
session: AsyncSession,
|
||||
user_id: int,
|
||||
parts: PaymentCallbackParts,
|
||||
subscription_service,
|
||||
currency: str = "rub",
|
||||
) -> tuple[Optional[PaymentCallbackParts], Optional[dict]]:
|
||||
if not sale_mode_is_hwid_devices(parts.sale_mode):
|
||||
return parts, None
|
||||
quote = await subscription_service.quote_hwid_device_topup(
|
||||
session,
|
||||
user_id=user_id,
|
||||
device_count=int(parts.months),
|
||||
tariff_key=sale_mode_tariff_key(parts.sale_mode),
|
||||
renewal=sale_mode_base(parts.sale_mode) == "hwid_devices_renewal",
|
||||
currency=currency,
|
||||
)
|
||||
if not quote:
|
||||
return None, None
|
||||
quoted_parts = PaymentCallbackParts(
|
||||
months=parts.months,
|
||||
price=float(quote.get("price") or 0),
|
||||
sale_mode=parts.sale_mode,
|
||||
)
|
||||
return quoted_parts, quote
|
||||
|
||||
|
||||
def payment_link_message_text(
|
||||
translator: Translator,
|
||||
parts: PaymentCallbackParts,
|
||||
|
||||
@@ -60,7 +60,7 @@ def build_payment_description(
|
||||
"payment_description_traffic",
|
||||
traffic_gb=human_value if human_value is not None else format_human_units(months),
|
||||
)
|
||||
if base in {"hwid_device", "hwid_devices"}:
|
||||
if base in {"hwid_device", "hwid_devices", "hwid_devices_renewal"}:
|
||||
return translator("payment_description_hwid_devices", count=int(float(months)))
|
||||
return translator("payment_description_subscription", months=int(float(months)))
|
||||
|
||||
@@ -75,6 +75,7 @@ def build_payment_record_payload(
|
||||
months: Any,
|
||||
provider: str,
|
||||
sale_mode: str,
|
||||
hwid_quote: Optional[dict] = None,
|
||||
) -> dict:
|
||||
"""Assemble the payment-record dict that every callback handler used to inline.
|
||||
|
||||
@@ -85,7 +86,7 @@ def build_payment_record_payload(
|
||||
base = sale_mode_base(sale_mode)
|
||||
is_traffic = sale_mode_is_traffic(sale_mode)
|
||||
is_hwid = sale_mode_is_hwid_devices(sale_mode)
|
||||
return {
|
||||
payload = {
|
||||
"user_id": user_id,
|
||||
"amount": amount,
|
||||
"currency": currency,
|
||||
@@ -98,6 +99,17 @@ def build_payment_record_payload(
|
||||
"purchased_gb": float(months) if is_traffic else None,
|
||||
"purchased_hwid_devices": int(float(months)) if is_hwid else None,
|
||||
}
|
||||
if hwid_quote and is_hwid:
|
||||
payload.update(
|
||||
{
|
||||
"hwid_valid_from": hwid_quote.get("valid_from"),
|
||||
"hwid_valid_until": hwid_quote.get("valid_until"),
|
||||
"hwid_pricing_period_months": hwid_quote.get("pricing_period_months"),
|
||||
"hwid_proration_ratio": hwid_quote.get("proration_ratio"),
|
||||
"hwid_full_price": hwid_quote.get("full_price"),
|
||||
}
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -119,7 +131,7 @@ def sale_mode_is_traffic(sale_mode: str) -> bool:
|
||||
|
||||
|
||||
def sale_mode_is_hwid_devices(sale_mode: str) -> bool:
|
||||
return sale_mode_base(sale_mode) in {"hwid_device", "hwid_devices"}
|
||||
return sale_mode_base(sale_mode) in {"hwid_device", "hwid_devices", "hwid_devices_renewal"}
|
||||
|
||||
|
||||
def sale_mode_tariff_key(sale_mode: str) -> Optional[str]:
|
||||
@@ -194,6 +206,11 @@ async def create_base_payment_record(
|
||||
tariff_key: Optional[str] = None,
|
||||
purchased_gb: Optional[float] = None,
|
||||
purchased_hwid_devices: Optional[int] = None,
|
||||
hwid_valid_from: Optional[Any] = None,
|
||||
hwid_valid_until: Optional[Any] = None,
|
||||
hwid_pricing_period_months: Optional[int] = None,
|
||||
hwid_proration_ratio: Optional[float] = None,
|
||||
hwid_full_price: Optional[float] = None,
|
||||
) -> Payment:
|
||||
payment = await payment_dal.create_payment_record(
|
||||
session,
|
||||
@@ -209,6 +226,11 @@ async def create_base_payment_record(
|
||||
"tariff_key": tariff_key,
|
||||
"purchased_gb": purchased_gb,
|
||||
"purchased_hwid_devices": purchased_hwid_devices,
|
||||
"hwid_valid_from": hwid_valid_from,
|
||||
"hwid_valid_until": hwid_valid_until,
|
||||
"hwid_pricing_period_months": hwid_pricing_period_months,
|
||||
"hwid_proration_ratio": hwid_proration_ratio,
|
||||
"hwid_full_price": hwid_full_price,
|
||||
},
|
||||
)
|
||||
await session.commit()
|
||||
@@ -241,6 +263,11 @@ async def create_webapp_payment_record(
|
||||
tariff_key=amounts.tariff_key,
|
||||
purchased_gb=amounts.purchased_gb,
|
||||
purchased_hwid_devices=amounts.purchased_hwid_devices,
|
||||
hwid_valid_from=ctx.hwid_valid_from,
|
||||
hwid_valid_until=ctx.hwid_valid_until,
|
||||
hwid_pricing_period_months=ctx.hwid_pricing_period_months,
|
||||
hwid_proration_ratio=ctx.hwid_proration_ratio,
|
||||
hwid_full_price=ctx.hwid_full_price,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ from db.models import Payment, User
|
||||
from .common import Translator, format_human_units, make_translator, sale_mode_base
|
||||
|
||||
_TRAFFIC_MODES = {"traffic", "traffic_package", "topup", "premium_topup"}
|
||||
_HWID_DEVICE_MODES = {"hwid_device", "hwid_devices"}
|
||||
_HWID_DEVICE_MODES = {"hwid_device", "hwid_devices", "hwid_devices_renewal"}
|
||||
|
||||
|
||||
def is_traffic_sale_base(sale_base: str) -> bool:
|
||||
@@ -128,6 +128,28 @@ def build_success_message(payload: SuccessMessage) -> str:
|
||||
)
|
||||
|
||||
|
||||
def append_hwid_renewal_note(
|
||||
text: str,
|
||||
translator: Translator,
|
||||
*,
|
||||
count: Any,
|
||||
valid_until: Optional[datetime],
|
||||
) -> str:
|
||||
try:
|
||||
count_int = int(count or 0)
|
||||
except (TypeError, ValueError):
|
||||
count_int = 0
|
||||
if count_int <= 0:
|
||||
return text
|
||||
date_text = valid_until.strftime("%Y-%m-%d") if valid_until else ""
|
||||
note = translator(
|
||||
"payment_successful_hwid_devices_renewal_note",
|
||||
count=format_human_units(count_int),
|
||||
date=date_text,
|
||||
)
|
||||
return f"{text}\n\n{note}"
|
||||
|
||||
|
||||
async def send_success_message_to_user(
|
||||
*,
|
||||
bot: Bot,
|
||||
@@ -333,6 +355,13 @@ async def finalize_successful_payment(
|
||||
inviter_name=inviter_name,
|
||||
)
|
||||
)
|
||||
if is_subscription and activation:
|
||||
success_text = append_hwid_renewal_note(
|
||||
success_text,
|
||||
translator,
|
||||
count=activation.get("hwid_devices_renewal_recommended_count"),
|
||||
valid_until=activation.get("hwid_devices_valid_until"),
|
||||
)
|
||||
if req.text_prefix:
|
||||
success_text = f"{req.text_prefix}\n{success_text}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user