diff --git a/cuenca/__init__.py b/cuenca/__init__.py index 64842c51..2bd7eb12 100644 --- a/cuenca/__init__.py +++ b/cuenca/__init__.py @@ -11,13 +11,16 @@ 'CardValidation', 'Commission', 'Deposit', + 'FraudValidation', 'LoginToken', 'Saving', 'ServiceProvider', 'Statement', + 'TransactionTokenValidation', 'Transfer', 'UserCredential', 'UserLogin', + 'UserPldRiskLevel', 'WalletTransaction', 'WhatsappTransfer', 'configure', @@ -39,13 +42,16 @@ CardValidation, Commission, Deposit, + FraudValidation, LoginToken, Saving, ServiceProvider, Statement, + TransactionTokenValidation, Transfer, UserCredential, UserLogin, + UserPldRiskLevel, WalletTransaction, WhatsappTransfer, ) diff --git a/cuenca/resources/__init__.py b/cuenca/resources/__init__.py index 1f4fb481..f29b6806 100644 --- a/cuenca/resources/__init__.py +++ b/cuenca/resources/__init__.py @@ -10,12 +10,15 @@ 'CardValidation', 'Commission', 'Deposit', + 'FraudValidation', 'LoginToken', 'Saving', 'ServiceProvider', 'Statement', + 'TransactionTokenValidation', 'Transfer', 'UserLogin', + 'UserPldRiskLevel', 'WalletTransaction', 'WhatsappTransfer', ] @@ -31,14 +34,17 @@ from .cards import Card from .commissions import Commission from .deposits import Deposit +from .fraud_validations import FraudValidation from .login_tokens import LoginToken from .resources import RESOURCES from .savings import Saving from .service_providers import ServiceProvider from .statements import Statement +from .transaction_token_validations import TransactionTokenValidation from .transfers import Transfer from .user_credentials import UserCredential from .user_logins import UserLogin +from .user_pld_risk_levels import UserPldRiskLevel from .wallet_transactions import WalletTransaction from .whatsapp_transfers import WhatsappTransfer @@ -55,13 +61,16 @@ CardValidation, Commission, Deposit, + FraudValidation, LoginToken, Saving, ServiceProvider, Statement, + TransactionTokenValidation, Transfer, UserCredential, UserLogin, + UserPldRiskLevel, WalletTransaction, WhatsappTransfer, ] diff --git a/cuenca/resources/fraud_validations.py b/cuenca/resources/fraud_validations.py new file mode 100644 index 00000000..9627e869 --- /dev/null +++ b/cuenca/resources/fraud_validations.py @@ -0,0 +1,59 @@ +import datetime as dt +from typing import ClassVar, Optional, cast + +from cuenca_validations.types.enums import ( + AuthorizerTransaction, + CardFraudType, + CardholderVerificationMethod, + CardStatus, + CardType, + EcommerceIndicator, + IssuerNetwork, + PosCapability, + TrackDataMethod, +) +from cuenca_validations.types.requests import FraudValidationRequest +from pydantic.dataclasses import dataclass + +from cuenca.resources.base import Creatable, Retrievable + +from ..http import Session, session as global_session + + +@dataclass +class FraudValidation(Retrievable, Creatable): + _resource: ClassVar = 'fraud_validations' + + id: str + created_at: dt.datetime + updated_at: dt.datetime + card_id: Optional[str] + user_id: Optional[str] + amount: int + merchant_name: str + merchant_type: str + merchant_data: str + currency_code: str + card_type: Optional[CardType] + card_status: Optional[CardStatus] + transaction_type: AuthorizerTransaction + track_data_method: TrackDataMethod + pos_capability: PosCapability + logical_network: Optional[str] + issuer: IssuerNetwork + cardholder_verification_method: CardholderVerificationMethod + ecommerce_indicator: EcommerceIndicator + token_validation_id: Optional[str] + result: Optional[CardFraudType] + is_cvv: bool = False + + @classmethod + def create( + cls, + request: FraudValidationRequest, + *, + session: Session = global_session, + ) -> 'FraudValidation': + return cast( + 'FraudValidation', cls._create(session=session, **request.dict()) + ) diff --git a/cuenca/resources/transaction_token_validations.py b/cuenca/resources/transaction_token_validations.py new file mode 100644 index 00000000..3a9c1ad4 --- /dev/null +++ b/cuenca/resources/transaction_token_validations.py @@ -0,0 +1,33 @@ +import datetime as dt +from typing import ClassVar, cast + +from cuenca_validations.types.enums import TransactionTokenValidationStatus +from cuenca_validations.types.requests import ( + TransactionTokenValidationUpdateRequest, +) +from pydantic.dataclasses import dataclass + +from cuenca.resources.base import Retrievable, Updateable + +from ..http import Session, session as global_session + + +@dataclass +class TransactionTokenValidation(Retrievable, Updateable): + _resource: ClassVar = 'transaction_token_validations' + + created_at: dt.datetime + deactivated_at: dt.datetime + status: TransactionTokenValidationStatus + + @classmethod + def update( + cls, + id: str, + status: TransactionTokenValidationStatus, + *, + session: Session = global_session, + ) -> 'TransactionTokenValidation': + req = TransactionTokenValidationUpdateRequest(status=status) + resp = cls._update(id, session=session, **req.dict()) + return cast('TransactionTokenValidation', resp) diff --git a/cuenca/resources/user_pld_risk_levels.py b/cuenca/resources/user_pld_risk_levels.py new file mode 100644 index 00000000..a7f6b80c --- /dev/null +++ b/cuenca/resources/user_pld_risk_levels.py @@ -0,0 +1,28 @@ +import datetime as dt +from typing import ClassVar, cast + +from cuenca_validations.types.requests import UserPldRiskLevelRequest +from pydantic.dataclasses import dataclass + +from cuenca.resources.base import Creatable, Retrievable + +from ..http import Session, session as global_session + + +@dataclass +class UserPldRiskLevel(Retrievable, Creatable): + _resource: ClassVar = 'user_pld_risk_levels' + + created_at: dt.datetime + updated_at: dt.datetime + level: float + is_user_defined: bool + + @classmethod + def create( + cls, user_id: str, level: float, *, session: Session = global_session + ) -> 'UserPldRiskLevel': + req = UserPldRiskLevelRequest(user_id=user_id, level=level) + return cast( + 'UserPldRiskLevel', cls._create(session=session, **req.dict()) + ) diff --git a/cuenca/version.py b/cuenca/version.py index 529befce..2d07f3b7 100644 --- a/cuenca/version.py +++ b/cuenca/version.py @@ -1,3 +1,3 @@ -__version__ = '0.7.13' +__version__ = '0.7.14.dev7' CLIENT_VERSION = __version__ API_VERSION = '2020-03-19' diff --git a/requirements.txt b/requirements.txt index 36905448..37839671 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ requests==2.26.0 -cuenca-validations==0.9.16 +cuenca-validations==0.9.18.dev1 dataclasses>=0.7;python_version<"3.7" diff --git a/tests/resources/cassettes/test_create_fraud_validation.yaml b/tests/resources/cassettes/test_create_fraud_validation.yaml new file mode 100644 index 00000000..37144657 --- /dev/null +++ b/tests/resources/cassettes/test_create_fraud_validation.yaml @@ -0,0 +1,61 @@ +interactions: +- request: + body: '{"card_id": "CA1234567890", "user_id": "US1234", "amount": 123, "merchant_name": + "AMAZON MX MARKETPLACE MEXICO DF 000MX", "merchant_type": "wtype", "merchant_data": + "0279288357 00012558", "currency_code": "458", "prosa_transaction_id": + "12345", "retrieval_reference": "who ks", "card_type": "virtual", "card_status": + "active", "transaction_type": "normal_purchase", "authorizer_number": "1231223123", + "track_data_method": "terminal", "pos_capability": "pin_accepted", "logical_network": + null, "is_cvv": true, "get_balance": false, "atm_fee": null, "issuer": "Mastercard", + "cardholder_verification_method": "signature", "ecommerce_indicator": "0"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - DUMMY + Connection: + - keep-alive + Content-Length: + - '657' + Content-Type: + - application/json + User-Agent: + - cuenca-python/0.7.12 + X-Cuenca-Api-Version: + - '2020-03-19' + method: POST + uri: https://sandbox.cuenca.com/fraud_validations + response: + body: + string: '{"id":"FVDnqqjjbXQ92OvY6g0Jf9nQ","created_at":"2021-10-21T20:20:58.240300","updated_at":"2021-10-21T20:20:58.240307","card_id":"CA1234567890","user_id":"US1234","amount":123,"merchant_name":"AMAZON + MX MARKETPLACE MEXICO DF 000MX","merchant_type":"wtype","merchant_data":"0279288357 00012558","currency_code":"458","prosa_transaction_id":"12345","retrieval_reference":"who + ks","card_type":"virtual","card_status":"active","transaction_type":"normal_purchase","authorizer_number":"1231223123","track_data_method":"terminal","pos_capability":"pin_accepted","logical_network":null,"is_cvv":true,"get_balance":false,"atm_fee":null,"issuer":"Mastercard","cardholder_verification_method":"signature","ecommerce_indicator":"0","token_validation_id":null,"result":null}' + headers: + Connection: + - keep-alive + Content-Length: + - '771' + Content-Type: + - application/json + Date: + - Thu, 21 Oct 2021 20:20:58 GMT + X-Request-Time: + - 'value: 0.563' + x-amz-apigw-id: + - HkzCkG_uCYcF2yg= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '771' + x-amzn-Remapped-Date: + - Thu, 21 Oct 2021 20:20:58 GMT + x-amzn-Remapped-Server: + - nginx/1.20.1 + x-amzn-RequestId: + - 9a30606c-f5e3-4406-aa00-ddac3137c3bf + status: + code: 201 + message: Created +version: 1 diff --git a/tests/resources/cassettes/test_create_user_pld_risk_level.yaml b/tests/resources/cassettes/test_create_user_pld_risk_level.yaml new file mode 100644 index 00000000..9cd7a37e --- /dev/null +++ b/tests/resources/cassettes/test_create_user_pld_risk_level.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: '{"user_id": "US6D1wbTEdLuTjf227iF05js", "level": 0.7}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - DUMMY + Connection: + - keep-alive + Content-Length: + - '53' + Content-Type: + - application/json + User-Agent: + - cuenca-python/0.7.12 + X-Cuenca-Api-Version: + - '2020-03-19' + method: POST + uri: https://sandbox.cuenca.com/user_pld_risk_levels + response: + body: + string: '{"id":"US6D1wbTEdLuTjf227iF05js","created_at":"2021-10-21T22:20:30.516179","updated_at":"2021-10-21T22:20:30.516191","level":"0.7","is_user_defined":true}' + headers: + Connection: + - keep-alive + Content-Length: + - '154' + Content-Type: + - application/json + Date: + - Thu, 21 Oct 2021 22:20:30 GMT + X-Request-Time: + - 'value: 0.450' + x-amz-apigw-id: + - HlEjOFkZiYcF2iw= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '154' + x-amzn-Remapped-Date: + - Thu, 21 Oct 2021 22:20:30 GMT + x-amzn-Remapped-Server: + - nginx/1.20.1 + x-amzn-RequestId: + - 0a21550f-54d1-4608-b4e6-7e5a32e10754 + status: + code: 201 + message: Created +version: 1 diff --git a/tests/resources/cassettes/test_retrieve_fraud_validation.yaml b/tests/resources/cassettes/test_retrieve_fraud_validation.yaml new file mode 100644 index 00000000..87321956 --- /dev/null +++ b/tests/resources/cassettes/test_retrieve_fraud_validation.yaml @@ -0,0 +1,50 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - DUMMY + Connection: + - keep-alive + User-Agent: + - cuenca-python/0.7.12 + X-Cuenca-Api-Version: + - '2020-03-19' + method: GET + uri: https://sandbox.cuenca.com/fraud_validations/FVDnqqjjbXQ92OvY6g0Jf9nQ + response: + body: + string: '{"id":"FVDnqqjjbXQ92OvY6g0Jf9nQ","created_at":"2021-10-21T20:20:58.240000","updated_at":"2021-10-21T20:20:58.240000","card_id":"CA1234567890","user_id":"US1234","amount":123,"merchant_name":"AMAZON + MX MARKETPLACE MEXICO DF 000MX","merchant_type":"wtype","merchant_data":"0279288357 00012558","currency_code":"458","prosa_transaction_id":"12345","retrieval_reference":"who + ks","card_type":"virtual","card_status":"active","transaction_type":"normal_purchase","authorizer_number":"1231223123","track_data_method":"terminal","pos_capability":"pin_accepted","logical_network":null,"is_cvv":true,"get_balance":false,"atm_fee":null,"issuer":"Mastercard","cardholder_verification_method":"signature","ecommerce_indicator":"0","token_validation_id":null,"result":"authorize"}' + headers: + Connection: + - keep-alive + Content-Length: + - '778' + Content-Type: + - application/json + Date: + - Thu, 21 Oct 2021 21:45:35 GMT + X-Request-Time: + - 'value: 0.245' + x-amz-apigw-id: + - Hk_b2Fe6CYcFbhg= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '778' + x-amzn-Remapped-Date: + - Thu, 21 Oct 2021 21:45:35 GMT + x-amzn-Remapped-Server: + - nginx/1.20.1 + x-amzn-RequestId: + - 632daa8a-074e-48ed-9bb0-a7de3e553a90 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/resources/cassettes/test_retrieve_transaction_token_validation.yaml b/tests/resources/cassettes/test_retrieve_transaction_token_validation.yaml new file mode 100644 index 00000000..5c9ac37f --- /dev/null +++ b/tests/resources/cassettes/test_retrieve_transaction_token_validation.yaml @@ -0,0 +1,100 @@ +interactions: +- request: + body: '{"password": "111111"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - DUMMY + Connection: + - keep-alive + Content-Length: + - '22' + Content-Type: + - application/json + User-Agent: + - cuenca-python/0.7.12 + X-Cuenca-Api-Version: + - '2020-03-19' + method: POST + uri: https://sandbox.cuenca.com/user_logins + response: + body: + string: '{"success":true,"id":"ULI3ZmawLoSxKDaD-fRIeHQg","last_login_at":"2021-10-21T22:04:06.612000Z"}' + headers: + Connection: + - keep-alive + Content-Length: + - '94' + Content-Type: + - application/json + Date: + - Thu, 21 Oct 2021 22:17:33 GMT + X-Request-Time: + - 'value: 0.388' + x-amz-apigw-id: + - HlEHhGFrCYcF-VQ= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '94' + x-amzn-Remapped-Date: + - Thu, 21 Oct 2021 22:17:33 GMT + x-amzn-Remapped-Server: + - nginx/1.20.1 + x-amzn-RequestId: + - 862cefa9-a206-4e0d-9767-15b55f48632a + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - DUMMY + Connection: + - keep-alive + User-Agent: + - cuenca-python/0.7.12 + X-Cuenca-Api-Version: + - '2020-03-19' + X-Cuenca-LoginId: + - ULI3ZmawLoSxKDaD-fRIeHQg + method: GET + uri: https://sandbox.cuenca.com/transaction_token_validations/TKSEZmmXa-SXuAMv6516sRKw + response: + body: + string: '{"id":"TKSEZmmXa-SXuAMv6516sRKw","user_id":"US6D1wbTEdLuTjf227iF05js","created_at":"2021-10-21T21:55:11.914000","updated_at":"2021-10-21T22:17:38.717414","deactivated_at":"2021-10-22T21:55:11.914000","status":"pending"}' + headers: + Connection: + - keep-alive + Content-Length: + - '219' + Content-Type: + - application/json + Date: + - Thu, 21 Oct 2021 22:17:38 GMT + X-Request-Time: + - 'value: 5.297' + x-amz-apigw-id: + - HlEHoF1JCYcF2iw= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '219' + x-amzn-Remapped-Date: + - Thu, 21 Oct 2021 22:17:38 GMT + x-amzn-Remapped-Server: + - nginx/1.20.1 + x-amzn-RequestId: + - 8741073a-4d0a-4525-9b19-7b2d2513895e + status: + code: 200 + message: OK +version: 1 diff --git a/tests/resources/cassettes/test_update_transaction_token_validation.yaml b/tests/resources/cassettes/test_update_transaction_token_validation.yaml new file mode 100644 index 00000000..f0792446 --- /dev/null +++ b/tests/resources/cassettes/test_update_transaction_token_validation.yaml @@ -0,0 +1,104 @@ +interactions: +- request: + body: '{"password": "111111"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - DUMMY + Connection: + - keep-alive + Content-Length: + - '22' + Content-Type: + - application/json + User-Agent: + - cuenca-python/0.7.12 + X-Cuenca-Api-Version: + - '2020-03-19' + method: POST + uri: https://sandbox.cuenca.com/user_logins + response: + body: + string: '{"success":true,"id":"ULZkAGFZtfR7u2aKVJjC5pdw","last_login_at":"2021-10-21T22:17:33.133000Z"}' + headers: + Connection: + - keep-alive + Content-Length: + - '94' + Content-Type: + - application/json + Date: + - Thu, 21 Oct 2021 22:18:51 GMT + X-Request-Time: + - 'value: 0.476' + x-amz-apigw-id: + - HlETzF-xCYcFZTA= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '94' + x-amzn-Remapped-Date: + - Thu, 21 Oct 2021 22:18:51 GMT + x-amzn-Remapped-Server: + - nginx/1.20.1 + x-amzn-RequestId: + - 4543679f-af05-498a-a8a9-e96426bbabcd + status: + code: 201 + message: Created +- request: + body: '{"status": "accepted"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - DUMMY + Connection: + - keep-alive + Content-Length: + - '22' + Content-Type: + - application/json + User-Agent: + - cuenca-python/0.7.12 + X-Cuenca-Api-Version: + - '2020-03-19' + X-Cuenca-LoginId: + - ULZkAGFZtfR7u2aKVJjC5pdw + method: PATCH + uri: https://sandbox.cuenca.com/transaction_token_validations/TKSEZmmXa-SXuAMv6516sRKw + response: + body: + string: '{"id":"TKSEZmmXa-SXuAMv6516sRKw","user_id":"US6D1wbTEdLuTjf227iF05js","created_at":"2021-10-21T21:55:11.914000","updated_at":"2021-10-21T22:18:52.325236","deactivated_at":"2021-10-22T22:18:52.325502","status":"accepted"}' + headers: + Connection: + - keep-alive + Content-Length: + - '220' + Content-Type: + - application/json + Date: + - Thu, 21 Oct 2021 22:18:52 GMT + X-Request-Time: + - 'value: 0.425' + x-amz-apigw-id: + - HlET5HkCiYcFWUw= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '220' + x-amzn-Remapped-Date: + - Thu, 21 Oct 2021 22:18:52 GMT + x-amzn-Remapped-Server: + - nginx/1.20.1 + x-amzn-RequestId: + - abbfb22e-70cc-4504-aa5f-23b9a72a7786 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/resources/test_fraud_validations.py b/tests/resources/test_fraud_validations.py new file mode 100644 index 00000000..2d5d336f --- /dev/null +++ b/tests/resources/test_fraud_validations.py @@ -0,0 +1,60 @@ +from typing import Dict + +import pytest +from cuenca_validations.types.enums import ( + AuthorizerTransaction, + CardFraudType, + CardholderVerificationMethod, + CardStatus, + CardType, + EcommerceIndicator, + IssuerNetwork, + PosCapability, + TrackDataMethod, +) +from cuenca_validations.types.requests import FraudValidationRequest + +from cuenca.resources import FraudValidation + + +@pytest.fixture +def fraud_validation_data() -> Dict: + return dict( + id='FVDnqqjjbXQ92OvY6g0Jf9nQ', + card_id='CA1234567890', + user_id='US1234', + amount=123, + merchant_name='AMAZON MX MARKETPLACE MEXICO DF 000MX', + merchant_type='wtype', + merchant_data='0279288357 00012558', + currency_code='458', + card_type=CardType.virtual, + card_status=CardStatus.active, + transaction_type=AuthorizerTransaction.normal_purchase, + track_data_method=TrackDataMethod.terminal, + pos_capability=PosCapability.pin_accepted, + is_cvv=True, + issuer=IssuerNetwork.mastercard, + cardholder_verification_method=CardholderVerificationMethod.signature, + ecommerce_indicator=EcommerceIndicator.not_ecommerce, + ) + + +@pytest.mark.vcr +def test_create_fraud_validation(fraud_validation_data: Dict): + charge_request = FraudValidationRequest(**fraud_validation_data) + fraud_validation = FraudValidation.create(charge_request) + assert all( + getattr(fraud_validation, key) == value + for key, value in fraud_validation_data.items() + ) + + +@pytest.mark.vcr +def test_retrieve_fraud_validation(fraud_validation_data): + fraud_validation = FraudValidation.retrieve('FVDnqqjjbXQ92OvY6g0Jf9nQ') + assert all( + getattr(fraud_validation, key) == value + for key, value in fraud_validation_data.items() + ) + assert fraud_validation.result is CardFraudType.authorize diff --git a/tests/resources/test_transaction_token_validations.py b/tests/resources/test_transaction_token_validations.py new file mode 100644 index 00000000..7d943e18 --- /dev/null +++ b/tests/resources/test_transaction_token_validations.py @@ -0,0 +1,20 @@ +import pytest +from cuenca_validations.types.enums import TransactionTokenValidationStatus + +from cuenca.resources import TransactionTokenValidation, UserLogin + + +@pytest.mark.vcr +def test_retrieve_transaction_token_validation() -> None: + UserLogin.create(password='111111') + t = TransactionTokenValidation.retrieve('TKSEZmmXa-SXuAMv6516sRKw') + assert t.status is TransactionTokenValidationStatus.pending # type: ignore + + +@pytest.mark.vcr +def test_update_transaction_token_validation() -> None: + UserLogin.create(password='111111') + token = TransactionTokenValidation.update( + 'TKSEZmmXa-SXuAMv6516sRKw', TransactionTokenValidationStatus.accepted + ) + assert token.status is TransactionTokenValidationStatus.accepted diff --git a/tests/resources/test_user_pld_risk_levels.py b/tests/resources/test_user_pld_risk_levels.py new file mode 100644 index 00000000..7abca585 --- /dev/null +++ b/tests/resources/test_user_pld_risk_levels.py @@ -0,0 +1,10 @@ +import pytest + +from cuenca.resources import UserPldRiskLevel + + +@pytest.mark.vcr +def test_create_user_pld_risk_level() -> None: + risk = UserPldRiskLevel.create('US6D1wbTEdLuTjf227iF05js', 0.7) + assert risk.level == 0.7 + assert risk.is_user_defined