Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for adjustment type and VND currency #83

Merged
merged 2 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ 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.3.0 - 2024-12-17

### Added

- Support for adjustment type, see [related changelog](https://developer.paddle.com/changelog/2024/refund-credit-full-total?utm_source=dx&utm_medium=paddle-python-sdk)
- Added Vietnamese Dong (`VND`) as a supported currency for payments [related changelog](https://developer.paddle.com/changelog/2024/vietnamese-dong-vnd-supported-currency?utm_source=dx&utm_medium=paddle-python-sdk)

## 1.2.2 - 2024-12-17

### Fixed
Expand Down
69 changes: 69 additions & 0 deletions examples/adjustments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from logging import getLogger
from os import getenv
from sys import exit # You should use classes/functions that returns instead of exits

from paddle_billing import Client, Environment, Options

from paddle_billing.Entities.Shared import (
Action,
AdjustmentType,
)

from paddle_billing.Exceptions.ApiError import ApiError

from paddle_billing.Resources.Adjustments.Operations import CreateAdjustment, CreateAdjustmentItem

log = getLogger("my_app")

# Verify your Paddle API key was provided by a PADDLE_SECRET_API_KEY environment variable
# It is strongly advised that you do not include secrets in your source code
# Use environment variables, and/or secrets management like Vault to obtain your secrets
api_key: str = getenv("PADDLE_SECRET_API_KEY", None)
if not api_key:
raise ValueError("You must provide the PADDLE_SECRET_API_KEY in your environment variables")

transaction_id: str = getenv("PADDLE_TRANSACTION_ID", None)
transaction_item_id: str = getenv("PADDLE_TRANSACTION_ITEM_ID", None)
full_adjustment_transaction_id: str = getenv("PADDLE_FULL_ADJUSTMENT_TRANSACTION_ID", None)

# Determine the environment, defaulting to sandbox
environment = Environment(getenv("PADDLE_ENVIRONMENT", "sandbox"))

# Initialize the Paddle client
paddle = Client(api_key, options=Options(environment), logger=log)


# ┌───
# │ Create Partial Adjustment │
# └───────────────────────────┘
try:
partial_adjustment = paddle.adjustments.create(
CreateAdjustment.partial(
Action.Refund,
[CreateAdjustmentItem(transaction_item_id, AdjustmentType.Partial, "100")],
"error",
transaction_id,
)
)
except ApiError as error:
print(error)
exit(1)

print(f"Partial Adjustment '{partial_adjustment.id}'")

# ┌───
# │ Create Full Adjustment │
# └────────────────────────┘
try:
full_adjustment = paddle.adjustments.create(
CreateAdjustment.full(
Action.Refund,
"error",
full_adjustment_transaction_id,
)
)
except ApiError as error:
print(error)
exit(1)

print(f"Full Adjustment '{full_adjustment.id}'")
2 changes: 1 addition & 1 deletion paddle_billing/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,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.2.2",
"User-Agent": "PaddleSDK/python 1.3.0",
}
)

Expand Down
3 changes: 3 additions & 0 deletions paddle_billing/Entities/Adjustment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from paddle_billing.Entities.Adjustments import AdjustmentItem, AdjustmentTaxRatesUsed
from paddle_billing.Entities.Shared import (
Action,
AdjustmentActionType,
AdjustmentStatus,
CurrencyCode,
PayoutTotalsAdjustment,
Expand All @@ -30,6 +31,7 @@ class Adjustment(Entity):
created_at: datetime
updated_at: datetime | None
tax_rates_used: list[AdjustmentTaxRatesUsed]
type: AdjustmentActionType

@staticmethod
def from_dict(data: dict) -> Adjustment:
Expand All @@ -51,4 +53,5 @@ def from_dict(data: dict) -> Adjustment:
),
updated_at=datetime.fromisoformat(data["updated_at"]) if data.get("updated_at") else None,
tax_rates_used=[AdjustmentTaxRatesUsed.from_dict(item) for item in data["tax_rates_used"]],
type=AdjustmentStatus(data["type"]),
)
6 changes: 6 additions & 0 deletions paddle_billing/Entities/Shared/AdjustmentActionType.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from paddle_billing.PaddleStrEnum import PaddleStrEnum, PaddleStrEnumMeta


class AdjustmentActionType(PaddleStrEnum, metaclass=PaddleStrEnumMeta):
Full: "AdjustmentActionType" = "full"
Partial: "AdjustmentActionType" = "partial"
1 change: 1 addition & 0 deletions paddle_billing/Entities/Shared/CurrencyCode.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ class CurrencyCode(PaddleStrEnum, metaclass=PaddleStrEnumMeta):
TRY: "CurrencyCode" = "TRY"
TWD: "CurrencyCode" = "TWD"
UAH: "CurrencyCode" = "UAH"
VND: "CurrencyCode" = "VND"
ZAR: "CurrencyCode" = "ZAR"
1 change: 1 addition & 0 deletions paddle_billing/Entities/Shared/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from paddle_billing.Entities.Shared.Action import Action
from paddle_billing.Entities.Shared.AddressPreview import AddressPreview
from paddle_billing.Entities.Shared.AdjustmentActionType import AdjustmentActionType
from paddle_billing.Entities.Shared.AdjustmentItemTotals import AdjustmentItemTotals
from paddle_billing.Entities.Shared.AdjustmentStatus import AdjustmentStatus
from paddle_billing.Entities.Shared.AdjustmentTotals import AdjustmentTotals
Expand Down
3 changes: 3 additions & 0 deletions paddle_billing/Notifications/Entities/Adjustment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from paddle_billing.Notifications.Entities.Entity import Entity
from paddle_billing.Notifications.Entities.Shared import (
Action,
AdjustmentActionType,
AdjustmentStatus,
CurrencyCode,
PayoutTotalsAdjustment,
Expand All @@ -30,6 +31,7 @@ class Adjustment(Entity):
created_at: datetime
updated_at: datetime | None
tax_rates_used: list[AdjustmentTaxRatesUsed] | None
type: AdjustmentActionType | None

@staticmethod
def from_dict(data: dict) -> Adjustment:
Expand All @@ -55,4 +57,5 @@ def from_dict(data: dict) -> Adjustment:
if data.get("tax_rates_used")
else None
),
type=AdjustmentActionType(data["type"]) if data.get("type") else None,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from paddle_billing.PaddleStrEnum import PaddleStrEnum, PaddleStrEnumMeta


class AdjustmentActionType(PaddleStrEnum, metaclass=PaddleStrEnumMeta):
Full: "AdjustmentActionType" = "full"
Partial: "AdjustmentActionType" = "partial"
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ class CurrencyCode(PaddleStrEnum, metaclass=PaddleStrEnumMeta):
TRY: "CurrencyCode" = "TRY"
TWD: "CurrencyCode" = "TWD"
UAH: "CurrencyCode" = "UAH"
VND: "CurrencyCode" = "VND"
ZAR: "CurrencyCode" = "ZAR"
1 change: 1 addition & 0 deletions paddle_billing/Notifications/Entities/Shared/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from paddle_billing.Notifications.Entities.Shared.Action import Action
from paddle_billing.Notifications.Entities.Shared.AddressPreview import AddressPreview
from paddle_billing.Notifications.Entities.Shared.AdjustmentActionType import AdjustmentActionType
from paddle_billing.Notifications.Entities.Shared.AdjustmentItemTotals import AdjustmentItemTotals
from paddle_billing.Notifications.Entities.Shared.AdjustmentStatus import AdjustmentStatus
from paddle_billing.Notifications.Entities.Shared.AdjustmentTotals import AdjustmentTotals
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,58 @@
from dataclasses import dataclass

from paddle_billing.Undefined import Undefined

from paddle_billing.Operation import Operation

from paddle_billing.Entities.Shared import Action
from paddle_billing.Entities.Shared import Action, AdjustmentActionType

from paddle_billing.Resources.Adjustments.Operations import CreateAdjustmentItem

from paddle_billing.Exceptions.SdkExceptions.InvalidArgumentException import InvalidArgumentException


@dataclass
class CreateAdjustment(Operation):
action: Action
items: list[CreateAdjustmentItem]
items: list[CreateAdjustmentItem] | None | Undefined
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

items cannot be set to Undefined() by default without moving the argument, which would be a breaking change.

CreateAdjustment.full() and CreateAdjustment.partial() have been introduced to make it easier to create this operation, e.g.

CreateAdjustment.partial(
    Action.Refund,
    [CreateAdjustmentItem("txnitm_01h8bxryv3065dyh6103p3yg28", AdjustmentType.Partial, "100")],
    "error",
    "txn_01h8bxpvx398a7zbawb77y0kp5",
)
CreateAdjustment.full(
    Action.Refund,
    "error",
    "txn_01h8bxpvx398a7zbawb77y0kp5",
)

The constructor can still be used, but items must always be provided, e.g.

CreateAdjustment(
    Action.Refund,
    None,
    "error",
    "txn_01h8bxpvx398a7zbawb77y0kp5",
    AdjustmentActionType.Full,
)
CreateAdjustment(
    Action.Refund,
    Undefined(),
    "error",
    "txn_01h8bxpvx398a7zbawb77y0kp5",
    AdjustmentActionType.Full,
)

reason: str
transaction_id: str
type: AdjustmentActionType | Undefined = Undefined()

def __post_init__(self):
if self.type != AdjustmentActionType.Full and (
self.items is None or isinstance(self.items, Undefined) or len(self.items) == 0
):
raise InvalidArgumentException.array_is_empty("items")

if self.type == AdjustmentActionType.Full and isinstance(self.items, list):
raise InvalidArgumentException("items are not allowed when the adjustment type is full")

@staticmethod
def full(
action: Action,
reason: str,
transaction_id: str,
) -> "CreateAdjustment":
return CreateAdjustment(
action=action,
reason=reason,
transaction_id=transaction_id,
items=Undefined(),
type=AdjustmentActionType.Full,
)

@staticmethod
def partial(
action: Action,
items: list[CreateAdjustmentItem],
reason: str,
transaction_id: str,
) -> "CreateAdjustment":
return CreateAdjustment(
action=action,
reason=reason,
transaction_id=transaction_id,
items=items,
type=AdjustmentActionType.Partial,
)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


setup(
version="1.2.2",
version="1.3.0",
author="Paddle and contributors",
author_email="[email protected]",
description="Paddle's Python SDK for Paddle Billing",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"action": "refund",
"type": "full",
"reason": "error",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"action": "refund",
"type": "full",
"items": null,
"reason": "error",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"action": "refund",
"type": "partial",
"items": [
{
"item_id": "txnitm_01h8bxryv3065dyh6103p3yg28",
"type": "partial",
"amount": "100"
}
],
"reason": "error",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"data": {
"id": "adj_01h8c65c2ggq5nxswnnwv78e75",
"action": "refund",
"type": "partial",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5",
"subscription_id": "sub_01h8bxswamxysj44zt5n48njwh",
"customer_id": "ctm_01h8441jn5pcwrfhwh78jqt8hk",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
{
"id": "adj_01h8c65c2ggq5nxswnnwv78e75",
"action": "refund",
"type": "partial",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5",
"subscription_id": "sub_01h8bxswamxysj44zt5n48njwh",
"customer_id": "ctm_01h8441jn5pcwrfhwh78jqt8hk",
Expand Down Expand Up @@ -55,6 +56,7 @@
{
"id": "adj_01h8bxezh16gm6t8rx21dx271b",
"action": "credit",
"type": "partial",
"credit_applied_to_balance": true,
"transaction_id": "txn_01h8bx69629a16wwm9z8rjmak3",
"subscription_id": "sub_01h8bx8fmywym11t6swgzba704",
Expand Down Expand Up @@ -114,6 +116,7 @@
{
"id": "adj_01h7jgzjqt2s8sab70e03ptkhv",
"action": "credit",
"type": "partial",
"credit_applied_to_balance": true,
"transaction_id": "txn_01h7jgd9bkwjscj3ae15g5d3vs",
"subscription_id": "sub_01h7ht5z5wdg9pz18jx1fagp8k",
Expand Down Expand Up @@ -173,6 +176,7 @@
{
"id": "adj_01h7jf6ptkfsc93hzc20fgf8wy",
"action": "credit",
"type": "partial",
"credit_applied_to_balance": true,
"transaction_id": "txn_01h7je77vc1qmzxntem45ebb5q",
"subscription_id": "sub_01h7ht5z5wdg9pz18jx1fagp8k",
Expand Down Expand Up @@ -232,6 +236,7 @@
{
"id": "adj_01h468w41ttb2j2bh8av74gwt1",
"action": "credit",
"type": "partial",
"credit_applied_to_balance": true,
"transaction_id": "txn_01h468crc3b3fe98a5ft53recb",
"subscription_id": "sub_01h468kv3jhs5jk330gszncsgt",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"data": {
"id": "adj_01h8c65c2ggq5nxswnnwv78e75",
"action": "refund",
"type": "partial",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5",
"subscription_id": "sub_01h8bxswamxysj44zt5n48njwh",
"customer_id": "ctm_01h8441jn5pcwrfhwh78jqt8hk",
Expand Down
Loading
Loading