Skip to content

Commit

Permalink
fix: Add billing details create/update operations (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgrayston-paddle authored Oct 9, 2024
1 parent 55a9f85 commit 0dee3db
Show file tree
Hide file tree
Showing 30 changed files with 310 additions and 80 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
25 changes: 19 additions & 6 deletions paddle_billing/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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 "{}"

Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions paddle_billing/Entities/DateTime.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ def from_datetime(cls, date: datetime | str):

def __str__(self):
return self.format()

def to_json(self):
return self.format()
21 changes: 0 additions & 21 deletions paddle_billing/Entities/Shared/BillingDetailsUpdate.py

This file was deleted.

1 change: 0 additions & 1 deletion paddle_billing/Entities/Shared/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions paddle_billing/Operation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from abc import ABC
from dataclasses import dataclass


@dataclass
class Operation(ABC):
pass
Original file line number Diff line number Diff line change
@@ -1,41 +1,35 @@
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,
SubscriptionOnPaymentFailure,
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()
currency_code: CurrencyCode | Undefined = Undefined()
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
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from paddle_billing.Resources.Subscriptions.Operations.Update.SubscriptionDiscount import SubscriptionDiscount
from paddle_billing.Resources.Subscriptions.Operations.Update.UpdateBillingDetails import UpdateBillingDetails
Original file line number Diff line number Diff line change
@@ -1,41 +1,35 @@
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,
SubscriptionOnPaymentFailure,
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()
currency_code: CurrencyCode | Undefined = Undefined()
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
4 changes: 2 additions & 2 deletions paddle_billing/Resources/Subscriptions/SubscriptionsClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from paddle_billing.Resources.Transactions.Operations.Create.CreateBillingDetails import CreateBillingDetails
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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()
Expand All @@ -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)
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from paddle_billing.Resources.Transactions.Operations.Update.UpdateBillingDetails import UpdateBillingDetails
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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()
Expand All @@ -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)
4 changes: 2 additions & 2 deletions paddle_billing/Resources/Transactions/TransactionsClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Loading

0 comments on commit 0dee3db

Please sign in to comment.