dodopayments 1.51.1__py3-none-any.whl → 1.52.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.

Potentially problematic release.


This version of dodopayments might be problematic. Click here for more details.

Files changed (47) hide show
  1. dodopayments/_base_client.py +3 -3
  2. dodopayments/_client.py +18 -0
  3. dodopayments/_compat.py +48 -48
  4. dodopayments/_models.py +40 -40
  5. dodopayments/_utils/__init__.py +8 -2
  6. dodopayments/_utils/_compat.py +45 -0
  7. dodopayments/_utils/_datetime_parse.py +136 -0
  8. dodopayments/_utils/_transform.py +11 -1
  9. dodopayments/_utils/_typing.py +1 -1
  10. dodopayments/_utils/_utils.py +0 -1
  11. dodopayments/_version.py +1 -1
  12. dodopayments/resources/__init__.py +28 -0
  13. dodopayments/resources/discounts.py +6 -6
  14. dodopayments/resources/invoices/payments.py +80 -0
  15. dodopayments/resources/meters.py +554 -0
  16. dodopayments/resources/products/products.py +16 -16
  17. dodopayments/resources/subscriptions.py +223 -0
  18. dodopayments/resources/usage_events.py +597 -0
  19. dodopayments/types/__init__.py +20 -0
  20. dodopayments/types/add_meter_to_price.py +29 -0
  21. dodopayments/types/add_meter_to_price_param.py +30 -0
  22. dodopayments/types/discount_create_params.py +3 -2
  23. dodopayments/types/discount_update_params.py +3 -2
  24. dodopayments/types/event.py +26 -0
  25. dodopayments/types/event_input_param.py +38 -0
  26. dodopayments/types/meter.py +40 -0
  27. dodopayments/types/meter_aggregation.py +16 -0
  28. dodopayments/types/meter_aggregation_param.py +16 -0
  29. dodopayments/types/meter_create_params.py +31 -0
  30. dodopayments/types/meter_filter.py +131 -0
  31. dodopayments/types/meter_filter_param.py +143 -0
  32. dodopayments/types/meter_list_params.py +18 -0
  33. dodopayments/types/payment.py +6 -0
  34. dodopayments/types/price.py +52 -5
  35. dodopayments/types/price_param.py +52 -5
  36. dodopayments/types/product_create_params.py +3 -2
  37. dodopayments/types/product_update_params.py +4 -3
  38. dodopayments/types/subscription.py +20 -1
  39. dodopayments/types/subscription_retrieve_usage_history_params.py +28 -0
  40. dodopayments/types/subscription_retrieve_usage_history_response.py +46 -0
  41. dodopayments/types/usage_event_ingest_params.py +15 -0
  42. dodopayments/types/usage_event_ingest_response.py +9 -0
  43. dodopayments/types/usage_event_list_params.py +42 -0
  44. {dodopayments-1.51.1.dist-info → dodopayments-1.52.5.dist-info}/METADATA +1 -1
  45. {dodopayments-1.51.1.dist-info → dodopayments-1.52.5.dist-info}/RECORD +47 -27
  46. {dodopayments-1.51.1.dist-info → dodopayments-1.52.5.dist-info}/WHEEL +0 -0
  47. {dodopayments-1.51.1.dist-info → dodopayments-1.52.5.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,136 @@
1
+ """
2
+ This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py
3
+ without the Pydantic v1 specific errors.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import re
9
+ from typing import Dict, Union, Optional
10
+ from datetime import date, datetime, timezone, timedelta
11
+
12
+ from .._types import StrBytesIntFloat
13
+
14
+ date_expr = r"(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})"
15
+ time_expr = (
16
+ r"(?P<hour>\d{1,2}):(?P<minute>\d{1,2})"
17
+ r"(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?"
18
+ r"(?P<tzinfo>Z|[+-]\d{2}(?::?\d{2})?)?$"
19
+ )
20
+
21
+ date_re = re.compile(f"{date_expr}$")
22
+ datetime_re = re.compile(f"{date_expr}[T ]{time_expr}")
23
+
24
+
25
+ EPOCH = datetime(1970, 1, 1)
26
+ # if greater than this, the number is in ms, if less than or equal it's in seconds
27
+ # (in seconds this is 11th October 2603, in ms it's 20th August 1970)
28
+ MS_WATERSHED = int(2e10)
29
+ # slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
30
+ MAX_NUMBER = int(3e20)
31
+
32
+
33
+ def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]:
34
+ if isinstance(value, (int, float)):
35
+ return value
36
+ try:
37
+ return float(value)
38
+ except ValueError:
39
+ return None
40
+ except TypeError:
41
+ raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None
42
+
43
+
44
+ def _from_unix_seconds(seconds: Union[int, float]) -> datetime:
45
+ if seconds > MAX_NUMBER:
46
+ return datetime.max
47
+ elif seconds < -MAX_NUMBER:
48
+ return datetime.min
49
+
50
+ while abs(seconds) > MS_WATERSHED:
51
+ seconds /= 1000
52
+ dt = EPOCH + timedelta(seconds=seconds)
53
+ return dt.replace(tzinfo=timezone.utc)
54
+
55
+
56
+ def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]:
57
+ if value == "Z":
58
+ return timezone.utc
59
+ elif value is not None:
60
+ offset_mins = int(value[-2:]) if len(value) > 3 else 0
61
+ offset = 60 * int(value[1:3]) + offset_mins
62
+ if value[0] == "-":
63
+ offset = -offset
64
+ return timezone(timedelta(minutes=offset))
65
+ else:
66
+ return None
67
+
68
+
69
+ def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:
70
+ """
71
+ Parse a datetime/int/float/string and return a datetime.datetime.
72
+
73
+ This function supports time zone offsets. When the input contains one,
74
+ the output uses a timezone with a fixed offset from UTC.
75
+
76
+ Raise ValueError if the input is well formatted but not a valid datetime.
77
+ Raise ValueError if the input isn't well formatted.
78
+ """
79
+ if isinstance(value, datetime):
80
+ return value
81
+
82
+ number = _get_numeric(value, "datetime")
83
+ if number is not None:
84
+ return _from_unix_seconds(number)
85
+
86
+ if isinstance(value, bytes):
87
+ value = value.decode()
88
+
89
+ assert not isinstance(value, (float, int))
90
+
91
+ match = datetime_re.match(value)
92
+ if match is None:
93
+ raise ValueError("invalid datetime format")
94
+
95
+ kw = match.groupdict()
96
+ if kw["microsecond"]:
97
+ kw["microsecond"] = kw["microsecond"].ljust(6, "0")
98
+
99
+ tzinfo = _parse_timezone(kw.pop("tzinfo"))
100
+ kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None}
101
+ kw_["tzinfo"] = tzinfo
102
+
103
+ return datetime(**kw_) # type: ignore
104
+
105
+
106
+ def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
107
+ """
108
+ Parse a date/int/float/string and return a datetime.date.
109
+
110
+ Raise ValueError if the input is well formatted but not a valid date.
111
+ Raise ValueError if the input isn't well formatted.
112
+ """
113
+ if isinstance(value, date):
114
+ if isinstance(value, datetime):
115
+ return value.date()
116
+ else:
117
+ return value
118
+
119
+ number = _get_numeric(value, "date")
120
+ if number is not None:
121
+ return _from_unix_seconds(number).date()
122
+
123
+ if isinstance(value, bytes):
124
+ value = value.decode()
125
+
126
+ assert not isinstance(value, (float, int))
127
+ match = date_re.match(value)
128
+ if match is None:
129
+ raise ValueError("invalid date format")
130
+
131
+ kw = {k: int(v) for k, v in match.groupdict().items()}
132
+
133
+ try:
134
+ return date(**kw)
135
+ except ValueError:
136
+ raise ValueError("invalid date format") from None
@@ -16,18 +16,20 @@ from ._utils import (
16
16
  lru_cache,
17
17
  is_mapping,
18
18
  is_iterable,
19
+ is_sequence,
19
20
  )
20
21
  from .._files import is_base64_file_input
22
+ from ._compat import get_origin, is_typeddict
21
23
  from ._typing import (
22
24
  is_list_type,
23
25
  is_union_type,
24
26
  extract_type_arg,
25
27
  is_iterable_type,
26
28
  is_required_type,
29
+ is_sequence_type,
27
30
  is_annotated_type,
28
31
  strip_annotated_type,
29
32
  )
30
- from .._compat import get_origin, model_dump, is_typeddict
31
33
 
32
34
  _T = TypeVar("_T")
33
35
 
@@ -167,6 +169,8 @@ def _transform_recursive(
167
169
 
168
170
  Defaults to the same value as the `annotation` argument.
169
171
  """
172
+ from .._compat import model_dump
173
+
170
174
  if inner_type is None:
171
175
  inner_type = annotation
172
176
 
@@ -184,6 +188,8 @@ def _transform_recursive(
184
188
  (is_list_type(stripped_type) and is_list(data))
185
189
  # Iterable[T]
186
190
  or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
191
+ # Sequence[T]
192
+ or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
187
193
  ):
188
194
  # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
189
195
  # intended as an iterable, so we don't transform it.
@@ -329,6 +335,8 @@ async def _async_transform_recursive(
329
335
 
330
336
  Defaults to the same value as the `annotation` argument.
331
337
  """
338
+ from .._compat import model_dump
339
+
332
340
  if inner_type is None:
333
341
  inner_type = annotation
334
342
 
@@ -346,6 +354,8 @@ async def _async_transform_recursive(
346
354
  (is_list_type(stripped_type) and is_list(data))
347
355
  # Iterable[T]
348
356
  or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
357
+ # Sequence[T]
358
+ or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
349
359
  ):
350
360
  # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
351
361
  # intended as an iterable, so we don't transform it.
@@ -15,7 +15,7 @@ from typing_extensions import (
15
15
 
16
16
  from ._utils import lru_cache
17
17
  from .._types import InheritsGeneric
18
- from .._compat import is_union as _is_union
18
+ from ._compat import is_union as _is_union
19
19
 
20
20
 
21
21
  def is_annotated_type(typ: type) -> bool:
@@ -22,7 +22,6 @@ from typing_extensions import TypeGuard
22
22
  import sniffio
23
23
 
24
24
  from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike
25
- from .._compat import parse_date as parse_date, parse_datetime as parse_datetime
26
25
 
27
26
  _T = TypeVar("_T")
28
27
  _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
dodopayments/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "dodopayments"
4
- __version__ = "1.51.1" # x-release-please-version
4
+ __version__ = "1.52.5" # x-release-please-version
@@ -24,6 +24,14 @@ from .brands import (
24
24
  BrandsResourceWithStreamingResponse,
25
25
  AsyncBrandsResourceWithStreamingResponse,
26
26
  )
27
+ from .meters import (
28
+ MetersResource,
29
+ AsyncMetersResource,
30
+ MetersResourceWithRawResponse,
31
+ AsyncMetersResourceWithRawResponse,
32
+ MetersResourceWithStreamingResponse,
33
+ AsyncMetersResourceWithStreamingResponse,
34
+ )
27
35
  from .payouts import (
28
36
  PayoutsResource,
29
37
  AsyncPayoutsResource,
@@ -112,6 +120,14 @@ from .license_keys import (
112
120
  LicenseKeysResourceWithStreamingResponse,
113
121
  AsyncLicenseKeysResourceWithStreamingResponse,
114
122
  )
123
+ from .usage_events import (
124
+ UsageEventsResource,
125
+ AsyncUsageEventsResource,
126
+ UsageEventsResourceWithRawResponse,
127
+ AsyncUsageEventsResourceWithRawResponse,
128
+ UsageEventsResourceWithStreamingResponse,
129
+ AsyncUsageEventsResourceWithStreamingResponse,
130
+ )
115
131
  from .subscriptions import (
116
132
  SubscriptionsResource,
117
133
  AsyncSubscriptionsResource,
@@ -240,4 +256,16 @@ __all__ = [
240
256
  "AsyncWebhooksResourceWithRawResponse",
241
257
  "WebhooksResourceWithStreamingResponse",
242
258
  "AsyncWebhooksResourceWithStreamingResponse",
259
+ "UsageEventsResource",
260
+ "AsyncUsageEventsResource",
261
+ "UsageEventsResourceWithRawResponse",
262
+ "AsyncUsageEventsResourceWithRawResponse",
263
+ "UsageEventsResourceWithStreamingResponse",
264
+ "AsyncUsageEventsResourceWithStreamingResponse",
265
+ "MetersResource",
266
+ "AsyncMetersResource",
267
+ "MetersResourceWithRawResponse",
268
+ "AsyncMetersResourceWithRawResponse",
269
+ "MetersResourceWithStreamingResponse",
270
+ "AsyncMetersResourceWithStreamingResponse",
243
271
  ]
@@ -2,13 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import List, Union, Optional
5
+ from typing import Union, Optional
6
6
  from datetime import datetime
7
7
 
8
8
  import httpx
9
9
 
10
10
  from ..types import DiscountType, discount_list_params, discount_create_params, discount_update_params
11
- from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven
11
+ from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, SequenceNotStr
12
12
  from .._utils import maybe_transform, async_maybe_transform
13
13
  from .._compat import cached_property
14
14
  from .._resource import SyncAPIResource, AsyncAPIResource
@@ -54,7 +54,7 @@ class DiscountsResource(SyncAPIResource):
54
54
  code: Optional[str] | NotGiven = NOT_GIVEN,
55
55
  expires_at: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
56
56
  name: Optional[str] | NotGiven = NOT_GIVEN,
57
- restricted_to: Optional[List[str]] | NotGiven = NOT_GIVEN,
57
+ restricted_to: Optional[SequenceNotStr[str]] | NotGiven = NOT_GIVEN,
58
58
  subscription_cycles: Optional[int] | NotGiven = NOT_GIVEN,
59
59
  usage_limit: Optional[int] | NotGiven = NOT_GIVEN,
60
60
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -165,7 +165,7 @@ class DiscountsResource(SyncAPIResource):
165
165
  code: Optional[str] | NotGiven = NOT_GIVEN,
166
166
  expires_at: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
167
167
  name: Optional[str] | NotGiven = NOT_GIVEN,
168
- restricted_to: Optional[List[str]] | NotGiven = NOT_GIVEN,
168
+ restricted_to: Optional[SequenceNotStr[str]] | NotGiven = NOT_GIVEN,
169
169
  subscription_cycles: Optional[int] | NotGiven = NOT_GIVEN,
170
170
  type: Optional[DiscountType] | NotGiven = NOT_GIVEN,
171
171
  usage_limit: Optional[int] | NotGiven = NOT_GIVEN,
@@ -341,7 +341,7 @@ class AsyncDiscountsResource(AsyncAPIResource):
341
341
  code: Optional[str] | NotGiven = NOT_GIVEN,
342
342
  expires_at: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
343
343
  name: Optional[str] | NotGiven = NOT_GIVEN,
344
- restricted_to: Optional[List[str]] | NotGiven = NOT_GIVEN,
344
+ restricted_to: Optional[SequenceNotStr[str]] | NotGiven = NOT_GIVEN,
345
345
  subscription_cycles: Optional[int] | NotGiven = NOT_GIVEN,
346
346
  usage_limit: Optional[int] | NotGiven = NOT_GIVEN,
347
347
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -452,7 +452,7 @@ class AsyncDiscountsResource(AsyncAPIResource):
452
452
  code: Optional[str] | NotGiven = NOT_GIVEN,
453
453
  expires_at: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
454
454
  name: Optional[str] | NotGiven = NOT_GIVEN,
455
- restricted_to: Optional[List[str]] | NotGiven = NOT_GIVEN,
455
+ restricted_to: Optional[SequenceNotStr[str]] | NotGiven = NOT_GIVEN,
456
456
  subscription_cycles: Optional[int] | NotGiven = NOT_GIVEN,
457
457
  type: Optional[DiscountType] | NotGiven = NOT_GIVEN,
458
458
  usage_limit: Optional[int] | NotGiven = NOT_GIVEN,
@@ -74,6 +74,38 @@ class PaymentsResource(SyncAPIResource):
74
74
  cast_to=BinaryAPIResponse,
75
75
  )
76
76
 
77
+ def retrieve_refund(
78
+ self,
79
+ refund_id: str,
80
+ *,
81
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
82
+ # The extra values given here take precedence over values defined on the client or passed to this method.
83
+ extra_headers: Headers | None = None,
84
+ extra_query: Query | None = None,
85
+ extra_body: Body | None = None,
86
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
87
+ ) -> BinaryAPIResponse:
88
+ """
89
+ Args:
90
+ extra_headers: Send extra headers
91
+
92
+ extra_query: Add additional query parameters to the request
93
+
94
+ extra_body: Add additional JSON properties to the request
95
+
96
+ timeout: Override the client-level default timeout for this request, in seconds
97
+ """
98
+ if not refund_id:
99
+ raise ValueError(f"Expected a non-empty value for `refund_id` but received {refund_id!r}")
100
+ extra_headers = {"Accept": "application/pdf", **(extra_headers or {})}
101
+ return self._get(
102
+ f"/invoices/refunds/{refund_id}",
103
+ options=make_request_options(
104
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
105
+ ),
106
+ cast_to=BinaryAPIResponse,
107
+ )
108
+
77
109
 
78
110
  class AsyncPaymentsResource(AsyncAPIResource):
79
111
  @cached_property
@@ -127,6 +159,38 @@ class AsyncPaymentsResource(AsyncAPIResource):
127
159
  cast_to=AsyncBinaryAPIResponse,
128
160
  )
129
161
 
162
+ async def retrieve_refund(
163
+ self,
164
+ refund_id: str,
165
+ *,
166
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
167
+ # The extra values given here take precedence over values defined on the client or passed to this method.
168
+ extra_headers: Headers | None = None,
169
+ extra_query: Query | None = None,
170
+ extra_body: Body | None = None,
171
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
172
+ ) -> AsyncBinaryAPIResponse:
173
+ """
174
+ Args:
175
+ extra_headers: Send extra headers
176
+
177
+ extra_query: Add additional query parameters to the request
178
+
179
+ extra_body: Add additional JSON properties to the request
180
+
181
+ timeout: Override the client-level default timeout for this request, in seconds
182
+ """
183
+ if not refund_id:
184
+ raise ValueError(f"Expected a non-empty value for `refund_id` but received {refund_id!r}")
185
+ extra_headers = {"Accept": "application/pdf", **(extra_headers or {})}
186
+ return await self._get(
187
+ f"/invoices/refunds/{refund_id}",
188
+ options=make_request_options(
189
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
190
+ ),
191
+ cast_to=AsyncBinaryAPIResponse,
192
+ )
193
+
130
194
 
131
195
  class PaymentsResourceWithRawResponse:
132
196
  def __init__(self, payments: PaymentsResource) -> None:
@@ -136,6 +200,10 @@ class PaymentsResourceWithRawResponse:
136
200
  payments.retrieve,
137
201
  BinaryAPIResponse,
138
202
  )
203
+ self.retrieve_refund = to_custom_raw_response_wrapper(
204
+ payments.retrieve_refund,
205
+ BinaryAPIResponse,
206
+ )
139
207
 
140
208
 
141
209
  class AsyncPaymentsResourceWithRawResponse:
@@ -146,6 +214,10 @@ class AsyncPaymentsResourceWithRawResponse:
146
214
  payments.retrieve,
147
215
  AsyncBinaryAPIResponse,
148
216
  )
217
+ self.retrieve_refund = async_to_custom_raw_response_wrapper(
218
+ payments.retrieve_refund,
219
+ AsyncBinaryAPIResponse,
220
+ )
149
221
 
150
222
 
151
223
  class PaymentsResourceWithStreamingResponse:
@@ -156,6 +228,10 @@ class PaymentsResourceWithStreamingResponse:
156
228
  payments.retrieve,
157
229
  StreamedBinaryAPIResponse,
158
230
  )
231
+ self.retrieve_refund = to_custom_streamed_response_wrapper(
232
+ payments.retrieve_refund,
233
+ StreamedBinaryAPIResponse,
234
+ )
159
235
 
160
236
 
161
237
  class AsyncPaymentsResourceWithStreamingResponse:
@@ -166,3 +242,7 @@ class AsyncPaymentsResourceWithStreamingResponse:
166
242
  payments.retrieve,
167
243
  AsyncStreamedBinaryAPIResponse,
168
244
  )
245
+ self.retrieve_refund = async_to_custom_streamed_response_wrapper(
246
+ payments.retrieve_refund,
247
+ AsyncStreamedBinaryAPIResponse,
248
+ )