dodopayments 1.51.1__py3-none-any.whl → 1.56.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. dodopayments/__init__.py +5 -1
  2. dodopayments/_base_client.py +12 -12
  3. dodopayments/_client.py +52 -14
  4. dodopayments/_compat.py +48 -48
  5. dodopayments/_exceptions.py +4 -0
  6. dodopayments/_models.py +50 -44
  7. dodopayments/_qs.py +7 -7
  8. dodopayments/_streaming.py +4 -6
  9. dodopayments/_types.py +18 -11
  10. dodopayments/_utils/__init__.py +8 -2
  11. dodopayments/_utils/_compat.py +45 -0
  12. dodopayments/_utils/_datetime_parse.py +136 -0
  13. dodopayments/_utils/_transform.py +13 -3
  14. dodopayments/_utils/_typing.py +1 -1
  15. dodopayments/_utils/_utils.py +5 -6
  16. dodopayments/_version.py +1 -1
  17. dodopayments/resources/__init__.py +28 -0
  18. dodopayments/resources/addons.py +29 -29
  19. dodopayments/resources/brands.py +29 -29
  20. dodopayments/resources/checkout_sessions.py +110 -27
  21. dodopayments/resources/customers/__init__.py +14 -0
  22. dodopayments/resources/customers/customer_portal.py +5 -5
  23. dodopayments/resources/customers/customers.py +53 -21
  24. dodopayments/resources/customers/wallets/__init__.py +33 -0
  25. dodopayments/resources/customers/wallets/ledger_entries.py +318 -0
  26. dodopayments/resources/customers/wallets/wallets.py +191 -0
  27. dodopayments/resources/discounts.py +44 -44
  28. dodopayments/resources/disputes.py +19 -19
  29. dodopayments/resources/invoices/payments.py +83 -3
  30. dodopayments/resources/license_key_instances.py +13 -13
  31. dodopayments/resources/license_keys.py +23 -23
  32. dodopayments/resources/licenses.py +14 -14
  33. dodopayments/resources/meters.py +554 -0
  34. dodopayments/resources/misc.py +3 -3
  35. dodopayments/resources/payments.py +49 -41
  36. dodopayments/resources/payouts.py +26 -7
  37. dodopayments/resources/products/images.py +5 -5
  38. dodopayments/resources/products/products.py +82 -82
  39. dodopayments/resources/refunds.py +39 -30
  40. dodopayments/resources/subscriptions.py +316 -73
  41. dodopayments/resources/usage_events.py +597 -0
  42. dodopayments/resources/webhooks/headers.py +5 -5
  43. dodopayments/resources/webhooks/webhooks.py +119 -44
  44. dodopayments/types/__init__.py +48 -0
  45. dodopayments/types/add_meter_to_price.py +29 -0
  46. dodopayments/types/add_meter_to_price_param.py +30 -0
  47. dodopayments/types/checkout_session_create_params.py +6 -0
  48. dodopayments/types/checkout_session_status.py +35 -0
  49. dodopayments/types/customer_limited_details.py +5 -0
  50. dodopayments/types/customers/__init__.py +2 -0
  51. dodopayments/types/customers/customer_wallet.py +20 -0
  52. dodopayments/types/customers/wallet_list_response.py +15 -0
  53. dodopayments/types/customers/wallets/__init__.py +7 -0
  54. dodopayments/types/customers/wallets/customer_wallet_transaction.py +38 -0
  55. dodopayments/types/customers/wallets/ledger_entry_create_params.py +25 -0
  56. dodopayments/types/customers/wallets/ledger_entry_list_params.py +18 -0
  57. dodopayments/types/discount_create_params.py +3 -2
  58. dodopayments/types/discount_update_params.py +3 -2
  59. dodopayments/types/dispute_accepted_webhook_event.py +29 -0
  60. dodopayments/types/dispute_accepted_webhook_event1.py +29 -0
  61. dodopayments/types/dispute_cancelled_webhook_event.py +29 -0
  62. dodopayments/types/dispute_cancelled_webhook_event1.py +29 -0
  63. dodopayments/types/dispute_challenged_webhook_event.py +29 -0
  64. dodopayments/types/dispute_challenged_webhook_event1.py +29 -0
  65. dodopayments/types/dispute_expired_webhook_event.py +29 -0
  66. dodopayments/types/dispute_expired_webhook_event1.py +29 -0
  67. dodopayments/types/dispute_lost_webhook_event.py +29 -0
  68. dodopayments/types/dispute_lost_webhook_event1.py +29 -0
  69. dodopayments/types/dispute_opened_webhook_event.py +29 -0
  70. dodopayments/types/dispute_opened_webhook_event1.py +29 -0
  71. dodopayments/types/dispute_won_webhook_event.py +29 -0
  72. dodopayments/types/dispute_won_webhook_event1.py +29 -0
  73. dodopayments/types/event.py +26 -0
  74. dodopayments/types/event_input_param.py +38 -0
  75. dodopayments/types/license_activate_response.py +40 -0
  76. dodopayments/types/license_key_created_webhook_event.py +29 -0
  77. dodopayments/types/license_key_created_webhook_event1.py +29 -0
  78. dodopayments/types/meter.py +40 -0
  79. dodopayments/types/meter_aggregation.py +16 -0
  80. dodopayments/types/meter_aggregation_param.py +16 -0
  81. dodopayments/types/meter_create_params.py +31 -0
  82. dodopayments/types/meter_filter.py +131 -0
  83. dodopayments/types/meter_filter_param.py +143 -0
  84. dodopayments/types/meter_list_params.py +18 -0
  85. dodopayments/types/new_customer_param.py +7 -1
  86. dodopayments/types/payment.py +37 -2
  87. dodopayments/types/payment_cancelled_webhook_event.py +29 -0
  88. dodopayments/types/payment_cancelled_webhook_event1.py +29 -0
  89. dodopayments/types/payment_create_params.py +3 -0
  90. dodopayments/types/payment_failed_webhook_event.py +29 -0
  91. dodopayments/types/payment_failed_webhook_event1.py +29 -0
  92. dodopayments/types/payment_method_types.py +1 -0
  93. dodopayments/types/payment_processing_webhook_event.py +29 -0
  94. dodopayments/types/payment_processing_webhook_event1.py +29 -0
  95. dodopayments/types/payment_succeeded_webhook_event.py +29 -0
  96. dodopayments/types/payment_succeeded_webhook_event1.py +29 -0
  97. dodopayments/types/payout_list_params.py +11 -1
  98. dodopayments/types/price.py +52 -5
  99. dodopayments/types/price_param.py +52 -5
  100. dodopayments/types/product_create_params.py +3 -2
  101. dodopayments/types/product_update_params.py +4 -3
  102. dodopayments/types/refund.py +8 -1
  103. dodopayments/types/refund_create_params.py +4 -1
  104. dodopayments/types/refund_failed_webhook_event.py +29 -0
  105. dodopayments/types/refund_failed_webhook_event1.py +29 -0
  106. dodopayments/types/refund_list_response.py +39 -0
  107. dodopayments/types/refund_succeeded_webhook_event.py +29 -0
  108. dodopayments/types/refund_succeeded_webhook_event1.py +29 -0
  109. dodopayments/types/subscription.py +23 -1
  110. dodopayments/types/subscription_active_webhook_event.py +29 -0
  111. dodopayments/types/subscription_active_webhook_event1.py +29 -0
  112. dodopayments/types/subscription_cancelled_webhook_event.py +29 -0
  113. dodopayments/types/subscription_cancelled_webhook_event1.py +29 -0
  114. dodopayments/types/subscription_charge_params.py +12 -1
  115. dodopayments/types/subscription_create_params.py +3 -0
  116. dodopayments/types/subscription_expired_webhook_event.py +29 -0
  117. dodopayments/types/subscription_expired_webhook_event1.py +29 -0
  118. dodopayments/types/subscription_failed_webhook_event.py +29 -0
  119. dodopayments/types/subscription_failed_webhook_event1.py +29 -0
  120. dodopayments/types/subscription_list_response.py +3 -0
  121. dodopayments/types/subscription_on_hold_webhook_event.py +29 -0
  122. dodopayments/types/subscription_on_hold_webhook_event1.py +29 -0
  123. dodopayments/types/subscription_plan_changed_webhook_event.py +29 -0
  124. dodopayments/types/subscription_plan_changed_webhook_event1.py +29 -0
  125. dodopayments/types/subscription_renewed_webhook_event.py +29 -0
  126. dodopayments/types/subscription_renewed_webhook_event1.py +29 -0
  127. dodopayments/types/subscription_retrieve_usage_history_params.py +28 -0
  128. dodopayments/types/subscription_retrieve_usage_history_response.py +46 -0
  129. dodopayments/types/subscription_update_params.py +2 -0
  130. dodopayments/types/unsafe_unwrap_webhook_event.py +52 -0
  131. dodopayments/types/unwrap_webhook_event.py +52 -0
  132. dodopayments/types/usage_event_ingest_params.py +15 -0
  133. dodopayments/types/usage_event_ingest_response.py +9 -0
  134. dodopayments/types/usage_event_list_params.py +42 -0
  135. {dodopayments-1.51.1.dist-info → dodopayments-1.56.5.dist-info}/METADATA +6 -4
  136. dodopayments-1.56.5.dist-info/RECORD +239 -0
  137. dodopayments-1.51.1.dist-info/RECORD +0 -163
  138. {dodopayments-1.51.1.dist-info → dodopayments-1.56.5.dist-info}/WHEEL +0 -0
  139. {dodopayments-1.51.1.dist-info → dodopayments-1.56.5.dist-info}/licenses/LICENSE +0 -0
dodopayments/__init__.py CHANGED
@@ -3,7 +3,7 @@
3
3
  import typing as _t
4
4
 
5
5
  from . import types
6
- from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes
6
+ from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given
7
7
  from ._utils import file_from_path
8
8
  from ._client import (
9
9
  ENVIRONMENTS,
@@ -35,6 +35,7 @@ from ._exceptions import (
35
35
  InternalServerError,
36
36
  PermissionDeniedError,
37
37
  UnprocessableEntityError,
38
+ APIWebhookValidationError,
38
39
  APIResponseValidationError,
39
40
  )
40
41
  from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
@@ -49,13 +50,16 @@ __all__ = [
49
50
  "ProxiesTypes",
50
51
  "NotGiven",
51
52
  "NOT_GIVEN",
53
+ "not_given",
52
54
  "Omit",
55
+ "omit",
53
56
  "DodoPaymentsError",
54
57
  "APIError",
55
58
  "APIStatusError",
56
59
  "APITimeoutError",
57
60
  "APIConnectionError",
58
61
  "APIResponseValidationError",
62
+ "APIWebhookValidationError",
59
63
  "BadRequestError",
60
64
  "AuthenticationError",
61
65
  "PermissionDeniedError",
@@ -42,7 +42,6 @@ from . import _exceptions
42
42
  from ._qs import Querystring
43
43
  from ._files import to_httpx_files, async_to_httpx_files
44
44
  from ._types import (
45
- NOT_GIVEN,
46
45
  Body,
47
46
  Omit,
48
47
  Query,
@@ -57,9 +56,10 @@ from ._types import (
57
56
  RequestOptions,
58
57
  HttpxRequestFiles,
59
58
  ModelBuilderProtocol,
59
+ not_given,
60
60
  )
61
61
  from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
62
- from ._compat import PYDANTIC_V2, model_copy, model_dump
62
+ from ._compat import PYDANTIC_V1, model_copy, model_dump
63
63
  from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type
64
64
  from ._response import (
65
65
  APIResponse,
@@ -145,9 +145,9 @@ class PageInfo:
145
145
  def __init__(
146
146
  self,
147
147
  *,
148
- url: URL | NotGiven = NOT_GIVEN,
149
- json: Body | NotGiven = NOT_GIVEN,
150
- params: Query | NotGiven = NOT_GIVEN,
148
+ url: URL | NotGiven = not_given,
149
+ json: Body | NotGiven = not_given,
150
+ params: Query | NotGiven = not_given,
151
151
  ) -> None:
152
152
  self.url = url
153
153
  self.json = json
@@ -232,7 +232,7 @@ class BaseSyncPage(BasePage[_T], Generic[_T]):
232
232
  model: Type[_T],
233
233
  options: FinalRequestOptions,
234
234
  ) -> None:
235
- if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None:
235
+ if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None:
236
236
  self.__pydantic_private__ = {}
237
237
 
238
238
  self._model = model
@@ -320,7 +320,7 @@ class BaseAsyncPage(BasePage[_T], Generic[_T]):
320
320
  client: AsyncAPIClient,
321
321
  options: FinalRequestOptions,
322
322
  ) -> None:
323
- if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None:
323
+ if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None:
324
324
  self.__pydantic_private__ = {}
325
325
 
326
326
  self._model = model
@@ -595,7 +595,7 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
595
595
  # we internally support defining a temporary header to override the
596
596
  # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response`
597
597
  # see _response.py for implementation details
598
- override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN)
598
+ override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given)
599
599
  if is_given(override_cast_to):
600
600
  options.headers = headers
601
601
  return cast(Type[ResponseT], override_cast_to)
@@ -825,7 +825,7 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
825
825
  version: str,
826
826
  base_url: str | URL,
827
827
  max_retries: int = DEFAULT_MAX_RETRIES,
828
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
828
+ timeout: float | Timeout | None | NotGiven = not_given,
829
829
  http_client: httpx.Client | None = None,
830
830
  custom_headers: Mapping[str, str] | None = None,
831
831
  custom_query: Mapping[str, object] | None = None,
@@ -1356,7 +1356,7 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1356
1356
  base_url: str | URL,
1357
1357
  _strict_response_validation: bool,
1358
1358
  max_retries: int = DEFAULT_MAX_RETRIES,
1359
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
1359
+ timeout: float | Timeout | None | NotGiven = not_given,
1360
1360
  http_client: httpx.AsyncClient | None = None,
1361
1361
  custom_headers: Mapping[str, str] | None = None,
1362
1362
  custom_query: Mapping[str, object] | None = None,
@@ -1818,8 +1818,8 @@ def make_request_options(
1818
1818
  extra_query: Query | None = None,
1819
1819
  extra_body: Body | None = None,
1820
1820
  idempotency_key: str | None = None,
1821
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
1822
- post_parser: PostParser | NotGiven = NOT_GIVEN,
1821
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
1822
+ post_parser: PostParser | NotGiven = not_given,
1823
1823
  ) -> RequestOptions:
1824
1824
  """Create a dict of type RequestOptions without keys of NotGiven values."""
1825
1825
  options: RequestOptions = {}
dodopayments/_client.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import os
6
- from typing import Any, Dict, Union, Mapping, cast
6
+ from typing import Any, Dict, Mapping, cast
7
7
  from typing_extensions import Self, Literal, override
8
8
 
9
9
  import httpx
@@ -11,13 +11,13 @@ import httpx
11
11
  from . import _exceptions
12
12
  from ._qs import Querystring
13
13
  from ._types import (
14
- NOT_GIVEN,
15
14
  Omit,
16
15
  Timeout,
17
16
  NotGiven,
18
17
  Transport,
19
18
  ProxiesTypes,
20
19
  RequestOptions,
20
+ not_given,
21
21
  )
22
22
  from ._utils import is_given, get_async_library
23
23
  from ._version import __version__
@@ -25,6 +25,7 @@ from .resources import (
25
25
  misc,
26
26
  addons,
27
27
  brands,
28
+ meters,
28
29
  payouts,
29
30
  refunds,
30
31
  disputes,
@@ -32,6 +33,7 @@ from .resources import (
32
33
  payments,
33
34
  discounts,
34
35
  license_keys,
36
+ usage_events,
35
37
  subscriptions,
36
38
  checkout_sessions,
37
39
  license_key_instances,
@@ -84,11 +86,14 @@ class DodoPayments(SyncAPIClient):
84
86
  addons: addons.AddonsResource
85
87
  brands: brands.BrandsResource
86
88
  webhooks: webhooks.WebhooksResource
89
+ usage_events: usage_events.UsageEventsResource
90
+ meters: meters.MetersResource
87
91
  with_raw_response: DodoPaymentsWithRawResponse
88
92
  with_streaming_response: DodoPaymentsWithStreamedResponse
89
93
 
90
94
  # client options
91
95
  bearer_token: str
96
+ webhook_key: str | None
92
97
 
93
98
  _environment: Literal["live_mode", "test_mode"] | NotGiven
94
99
 
@@ -96,9 +101,10 @@ class DodoPayments(SyncAPIClient):
96
101
  self,
97
102
  *,
98
103
  bearer_token: str | None = None,
99
- environment: Literal["live_mode", "test_mode"] | NotGiven = NOT_GIVEN,
100
- base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
101
- timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
104
+ webhook_key: str | None = None,
105
+ environment: Literal["live_mode", "test_mode"] | NotGiven = not_given,
106
+ base_url: str | httpx.URL | None | NotGiven = not_given,
107
+ timeout: float | Timeout | None | NotGiven = not_given,
102
108
  max_retries: int = DEFAULT_MAX_RETRIES,
103
109
  default_headers: Mapping[str, str] | None = None,
104
110
  default_query: Mapping[str, object] | None = None,
@@ -118,7 +124,9 @@ class DodoPayments(SyncAPIClient):
118
124
  ) -> None:
119
125
  """Construct a new synchronous DodoPayments client instance.
120
126
 
121
- This automatically infers the `bearer_token` argument from the `DODO_PAYMENTS_API_KEY` environment variable if it is not provided.
127
+ This automatically infers the following arguments from their corresponding environment variables if they are not provided:
128
+ - `bearer_token` from `DODO_PAYMENTS_API_KEY`
129
+ - `webhook_key` from `DODO_PAYMENTS_WEBHOOK_KEY`
122
130
  """
123
131
  if bearer_token is None:
124
132
  bearer_token = os.environ.get("DODO_PAYMENTS_API_KEY")
@@ -128,6 +136,10 @@ class DodoPayments(SyncAPIClient):
128
136
  )
129
137
  self.bearer_token = bearer_token
130
138
 
139
+ if webhook_key is None:
140
+ webhook_key = os.environ.get("DODO_PAYMENTS_WEBHOOK_KEY")
141
+ self.webhook_key = webhook_key
142
+
131
143
  self._environment = environment
132
144
 
133
145
  base_url_env = os.environ.get("DODO_PAYMENTS_BASE_URL")
@@ -182,6 +194,8 @@ class DodoPayments(SyncAPIClient):
182
194
  self.addons = addons.AddonsResource(self)
183
195
  self.brands = brands.BrandsResource(self)
184
196
  self.webhooks = webhooks.WebhooksResource(self)
197
+ self.usage_events = usage_events.UsageEventsResource(self)
198
+ self.meters = meters.MetersResource(self)
185
199
  self.with_raw_response = DodoPaymentsWithRawResponse(self)
186
200
  self.with_streaming_response = DodoPaymentsWithStreamedResponse(self)
187
201
 
@@ -209,11 +223,12 @@ class DodoPayments(SyncAPIClient):
209
223
  self,
210
224
  *,
211
225
  bearer_token: str | None = None,
226
+ webhook_key: str | None = None,
212
227
  environment: Literal["live_mode", "test_mode"] | None = None,
213
228
  base_url: str | httpx.URL | None = None,
214
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
229
+ timeout: float | Timeout | None | NotGiven = not_given,
215
230
  http_client: httpx.Client | None = None,
216
- max_retries: int | NotGiven = NOT_GIVEN,
231
+ max_retries: int | NotGiven = not_given,
217
232
  default_headers: Mapping[str, str] | None = None,
218
233
  set_default_headers: Mapping[str, str] | None = None,
219
234
  default_query: Mapping[str, object] | None = None,
@@ -244,6 +259,7 @@ class DodoPayments(SyncAPIClient):
244
259
  http_client = http_client or self._client
245
260
  return self.__class__(
246
261
  bearer_token=bearer_token or self.bearer_token,
262
+ webhook_key=webhook_key or self.webhook_key,
247
263
  base_url=base_url or self.base_url,
248
264
  environment=environment or self._environment,
249
265
  timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
@@ -310,11 +326,14 @@ class AsyncDodoPayments(AsyncAPIClient):
310
326
  addons: addons.AsyncAddonsResource
311
327
  brands: brands.AsyncBrandsResource
312
328
  webhooks: webhooks.AsyncWebhooksResource
329
+ usage_events: usage_events.AsyncUsageEventsResource
330
+ meters: meters.AsyncMetersResource
313
331
  with_raw_response: AsyncDodoPaymentsWithRawResponse
314
332
  with_streaming_response: AsyncDodoPaymentsWithStreamedResponse
315
333
 
316
334
  # client options
317
335
  bearer_token: str
336
+ webhook_key: str | None
318
337
 
319
338
  _environment: Literal["live_mode", "test_mode"] | NotGiven
320
339
 
@@ -322,9 +341,10 @@ class AsyncDodoPayments(AsyncAPIClient):
322
341
  self,
323
342
  *,
324
343
  bearer_token: str | None = None,
325
- environment: Literal["live_mode", "test_mode"] | NotGiven = NOT_GIVEN,
326
- base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
327
- timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
344
+ webhook_key: str | None = None,
345
+ environment: Literal["live_mode", "test_mode"] | NotGiven = not_given,
346
+ base_url: str | httpx.URL | None | NotGiven = not_given,
347
+ timeout: float | Timeout | None | NotGiven = not_given,
328
348
  max_retries: int = DEFAULT_MAX_RETRIES,
329
349
  default_headers: Mapping[str, str] | None = None,
330
350
  default_query: Mapping[str, object] | None = None,
@@ -344,7 +364,9 @@ class AsyncDodoPayments(AsyncAPIClient):
344
364
  ) -> None:
345
365
  """Construct a new async AsyncDodoPayments client instance.
346
366
 
347
- This automatically infers the `bearer_token` argument from the `DODO_PAYMENTS_API_KEY` environment variable if it is not provided.
367
+ This automatically infers the following arguments from their corresponding environment variables if they are not provided:
368
+ - `bearer_token` from `DODO_PAYMENTS_API_KEY`
369
+ - `webhook_key` from `DODO_PAYMENTS_WEBHOOK_KEY`
348
370
  """
349
371
  if bearer_token is None:
350
372
  bearer_token = os.environ.get("DODO_PAYMENTS_API_KEY")
@@ -354,6 +376,10 @@ class AsyncDodoPayments(AsyncAPIClient):
354
376
  )
355
377
  self.bearer_token = bearer_token
356
378
 
379
+ if webhook_key is None:
380
+ webhook_key = os.environ.get("DODO_PAYMENTS_WEBHOOK_KEY")
381
+ self.webhook_key = webhook_key
382
+
357
383
  self._environment = environment
358
384
 
359
385
  base_url_env = os.environ.get("DODO_PAYMENTS_BASE_URL")
@@ -408,6 +434,8 @@ class AsyncDodoPayments(AsyncAPIClient):
408
434
  self.addons = addons.AsyncAddonsResource(self)
409
435
  self.brands = brands.AsyncBrandsResource(self)
410
436
  self.webhooks = webhooks.AsyncWebhooksResource(self)
437
+ self.usage_events = usage_events.AsyncUsageEventsResource(self)
438
+ self.meters = meters.AsyncMetersResource(self)
411
439
  self.with_raw_response = AsyncDodoPaymentsWithRawResponse(self)
412
440
  self.with_streaming_response = AsyncDodoPaymentsWithStreamedResponse(self)
413
441
 
@@ -435,11 +463,12 @@ class AsyncDodoPayments(AsyncAPIClient):
435
463
  self,
436
464
  *,
437
465
  bearer_token: str | None = None,
466
+ webhook_key: str | None = None,
438
467
  environment: Literal["live_mode", "test_mode"] | None = None,
439
468
  base_url: str | httpx.URL | None = None,
440
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
469
+ timeout: float | Timeout | None | NotGiven = not_given,
441
470
  http_client: httpx.AsyncClient | None = None,
442
- max_retries: int | NotGiven = NOT_GIVEN,
471
+ max_retries: int | NotGiven = not_given,
443
472
  default_headers: Mapping[str, str] | None = None,
444
473
  set_default_headers: Mapping[str, str] | None = None,
445
474
  default_query: Mapping[str, object] | None = None,
@@ -470,6 +499,7 @@ class AsyncDodoPayments(AsyncAPIClient):
470
499
  http_client = http_client or self._client
471
500
  return self.__class__(
472
501
  bearer_token=bearer_token or self.bearer_token,
502
+ webhook_key=webhook_key or self.webhook_key,
473
503
  base_url=base_url or self.base_url,
474
504
  environment=environment or self._environment,
475
505
  timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
@@ -539,6 +569,8 @@ class DodoPaymentsWithRawResponse:
539
569
  self.addons = addons.AddonsResourceWithRawResponse(client.addons)
540
570
  self.brands = brands.BrandsResourceWithRawResponse(client.brands)
541
571
  self.webhooks = webhooks.WebhooksResourceWithRawResponse(client.webhooks)
572
+ self.usage_events = usage_events.UsageEventsResourceWithRawResponse(client.usage_events)
573
+ self.meters = meters.MetersResourceWithRawResponse(client.meters)
542
574
 
543
575
 
544
576
  class AsyncDodoPaymentsWithRawResponse:
@@ -564,6 +596,8 @@ class AsyncDodoPaymentsWithRawResponse:
564
596
  self.addons = addons.AsyncAddonsResourceWithRawResponse(client.addons)
565
597
  self.brands = brands.AsyncBrandsResourceWithRawResponse(client.brands)
566
598
  self.webhooks = webhooks.AsyncWebhooksResourceWithRawResponse(client.webhooks)
599
+ self.usage_events = usage_events.AsyncUsageEventsResourceWithRawResponse(client.usage_events)
600
+ self.meters = meters.AsyncMetersResourceWithRawResponse(client.meters)
567
601
 
568
602
 
569
603
  class DodoPaymentsWithStreamedResponse:
@@ -589,6 +623,8 @@ class DodoPaymentsWithStreamedResponse:
589
623
  self.addons = addons.AddonsResourceWithStreamingResponse(client.addons)
590
624
  self.brands = brands.BrandsResourceWithStreamingResponse(client.brands)
591
625
  self.webhooks = webhooks.WebhooksResourceWithStreamingResponse(client.webhooks)
626
+ self.usage_events = usage_events.UsageEventsResourceWithStreamingResponse(client.usage_events)
627
+ self.meters = meters.MetersResourceWithStreamingResponse(client.meters)
592
628
 
593
629
 
594
630
  class AsyncDodoPaymentsWithStreamedResponse:
@@ -614,6 +650,8 @@ class AsyncDodoPaymentsWithStreamedResponse:
614
650
  self.addons = addons.AsyncAddonsResourceWithStreamingResponse(client.addons)
615
651
  self.brands = brands.AsyncBrandsResourceWithStreamingResponse(client.brands)
616
652
  self.webhooks = webhooks.AsyncWebhooksResourceWithStreamingResponse(client.webhooks)
653
+ self.usage_events = usage_events.AsyncUsageEventsResourceWithStreamingResponse(client.usage_events)
654
+ self.meters = meters.AsyncMetersResourceWithStreamingResponse(client.meters)
617
655
 
618
656
 
619
657
  Client = DodoPayments
dodopayments/_compat.py CHANGED
@@ -12,14 +12,13 @@ from ._types import IncEx, StrBytesIntFloat
12
12
  _T = TypeVar("_T")
13
13
  _ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel)
14
14
 
15
- # --------------- Pydantic v2 compatibility ---------------
15
+ # --------------- Pydantic v2, v3 compatibility ---------------
16
16
 
17
17
  # Pyright incorrectly reports some of our functions as overriding a method when they don't
18
18
  # pyright: reportIncompatibleMethodOverride=false
19
19
 
20
- PYDANTIC_V2 = pydantic.VERSION.startswith("2.")
20
+ PYDANTIC_V1 = pydantic.VERSION.startswith("1.")
21
21
 
22
- # v1 re-exports
23
22
  if TYPE_CHECKING:
24
23
 
25
24
  def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001
@@ -44,90 +43,92 @@ if TYPE_CHECKING:
44
43
  ...
45
44
 
46
45
  else:
47
- if PYDANTIC_V2:
48
- from pydantic.v1.typing import (
46
+ # v1 re-exports
47
+ if PYDANTIC_V1:
48
+ from pydantic.typing import (
49
49
  get_args as get_args,
50
50
  is_union as is_union,
51
51
  get_origin as get_origin,
52
52
  is_typeddict as is_typeddict,
53
53
  is_literal_type as is_literal_type,
54
54
  )
55
- from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
55
+ from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
56
56
  else:
57
- from pydantic.typing import (
57
+ from ._utils import (
58
58
  get_args as get_args,
59
59
  is_union as is_union,
60
60
  get_origin as get_origin,
61
+ parse_date as parse_date,
61
62
  is_typeddict as is_typeddict,
63
+ parse_datetime as parse_datetime,
62
64
  is_literal_type as is_literal_type,
63
65
  )
64
- from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
65
66
 
66
67
 
67
68
  # refactored config
68
69
  if TYPE_CHECKING:
69
70
  from pydantic import ConfigDict as ConfigDict
70
71
  else:
71
- if PYDANTIC_V2:
72
- from pydantic import ConfigDict
73
- else:
72
+ if PYDANTIC_V1:
74
73
  # TODO: provide an error message here?
75
74
  ConfigDict = None
75
+ else:
76
+ from pydantic import ConfigDict as ConfigDict
76
77
 
77
78
 
78
79
  # renamed methods / properties
79
80
  def parse_obj(model: type[_ModelT], value: object) -> _ModelT:
80
- if PYDANTIC_V2:
81
- return model.model_validate(value)
82
- else:
81
+ if PYDANTIC_V1:
83
82
  return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
83
+ else:
84
+ return model.model_validate(value)
84
85
 
85
86
 
86
87
  def field_is_required(field: FieldInfo) -> bool:
87
- if PYDANTIC_V2:
88
- return field.is_required()
89
- return field.required # type: ignore
88
+ if PYDANTIC_V1:
89
+ return field.required # type: ignore
90
+ return field.is_required()
90
91
 
91
92
 
92
93
  def field_get_default(field: FieldInfo) -> Any:
93
94
  value = field.get_default()
94
- if PYDANTIC_V2:
95
- from pydantic_core import PydanticUndefined
96
-
97
- if value == PydanticUndefined:
98
- return None
95
+ if PYDANTIC_V1:
99
96
  return value
97
+ from pydantic_core import PydanticUndefined
98
+
99
+ if value == PydanticUndefined:
100
+ return None
100
101
  return value
101
102
 
102
103
 
103
104
  def field_outer_type(field: FieldInfo) -> Any:
104
- if PYDANTIC_V2:
105
- return field.annotation
106
- return field.outer_type_ # type: ignore
105
+ if PYDANTIC_V1:
106
+ return field.outer_type_ # type: ignore
107
+ return field.annotation
107
108
 
108
109
 
109
110
  def get_model_config(model: type[pydantic.BaseModel]) -> Any:
110
- if PYDANTIC_V2:
111
- return model.model_config
112
- return model.__config__ # type: ignore
111
+ if PYDANTIC_V1:
112
+ return model.__config__ # type: ignore
113
+ return model.model_config
113
114
 
114
115
 
115
116
  def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]:
116
- if PYDANTIC_V2:
117
- return model.model_fields
118
- return model.__fields__ # type: ignore
117
+ if PYDANTIC_V1:
118
+ return model.__fields__ # type: ignore
119
+ return model.model_fields
119
120
 
120
121
 
121
122
  def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT:
122
- if PYDANTIC_V2:
123
- return model.model_copy(deep=deep)
124
- return model.copy(deep=deep) # type: ignore
123
+ if PYDANTIC_V1:
124
+ return model.copy(deep=deep) # type: ignore
125
+ return model.model_copy(deep=deep)
125
126
 
126
127
 
127
128
  def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str:
128
- if PYDANTIC_V2:
129
- return model.model_dump_json(indent=indent)
130
- return model.json(indent=indent) # type: ignore
129
+ if PYDANTIC_V1:
130
+ return model.json(indent=indent) # type: ignore
131
+ return model.model_dump_json(indent=indent)
131
132
 
132
133
 
133
134
  def model_dump(
@@ -139,14 +140,14 @@ def model_dump(
139
140
  warnings: bool = True,
140
141
  mode: Literal["json", "python"] = "python",
141
142
  ) -> dict[str, Any]:
142
- if PYDANTIC_V2 or hasattr(model, "model_dump"):
143
+ if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
143
144
  return model.model_dump(
144
145
  mode=mode,
145
146
  exclude=exclude,
146
147
  exclude_unset=exclude_unset,
147
148
  exclude_defaults=exclude_defaults,
148
149
  # warnings are not supported in Pydantic v1
149
- warnings=warnings if PYDANTIC_V2 else True,
150
+ warnings=True if PYDANTIC_V1 else warnings,
150
151
  )
151
152
  return cast(
152
153
  "dict[str, Any]",
@@ -159,9 +160,9 @@ def model_dump(
159
160
 
160
161
 
161
162
  def model_parse(model: type[_ModelT], data: Any) -> _ModelT:
162
- if PYDANTIC_V2:
163
- return model.model_validate(data)
164
- return model.parse_obj(data) # pyright: ignore[reportDeprecated]
163
+ if PYDANTIC_V1:
164
+ return model.parse_obj(data) # pyright: ignore[reportDeprecated]
165
+ return model.model_validate(data)
165
166
 
166
167
 
167
168
  # generic models
@@ -170,17 +171,16 @@ if TYPE_CHECKING:
170
171
  class GenericModel(pydantic.BaseModel): ...
171
172
 
172
173
  else:
173
- if PYDANTIC_V2:
174
+ if PYDANTIC_V1:
175
+ import pydantic.generics
176
+
177
+ class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ...
178
+ else:
174
179
  # there no longer needs to be a distinction in v2 but
175
180
  # we still have to create our own subclass to avoid
176
181
  # inconsistent MRO ordering errors
177
182
  class GenericModel(pydantic.BaseModel): ...
178
183
 
179
- else:
180
- import pydantic.generics
181
-
182
- class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ...
183
-
184
184
 
185
185
  # cached properties
186
186
  if TYPE_CHECKING:
@@ -54,6 +54,10 @@ class APIResponseValidationError(APIError):
54
54
  self.status_code = response.status_code
55
55
 
56
56
 
57
+ class APIWebhookValidationError(APIError):
58
+ pass
59
+
60
+
57
61
  class APIStatusError(APIError):
58
62
  """Raised when an API response has a status code of 4xx or 5xx."""
59
63