From 22cbec052e9f34cf1bab65a06062716a99242f4d Mon Sep 17 00:00:00 2001 From: Hersonls Date: Fri, 7 Feb 2025 20:42:14 -0300 Subject: [PATCH] Fix create card token endpoint and payload --- barte/client.py | 73 +++++++++++++++++++++++++++++--------------- barte/models.py | 19 +++++++----- tests/test_client.py | 33 +++++++++++--------- 3 files changed, 80 insertions(+), 45 deletions(-) diff --git a/barte/client.py b/barte/client.py index 9cf9b2a..b083a85 100644 --- a/barte/client.py +++ b/barte/client.py @@ -2,11 +2,20 @@ import requests from dacite import from_dict from .models import ( - Charge, CardToken, Refund, InstallmentOptions, - PixCharge, PixQRCode, DACITE_CONFIG, Config, - InstallmentSimulation, Buyer, BuyerList + Charge, + CardToken, + Refund, + InstallmentOptions, + PixCharge, + PixQRCode, + DACITE_CONFIG, + Config, + InstallmentSimulation, + Buyer, + BuyerList, ) + class BarteClient: VALID_ENVIRONMENTS = ["production", "sandbox"] _instance = None @@ -23,20 +32,25 @@ def __init__(self, api_key: str, environment: str = "production"): ValueError: If the environment is not "production" or "sandbox" """ if environment not in self.VALID_ENVIRONMENTS: - raise ValueError(f"Invalid environment. Must be one of: {', '.join(self.VALID_ENVIRONMENTS)}") + raise ValueError( + f"Invalid environment. Must be one of: {', '.join(self.VALID_ENVIRONMENTS)}" + ) self.api_key = api_key - self.base_url = "https://api.barte.com" if environment == "production" else "https://sandbox-api.barte.com" - self.headers = { - "X-Token-Api": api_key, - "Content-Type": "application/json" - } + self.base_url = ( + "https://api.barte.com" + if environment == "production" + else "https://sandbox-api.barte.com" + ) + self.headers = {"X-Token-Api": api_key, "Content-Type": "application/json"} BarteClient._instance = self @classmethod def get_instance(cls) -> "BarteClient": if cls._instance is None: - raise RuntimeError("BarteClient not initialized. Call BarteClient(api_key) first.") + raise RuntimeError( + "BarteClient not initialized. Call BarteClient(api_key) first." + ) return cls._instance def create_charge(self, data: Dict[str, Any]) -> Charge: @@ -58,7 +72,10 @@ def list_charges(self, params: Optional[Dict[str, Any]] = None) -> List[Charge]: endpoint = f"{self.base_url}/v1/charges" response = requests.get(endpoint, headers=self.headers, params=params) response.raise_for_status() - return [from_dict(data_class=Charge, data=item, config=DACITE_CONFIG) for item in response.json()["data"]] + return [ + from_dict(data_class=Charge, data=item, config=DACITE_CONFIG) + for item in response.json()["data"] + ] def cancel_charge(self, charge_id: str) -> Charge: """Cancel a specific charge""" @@ -77,14 +94,18 @@ def get_buyer(self, filters: Dict[str, any]) -> BuyerList: endpoint = f"{self.base_url}/v2/buyers" response = requests.get(endpoint, params=filters, headers=self.headers) response.raise_for_status() - return from_dict(data_class=BuyerList, data=response.json(), config=DACITE_CONFIG) + return from_dict( + data_class=BuyerList, data=response.json(), config=DACITE_CONFIG + ) def create_card_token(self, card_data: Dict[str, Any]) -> CardToken: """Create a token for a credit card""" - endpoint = f"{self.base_url}/v1/tokens" + endpoint = f"{self.base_url}/v2/cards" response = requests.post(endpoint, headers=self.headers, json=card_data) response.raise_for_status() - return from_dict(data_class=CardToken, data=response.json(), config=DACITE_CONFIG) + return from_dict( + data_class=CardToken, data=response.json(), config=DACITE_CONFIG + ) def charge_with_card_token(self, token_id: str, data: Dict[str, Any]) -> Charge: """Create a charge using an existing card token""" @@ -93,7 +114,7 @@ def charge_with_card_token(self, token_id: str, data: Dict[str, Any]) -> Charge: transaction_data = { **data, "payment_method": "credit_card", - "card_token": token_id + "card_token": token_id, } response = requests.post(endpoint, headers=self.headers, json=transaction_data) @@ -104,14 +125,13 @@ def create_pix_charge(self, data: Dict[str, Any]) -> PixCharge: """Create a PIX charge""" endpoint = f"{self.base_url}/v1/charges" - pix_data = { - **data, - "payment_method": "pix" - } + pix_data = {**data, "payment_method": "pix"} response = requests.post(endpoint, headers=self.headers, json=pix_data) response.raise_for_status() - return from_dict(data_class=PixCharge, data=response.json(), config=DACITE_CONFIG) + return from_dict( + data_class=PixCharge, data=response.json(), config=DACITE_CONFIG + ) def get_pix_qrcode(self, charge_id: str) -> PixQRCode: """Get PIX QR Code data for a charge""" @@ -129,7 +149,7 @@ def simulate_installments(self, amount: int, brand: str) -> InstallmentOptions: return from_dict( data_class=InstallmentOptions, data=response.json(), - config=Config(cast=[List[InstallmentSimulation]]) + config=Config(cast=[List[InstallmentSimulation]]), ) def get_charge_refunds(self, charge_id: str) -> List[Refund]: @@ -137,9 +157,14 @@ def get_charge_refunds(self, charge_id: str) -> List[Refund]: endpoint = f"{self.base_url}/v1/charges/{charge_id}/refunds" response = requests.get(endpoint, headers=self.headers) response.raise_for_status() - return [from_dict(data_class=Refund, data=item, config=DACITE_CONFIG) for item in response.json()["data"]] - - def refund_charge(self, charge_id: str, data: Optional[Dict[str, Any]] = None) -> Refund: + return [ + from_dict(data_class=Refund, data=item, config=DACITE_CONFIG) + for item in response.json()["data"] + ] + + def refund_charge( + self, charge_id: str, data: Optional[Dict[str, Any]] = None + ) -> Refund: """Refund a charge""" endpoint = f"{self.base_url}/v1/charges/{charge_id}/refund" response = requests.post(endpoint, headers=self.headers, json=data or {}) diff --git a/barte/models.py b/barte/models.py index e61500e..c8c3ae9 100644 --- a/barte/models.py +++ b/barte/models.py @@ -19,14 +19,19 @@ class Customer: @dataclass class CardToken: - id: str - type: str - created_at: datetime - last_digits: str - holder_name: str - expiration_month: int - expiration_year: int + uuid: str + status: str + createdAt: datetime brand: str + cardHolderName: str + cvvChecked: bool + fingerprint: str + first6digits: str + last4digits: str + buyerId: str + expirationMonth: str + expirationYear: str + cardId: str @dataclass diff --git a/tests/test_client.py b/tests/test_client.py index 1e9da6b..a3c4288 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -115,20 +115,25 @@ def test_create_pix_charge(self, mock_post, barte_client, mock_charge_response): def test_create_card_token(self, mock_post, barte_client): """Test creating a card token""" mock_response = { - "id": "tok_123456", - "type": "card", - "created_at": "2024-03-20T10:00:00Z", - "last_digits": "1111", - "holder_name": "John Doe", - "expiration_month": 12, - "expiration_year": 2025, - "brand": "visa", + "uuid": "790e8637-c16b-4ed5-a9bf-faec76dbc5aa", + "status": "ACTIVE", + "createdAt": "2025-02-07", + "brand": "mastercard", + "cardHolderName": "John Doe", + "cvvChecked": True, + "fingerprint": "MLvWOfRXBcGIvK9cWSj9vLy0yhmBMzbxldLSJHYvEEw=", + "first6digits": "538363", + "last4digits": "0891", + "buyerId": "5929a30b-e68f-4c81-9481-d25adbabafeb", + "expirationMonth": "12", + "expirationYear": "2025", + "cardId": "9dc2ffe0-d588-44b7-b74d-d5ad88a31143", } mock_post.return_value.json.return_value = mock_response mock_post.return_value.raise_for_status = Mock() card_data = { - "number": "4111111111111111", + "number": "5383630891", "holder_name": "John Doe", "expiration_month": 12, "expiration_year": 2025, @@ -138,13 +143,13 @@ def test_create_card_token(self, mock_post, barte_client): token = barte_client.create_card_token(card_data) assert isinstance(token, CardToken) - assert token.id == "tok_123456" - assert token.last_digits == "1111" - assert token.holder_name == "John Doe" - assert isinstance(token.created_at, datetime) + assert token.uuid == "790e8637-c16b-4ed5-a9bf-faec76dbc5aa" + assert token.last4digits == "0891" + assert token.cardHolderName == "John Doe" + assert isinstance(token.createdAt, datetime) mock_post.assert_called_once_with( - f"{barte_client.base_url}/v1/tokens", + f"{barte_client.base_url}/v2/cards", headers=barte_client.headers, json=card_data, )