Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx
### Added

- Added support for `wechat_pay` payment method type. See [related changelog](https://developer.paddle.com/changelog/2025/wechat-pay-payment-method?utm_source=dx&utm_medium=paddle-python-sdk)
- Added `api_key_exposure.created` event support. See [related changelog](https://developer.paddle.com/changelog/2025/secret-scanning?utm_source=dx&utm_medium=paddle-python-sdk)
- Added `grand_total_tax` to transaction totals and adjusted totals. See [related changelog](https://developer.paddle.com/changelog/2026/grand-total-tax-field?utm_source=dx&utm_medium=paddle-python-sdk)

## 1.12.0 - 2025-11-12
Expand Down
1 change: 1 addition & 0 deletions paddle_billing/Entities/Events/EventTypeName.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class EventTypeName(PaddleStrEnum, metaclass=PaddleStrEnumMeta):
ApiKeyCreated: "EventTypeName" = "api_key.created"
ApiKeyExpired: "EventTypeName" = "api_key.expired"
ApiKeyExpiring: "EventTypeName" = "api_key.expiring"
ApiKeyExposureCreated: "EventTypeName" = "api_key_exposure.created"
ApiKeyRevoked: "EventTypeName" = "api_key.revoked"
ApiKeyUpdated: "EventTypeName" = "api_key.updated"
BusinessCreated: "EventTypeName" = "business.created"
Expand Down
36 changes: 36 additions & 0 deletions paddle_billing/Notifications/Entities/ApiKeyExposure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Any

from paddle_billing.Notifications.Entities.Entity import Entity
from paddle_billing.Notifications.Entities.ApiKeyExposures import (
ApiKeyExposureActionTaken,
ApiKeyExposureRiskLevel,
ApiKeyExposureSource,
)


@dataclass
class ApiKeyExposure(Entity):
id: str
api_key_id: str
risk_level: ApiKeyExposureRiskLevel
action_taken: ApiKeyExposureActionTaken
source: ApiKeyExposureSource
reference: str
description: str | None
created_at: datetime

@staticmethod
def from_dict(data: dict[str, Any]) -> ApiKeyExposure:
return ApiKeyExposure(
id=data["id"],
api_key_id=data["api_key_id"],
risk_level=ApiKeyExposureRiskLevel(data["risk_level"]),
action_taken=ApiKeyExposureActionTaken(data["action_taken"]),
source=ApiKeyExposureSource(data["source"]),
reference=data["reference"],
description=data.get("description"),
created_at=datetime.fromisoformat(data["created_at"]),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from paddle_billing.PaddleStrEnum import PaddleStrEnum, PaddleStrEnumMeta


class ApiKeyExposureActionTaken(PaddleStrEnum, metaclass=PaddleStrEnumMeta):
Revoked: "ApiKeyExposureActionTaken" = "revoked"
None_: "ApiKeyExposureActionTaken" = "none"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from paddle_billing.PaddleStrEnum import PaddleStrEnum, PaddleStrEnumMeta


class ApiKeyExposureRiskLevel(PaddleStrEnum, metaclass=PaddleStrEnumMeta):
High: "ApiKeyExposureRiskLevel" = "high"
Low: "ApiKeyExposureRiskLevel" = "low"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from paddle_billing.PaddleStrEnum import PaddleStrEnum, PaddleStrEnumMeta


class ApiKeyExposureSource(PaddleStrEnum, metaclass=PaddleStrEnumMeta):
Github: "ApiKeyExposureSource" = "github"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from paddle_billing.Notifications.Entities.ApiKeyExposures.ApiKeyExposureActionTaken import ApiKeyExposureActionTaken
from paddle_billing.Notifications.Entities.ApiKeyExposures.ApiKeyExposureRiskLevel import ApiKeyExposureRiskLevel
from paddle_billing.Notifications.Entities.ApiKeyExposures.ApiKeyExposureSource import ApiKeyExposureSource
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Any

from paddle_billing.Notifications.Entities.ApiKeyExposures import (
ApiKeyExposureActionTaken,
ApiKeyExposureRiskLevel,
ApiKeyExposureSource,
)
from paddle_billing.Notifications.Entities.Simulations.SimulationEntity import SimulationEntity
from paddle_billing.Undefined import Undefined


@dataclass
class ApiKeyExposure(SimulationEntity):
id: str | Undefined = Undefined()
api_key_id: str | Undefined = Undefined()
risk_level: ApiKeyExposureRiskLevel | Undefined = Undefined()
action_taken: ApiKeyExposureActionTaken | Undefined = Undefined()
source: ApiKeyExposureSource | Undefined = Undefined()
reference: str | Undefined = Undefined()
description: str | None | Undefined = Undefined()
created_at: datetime | Undefined = Undefined()

@staticmethod
def from_dict(data: dict[str, Any]) -> ApiKeyExposure:
return ApiKeyExposure(
id=data.get("id", Undefined()),
api_key_id=data.get("api_key_id", Undefined()),
risk_level=ApiKeyExposureRiskLevel(data["risk_level"]) if data.get("risk_level") else Undefined(),
action_taken=ApiKeyExposureActionTaken(data["action_taken"]) if data.get("action_taken") else Undefined(),
source=ApiKeyExposureSource(data["source"]) if data.get("source") else Undefined(),
reference=data.get("reference", Undefined()),
description=data.get("description", Undefined()),
created_at=(
datetime.fromisoformat(data["created_at"])
if data.get("created_at")
else data.get("created_at", Undefined())
),
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from paddle_billing.Notifications.Entities.Simulations.Address import Address
from paddle_billing.Notifications.Entities.Simulations.Adjustment import Adjustment
from paddle_billing.Notifications.Entities.Simulations.ApiKeyExposure import ApiKeyExposure
from paddle_billing.Notifications.Entities.Simulations.Business import Business
from paddle_billing.Notifications.Entities.Simulations.ClientToken import ClientToken
from paddle_billing.Notifications.Entities.Simulations.Customer import Customer
Expand Down
17 changes: 17 additions & 0 deletions paddle_billing/Notifications/Events/ApiKeyExposureCreated.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from datetime import datetime

from paddle_billing.Entities.Event import Event
from paddle_billing.Entities.Events import EventTypeName

from paddle_billing.Notifications.Entities.ApiKeyExposure import ApiKeyExposure


class ApiKeyExposureCreated(Event):
def __init__(
self,
event_id: str,
event_type: EventTypeName,
occurred_at: datetime,
data: ApiKeyExposure,
):
super().__init__(event_id, event_type, occurred_at, data)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"id": "apkexp_01jkas9tppn3bhadwpcyag45zd",
"api_key_id": "apikey_01jkdpbhazdpn3wpcya45as9tg",
"risk_level": "high",
"action_taken": "revoked",
"source": "github",
"reference": "https://github.com/octocat/Hello-World/blob/12345600b9cbe38a219f39a9941c9319b600c002/foo/bar.txt",
"description": "Exposed in Commit 12345600b9cbe38a219f39a9941c9319b600c002",
"created_at": "2025-08-19T12:58:38.746382Z"
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ def test_create_simulation_uses_expected_payload(
("api_key.created", "ApiKey"),
("api_key.expired", "ApiKey"),
("api_key.expiring", "ApiKey"),
("api_key_exposure.created", "ApiKeyExposure"),
("api_key.revoked", "ApiKey"),
("api_key.updated", "ApiKey"),
("business.created", "Business"),
Expand Down Expand Up @@ -219,6 +220,7 @@ def test_create_simulation_uses_expected_payload(
"api_key.created",
"api_key.expired",
"api_key.expiring",
"api_key_exposure.created",
"api_key.revoked",
"api_key.updated",
"business.created",
Expand Down
2 changes: 2 additions & 0 deletions tests/Unit/Entities/Notifications/test_NotificationEvent.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class TestNotificationEvent:
("api_key.created", "ApiKey"),
("api_key.expired", "ApiKey"),
("api_key.expiring", "ApiKey"),
("api_key_exposure.created", "ApiKeyExposure"),
("api_key.revoked", "ApiKey"),
("api_key.updated", "ApiKey"),
("business.created", "Business"),
Expand Down Expand Up @@ -78,6 +79,7 @@ class TestNotificationEvent:
"api_key.created",
"api_key.expired",
"api_key.expiring",
"api_key_exposure.created",
"api_key.revoked",
"api_key.updated",
"business.created",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"id": "apkexp_01jkas9tppn3bhadwpcyag45zd",
"api_key_id": "apikey_01jkdpbhazdpn3wpcya45as9tg",
"risk_level": "high",
"action_taken": "revoked",
"source": "github",
"reference": "https://github.com/octocat/Hello-World/blob/12345600b9cbe38a219f39a9941c9319b600c002/foo/bar.txt",
"description": "Exposed in Commit 12345600b9cbe38a219f39a9941c9319b600c002",
"created_at": "2025-08-19T12:58:38.746382Z"
}
2 changes: 2 additions & 0 deletions tests/Unit/Entities/test_Event.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class TestEvent:
("api_key.created", "ApiKey"),
("api_key.expired", "ApiKey"),
("api_key.expiring", "ApiKey"),
("api_key_exposure.created", "ApiKeyExposure"),
("api_key.revoked", "ApiKey"),
("api_key.updated", "ApiKey"),
("business.created", "Business"),
Expand Down Expand Up @@ -85,6 +86,7 @@ class TestEvent:
"api_key.created",
"api_key.expired",
"api_key.expiring",
"api_key_exposure.created",
"api_key.revoked",
"api_key.updated",
"business.created",
Expand Down