diff --git a/CHANGELOG.md b/CHANGELOG.md index e3d12145..9a0bc89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-python-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools. +## 1.6.0 - 2025-02-10 + +### Added + +- Added `PreviewTransaction` operation to support transaction previews without location information. + +### Fixed + +- Transaction preview `currency_code` can now be set to `null`. + ## 1.5.0 - 2025-01-28 ### Added diff --git a/paddle_billing/Client.py b/paddle_billing/Client.py index 0bc2f150..bea6fa14 100644 --- a/paddle_billing/Client.py +++ b/paddle_billing/Client.py @@ -192,7 +192,7 @@ def build_request_session(self) -> Session: "Authorization": f"Bearer {self.__api_key}", "Content-Type": "application/json", "Paddle-Version": str(self.use_api_version), - "User-Agent": "PaddleSDK/python 1.5.0", + "User-Agent": "PaddleSDK/python 1.6.0", } ) diff --git a/paddle_billing/Resources/Transactions/Operations/PreviewTransaction.py b/paddle_billing/Resources/Transactions/Operations/PreviewTransaction.py new file mode 100644 index 00000000..a8d90e39 --- /dev/null +++ b/paddle_billing/Resources/Transactions/Operations/PreviewTransaction.py @@ -0,0 +1,18 @@ +from dataclasses import dataclass + +from paddle_billing.Operation import Operation +from paddle_billing.Undefined import Undefined +from paddle_billing.Entities.Shared import CurrencyCode +from paddle_billing.Resources.Transactions.Operations.Preview import ( + TransactionItemPreviewWithPriceId, + TransactionItemPreviewWithNonCatalogPrice, +) + + +@dataclass +class PreviewTransaction(Operation): + items: list[TransactionItemPreviewWithPriceId | TransactionItemPreviewWithNonCatalogPrice] + customer_id: str | None | Undefined = Undefined() + currency_code: CurrencyCode | None | Undefined = Undefined() + discount_id: str | None | Undefined = Undefined() + ignore_trials: bool | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByAddress.py b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByAddress.py index 2571add9..8358869e 100644 --- a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByAddress.py +++ b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByAddress.py @@ -14,6 +14,6 @@ class PreviewTransactionByAddress(Operation): address: AddressPreview items: list[TransactionItemPreviewWithPriceId | TransactionItemPreviewWithNonCatalogPrice] customer_id: str | None | Undefined = Undefined() - currency_code: CurrencyCode | Undefined = Undefined() + currency_code: CurrencyCode | None | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() ignore_trials: bool | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByCustomer.py b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByCustomer.py index 2d0aefcc..87990877 100644 --- a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByCustomer.py +++ b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByCustomer.py @@ -15,6 +15,6 @@ class PreviewTransactionByCustomer(Operation): customer_id: str items: list[TransactionItemPreviewWithPriceId | TransactionItemPreviewWithNonCatalogPrice] business_id: str | None | Undefined = Undefined() - currency_code: CurrencyCode | Undefined = Undefined() + currency_code: CurrencyCode | None | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() ignore_trials: bool | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByIP.py b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByIP.py index a2d0853e..bc92c2c6 100644 --- a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByIP.py +++ b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByIP.py @@ -14,6 +14,6 @@ class PreviewTransactionByIP(Operation): customer_ip_address: str items: list[TransactionItemPreviewWithPriceId | TransactionItemPreviewWithNonCatalogPrice] customer_id: str | None | Undefined = Undefined() - currency_code: CurrencyCode | Undefined = Undefined() + currency_code: CurrencyCode | None | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() ignore_trials: bool | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/__init__.py b/paddle_billing/Resources/Transactions/Operations/__init__.py index f4b79a26..edf72660 100644 --- a/paddle_billing/Resources/Transactions/Operations/__init__.py +++ b/paddle_billing/Resources/Transactions/Operations/__init__.py @@ -2,6 +2,7 @@ from paddle_billing.Resources.Transactions.Operations.ListTransactions import ListTransactions from paddle_billing.Resources.Transactions.Operations.List.Includes import Includes as TransactionIncludes from paddle_billing.Resources.Transactions.Operations.List.Origin import Origin as TransactionOrigin +from paddle_billing.Resources.Transactions.Operations.PreviewTransaction import PreviewTransaction from paddle_billing.Resources.Transactions.Operations.PreviewTransactionByAddress import PreviewTransactionByAddress from paddle_billing.Resources.Transactions.Operations.PreviewTransactionByCustomer import PreviewTransactionByCustomer from paddle_billing.Resources.Transactions.Operations.PreviewTransactionByIP import PreviewTransactionByIP diff --git a/paddle_billing/Resources/Transactions/TransactionsClient.py b/paddle_billing/Resources/Transactions/TransactionsClient.py index 62da05a2..fa67072f 100644 --- a/paddle_billing/Resources/Transactions/TransactionsClient.py +++ b/paddle_billing/Resources/Transactions/TransactionsClient.py @@ -11,6 +11,7 @@ CreateTransaction, ListTransactions, UpdateTransaction, + PreviewTransaction, PreviewTransactionByAddress, PreviewTransactionByCustomer, PreviewTransactionByIP, @@ -80,7 +81,10 @@ def update(self, transaction_id: str, operation: UpdateTransaction) -> Transacti return Transaction.from_dict(parser.get_data()) def preview( - self, operation: PreviewTransactionByAddress | PreviewTransactionByCustomer | PreviewTransactionByIP + self, + operation: ( + PreviewTransaction | PreviewTransactionByAddress | PreviewTransactionByCustomer | PreviewTransactionByIP + ), ) -> TransactionPreview: self.response = self.client.post_raw("/transactions/preview", operation) parser = ResponseParser(self.response) diff --git a/setup.py b/setup.py index ee0daff4..4e6472a8 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( - version="1.5.0", + version="1.6.0", author="Paddle and contributors", author_email="team-dx@paddle.com", description="Paddle's Python SDK for Paddle Billing", diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_default_basic.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_default_basic.json new file mode 100644 index 00000000..1a9ba348 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_default_basic.json @@ -0,0 +1,9 @@ +{ + "items": [ + { + "quantity": 1, + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "include_in_totals": true + } + ] +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_default_full.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_default_full.json new file mode 100644 index 00000000..2399c559 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_default_full.json @@ -0,0 +1,96 @@ +{ + "customer_id": "ctm_01h844q4mznqpgqgm6evgw1w63", + "currency_code": "USD", + "discount_id": "dsc_01gtgztp8fpchantd5g1wrksa3", + "ignore_trials": true, + "items": [ + { + "quantity": 20, + "price": { + "description": "Annual (per seat)", + "name": "Annual (per seat)", + "product_id": "pro_01gsz4t5hdjse780zja8vvr7jg", + "billing_cycle": { + "interval": "year", + "frequency": 1 + }, + "trial_period": null, + "tax_mode": "account_setting", + "unit_price": { + "amount": "30000", + "currency_code": "USD" + }, + "unit_price_overrides": [], + "quantity": { + "minimum": 10, + "maximum": 999 + }, + "custom_data": null + }, + "include_in_totals": true + }, + { + "quantity": 20, + "price": { + "description": "Annual (per seat)", + "name": "Annual (per seat)", + "product": { + "name": "Analytics addon", + "description": "Some Description", + "tax_category": "standard", + "image_url": "https://paddle.s3.amazonaws.com/user/165798/97dRpA6SXzcE6ekK9CAr_analytics.png", + "custom_data": { + "key": "value" + } + }, + "billing_cycle": { + "interval": "year", + "frequency": 1 + }, + "trial_period": null, + "tax_mode": "account_setting", + "unit_price": { + "amount": "30000", + "currency_code": "USD" + }, + "unit_price_overrides": [], + "quantity": { + "minimum": 10, + "maximum": 999 + }, + "custom_data": null + }, + "include_in_totals": true + }, + { + "quantity": 20, + "price": { + "description": "Annual (per seat)", + "product_id": "pro_01gsz4t5hdjse780zja8vvr7jg", + "unit_price": { + "amount": "30000", + "currency_code": "USD" + } + } + }, + { + "quantity": 20, + "price": { + "description": "Annual (per seat)", + "product": { + "name": "Analytics addon", + "description": "Some Description", + "tax_category": "standard", + "image_url": "https://paddle.s3.amazonaws.com/user/165798/97dRpA6SXzcE6ekK9CAr_analytics.png", + "custom_data": { + "key": "value" + } + }, + "unit_price": { + "amount": "30000", + "currency_code": "USD" + } + } + } + ] +} diff --git a/tests/Functional/Resources/Transactions/test_TransactionsClient.py b/tests/Functional/Resources/Transactions/test_TransactionsClient.py index 8fd999e3..8f7c8efa 100644 --- a/tests/Functional/Resources/Transactions/test_TransactionsClient.py +++ b/tests/Functional/Resources/Transactions/test_TransactionsClient.py @@ -32,6 +32,7 @@ ListTransactions, TransactionIncludes, TransactionOrigin, + PreviewTransaction, PreviewTransactionByAddress, PreviewTransactionByCustomer, PreviewTransactionByIP, @@ -840,6 +841,95 @@ def test_get_transaction_with_and_without_payment_method_id( @mark.parametrize( "operation, expected_request_body, expected_response_status, expected_response_body, expected_url", [ + ( + PreviewTransaction( + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + ), + ReadsFixtures.read_raw_json_fixture("request/preview_default_basic"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), + ( + PreviewTransaction( + customer_id="ctm_01h844q4mznqpgqgm6evgw1w63", + currency_code=CurrencyCode.USD, + discount_id="dsc_01gtgztp8fpchantd5g1wrksa3", + ignore_trials=True, + items=[ + TransactionItemPreviewWithNonCatalogPrice( + quantity=20, + include_in_totals=True, + price=TransactionNonCatalogPrice( + description="Annual (per seat)", + name="Annual (per seat)", + billing_cycle=Duration(Interval.Year, 1), + trial_period=None, + tax_mode=TaxMode.AccountSetting, + unit_price=Money("30000", CurrencyCode.USD), + unit_price_overrides=[], + quantity=PriceQuantity(minimum=10, maximum=999), + custom_data=None, + product_id="pro_01gsz4t5hdjse780zja8vvr7jg", + ), + ), + TransactionItemPreviewWithNonCatalogPrice( + quantity=20, + include_in_totals=True, + price=TransactionNonCatalogPriceWithProduct( + description="Annual (per seat)", + name="Annual (per seat)", + billing_cycle=Duration(Interval.Year, 1), + trial_period=None, + tax_mode=TaxMode.AccountSetting, + unit_price=Money("30000", CurrencyCode.USD), + unit_price_overrides=[], + quantity=PriceQuantity(minimum=10, maximum=999), + custom_data=None, + product=TransactionNonCatalogProduct( + name="Analytics addon", + description="Some Description", + image_url="https://paddle.s3.amazonaws.com/user/165798/97dRpA6SXzcE6ekK9CAr_analytics.png", + tax_category=TaxCategory.Standard, + custom_data=CustomData({"key": "value"}), + ), + ), + ), + TransactionItemPreviewWithNonCatalogPrice( + quantity=20, + price=TransactionNonCatalogPrice( + description="Annual (per seat)", + unit_price=Money("30000", CurrencyCode.USD), + product_id="pro_01gsz4t5hdjse780zja8vvr7jg", + ), + ), + TransactionItemPreviewWithNonCatalogPrice( + quantity=20, + price=TransactionNonCatalogPriceWithProduct( + description="Annual (per seat)", + unit_price=Money("30000", CurrencyCode.USD), + product=TransactionNonCatalogProduct( + name="Analytics addon", + description="Some Description", + image_url="https://paddle.s3.amazonaws.com/user/165798/97dRpA6SXzcE6ekK9CAr_analytics.png", + tax_category=TaxCategory.Standard, + custom_data=CustomData({"key": "value"}), + ), + ), + ), + ], + ), + ReadsFixtures.read_raw_json_fixture("request/preview_default_full"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), ( PreviewTransactionByAddress( address=AddressPreview( @@ -1123,6 +1213,8 @@ def test_get_transaction_with_and_without_payment_method_id( ), ], ids=[ + "Basic transaction preview with no location information", + "Full transaction preview with no location information", "Basic transaction preview by address", "Full transaction preview by address", "Basic transaction preview by customer",