diff --git a/CHANGELOG.md b/CHANGELOG.md index c879685c..d9d52685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,10 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx - `paddle_billing.Entities.Event` `data` will now be `paddle_billing.Notifications.Entities.UndefinedEntity` for unknown event types - `paddle_billing.Resources.Reports.Operations.CreateReport` is replaced by report specific operations `CreateAdjustmentsReport` | `CreateDiscountsReport` | `CreateProductsAndPricesReport` | `CreateTransactionsReport` - `paddle_billing.Entities.Notification` `payload` is now `paddle_billing.Entities.Notifications.NotificationEvent` +- `paddle_billing.Entities.Shared.BillingDetails` is no longer used for `billing_details` in request operations + - `CreateTransaction` now uses `paddle_billing.Resources.Transactions.Operations.Create.CreateBillingDetails` + - `UpdateTransaction` now uses `paddle_billing.Resources.Transactions.Operations.Update.UpdateBillingDetails` + - `UpdateSubscription` | `PreviewUpdateSubscription` now uses `paddle_billing.Resources.Subscriptions.Operations.Update.UpdateBillingDetails` ### Fixed diff --git a/UPGRADING.md b/UPGRADING.md index d1187a04..f2181d2f 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -50,6 +50,13 @@ Usage of `paddle_billing.Resources.Reports.Operations.CreateReport` should be re - `paddle_billing.Resources.Reports.Operations.CreateProductsAndPricesReport` - `paddle_billing.Resources.Reports.Operations.CreateTransactionsReport` +### 5. `BillingDetails` entity is no longer used for `billing_details` in request operations + +Usage of `paddle_billing.Entities.Shared.BillingDetails` for `billing_details` in request operations, should be replaced with: +- `paddle_billing.Resources.Transactions.Operations.Create.CreateBillingDetails` for `CreateTransaction` +- `paddle_billing.Resources.Transactions.Operations.Update.UpdateBillingDetails` for `UpdateTransaction` +- `paddle_billing.Resources.Subscriptions.Operations.Update.UpdateBillingDetails` for `UpdateSubscription` | `PreviewUpdateSubscription` + ## v0.2.0 This release includes a few breaking changes. These changes should be limited impact on most integrations but may cause problems in some circumstances. diff --git a/paddle_billing/Client.py b/paddle_billing/Client.py index 5a588ab0..72d23d8a 100644 --- a/paddle_billing/Client.py +++ b/paddle_billing/Client.py @@ -5,8 +5,11 @@ from urllib3.util.retry import Retry from urllib.parse import urljoin, urlencode from uuid import uuid4 +from dataclasses import fields, is_dataclass +from paddle_billing.Operation import Operation from paddle_billing.FiltersUndefined import FiltersUndefined +from paddle_billing.Undefined import Undefined from paddle_billing.HasParameters import HasParameters from paddle_billing.Options import Options from paddle_billing.ResponseParser import ResponseParser @@ -34,6 +37,16 @@ class PayloadEncoder(JSONEncoder): def default(self, z): + if is_dataclass(z): + data = {} + for field in fields(z): + data[field.name] = getattr(z, field.name) + + return FiltersUndefined.filter_undefined_values(data) + + if isinstance(z, Undefined): + return None + if hasattr(z, "to_json") and callable(z.to_json): return z.to_json() @@ -105,7 +118,7 @@ def logging_hook(self, response, *args, **kwargs): self.log.debug(f"Response: {response.status_code} {response.text}") @staticmethod - def serialize_json_payload(payload: dict) -> str: + def serialize_json_payload(payload: dict | Operation) -> str: json_payload = json_dumps(payload, cls=PayloadEncoder) final_json = json_payload if json_payload != "[]" else "{}" @@ -115,7 +128,7 @@ def _make_request( self, method: str, url: str, - payload: dict | None = None, + payload: dict | Operation | None = None, ) -> Response: """ Makes an actual API call to Paddle @@ -168,17 +181,17 @@ def get_raw(self, url: str, parameters: HasParameters | dict = None) -> Response return self._make_request("GET", url, None) def post_raw( - self, url: str, payload: dict | None = None, parameters: HasParameters | dict | None = None + self, url: str, payload: dict | Operation | None = None, parameters: HasParameters | dict | None = None ) -> Response: - if payload: + if isinstance(payload, dict): payload = FiltersUndefined.filter_undefined_values(payload) # Strip Undefined items from the dict url = Client.format_uri_parameters(url, parameters) if parameters else url return self._make_request("POST", url, payload) - def patch_raw(self, url: str, payload: dict | None) -> Response: - if payload: + def patch_raw(self, url: str, payload: dict | Operation | None) -> Response: + if isinstance(payload, dict): payload = FiltersUndefined.filter_undefined_values(payload) # Strip Undefined items from the dict return self._make_request("PATCH", url, payload) diff --git a/paddle_billing/Entities/DateTime.py b/paddle_billing/Entities/DateTime.py index 85a66604..dd87aca4 100644 --- a/paddle_billing/Entities/DateTime.py +++ b/paddle_billing/Entities/DateTime.py @@ -36,3 +36,6 @@ def from_datetime(cls, date: datetime | str): def __str__(self): return self.format() + + def to_json(self): + return self.format() diff --git a/paddle_billing/Entities/Shared/BillingDetailsUpdate.py b/paddle_billing/Entities/Shared/BillingDetailsUpdate.py deleted file mode 100644 index 3b46fb29..00000000 --- a/paddle_billing/Entities/Shared/BillingDetailsUpdate.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations -from dataclasses import dataclass - -from paddle_billing.Entities.Shared.TimePeriod import TimePeriod - - -@dataclass -class BillingDetailsUpdate: - enable_checkout: bool - purchase_order_number: str - additional_information: str - payment_terms: TimePeriod - - @staticmethod - def from_dict(data: dict) -> BillingDetailsUpdate: - return BillingDetailsUpdate( - enable_checkout=data["enable_checkout"], - purchase_order_number=data["purchase_order_number"], - additional_information=data["additional_information"], - payment_terms=TimePeriod.from_dict(data["payment_terms"]), - ) diff --git a/paddle_billing/Entities/Shared/__init__.py b/paddle_billing/Entities/Shared/__init__.py index 3ff1cbeb..6e2bd6e6 100644 --- a/paddle_billing/Entities/Shared/__init__.py +++ b/paddle_billing/Entities/Shared/__init__.py @@ -5,7 +5,6 @@ from paddle_billing.Entities.Shared.AdjustmentTotals import AdjustmentTotals from paddle_billing.Entities.Shared.AdjustmentType import AdjustmentType from paddle_billing.Entities.Shared.BillingDetails import BillingDetails -from paddle_billing.Entities.Shared.BillingDetailsUpdate import BillingDetailsUpdate from paddle_billing.Entities.Shared.Card import Card from paddle_billing.Entities.Shared.CatalogType import CatalogType from paddle_billing.Entities.Shared.ChargebackFee import ChargebackFee diff --git a/paddle_billing/Operation.py b/paddle_billing/Operation.py new file mode 100644 index 00000000..e66cd3d6 --- /dev/null +++ b/paddle_billing/Operation.py @@ -0,0 +1,7 @@ +from abc import ABC +from dataclasses import dataclass + + +@dataclass +class Operation(ABC): + pass diff --git a/paddle_billing/Resources/Subscriptions/Operations/PreviewUpdateSubscription.py b/paddle_billing/Resources/Subscriptions/Operations/PreviewUpdateSubscription.py index 0c595b4b..35c5b6f2 100644 --- a/paddle_billing/Resources/Subscriptions/Operations/PreviewUpdateSubscription.py +++ b/paddle_billing/Resources/Subscriptions/Operations/PreviewUpdateSubscription.py @@ -1,9 +1,10 @@ -from dataclasses import asdict, dataclass +from dataclasses import dataclass +from paddle_billing.Operation import Operation from paddle_billing.Undefined import Undefined from paddle_billing.Entities.DateTime import DateTime -from paddle_billing.Entities.Shared import BillingDetails, CollectionMode, CurrencyCode, CustomData +from paddle_billing.Entities.Shared import CollectionMode, CurrencyCode, CustomData from paddle_billing.Entities.Subscriptions import ( SubscriptionItems, SubscriptionItemsWithPrice, @@ -11,11 +12,14 @@ SubscriptionProrationBillingMode, ) -from paddle_billing.Resources.Subscriptions.Operations.Update.SubscriptionDiscount import SubscriptionDiscount +from paddle_billing.Resources.Subscriptions.Operations.Update import ( + SubscriptionDiscount, + UpdateBillingDetails, +) @dataclass -class PreviewUpdateSubscription: +class PreviewUpdateSubscription(Operation): customer_id: str | Undefined = Undefined() address_id: str | Undefined = Undefined() business_id: str | None | Undefined = Undefined() @@ -23,19 +27,9 @@ class PreviewUpdateSubscription: next_billed_at: DateTime | Undefined = Undefined() discount: SubscriptionDiscount | None | Undefined = Undefined() collection_mode: CollectionMode | Undefined = Undefined() - billing_details: BillingDetails | None | Undefined = Undefined() + billing_details: UpdateBillingDetails | None | Undefined = Undefined() scheduled_change: None | Undefined = Undefined() items: list[SubscriptionItems | SubscriptionItemsWithPrice] | Undefined = Undefined() custom_data: CustomData | None | Undefined = Undefined() proration_billing_mode: SubscriptionProrationBillingMode | Undefined = Undefined() on_payment_failure: SubscriptionOnPaymentFailure | Undefined = Undefined() - - def get_parameters(self) -> dict: - parameters = asdict(self) - - if isinstance(self.next_billed_at, DateTime): - parameters["next_billed_at"] = self.next_billed_at.format() - if not isinstance(self.items, Undefined): - parameters["items"] = [item.get_parameters() for item in self.items] - - return parameters diff --git a/paddle_billing/Resources/Subscriptions/Operations/Update/UpdateBillingDetails.py b/paddle_billing/Resources/Subscriptions/Operations/Update/UpdateBillingDetails.py new file mode 100644 index 00000000..d09bf523 --- /dev/null +++ b/paddle_billing/Resources/Subscriptions/Operations/Update/UpdateBillingDetails.py @@ -0,0 +1,13 @@ +from __future__ import annotations +from dataclasses import dataclass + +from paddle_billing.Entities.Shared.Duration import Duration +from paddle_billing.Undefined import Undefined + + +@dataclass +class UpdateBillingDetails: + payment_terms: Duration | Undefined = Undefined() + enable_checkout: bool | Undefined = Undefined() + purchase_order_number: str | Undefined = Undefined() + additional_information: str | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Subscriptions/Operations/Update/__init__.py b/paddle_billing/Resources/Subscriptions/Operations/Update/__init__.py index e69de29b..3033e756 100644 --- a/paddle_billing/Resources/Subscriptions/Operations/Update/__init__.py +++ b/paddle_billing/Resources/Subscriptions/Operations/Update/__init__.py @@ -0,0 +1,2 @@ +from paddle_billing.Resources.Subscriptions.Operations.Update.SubscriptionDiscount import SubscriptionDiscount +from paddle_billing.Resources.Subscriptions.Operations.Update.UpdateBillingDetails import UpdateBillingDetails diff --git a/paddle_billing/Resources/Subscriptions/Operations/UpdateSubscription.py b/paddle_billing/Resources/Subscriptions/Operations/UpdateSubscription.py index d8c1da63..2139f6a6 100644 --- a/paddle_billing/Resources/Subscriptions/Operations/UpdateSubscription.py +++ b/paddle_billing/Resources/Subscriptions/Operations/UpdateSubscription.py @@ -1,9 +1,10 @@ -from dataclasses import asdict, dataclass +from dataclasses import dataclass +from paddle_billing.Operation import Operation from paddle_billing.Undefined import Undefined from paddle_billing.Entities.DateTime import DateTime -from paddle_billing.Entities.Shared import BillingDetails, CollectionMode, CurrencyCode, CustomData +from paddle_billing.Entities.Shared import CollectionMode, CurrencyCode, CustomData from paddle_billing.Entities.Subscriptions import ( SubscriptionItems, SubscriptionItemsWithPrice, @@ -11,11 +12,14 @@ SubscriptionProrationBillingMode, ) -from paddle_billing.Resources.Subscriptions.Operations.Update.SubscriptionDiscount import SubscriptionDiscount +from paddle_billing.Resources.Subscriptions.Operations.Update import ( + SubscriptionDiscount, + UpdateBillingDetails, +) @dataclass -class UpdateSubscription: +class UpdateSubscription(Operation): customer_id: str | Undefined = Undefined() address_id: str | Undefined = Undefined() business_id: str | None | Undefined = Undefined() @@ -23,19 +27,9 @@ class UpdateSubscription: next_billed_at: DateTime | Undefined = Undefined() discount: SubscriptionDiscount | None | Undefined = Undefined() collection_mode: CollectionMode | Undefined = Undefined() - billing_details: BillingDetails | None | Undefined = Undefined() + billing_details: UpdateBillingDetails | None | Undefined = Undefined() scheduled_change: None | Undefined = Undefined() items: list[SubscriptionItems | SubscriptionItemsWithPrice] | Undefined = Undefined() custom_data: CustomData | None | Undefined = Undefined() proration_billing_mode: SubscriptionProrationBillingMode | Undefined = Undefined() on_payment_failure: SubscriptionOnPaymentFailure | Undefined = Undefined() - - def get_parameters(self) -> dict: - parameters = asdict(self) - - if isinstance(self.next_billed_at, DateTime): - parameters["next_billed_at"] = self.next_billed_at.format() - if not isinstance(self.items, Undefined): - parameters["items"] = [item.get_parameters() for item in self.items] - - return parameters diff --git a/paddle_billing/Resources/Subscriptions/SubscriptionsClient.py b/paddle_billing/Resources/Subscriptions/SubscriptionsClient.py index 8d379a9c..4348d2f0 100644 --- a/paddle_billing/Resources/Subscriptions/SubscriptionsClient.py +++ b/paddle_billing/Resources/Subscriptions/SubscriptionsClient.py @@ -58,7 +58,7 @@ def get(self, subscription_id: str, includes=None) -> Subscription: return Subscription.from_dict(parser.get_data()) def update(self, subscription_id: str, operation: UpdateSubscription) -> Subscription: - self.response = self.client.patch_raw(f"/subscriptions/{subscription_id}", operation.get_parameters()) + self.response = self.client.patch_raw(f"/subscriptions/{subscription_id}", operation) parser = ResponseParser(self.response) return Subscription.from_dict(parser.get_data()) @@ -100,7 +100,7 @@ def create_one_time_charge(self, subscription_id: str, operation: CreateOneTimeC return Subscription.from_dict(parser.get_data()) def preview_update(self, subscription_id: str, operation: PreviewUpdateSubscription) -> SubscriptionPreview: - self.response = self.client.patch_raw(f"/subscriptions/{subscription_id}/preview", operation.get_parameters()) + self.response = self.client.patch_raw(f"/subscriptions/{subscription_id}/preview", operation) parser = ResponseParser(self.response) return SubscriptionPreview.from_dict(parser.get_data()) diff --git a/paddle_billing/Resources/Transactions/Operations/Create/CreateBillingDetails.py b/paddle_billing/Resources/Transactions/Operations/Create/CreateBillingDetails.py new file mode 100644 index 00000000..e446760a --- /dev/null +++ b/paddle_billing/Resources/Transactions/Operations/Create/CreateBillingDetails.py @@ -0,0 +1,13 @@ +from __future__ import annotations +from dataclasses import dataclass + +from paddle_billing.Entities.Shared.Duration import Duration +from paddle_billing.Undefined import Undefined + + +@dataclass +class CreateBillingDetails: + payment_terms: Duration + enable_checkout: bool | Undefined = Undefined() + purchase_order_number: str | Undefined = Undefined() + additional_information: str | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/Create/__init__.py b/paddle_billing/Resources/Transactions/Operations/Create/__init__.py new file mode 100644 index 00000000..d379e8a1 --- /dev/null +++ b/paddle_billing/Resources/Transactions/Operations/Create/__init__.py @@ -0,0 +1 @@ +from paddle_billing.Resources.Transactions.Operations.Create.CreateBillingDetails import CreateBillingDetails diff --git a/paddle_billing/Resources/Transactions/Operations/CreateTransaction.py b/paddle_billing/Resources/Transactions/Operations/CreateTransaction.py index 37ad0c54..9a996053 100644 --- a/paddle_billing/Resources/Transactions/Operations/CreateTransaction.py +++ b/paddle_billing/Resources/Transactions/Operations/CreateTransaction.py @@ -1,8 +1,8 @@ -from dataclasses import asdict, dataclass +from dataclasses import dataclass +from paddle_billing.Operation import Operation from paddle_billing.Undefined import Undefined from paddle_billing.Entities.Shared import ( - BillingDetails, Checkout, CollectionMode, CurrencyCode, @@ -11,11 +11,12 @@ TransactionStatus, ) from paddle_billing.Entities.Transactions import TransactionCreateItem, TransactionCreateItemWithPrice +from paddle_billing.Resources.Transactions.Operations.Create import CreateBillingDetails @dataclass -class CreateTransaction: - items: list[TransactionCreateItem | TransactionCreateItemWithPrice] = Undefined() +class CreateTransaction(Operation): + items: list[TransactionCreateItem | TransactionCreateItemWithPrice] status: TransactionStatus | Undefined = Undefined() customer_id: str | None | Undefined = Undefined() address_id: str | None | Undefined = Undefined() @@ -24,9 +25,6 @@ class CreateTransaction: currency_code: CurrencyCode | Undefined = Undefined() collection_mode: CollectionMode | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() - billing_details: BillingDetails | None | Undefined = Undefined() + billing_details: CreateBillingDetails | None | Undefined = Undefined() billing_period: TimePeriod | None | Undefined = Undefined() checkout: Checkout | None | Undefined = Undefined() - - def get_parameters(self) -> dict: - return asdict(self) diff --git a/paddle_billing/Resources/Transactions/Operations/Update/UpdateBillingDetails.py b/paddle_billing/Resources/Transactions/Operations/Update/UpdateBillingDetails.py new file mode 100644 index 00000000..d09bf523 --- /dev/null +++ b/paddle_billing/Resources/Transactions/Operations/Update/UpdateBillingDetails.py @@ -0,0 +1,13 @@ +from __future__ import annotations +from dataclasses import dataclass + +from paddle_billing.Entities.Shared.Duration import Duration +from paddle_billing.Undefined import Undefined + + +@dataclass +class UpdateBillingDetails: + payment_terms: Duration | Undefined = Undefined() + enable_checkout: bool | Undefined = Undefined() + purchase_order_number: str | Undefined = Undefined() + additional_information: str | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/Update/__init__.py b/paddle_billing/Resources/Transactions/Operations/Update/__init__.py new file mode 100644 index 00000000..37aec216 --- /dev/null +++ b/paddle_billing/Resources/Transactions/Operations/Update/__init__.py @@ -0,0 +1 @@ +from paddle_billing.Resources.Transactions.Operations.Update.UpdateBillingDetails import UpdateBillingDetails diff --git a/paddle_billing/Resources/Transactions/Operations/UpdateTransaction.py b/paddle_billing/Resources/Transactions/Operations/UpdateTransaction.py index 493ee590..bd8dbc60 100644 --- a/paddle_billing/Resources/Transactions/Operations/UpdateTransaction.py +++ b/paddle_billing/Resources/Transactions/Operations/UpdateTransaction.py @@ -1,8 +1,8 @@ -from dataclasses import asdict, dataclass +from dataclasses import dataclass +from paddle_billing.Operation import Operation from paddle_billing.Undefined import Undefined from paddle_billing.Entities.Shared import ( - BillingDetails, Checkout, CollectionMode, CurrencyCode, @@ -11,10 +11,11 @@ TransactionStatus, ) from paddle_billing.Entities.Transactions import TransactionCreateItem, TransactionCreateItemWithPrice +from paddle_billing.Resources.Transactions.Operations.Update import UpdateBillingDetails @dataclass -class UpdateTransaction: +class UpdateTransaction(Operation): items: list[TransactionCreateItem | TransactionCreateItemWithPrice] = Undefined() status: TransactionStatus | Undefined = Undefined() customer_id: str | None | Undefined = Undefined() @@ -24,9 +25,6 @@ class UpdateTransaction: currency_code: CurrencyCode | Undefined = Undefined() collection_mode: CollectionMode | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() - billing_details: BillingDetails | None | Undefined = Undefined() + billing_details: UpdateBillingDetails | None | Undefined = Undefined() billing_period: TimePeriod | None | Undefined = Undefined() checkout: Checkout | None | Undefined = Undefined() - - def get_parameters(self) -> dict: - return asdict(self) diff --git a/paddle_billing/Resources/Transactions/TransactionsClient.py b/paddle_billing/Resources/Transactions/TransactionsClient.py index 7d792ad9..7af36612 100644 --- a/paddle_billing/Resources/Transactions/TransactionsClient.py +++ b/paddle_billing/Resources/Transactions/TransactionsClient.py @@ -67,13 +67,13 @@ def create(self, operation: CreateTransaction, includes=None) -> Transaction: ) params = {"include": ",".join(include.value for include in includes)} if includes else {} - self.response = self.client.post_raw("/transactions", operation.get_parameters(), params) + self.response = self.client.post_raw("/transactions", operation, params) parser = ResponseParser(self.response) return Transaction.from_dict(parser.get_data()) def update(self, transaction_id: str, operation: UpdateTransaction) -> Transaction: - self.response = self.client.patch_raw(f"/transactions/{transaction_id}", operation.get_parameters()) + self.response = self.client.patch_raw(f"/transactions/{transaction_id}", operation) parser = ResponseParser(self.response) return Transaction.from_dict(parser.get_data()) diff --git a/tests/Functional/Resources/Subscriptions/_fixtures/request/preview_update_full_billing_details.json b/tests/Functional/Resources/Subscriptions/_fixtures/request/preview_update_full_billing_details.json new file mode 100644 index 00000000..e310fdef --- /dev/null +++ b/tests/Functional/Resources/Subscriptions/_fixtures/request/preview_update_full_billing_details.json @@ -0,0 +1,11 @@ +{ + "billing_details": { + "additional_information": "Some additional information", + "enable_checkout": true, + "purchase_order_number": "10009", + "payment_terms": { + "interval": "month", + "frequency": 1 + } + } +} diff --git a/tests/Functional/Resources/Subscriptions/_fixtures/request/preview_update_minimal_billing_details.json b/tests/Functional/Resources/Subscriptions/_fixtures/request/preview_update_minimal_billing_details.json new file mode 100644 index 00000000..d3cc90c5 --- /dev/null +++ b/tests/Functional/Resources/Subscriptions/_fixtures/request/preview_update_minimal_billing_details.json @@ -0,0 +1,3 @@ +{ + "billing_details": {} +} diff --git a/tests/Functional/Resources/Subscriptions/_fixtures/request/update_full_billing_details.json b/tests/Functional/Resources/Subscriptions/_fixtures/request/update_full_billing_details.json new file mode 100644 index 00000000..e310fdef --- /dev/null +++ b/tests/Functional/Resources/Subscriptions/_fixtures/request/update_full_billing_details.json @@ -0,0 +1,11 @@ +{ + "billing_details": { + "additional_information": "Some additional information", + "enable_checkout": true, + "purchase_order_number": "10009", + "payment_terms": { + "interval": "month", + "frequency": 1 + } + } +} diff --git a/tests/Functional/Resources/Subscriptions/_fixtures/request/update_minimal_billing_details.json b/tests/Functional/Resources/Subscriptions/_fixtures/request/update_minimal_billing_details.json new file mode 100644 index 00000000..d3cc90c5 --- /dev/null +++ b/tests/Functional/Resources/Subscriptions/_fixtures/request/update_minimal_billing_details.json @@ -0,0 +1,3 @@ +{ + "billing_details": {} +} diff --git a/tests/Functional/Resources/Subscriptions/test_SubscriptionsClient.py b/tests/Functional/Resources/Subscriptions/test_SubscriptionsClient.py index 216526c6..892032a6 100644 --- a/tests/Functional/Resources/Subscriptions/test_SubscriptionsClient.py +++ b/tests/Functional/Resources/Subscriptions/test_SubscriptionsClient.py @@ -52,6 +52,8 @@ UpdateSubscription, ) +from paddle_billing.Resources.Subscriptions.Operations.Update import UpdateBillingDetails + from tests.Utils.ReadsFixture import ReadsFixtures @@ -142,11 +144,36 @@ class TestSubscriptionsClient: ReadsFixtures.read_raw_json_fixture("response/full_entity"), "/subscriptions/sub_01h8bx8fmywym11t6swgzba704", ), + ( + "sub_01h8bx8fmywym11t6swgzba704", + UpdateSubscription(billing_details=UpdateBillingDetails()), + ReadsFixtures.read_raw_json_fixture("request/update_minimal_billing_details"), + 200, + ReadsFixtures.read_raw_json_fixture("response/full_entity"), + "/subscriptions/sub_01h8bx8fmywym11t6swgzba704", + ), + ( + "sub_01h8bx8fmywym11t6swgzba704", + UpdateSubscription( + billing_details=UpdateBillingDetails( + enable_checkout=True, + payment_terms=Duration(interval=Interval.Month, frequency=1), + purchase_order_number="10009", + additional_information="Some additional information", + ), + ), + ReadsFixtures.read_raw_json_fixture("request/update_full_billing_details"), + 200, + ReadsFixtures.read_raw_json_fixture("response/full_entity"), + "/subscriptions/sub_01h8bx8fmywym11t6swgzba704", + ), ], ids=[ "Update subscription with a single new value", "Update subscription with partial new values", "Update subscription with all new values", + "Update subscription with minimal billing details", + "Update subscription with full billing details", ], ) def test_update_subscription_uses_expected_payload( @@ -902,11 +929,38 @@ def test_create_subscription_one_time_charge_uses_expected_payload( ReadsFixtures.read_raw_json_fixture("response/preview_update_full_entity"), "/subscriptions/sub_01h8bx8fmywym11t6swgzba704/preview", ), + ( + "sub_01h8bx8fmywym11t6swgzba704", + PreviewUpdateSubscription( + billing_details=UpdateBillingDetails(), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_update_minimal_billing_details"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_update_full_entity"), + "/subscriptions/sub_01h8bx8fmywym11t6swgzba704/preview", + ), + ( + "sub_01h8bx8fmywym11t6swgzba704", + PreviewUpdateSubscription( + billing_details=UpdateBillingDetails( + enable_checkout=True, + payment_terms=Duration(interval=Interval.Month, frequency=1), + purchase_order_number="10009", + additional_information="Some additional information", + ), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_update_full_billing_details"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_update_full_entity"), + "/subscriptions/sub_01h8bx8fmywym11t6swgzba704/preview", + ), ], ids=[ "Preview updating a subscription with a single new value", "Preview updating a subscription with partial new values", "Preview updating a subscription with all new values", + "Preview updating a subscription with minimal billing details", + "Preview updating a subscription with full billing details", ], ) def test_preview_update_subscription_uses_expected_payload( diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/create_full_billing_details.json b/tests/Functional/Resources/Transactions/_fixtures/request/create_full_billing_details.json new file mode 100644 index 00000000..5262ec59 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/create_full_billing_details.json @@ -0,0 +1,17 @@ +{ + "items": [ + { + "quantity": 1, + "price_id": "pri_01gsz8x8sawmvhz1pv30nge1ke" + } + ], + "billing_details": { + "additional_information": "Some additional information", + "enable_checkout": true, + "purchase_order_number": "10009", + "payment_terms": { + "interval": "month", + "frequency": 1 + } + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/create_manual.json b/tests/Functional/Resources/Transactions/_fixtures/request/create_manual.json index 40b4d5fe..47f3180f 100644 --- a/tests/Functional/Resources/Transactions/_fixtures/request/create_manual.json +++ b/tests/Functional/Resources/Transactions/_fixtures/request/create_manual.json @@ -13,7 +13,6 @@ "collection_mode": "manual", "discount_id": "dsc_01hen7bjzh12m0v2peer15d9qt", "billing_details": { - "additional_information": null, "enable_checkout": true, "purchase_order_number": "10009", "payment_terms": { diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/create_minimal_billing_details.json b/tests/Functional/Resources/Transactions/_fixtures/request/create_minimal_billing_details.json new file mode 100644 index 00000000..3570093d --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/create_minimal_billing_details.json @@ -0,0 +1,14 @@ +{ + "items": [ + { + "quantity": 1, + "price_id": "pri_01gsz8x8sawmvhz1pv30nge1ke" + } + ], + "billing_details": { + "payment_terms": { + "interval": "month", + "frequency": 1 + } + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/update_full_billing_details.json b/tests/Functional/Resources/Transactions/_fixtures/request/update_full_billing_details.json new file mode 100644 index 00000000..e310fdef --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/update_full_billing_details.json @@ -0,0 +1,11 @@ +{ + "billing_details": { + "additional_information": "Some additional information", + "enable_checkout": true, + "purchase_order_number": "10009", + "payment_terms": { + "interval": "month", + "frequency": 1 + } + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/update_minimal_billing_details.json b/tests/Functional/Resources/Transactions/_fixtures/request/update_minimal_billing_details.json new file mode 100644 index 00000000..66d67358 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/update_minimal_billing_details.json @@ -0,0 +1,8 @@ +{ + "billing_details": { + "payment_terms": { + "interval": "month", + "frequency": 1 + } + } +} diff --git a/tests/Functional/Resources/Transactions/test_TransactionsClient.py b/tests/Functional/Resources/Transactions/test_TransactionsClient.py index fb6de294..ae4f9c02 100644 --- a/tests/Functional/Resources/Transactions/test_TransactionsClient.py +++ b/tests/Functional/Resources/Transactions/test_TransactionsClient.py @@ -12,7 +12,6 @@ from paddle_billing.Entities.Shared import ( AddressPreview, - BillingDetails, CollectionMode, CountryCode, CurrencyCode, @@ -50,6 +49,9 @@ GetTransactionInvoice, ) +from paddle_billing.Resources.Transactions.Operations.Create import CreateBillingDetails +from paddle_billing.Resources.Transactions.Operations.Update import UpdateBillingDetails + from tests.Utils.ReadsFixture import ReadsFixtures @@ -156,7 +158,7 @@ def test_list_transaction_can_paginate(self, test_client, mock_requests, operati currency_code=CurrencyCode.GBP, collection_mode=CollectionMode.Manual, discount_id="dsc_01hen7bjzh12m0v2peer15d9qt", - billing_details=BillingDetails( + billing_details=CreateBillingDetails( enable_checkout=True, payment_terms=Duration(interval=Interval.Month, frequency=1), purchase_order_number="10009", @@ -167,11 +169,40 @@ def test_list_transaction_can_paginate(self, test_client, mock_requests, operati ReadsFixtures.read_raw_json_fixture("response/full_entity"), "/transactions", ), + ( + CreateTransaction( + items=[TransactionCreateItem(price_id="pri_01gsz8x8sawmvhz1pv30nge1ke", quantity=1)], + billing_details=CreateBillingDetails( + payment_terms=Duration(interval=Interval.Month, frequency=1), + ), + ), + ReadsFixtures.read_raw_json_fixture("request/create_minimal_billing_details"), + 200, + ReadsFixtures.read_raw_json_fixture("response/full_entity"), + "/transactions", + ), + ( + CreateTransaction( + items=[TransactionCreateItem(price_id="pri_01gsz8x8sawmvhz1pv30nge1ke", quantity=1)], + billing_details=CreateBillingDetails( + enable_checkout=True, + payment_terms=Duration(interval=Interval.Month, frequency=1), + purchase_order_number="10009", + additional_information="Some additional information", + ), + ), + ReadsFixtures.read_raw_json_fixture("request/create_full_billing_details"), + 200, + ReadsFixtures.read_raw_json_fixture("response/full_entity"), + "/transactions", + ), ], ids=[ "Create transaction with basic data", "Create transaction with non-catalog price", "Create transaction with manual collection mode", + "Create transaction with minimal billing details", + "Create transaction with full billing details", ], ) def test_create_transaction_uses_expected_payload( @@ -266,10 +297,39 @@ def test_create_transaction_with_includes_returns_expected_payload( ReadsFixtures.read_raw_json_fixture("response/full_entity"), "/transactions/txn_01h7zcgmdc6tmwtjehp3sh7azf", ), + ( + "txn_01h7zcgmdc6tmwtjehp3sh7azf", + UpdateTransaction( + billing_details=UpdateBillingDetails( + payment_terms=Duration(interval=Interval.Month, frequency=1), + ), + ), + ReadsFixtures.read_raw_json_fixture("request/update_minimal_billing_details"), + 200, + ReadsFixtures.read_raw_json_fixture("response/full_entity"), + "/transactions/txn_01h7zcgmdc6tmwtjehp3sh7azf", + ), + ( + "txn_01h7zcgmdc6tmwtjehp3sh7azf", + UpdateTransaction( + billing_details=UpdateBillingDetails( + enable_checkout=True, + payment_terms=Duration(interval=Interval.Month, frequency=1), + purchase_order_number="10009", + additional_information="Some additional information", + ), + ), + ReadsFixtures.read_raw_json_fixture("request/update_full_billing_details"), + 200, + ReadsFixtures.read_raw_json_fixture("response/full_entity"), + "/transactions/txn_01h7zcgmdc6tmwtjehp3sh7azf", + ), ], ids=[ "Update transaction with single new value", "Update transaction with partial new values", + "Create transaction with minimal billing details", + "Create transaction with full billing details", ], ) def test_update_transaction_uses_expected_payload(