diff options
| author | stuppie | 2026-09-18 13:40:54 -0600 |
|---|---|---|
| committer | stuppie | 2026-09-18 13:40:54 -0600 |
| commit | 438ea05fdb98c847d534ae190179b4766fa96f26 (patch) | |
| tree | 45dbe0d228ac2e3b715801c0b4b28e3d244b54c0 | |
| parent | 1a98418317c115b92739639cf0b837bd974fc5d6 (diff) | |
| download | generalresearch-438ea05fdb98c847d534ae190179b4766fa96f26.tar.gz generalresearch-438ea05fdb98c847d534ae190179b4766fa96f26.zip | |
test user payout paypal and tango, and fix bugs
| -rw-r--r-- | generalresearch/managers/thl/cashout_method.py | 2 | ||||
| -rw-r--r-- | generalresearch/managers/thl/wallet/tango.py | 6 | ||||
| -rw-r--r-- | generalresearch/managers/thl/wallet/user_payout.py | 23 | ||||
| -rw-r--r-- | generalresearch/models/thl/wallet/cashout_method.py | 4 | ||||
| -rw-r--r-- | test_utils/managers/conftest.py | 25 | ||||
| -rw-r--r-- | test_utils/managers/thl/conftest.py | 11 | ||||
| -rw-r--r-- | tests/managers/thl/test_ledger/test_thl_pem.py | 32 | ||||
| -rw-r--r-- | tests/managers/thl/test_payout.py | 2 | ||||
| -rw-r--r-- | tests/managers/thl/test_wallet/__init__.py | 0 | ||||
| -rw-r--r-- | tests/managers/thl/test_wallet/test_user_payout.py | 435 |
10 files changed, 454 insertions, 86 deletions
diff --git a/generalresearch/managers/thl/cashout_method.py b/generalresearch/managers/thl/cashout_method.py index 46bde38..90a6876 100644 --- a/generalresearch/managers/thl/cashout_method.py +++ b/generalresearch/managers/thl/cashout_method.py @@ -376,7 +376,7 @@ class CashoutMethodManager(PostgresManager): if user and x["type"] in {PayoutType.PAYPAL, PayoutType.CASH_IN_MAIL}: user = user if isinstance(user, UserRef) else user.to_user_ref() x["user"] = user - x["original_currency"] = x.get("currency") or Currency.USD + x["original_currency"] = x.get("original_currency") or Currency.USD x["currency"] = Currency.USD return CashoutMethod.model_validate(x) diff --git a/generalresearch/managers/thl/wallet/tango.py b/generalresearch/managers/thl/wallet/tango.py index ca993ea..361a885 100644 --- a/generalresearch/managers/thl/wallet/tango.py +++ b/generalresearch/managers/thl/wallet/tango.py @@ -1,6 +1,7 @@ from __future__ import annotations from datetime import timedelta +from decimal import Decimal from threading import Lock from typing import TYPE_CHECKING, Any @@ -69,13 +70,14 @@ class TangoManager: utid = cashout_method.data.utid currency = cashout_method.original_currency if currency and currency != Currency.USD: - amount = round(float(amount) / self.get_exchange_rates()[currency], 2) + amount = round(float(amount) / self.get_exchange_rates()[currency]) + amount_in_currency = Decimal(float(amount) / 100).quantize(Decimal('0.01')) return TangoCashoutMethodRequestData.model_validate( { "accountIdentifier": self.tango_account_id, "customerIdentifier": self.tango_customer_id, "utid": utid, - "amount": str(amount), + "amount": amount_in_currency, "campaign": "300large", "sendEmail": False, "externalRefID": payout_event_id, diff --git a/generalresearch/managers/thl/wallet/user_payout.py b/generalresearch/managers/thl/wallet/user_payout.py index a1c628b..983ab3a 100644 --- a/generalresearch/managers/thl/wallet/user_payout.py +++ b/generalresearch/managers/thl/wallet/user_payout.py @@ -68,8 +68,8 @@ class UserPayoutEventManager(PayoutEventManager): return pe def get_payout_detail(self, pe_uuid: UUIDStr) -> CashoutRequestInfo: - # This gets the payout event, and then extracts information for - # the purposes of returning to the user. + # This gets the payout event and then extracts information to return + # to the user. pe = self.get_by_uuid(pe_uuid=pe_uuid) transaction_info = {} @@ -205,9 +205,6 @@ class UserPayoutEventManager(PayoutEventManager): created: AwareDatetimeISO | None = None, request_data: dict[str, Any] | None = None, # --- Optional: None --- - account_reference_type: str | None = None, - account_reference_uuid: UUIDStr | None = None, - description: str | None = None, ext_ref_id: str | None = None, order_data: dict[str, Any] | CashMailOrderData | None = None, ) -> UserPayoutEvent: @@ -215,10 +212,7 @@ class UserPayoutEventManager(PayoutEventManager): payout_event = UserPayoutEvent( uuid=uuid or uuid4().hex, debit_account_uuid=debit_account_uuid, - account_reference_type=account_reference_type, - account_reference_uuid=account_reference_uuid, cashout_method_uuid=cashout_method_uuid, - description=description, created=created or datetime.now(tz=UTC), amount=amount, status=status or PayoutStatus.PENDING, @@ -250,7 +244,7 @@ class UserPayoutEventManager(PayoutEventManager): assert c.rowcount == 1, f"expected 1 row inserted, got {c.rowcount}" conn.commit() - return payout_event + return self.get_by_uuid(payout_event.uuid) def try_user_request_redeem( self, @@ -261,7 +255,6 @@ class UserPayoutEventManager(PayoutEventManager): cashout_method_manager: CashoutMethodManager, ledger_manager: ThlLedgerManager, user_ip_history_manager: UserIpHistoryManager, - geoip_info_manager: GeoIpInfoManager, redis_client: Redis, slack_client: slack.WebClient | None = None, ) -> tuple[UserPayoutEvent | None, str | None]: @@ -274,7 +267,6 @@ class UserPayoutEventManager(PayoutEventManager): cashout_method_manager=cashout_method_manager, ledger_manager=ledger_manager, user_ip_history_manager=user_ip_history_manager, - geoip_info_manager=geoip_info_manager, redis_client=redis_client, slack_client=slack_client, ), None @@ -290,7 +282,6 @@ class UserPayoutEventManager(PayoutEventManager): cashout_method_manager: CashoutMethodManager, ledger_manager: ThlLedgerManager, user_ip_history_manager: UserIpHistoryManager, - geoip_info_manager: GeoIpInfoManager, redis_client: Redis, slack_client: slack.WebClient | None = None, ) -> UserPayoutEvent: @@ -303,9 +294,7 @@ class UserPayoutEventManager(PayoutEventManager): now = datetime.now(tz=UTC) user.prefetch_product(pg_config=self.pg_config) - country_iso = user_ip_history_manager.get_user_latest_country( - user, geoip_info_manager - ) + country_iso = user_ip_history_manager.get_user_latest_country(user) assert country_iso, "user has no country" usd_exchange_rates = tango_manager.get_exchange_rates() @@ -345,9 +334,7 @@ class UserPayoutEventManager(PayoutEventManager): product = user.product banned_countries = user.product.user_health_config.banned_countries - assert not user_ip_history_manager.is_user_anonymous( - user, geoip_info_manager=geoip_info_manager - ), "Anonymous user requesting redemption" + assert not user_ip_history_manager.is_user_anonymous(user), "Anonymous user requesting redemption" if country_iso in banned_countries: raise AssertionError("Banned country requesting redemption") diff --git a/generalresearch/models/thl/wallet/cashout_method.py b/generalresearch/models/thl/wallet/cashout_method.py index a82fa31..61a8992 100644 --- a/generalresearch/models/thl/wallet/cashout_method.py +++ b/generalresearch/models/thl/wallet/cashout_method.py @@ -3,6 +3,7 @@ from __future__ import annotations import hashlib import logging from datetime import UTC, datetime +from decimal import Decimal from enum import StrEnum from typing import Any, Literal, Self @@ -240,7 +241,8 @@ class TangoCashoutMethodRequestData(BaseModel): accountIdentifier: str = Field() customerIdentifier: str = Field() utid: str = Field(description="tango utid") - amount: USDCent = Field(description="Amount to be paid out") + # This is not necessarily in USD. It is in whatever currency the card is in + amount: Decimal = Field(description="Amount to be paid out (in USD, EUR, etc)") campaign: Literal["300large"] sendEmail: bool = Field(default=False) externalRefID: str = Field(description="External Ref ID") diff --git a/test_utils/managers/conftest.py b/test_utils/managers/conftest.py index 72be79e..a9f92a2 100644 --- a/test_utils/managers/conftest.py +++ b/test_utils/managers/conftest.py @@ -25,7 +25,6 @@ from generalresearch.managers.thl.user_streak import ( ) from generalresearch.managers.thl.userhealth import ( AuditLogManager, - IPRecordManager, UserIpHistoryManager, ) from generalresearch.models.definitions import Source @@ -57,24 +56,6 @@ def audit_log_manager(thl_web_rw: PostgresConfig) -> AuditLogManager: @pytest.fixture -def ip_record_manager( - thl_web_rw: PostgresConfig, - thl_redis_config: RedisConfig, - geoip_info_manager: GeoIpInfoManager, -) -> IPRecordManager: - assert thl_web_rw.dsn.path - assert "/unittest-" in thl_web_rw.dsn.path - - from generalresearch.managers.thl.userhealth import IPRecordManager - - return IPRecordManager( - pg_config=thl_web_rw, - redis_config=thl_redis_config, - geoip_info_manager=geoip_info_manager, - ) - - -@pytest.fixture def user_iphistory_manager( thl_web_rw: PostgresConfig, thl_redis_config: RedisConfig, @@ -124,6 +105,11 @@ def grip_lookup_results() -> dict[str, GRIPMMDBLookupResult]: network_operator="Cloudflare", ), ), + "2.2.2.2": GRIPMMDBLookupResult( + country=GRIPCountryRecord(country_iso="IT"), + anonymous=GRIPAnonymousRecord(is_anonymous=False), + asn=GRIPAsnRecord(), + ), }, ) @@ -214,7 +200,6 @@ def random_ext_id_factory(base: str = "U02") -> Callable[..., str]: @pytest.fixture(scope="session") def example_tango_cashout_methods( - random_ext_id_factory: Callable[..., str], ) -> list[CashoutMethod]: return [ CashoutMethod( diff --git a/test_utils/managers/thl/conftest.py b/test_utils/managers/thl/conftest.py index 5b96680..2dce648 100644 --- a/test_utils/managers/thl/conftest.py +++ b/test_utils/managers/thl/conftest.py @@ -40,7 +40,6 @@ if TYPE_CHECKING: UserMetadataManager, ) from generalresearch.managers.thl.userhealth import ( - AuditLogManager, IPRecordManager, ) from generalresearch.managers.thl.wall import ( @@ -162,13 +161,6 @@ def brokerage_product_payout_event_manager( ) -@pytest.fixture() -def audit_log_manager(thl_web_rw: PostgresConfig) -> AuditLogManager: - from generalresearch.managers.thl.userhealth import AuditLogManager - - return AuditLogManager(pg_config=thl_web_rw) - - @pytest.fixture(scope="session") def business_payout_event_manager( thl_web_rw: PostgresConfig, thl_redis_config: RedisConfig @@ -338,9 +330,6 @@ def surveypenalty_manager(thl_redis_config: RedisConfig): return SurveyPenaltyManager(redis_config=thl_redis_config) -# --- IP Record --- - - @pytest.fixture(scope="session") def ip_record_manager( thl_web_rw: PostgresConfig, thl_redis_config: RedisConfig diff --git a/tests/managers/thl/test_ledger/test_thl_pem.py b/tests/managers/thl/test_ledger/test_thl_pem.py index 4761438..2d03b25 100644 --- a/tests/managers/thl/test_ledger/test_thl_pem.py +++ b/tests/managers/thl/test_ledger/test_thl_pem.py @@ -122,38 +122,6 @@ class TestThlPayoutEventManager: assert len(res) == (N_PRODUCTS * N_PAYOUT_EVENTS) assert sum([i.amount for i in res]) == sum(amounts) - @pytest.mark.skip - def test_get_payout_detail(self, user_payout_event_manager: UserPayoutEventManager): - """This fails because the description coming back is None, but then - it tries to return a PayoutEvent which validates that the - description can't be None - """ - from generalresearch.models.thl.payout import ( - PayoutType, - ) - - rand_amount = randint(a=99, b=999) - - pe = user_payout_event_manager.create( - debit_account_uuid=uuid4().hex, - account_reference_type="str-type-random", - account_reference_uuid=uuid4().hex, - cashout_method_uuid=uuid4().hex, - description="Best payout !", - amount=rand_amount, - status=PayoutStatus.PENDING, - ext_ref_id="123", - payout_type=PayoutType.CASH_IN_MAIL, - request_data={"foo": 123}, - order_data={}, - ) - - res = user_payout_event_manager.get_payout_detail(pe_uuid=pe.uuid) - assert isinstance(res, CashoutRequestInfo) - - # def test_filter_by(self): - # raise NotImplementedError - def test_create( self, user_payout_event_factory: Callable[..., UserPayoutEvent], diff --git a/tests/managers/thl/test_payout.py b/tests/managers/thl/test_payout.py index 69b9737..a9cd52a 100644 --- a/tests/managers/thl/test_payout.py +++ b/tests/managers/thl/test_payout.py @@ -34,9 +34,9 @@ if TYPE_CHECKING: BrokerageProductPayoutEventManager, BusinessPayoutEventManager, PayoutEventManager, - UserPayoutEventManager, ) from generalresearch.managers.thl.product import ProductManager + from generalresearch.managers.thl.wallet.user_payout import UserPayoutEventManager from generalresearch.models.gr.business import Business from generalresearch.models.thl.ledger import LedgerAccount from generalresearch.models.thl.payout import ( diff --git a/tests/managers/thl/test_wallet/__init__.py b/tests/managers/thl/test_wallet/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/managers/thl/test_wallet/__init__.py diff --git a/tests/managers/thl/test_wallet/test_user_payout.py b/tests/managers/thl/test_wallet/test_user_payout.py new file mode 100644 index 0000000..bbc97a3 --- /dev/null +++ b/tests/managers/thl/test_wallet/test_user_payout.py @@ -0,0 +1,435 @@ +from datetime import datetime +from decimal import Decimal +from random import randint +from unittest.mock import Mock +from uuid import uuid4 + +import pytest +from faker import Faker + +from generalresearch.currency import USDCent +from generalresearch.managers.thl.cashout_method import CashoutMethodManager +from generalresearch.managers.thl.ipinfo import GeoIpInfoManager +from generalresearch.managers.thl.ledger_manager.thl_ledger import ThlLedgerManager +from generalresearch.managers.thl.userhealth import UserIpHistoryManager +from generalresearch.managers.thl.wallet.tango import TangoManager +from generalresearch.managers.thl.wallet.user_payout import ( + UserPayoutEventManager, + make_request_paypal, +) +from generalresearch.models.thl.definitions import PayoutStatus +from generalresearch.models.thl.payout import UserPayoutEvent +from generalresearch.models.thl.user import User +from generalresearch.models.thl.wallet.cashout_method import ( + CashMailCashoutMethodData, + CashMailCashoutMethodRequestData, + CashoutRequestInfo, + PaypalCashoutMethodData, + PaypalCashoutMethodRequestData, + USDeliveryAddress, +) +from generalresearch.models.thl.wallet.definitions import PayoutType + +fake = Faker() + + +class TestUserPayoutEventManager: + def test_get_by_uuid_and_create( + self, + user: User, + user_payout_event_manager: UserPayoutEventManager, + thl_ledger_manager: ThlLedgerManager, + utc_now: datetime, + ): + user_account = thl_ledger_manager.get_account_or_create_user_wallet(user=user) + request_data = PaypalCashoutMethodRequestData.model_validate( + { + "email": fake.email(), + "interface": "api", + } + ) + pe1: UserPayoutEvent = user_payout_event_manager.create( + debit_account_uuid=user_account.uuid, + payout_type=PayoutType.PAYPAL, + cashout_method_uuid=uuid4().hex, + amount=100, + created=utc_now, + request_data=request_data.model_dump(mode="json"), + ) + # these get added by the query + pe1.account_reference_type = "user" + pe1.account_reference_uuid = user.uuid + + pe2 = user_payout_event_manager.get_by_uuid(pe_uuid=pe1.uuid) + + assert pe1 == pe2 + + def test_get_payout_detail_cash_in_mail( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + ): + user = user_with_wallet + data = CashMailCashoutMethodData( + delivery_address=USDeliveryAddress.model_validate( + { + "name_or_attn": "Josh Ackerman", + "address": "123 Fake St", + "city": "San Francisco", + "state": "CA", + "postal_code": "12345", + } + ) + ) + cm = cashout_method_manager.create_cash_in_mail_cashout_method( + data=data, user=user + ) + user_account = thl_ledger_manager.get_account_or_create_user_wallet(user=user) + + rand_amount = randint(a=99, b=999) + request_data = CashMailCashoutMethodRequestData.model_validate( + cm.data.model_dump() + ) + + pe = user_payout_event_manager.create( + debit_account_uuid=user_account.uuid, + cashout_method_uuid=cm.id, + amount=rand_amount, + ext_ref_id=uuid4().hex, + payout_type=PayoutType.CASH_IN_MAIL, + request_data=request_data.model_dump(mode="json"), + ) + + res = user_payout_event_manager.get_payout_detail(pe_uuid=pe.uuid) + assert isinstance(res, CashoutRequestInfo) + + def test_get_payout_detail_paypal( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + ): + user = user_with_wallet + data = PaypalCashoutMethodData(email=fake.email()) + cm = cashout_method_manager.create_paypal_cashout_method(data=data, user=user) + user_account = thl_ledger_manager.get_account_or_create_user_wallet(user=user) + + rand_amount = randint(a=99, b=999) + request_data = make_request_paypal(cm) + pe = user_payout_event_manager.create( + debit_account_uuid=user_account.uuid, + cashout_method_uuid=cm.id, + amount=rand_amount, + ext_ref_id=uuid4().hex, + payout_type=PayoutType.PAYPAL, + request_data=request_data.model_dump(mode="json"), + ) + + res = user_payout_event_manager.get_payout_detail(pe_uuid=pe.uuid) + assert isinstance(res, CashoutRequestInfo) + + +class TestUserRequestRedeem: + @staticmethod + def request_paypal_payout( + *, + user: User, + amount: USDCent, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + ip: str = "8.8.8.8", + fund_amount: Decimal = Decimal("10.00"), + ) -> UserPayoutEvent: + email = fake.email() + cashout_method = cashout_method_manager.create_paypal_cashout_method( + data=PaypalCashoutMethodData(email=email), + user=user, + ) + ip_record_factory(user=user, ip=ip) + thl_ledger_manager.create_tx_user_bonus( + user=user, + amount=fund_amount, + ref_uuid=uuid4().hex, + description="Fund PayPal redemption test", + ) + + tango_manager = Mock(spec=TangoManager) + tango_manager.get_exchange_rates.return_value = {} + + return user_payout_event_manager.user_request_redeem( + user=user, + cashout_method_id=cashout_method.id, + amount=amount, + tango_manager=tango_manager, + cashout_method_manager=cashout_method_manager, + ledger_manager=thl_ledger_manager, + user_ip_history_manager=user_iphistory_manager, + redis_client=thl_redis_client, + ) + + def test_user_request_redeem_paypal( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + geoip_info_manager: GeoIpInfoManager, + ip_record_factory, + thl_redis_client, + ): + user = user_with_wallet + payout = self.request_paypal_payout( + user=user, + amount=USDCent(600), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ) + + assert payout.status == PayoutStatus.PENDING + assert payout.payout_type == PayoutType.PAYPAL + assert payout.amount == 600 + assert payout.request_data["interface"] == "api" + assert thl_ledger_manager.get_user_wallet_balance(user) == 400 + assert user_payout_event_manager.get_by_uuid(payout.uuid) == payout + + detail = user_payout_event_manager.get_payout_detail(payout.uuid) + assert not detail.transaction_info + assert detail.description == "PayPal" + + def test_user_request_redeem_paypal_complete( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + geoip_info_manager: GeoIpInfoManager, + ip_record_factory, + thl_redis_client, + create_main_accounts, + ): + create_main_accounts() + user = user_with_wallet + payout = self.request_paypal_payout( + user=user, + amount=USDCent(600), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ) + + user_payout_event_manager.update( + payout_event=payout, + status=PayoutStatus.APPROVED, + ext_ref_id="paypal-batch-id", + ) + thl_ledger_manager.create_tx_user_payout_complete( + user=user, + payout_event=payout, + fee_amount=Decimal("0.25"), + ) + user_payout_event_manager.update( + payout_event=payout, + status=PayoutStatus.COMPLETE, + order_data={"transaction_id": "paypal-transaction-id"}, + ) + + detail = user_payout_event_manager.get_payout_detail(pe_uuid=payout.uuid) + assert detail.status == PayoutStatus.COMPLETE + assert detail.transaction_info == {"transaction_id": "paypal-transaction-id"} + + assert thl_ledger_manager.get_user_wallet_balance(user_with_wallet) == 400 + bp_expense = thl_ledger_manager.get_account_or_create_bp_expense_by_uuid( + product_uuid=user.to_user_ref().product_id, expense_name="paypal" + ) + assert thl_ledger_manager.get_account_balance(bp_expense) == -25 + + def test_user_request_redeem_tango_complete( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + create_main_accounts, + setup_cashoutmethod_db, + example_tango_cashout_methods, + ): + create_main_accounts() + setup_cashoutmethod_db() + user = user_with_wallet + # This is the italian Amazon.it gift card + cashout_method = example_tango_cashout_methods[1] + # This is an IP that we have hard-coded by the geoip_info_manager as Italy + ip_record_factory(user=user, ip="2.2.2.2") + + thl_ledger_manager.create_tx_user_bonus( + user=user, + amount=Decimal("5.00"), + ref_uuid=uuid4().hex, + description="Fund Tango redemption test", + ) + + tango_client = Mock() + tango_client.get_exchange_rates.return_value = { + "exchangeRates": [ + {"rewardCurrency": "USD", "baseCurrency": "EUR", "baseFx": 1.14792} + ] + } + tango_manager = TangoManager( + tango_client=tango_client, + tango_account_id="test-account", + tango_customer_id="test-customer", + cashout_method_manager=cashout_method_manager, + ) + request = tango_manager.make_request( + amount=USDCent(2_00), + cashout_method=cashout_method, + payout_event_id=uuid4().hex, + ) + assert request.amount == Decimal(2.0 / 1.14792).quantize( + Decimal("0.01") + ) # ~ 1.74 EUR + + payout = user_payout_event_manager.user_request_redeem( + user=user, + cashout_method_id=cashout_method.id, + amount=USDCent(2_00), + tango_manager=tango_manager, + cashout_method_manager=cashout_method_manager, + ledger_manager=thl_ledger_manager, + user_ip_history_manager=user_iphistory_manager, + redis_client=thl_redis_client, + ) + assert payout.request_data["amount"] == '1.74' + assert payout.request_data["externalRefID"] == payout.uuid + + user_payout_event_manager.update( + payout_event=payout, + status=PayoutStatus.APPROVED, + ext_ref_id="tango-order-id", + ) + thl_ledger_manager.create_tx_user_payout_complete( + user=user, + payout_event=payout, + ) + user_payout_event_manager.update( + payout_event=payout, + status=PayoutStatus.COMPLETE, + order_data={ + "reward": { + "credentialList": [ + { + "credentialType": "giftCardCode", + "credentialValue": "TEST-CODE", + } + ], + "redemptionInstructions": "Use this code at checkout.", + } + }, + ) + + detail = user_payout_event_manager.get_payout_detail(pe_uuid=payout.uuid) + assert payout.payout_type == PayoutType.TANGO + assert detail.status == PayoutStatus.COMPLETE + assert detail.transaction_info == { + "credential_list": [ + { + "credentialType": "giftCardCode", + "credentialValue": "TEST-CODE", + } + ], + "redemption_instructions": "Use this code at checkout.", + } + assert thl_ledger_manager.get_user_wallet_balance(user) == 300 + bp_expense = thl_ledger_manager.get_account_or_create_bp_expense_by_uuid( + product_uuid=user.to_user_ref().product_id, + expense_name="tango", + ) + assert thl_ledger_manager.get_account_balance(bp_expense) == -7 + + def test_user_request_redeem_paypal_insufficient_balance( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + ): + with pytest.raises(AssertionError, match="more than their redeemable balance"): + self.request_paypal_payout( + user=user_with_wallet, + amount=USDCent(1_100), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ) + + def test_user_request_redeem_paypal_blocked_user( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + ): + user_with_wallet.blocked = True + + with pytest.raises(AssertionError, match="Blocked user"): + self.request_paypal_payout( + user=user_with_wallet, + amount=USDCent(600), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ) + + def test_user_request_redeem_paypal_anonymous_user( + self, + user_with_wallet: User, + user_payout_event_manager: UserPayoutEventManager, + cashout_method_manager: CashoutMethodManager, + thl_ledger_manager: ThlLedgerManager, + user_iphistory_manager: UserIpHistoryManager, + ip_record_factory, + thl_redis_client, + ): + with pytest.raises(AssertionError, match="Anonymous user"): + self.request_paypal_payout( + user=user_with_wallet, + amount=USDCent(600), + user_payout_event_manager=user_payout_event_manager, + cashout_method_manager=cashout_method_manager, + thl_ledger_manager=thl_ledger_manager, + user_iphistory_manager=user_iphistory_manager, + ip_record_factory=ip_record_factory, + thl_redis_client=thl_redis_client, + ip="1.1.1.1", + ) |
