fix: harden hwid provider payment edge cases
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
import asyncio
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from bot.payment_providers import cryptopay, severpay, stars
|
||||
|
||||
|
||||
class _FakeSession:
|
||||
def __call__(self):
|
||||
return self
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
async def commit(self):
|
||||
pass
|
||||
|
||||
async def rollback(self):
|
||||
pass
|
||||
|
||||
|
||||
class _FakeJsonRequest:
|
||||
def __init__(self, payload):
|
||||
self._payload = payload
|
||||
|
||||
async def json(self):
|
||||
return self._payload
|
||||
|
||||
|
||||
def test_cryptopay_duplicate_success_webhook_does_not_finalize_again(monkeypatch):
|
||||
session = _FakeSession()
|
||||
payment = SimpleNamespace(payment_id=77, status="succeeded")
|
||||
update = SimpleNamespace(
|
||||
payload=SimpleNamespace(
|
||||
payload=json.dumps(
|
||||
{
|
||||
"user_id": "42",
|
||||
"subscription_months": "0",
|
||||
"payment_db_id": "77",
|
||||
"sale_mode": "hwid_devices@standard",
|
||||
}
|
||||
),
|
||||
invoice_id=9001,
|
||||
amount=100,
|
||||
asset="USDT",
|
||||
)
|
||||
)
|
||||
app = {
|
||||
"async_session_factory": session,
|
||||
"bot": SimpleNamespace(),
|
||||
"settings": SimpleNamespace(traffic_sale_mode=False, DEFAULT_CURRENCY_SYMBOL="RUB"),
|
||||
"i18n": SimpleNamespace(),
|
||||
"subscription_service": SimpleNamespace(),
|
||||
"referral_service": SimpleNamespace(),
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
cryptopay.payment_dal,
|
||||
"get_payment_by_db_id",
|
||||
AsyncMock(return_value=payment),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
cryptopay.payment_dal,
|
||||
"update_provider_payment_and_status",
|
||||
AsyncMock(side_effect=AssertionError("duplicate webhook must not update payment")),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
cryptopay,
|
||||
"finalize_successful_payment",
|
||||
AsyncMock(side_effect=AssertionError("duplicate webhook must not finalize")),
|
||||
)
|
||||
|
||||
service = SimpleNamespace(settings=SimpleNamespace(traffic_sale_mode=False))
|
||||
asyncio.run(cryptopay.CryptoPayService._invoice_paid_handler(service, update, app))
|
||||
|
||||
|
||||
def test_severpay_duplicate_success_webhook_does_not_finalize_again(monkeypatch):
|
||||
session = _FakeSession()
|
||||
payment = SimpleNamespace(
|
||||
payment_id=88,
|
||||
user_id=42,
|
||||
status="succeeded",
|
||||
sale_mode="hwid_devices@standard",
|
||||
purchased_hwid_devices=3,
|
||||
purchased_gb=None,
|
||||
subscription_duration_months=None,
|
||||
amount=150.0,
|
||||
currency="RUB",
|
||||
user=None,
|
||||
)
|
||||
|
||||
async def lookup_payment(_session, *, order_id_raw=None, provider_payment_id=None):
|
||||
assert _session is session
|
||||
assert order_id_raw == "88"
|
||||
assert provider_payment_id == "sev-1"
|
||||
return payment
|
||||
|
||||
monkeypatch.setattr(severpay, "lookup_payment_by_order_or_provider_id", lookup_payment)
|
||||
monkeypatch.setattr(
|
||||
severpay.payment_dal,
|
||||
"update_provider_payment_and_status",
|
||||
AsyncMock(side_effect=AssertionError("duplicate webhook must not update payment")),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
severpay,
|
||||
"finalize_successful_payment",
|
||||
AsyncMock(side_effect=AssertionError("duplicate webhook must not finalize")),
|
||||
)
|
||||
|
||||
service = SimpleNamespace(
|
||||
configured=True,
|
||||
_validate_signature=lambda _payload: True,
|
||||
async_session_factory=session,
|
||||
settings=SimpleNamespace(traffic_sale_mode=False),
|
||||
bot=SimpleNamespace(),
|
||||
i18n=SimpleNamespace(),
|
||||
subscription_service=SimpleNamespace(),
|
||||
referral_service=SimpleNamespace(),
|
||||
)
|
||||
response = asyncio.run(
|
||||
severpay.SeverPayService.webhook_route(
|
||||
service,
|
||||
_FakeJsonRequest(
|
||||
{
|
||||
"type": "payin",
|
||||
"data": {
|
||||
"id": "sev-1",
|
||||
"order_id": "88",
|
||||
"status": "success",
|
||||
},
|
||||
}
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_stars_duplicate_success_message_does_not_finalize_again(monkeypatch):
|
||||
session = _FakeSession()
|
||||
payment = SimpleNamespace(payment_id=99, status="succeeded")
|
||||
message = SimpleNamespace(
|
||||
successful_payment=SimpleNamespace(provider_payment_charge_id="stars-charge-1"),
|
||||
from_user=SimpleNamespace(id=42),
|
||||
)
|
||||
|
||||
monkeypatch.setattr(
|
||||
stars.payment_dal,
|
||||
"get_payment_by_db_id",
|
||||
AsyncMock(return_value=payment),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
stars.payment_dal,
|
||||
"update_provider_payment_and_status",
|
||||
AsyncMock(side_effect=AssertionError("duplicate stars payment must not update")),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
stars,
|
||||
"finalize_successful_payment",
|
||||
AsyncMock(side_effect=AssertionError("duplicate stars payment must not finalize")),
|
||||
)
|
||||
|
||||
service = SimpleNamespace()
|
||||
asyncio.run(
|
||||
stars.StarsService.process_successful_payment(
|
||||
service,
|
||||
session=session,
|
||||
message=message,
|
||||
payment_db_id=99,
|
||||
months=3,
|
||||
stars_amount=150,
|
||||
i18n_data={},
|
||||
sale_mode="hwid_devices@standard",
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user