feat: add wata payment provider
This commit is contained in:
@@ -295,6 +295,60 @@ SETTINGS_MANIFEST: List[SettingField] = [
|
||||
min=30,
|
||||
max=4320,
|
||||
),
|
||||
# Wata
|
||||
SettingField("WATA_ENABLED", "bool", "payments", "Enabled", subsection="Wata"),
|
||||
SettingField(
|
||||
"WATA_API_TOKEN",
|
||||
"string",
|
||||
"payments",
|
||||
"API token",
|
||||
subsection="Wata",
|
||||
secret=True,
|
||||
),
|
||||
SettingField(
|
||||
"WATA_BASE_URL",
|
||||
"url",
|
||||
"payments",
|
||||
"Base URL",
|
||||
placeholder="https://api.wata.pro/api/h2h",
|
||||
subsection="Wata",
|
||||
),
|
||||
SettingField("WATA_RETURN_URL", "url", "payments", "Return URL", subsection="Wata"),
|
||||
SettingField("WATA_FAILED_URL", "url", "payments", "Failed URL", subsection="Wata"),
|
||||
SettingField(
|
||||
"WATA_PAYMENT_LINK_TTL_DAYS",
|
||||
"int",
|
||||
"payments",
|
||||
"Payment link lifetime (days)",
|
||||
"1..30; Wata defaults to 3 days and allows up to 30 days.",
|
||||
subsection="Wata",
|
||||
min=1,
|
||||
max=30,
|
||||
),
|
||||
SettingField(
|
||||
"WATA_WEBHOOK_VERIFY_SIGNATURE",
|
||||
"bool",
|
||||
"payments",
|
||||
"Verify webhook signature",
|
||||
subsection="Wata",
|
||||
),
|
||||
SettingField(
|
||||
"WATA_PUBLIC_KEY",
|
||||
"text",
|
||||
"payments",
|
||||
"Webhook public key",
|
||||
"Optional. If empty, the backend fetches it from Wata.",
|
||||
subsection="Wata",
|
||||
secret=True,
|
||||
),
|
||||
SettingField(
|
||||
"WATA_TRUSTED_IPS",
|
||||
"string",
|
||||
"payments",
|
||||
"Trusted IPs",
|
||||
"Comma-separated IP addresses accepted for Wata webhooks.",
|
||||
subsection="Wata",
|
||||
),
|
||||
# CryptoPay
|
||||
SettingField("CRYPTOPAY_ENABLED", "bool", "payments", "Включена", subsection="CryptoPay"),
|
||||
SettingField(
|
||||
|
||||
@@ -41,6 +41,7 @@ def _inject_shared_instances(
|
||||
"panel_webhook_service",
|
||||
"platega_service",
|
||||
"severpay_service",
|
||||
"wata_service",
|
||||
):
|
||||
if hasattr(dp, "workflow_data") and key in dp.workflow_data: # type: ignore
|
||||
app[key] = dp.workflow_data[key] # type: ignore
|
||||
@@ -96,6 +97,7 @@ async def build_and_start_web_app(
|
||||
from bot.services.panel_webhook_service import panel_webhook_route
|
||||
from bot.services.platega_service import platega_webhook_route
|
||||
from bot.services.severpay_service import severpay_webhook_route
|
||||
from bot.services.wata_service import wata_webhook_route
|
||||
|
||||
cp_path = settings.cryptopay_webhook_path
|
||||
if cp_path.startswith("/"):
|
||||
@@ -117,6 +119,11 @@ async def build_and_start_web_app(
|
||||
app.router.add_post(sp_path, severpay_webhook_route)
|
||||
logging.info(f"SeverPay webhook route configured at: [POST] {sp_path}")
|
||||
|
||||
wata_path = settings.wata_webhook_path
|
||||
if wata_path.startswith("/"):
|
||||
app.router.add_post(wata_path, wata_webhook_route)
|
||||
logging.info(f"Wata webhook route configured at: [POST] {wata_path}")
|
||||
|
||||
# YooKassa webhook (register only when base URL present and path configured)
|
||||
yk_path = settings.yookassa_webhook_path
|
||||
if settings.WEBHOOK_BASE_URL and yk_path and yk_path.startswith("/"):
|
||||
|
||||
@@ -52,6 +52,7 @@ from bot.services.promo_code_service import PromoCodeService
|
||||
from bot.services.referral_service import ReferralService
|
||||
from bot.services.severpay_service import SeverPayService
|
||||
from bot.services.subscription_service import SubscriptionService
|
||||
from bot.services.wata_service import WataService
|
||||
from bot.services.yookassa_service import YooKassaService
|
||||
from bot.utils.config_link import prepare_config_links
|
||||
from bot.utils.request_security import parse_ip_entries, request_client_ip
|
||||
|
||||
@@ -45,6 +45,7 @@ def create_subscription_webapp_application(
|
||||
"cryptopay_service",
|
||||
"platega_service",
|
||||
"severpay_service",
|
||||
"wata_service",
|
||||
"promo_code_service",
|
||||
"referral_service",
|
||||
"panel_service",
|
||||
|
||||
@@ -708,6 +708,19 @@ async def _create_subscription_payment(
|
||||
sale_mode=sale_mode,
|
||||
traffic_gb=traffic_gb,
|
||||
)
|
||||
if method == "wata":
|
||||
if not settings.WATA_ENABLED:
|
||||
return _json_error(400, "payment_unavailable", "Payment method unavailable")
|
||||
return await _create_wata_payment(
|
||||
request,
|
||||
session,
|
||||
user_id,
|
||||
months,
|
||||
price,
|
||||
description,
|
||||
sale_mode=sale_mode,
|
||||
traffic_gb=traffic_gb,
|
||||
)
|
||||
if method == "cryptopay":
|
||||
service: CryptoPayService = request.app["cryptopay_service"]
|
||||
if not settings.CRYPTOPAY_ENABLED or not service or not service.configured:
|
||||
@@ -1118,6 +1131,77 @@ async def _create_severpay_payment(
|
||||
return _json_error(502, "payment_failed", "Failed to create payment")
|
||||
|
||||
|
||||
async def _create_wata_payment(
|
||||
request: web.Request,
|
||||
session: AsyncSession,
|
||||
user_id: int,
|
||||
months: Any,
|
||||
price: float,
|
||||
description: str,
|
||||
*,
|
||||
sale_mode: str = "subscription",
|
||||
traffic_gb: Optional[float] = None,
|
||||
) -> web.Response:
|
||||
settings: Settings = request.app["settings"]
|
||||
service: WataService = request.app["wata_service"]
|
||||
if not service or not service.configured:
|
||||
return _json_error(400, "payment_unavailable", "Payment method unavailable")
|
||||
|
||||
try:
|
||||
traffic_sale = _sale_mode_is_traffic(sale_mode)
|
||||
hwid_devices_sale = _sale_mode_is_hwid_devices(sale_mode)
|
||||
payment = await _create_base_payment_record(
|
||||
session,
|
||||
user_id=user_id,
|
||||
amount=price,
|
||||
currency=settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
status="pending_wata",
|
||||
description=description,
|
||||
months=int(float(months)) if not traffic_sale else int(float(traffic_gb or months)),
|
||||
provider="wata",
|
||||
sale_mode=sale_mode,
|
||||
tariff_key=_sale_mode_tariff_key(sale_mode),
|
||||
purchased_gb=float(traffic_gb or months) if traffic_sale else None,
|
||||
purchased_hwid_devices=int(float(months)) if hwid_devices_sale else None,
|
||||
)
|
||||
success, response_data = await service.create_payment_link(
|
||||
payment_db_id=payment.payment_id,
|
||||
amount=price,
|
||||
currency=settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
description=description,
|
||||
)
|
||||
payment_url = response_data.get("url") if success else None
|
||||
provider_id = response_data.get("id")
|
||||
if provider_id:
|
||||
await payment_dal.update_provider_payment_and_status(
|
||||
session,
|
||||
payment.payment_id,
|
||||
str(provider_id),
|
||||
payment.status,
|
||||
)
|
||||
await session.commit()
|
||||
if not payment_url:
|
||||
await payment_dal.update_payment_status_by_db_id(
|
||||
session,
|
||||
payment.payment_id,
|
||||
"failed_creation",
|
||||
)
|
||||
await session.commit()
|
||||
return _json_error(502, "payment_failed", "Failed to create payment")
|
||||
return web.json_response(
|
||||
{
|
||||
"ok": True,
|
||||
"action": "open_link",
|
||||
"payment_url": payment_url,
|
||||
"payment_id": payment.payment_id,
|
||||
}
|
||||
)
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
logger.exception("Wata WebApp payment failed")
|
||||
return _json_error(502, "payment_failed", "Failed to create payment")
|
||||
|
||||
|
||||
async def _create_stars_payment(
|
||||
request: web.Request,
|
||||
session: AsyncSession,
|
||||
|
||||
@@ -568,6 +568,7 @@ def _serialize_payment_methods(
|
||||
app: web.Application,
|
||||
) -> List[Dict[str, Any]]:
|
||||
labels = {
|
||||
"wata": "Wata",
|
||||
"severpay": "SeverPay",
|
||||
"freekassa": "FreeKassa / СБП",
|
||||
"platega_sbp": "Platega · СБП",
|
||||
@@ -605,6 +606,12 @@ def _serialize_payment_methods(
|
||||
and _service_configured(app, "platega_service")
|
||||
):
|
||||
methods.append({"id": method, "name": labels[method]})
|
||||
elif (
|
||||
method == "wata"
|
||||
and settings.WATA_ENABLED
|
||||
and _service_configured(app, "wata_service")
|
||||
):
|
||||
methods.append({"id": method, "name": labels[method]})
|
||||
elif (
|
||||
method == "yookassa"
|
||||
and settings.YOOKASSA_ENABLED
|
||||
|
||||
Reference in New Issue
Block a user