checkout-intents 0.1.0__py3-none-any.whl → 0.2.0__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.
- checkout_intents/__init__.py +2 -0
- checkout_intents/_client.py +65 -14
- checkout_intents/_exceptions.py +34 -0
- checkout_intents/_models.py +31 -14
- checkout_intents/_utils/_compat.py +1 -1
- checkout_intents/_utils/_utils.py +3 -3
- checkout_intents/_version.py +1 -1
- checkout_intents/resources/checkout_intents.py +780 -2
- {checkout_intents-0.1.0.dist-info → checkout_intents-0.2.0.dist-info}/METADATA +113 -1
- {checkout_intents-0.1.0.dist-info → checkout_intents-0.2.0.dist-info}/RECORD +12 -12
- {checkout_intents-0.1.0.dist-info → checkout_intents-0.2.0.dist-info}/WHEEL +0 -0
- {checkout_intents-0.1.0.dist-info → checkout_intents-0.2.0.dist-info}/licenses/LICENSE +0 -0
checkout_intents/__init__.py
CHANGED
|
@@ -29,6 +29,7 @@ from ._exceptions import (
|
|
|
29
29
|
RateLimitError,
|
|
30
30
|
APITimeoutError,
|
|
31
31
|
BadRequestError,
|
|
32
|
+
PollTimeoutError,
|
|
32
33
|
APIConnectionError,
|
|
33
34
|
AuthenticationError,
|
|
34
35
|
InternalServerError,
|
|
@@ -66,6 +67,7 @@ __all__ = [
|
|
|
66
67
|
"UnprocessableEntityError",
|
|
67
68
|
"RateLimitError",
|
|
68
69
|
"InternalServerError",
|
|
70
|
+
"PollTimeoutError",
|
|
69
71
|
"Timeout",
|
|
70
72
|
"RequestOptions",
|
|
71
73
|
"Client",
|
checkout_intents/_client.py
CHANGED
|
@@ -48,6 +48,23 @@ ENVIRONMENTS: Dict[str, str] = {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def _extract_environment_from_api_key(api_key: str) -> Literal["staging", "production"] | None:
|
|
52
|
+
"""
|
|
53
|
+
Extracts the environment from a Rye API key.
|
|
54
|
+
API keys follow the format: RYE/{environment}-{key}
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
api_key: The API key to parse
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The extracted environment ('staging' or 'production'), or None if the format doesn't match
|
|
61
|
+
"""
|
|
62
|
+
import re
|
|
63
|
+
|
|
64
|
+
match = re.match(r"^RYE/(staging|production)-", api_key)
|
|
65
|
+
return match.group(1) if match else None # type: ignore[return-value]
|
|
66
|
+
|
|
67
|
+
|
|
51
68
|
class CheckoutIntents(SyncAPIClient):
|
|
52
69
|
checkout_intents: checkout_intents.CheckoutIntentsResource
|
|
53
70
|
brands: brands.BrandsResource
|
|
@@ -95,7 +112,26 @@ class CheckoutIntents(SyncAPIClient):
|
|
|
95
112
|
)
|
|
96
113
|
self.api_key = api_key
|
|
97
114
|
|
|
98
|
-
|
|
115
|
+
# Auto-infer environment from API key
|
|
116
|
+
inferred_environment = _extract_environment_from_api_key(api_key)
|
|
117
|
+
|
|
118
|
+
# Validate environment option matches API key (if both provided)
|
|
119
|
+
if is_given(environment) and inferred_environment and environment != inferred_environment:
|
|
120
|
+
raise CheckoutIntentsError(
|
|
121
|
+
f"Environment mismatch: API key is for '{inferred_environment}' environment but 'environment' option is set to '{environment}'. Please use an API key that matches your desired environment or omit the 'environment' option to auto-detect from the API key (only auto-detectable with the RYE/{{environment}}-abcdef api key format)."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Use provided environment, or infer from API key, or default to staging
|
|
125
|
+
resolved_environment: Literal["staging", "production"]
|
|
126
|
+
if is_given(environment):
|
|
127
|
+
resolved_environment = cast(Literal["staging", "production"], environment)
|
|
128
|
+
self._environment = cast(Literal["staging", "production"], environment)
|
|
129
|
+
elif inferred_environment:
|
|
130
|
+
resolved_environment = inferred_environment
|
|
131
|
+
self._environment = inferred_environment
|
|
132
|
+
else:
|
|
133
|
+
resolved_environment = "staging"
|
|
134
|
+
self._environment = "staging"
|
|
99
135
|
|
|
100
136
|
base_url_env = os.environ.get("CHECKOUT_INTENTS_BASE_URL")
|
|
101
137
|
if is_given(base_url) and base_url is not None:
|
|
@@ -108,18 +144,16 @@ class CheckoutIntents(SyncAPIClient):
|
|
|
108
144
|
)
|
|
109
145
|
|
|
110
146
|
try:
|
|
111
|
-
base_url = ENVIRONMENTS[
|
|
147
|
+
base_url = ENVIRONMENTS[resolved_environment]
|
|
112
148
|
except KeyError as exc:
|
|
113
|
-
raise ValueError(f"Unknown environment: {
|
|
149
|
+
raise ValueError(f"Unknown environment: {resolved_environment}") from exc
|
|
114
150
|
elif base_url_env is not None:
|
|
115
151
|
base_url = base_url_env
|
|
116
152
|
else:
|
|
117
|
-
self._environment = environment = "staging"
|
|
118
|
-
|
|
119
153
|
try:
|
|
120
|
-
base_url = ENVIRONMENTS[
|
|
154
|
+
base_url = ENVIRONMENTS[resolved_environment]
|
|
121
155
|
except KeyError as exc:
|
|
122
|
-
raise ValueError(f"Unknown environment: {
|
|
156
|
+
raise ValueError(f"Unknown environment: {resolved_environment}") from exc
|
|
123
157
|
|
|
124
158
|
super().__init__(
|
|
125
159
|
version=__version__,
|
|
@@ -291,7 +325,26 @@ class AsyncCheckoutIntents(AsyncAPIClient):
|
|
|
291
325
|
)
|
|
292
326
|
self.api_key = api_key
|
|
293
327
|
|
|
294
|
-
|
|
328
|
+
# Auto-infer environment from API key
|
|
329
|
+
inferred_environment = _extract_environment_from_api_key(api_key)
|
|
330
|
+
|
|
331
|
+
# Validate environment option matches API key (if both provided)
|
|
332
|
+
if is_given(environment) and inferred_environment and environment != inferred_environment:
|
|
333
|
+
raise CheckoutIntentsError(
|
|
334
|
+
f"Environment mismatch: API key is for '{inferred_environment}' environment but 'environment' option is set to '{environment}'. Please use an API key that matches your desired environment or omit the 'environment' option to auto-detect from the API key (only auto-detectable with the RYE/{{environment}}-abcdef api key format)."
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# Use provided environment, or infer from API key, or default to staging
|
|
338
|
+
resolved_environment: Literal["staging", "production"]
|
|
339
|
+
if is_given(environment):
|
|
340
|
+
resolved_environment = cast(Literal["staging", "production"], environment)
|
|
341
|
+
self._environment = cast(Literal["staging", "production"], environment)
|
|
342
|
+
elif inferred_environment:
|
|
343
|
+
resolved_environment = inferred_environment
|
|
344
|
+
self._environment = inferred_environment
|
|
345
|
+
else:
|
|
346
|
+
resolved_environment = "staging"
|
|
347
|
+
self._environment = "staging"
|
|
295
348
|
|
|
296
349
|
base_url_env = os.environ.get("CHECKOUT_INTENTS_BASE_URL")
|
|
297
350
|
if is_given(base_url) and base_url is not None:
|
|
@@ -304,18 +357,16 @@ class AsyncCheckoutIntents(AsyncAPIClient):
|
|
|
304
357
|
)
|
|
305
358
|
|
|
306
359
|
try:
|
|
307
|
-
base_url = ENVIRONMENTS[
|
|
360
|
+
base_url = ENVIRONMENTS[resolved_environment]
|
|
308
361
|
except KeyError as exc:
|
|
309
|
-
raise ValueError(f"Unknown environment: {
|
|
362
|
+
raise ValueError(f"Unknown environment: {resolved_environment}") from exc
|
|
310
363
|
elif base_url_env is not None:
|
|
311
364
|
base_url = base_url_env
|
|
312
365
|
else:
|
|
313
|
-
self._environment = environment = "staging"
|
|
314
|
-
|
|
315
366
|
try:
|
|
316
|
-
base_url = ENVIRONMENTS[
|
|
367
|
+
base_url = ENVIRONMENTS[resolved_environment]
|
|
317
368
|
except KeyError as exc:
|
|
318
|
-
raise ValueError(f"Unknown environment: {
|
|
369
|
+
raise ValueError(f"Unknown environment: {resolved_environment}") from exc
|
|
319
370
|
|
|
320
371
|
super().__init__(
|
|
321
372
|
version=__version__,
|
checkout_intents/_exceptions.py
CHANGED
|
@@ -15,6 +15,7 @@ __all__ = [
|
|
|
15
15
|
"UnprocessableEntityError",
|
|
16
16
|
"RateLimitError",
|
|
17
17
|
"InternalServerError",
|
|
18
|
+
"PollTimeoutError",
|
|
18
19
|
]
|
|
19
20
|
|
|
20
21
|
|
|
@@ -106,3 +107,36 @@ class RateLimitError(APIStatusError):
|
|
|
106
107
|
|
|
107
108
|
class InternalServerError(APIStatusError):
|
|
108
109
|
pass
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class PollTimeoutError(CheckoutIntentsError):
|
|
113
|
+
"""Raised when a polling operation times out before the condition is met."""
|
|
114
|
+
|
|
115
|
+
intent_id: str
|
|
116
|
+
attempts: int
|
|
117
|
+
poll_interval: float
|
|
118
|
+
max_attempts: int
|
|
119
|
+
|
|
120
|
+
def __init__(
|
|
121
|
+
self,
|
|
122
|
+
*,
|
|
123
|
+
intent_id: str,
|
|
124
|
+
attempts: int,
|
|
125
|
+
poll_interval: float,
|
|
126
|
+
max_attempts: int,
|
|
127
|
+
message: str | None = None,
|
|
128
|
+
) -> None:
|
|
129
|
+
self.intent_id = intent_id
|
|
130
|
+
self.attempts = attempts
|
|
131
|
+
self.poll_interval = poll_interval
|
|
132
|
+
self.max_attempts = max_attempts
|
|
133
|
+
|
|
134
|
+
if message is None:
|
|
135
|
+
total_time_s = max_attempts * poll_interval
|
|
136
|
+
message = (
|
|
137
|
+
f"Polling timeout for checkout intent '{intent_id}': "
|
|
138
|
+
f"condition not met after {attempts} attempts ({total_time_s}s). "
|
|
139
|
+
f"Consider increasing max_attempts, polling_interval_ms, or checking the intent state manually."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
super().__init__(message)
|
checkout_intents/_models.py
CHANGED
|
@@ -251,21 +251,22 @@ class BaseModel(pydantic.BaseModel):
|
|
|
251
251
|
# pydantic version they are currently using
|
|
252
252
|
|
|
253
253
|
@override
|
|
254
|
-
def model_dump(
|
|
254
|
+
def model_dump( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
|
|
255
255
|
self,
|
|
256
256
|
*,
|
|
257
257
|
mode: Literal["json", "python"] | str = "python",
|
|
258
258
|
include: IncEx | None = None,
|
|
259
259
|
exclude: IncEx | None = None,
|
|
260
|
+
context: Any | None = None,
|
|
260
261
|
by_alias: bool | None = None,
|
|
261
262
|
exclude_unset: bool = False,
|
|
262
263
|
exclude_defaults: bool = False,
|
|
263
264
|
exclude_none: bool = False,
|
|
265
|
+
exclude_computed_fields: bool = False,
|
|
264
266
|
round_trip: bool = False,
|
|
265
267
|
warnings: bool | Literal["none", "warn", "error"] = True,
|
|
266
|
-
context: dict[str, Any] | None = None,
|
|
267
|
-
serialize_as_any: bool = False,
|
|
268
268
|
fallback: Callable[[Any], Any] | None = None,
|
|
269
|
+
serialize_as_any: bool = False,
|
|
269
270
|
) -> dict[str, Any]:
|
|
270
271
|
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump
|
|
271
272
|
|
|
@@ -273,16 +274,24 @@ class BaseModel(pydantic.BaseModel):
|
|
|
273
274
|
|
|
274
275
|
Args:
|
|
275
276
|
mode: The mode in which `to_python` should run.
|
|
276
|
-
If mode is 'json', the
|
|
277
|
-
If mode is 'python', the
|
|
278
|
-
include: A
|
|
279
|
-
exclude: A
|
|
277
|
+
If mode is 'json', the output will only contain JSON serializable types.
|
|
278
|
+
If mode is 'python', the output may contain non-JSON-serializable Python objects.
|
|
279
|
+
include: A set of fields to include in the output.
|
|
280
|
+
exclude: A set of fields to exclude from the output.
|
|
281
|
+
context: Additional context to pass to the serializer.
|
|
280
282
|
by_alias: Whether to use the field's alias in the dictionary key if defined.
|
|
281
|
-
exclude_unset: Whether to exclude fields that
|
|
282
|
-
exclude_defaults: Whether to exclude fields that are set to their default value
|
|
283
|
-
exclude_none: Whether to exclude fields that have a value of `None
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
exclude_unset: Whether to exclude fields that have not been explicitly set.
|
|
284
|
+
exclude_defaults: Whether to exclude fields that are set to their default value.
|
|
285
|
+
exclude_none: Whether to exclude fields that have a value of `None`.
|
|
286
|
+
exclude_computed_fields: Whether to exclude computed fields.
|
|
287
|
+
While this can be useful for round-tripping, it is usually recommended to use the dedicated
|
|
288
|
+
`round_trip` parameter instead.
|
|
289
|
+
round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
|
|
290
|
+
warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
|
|
291
|
+
"error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
|
|
292
|
+
fallback: A function to call when an unknown value is encountered. If not provided,
|
|
293
|
+
a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
|
|
294
|
+
serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
|
|
286
295
|
|
|
287
296
|
Returns:
|
|
288
297
|
A dictionary representation of the model.
|
|
@@ -299,6 +308,8 @@ class BaseModel(pydantic.BaseModel):
|
|
|
299
308
|
raise ValueError("serialize_as_any is only supported in Pydantic v2")
|
|
300
309
|
if fallback is not None:
|
|
301
310
|
raise ValueError("fallback is only supported in Pydantic v2")
|
|
311
|
+
if exclude_computed_fields != False:
|
|
312
|
+
raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
|
|
302
313
|
dumped = super().dict( # pyright: ignore[reportDeprecated]
|
|
303
314
|
include=include,
|
|
304
315
|
exclude=exclude,
|
|
@@ -311,19 +322,21 @@ class BaseModel(pydantic.BaseModel):
|
|
|
311
322
|
return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped
|
|
312
323
|
|
|
313
324
|
@override
|
|
314
|
-
def model_dump_json(
|
|
325
|
+
def model_dump_json( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
|
|
315
326
|
self,
|
|
316
327
|
*,
|
|
317
328
|
indent: int | None = None,
|
|
329
|
+
ensure_ascii: bool = False,
|
|
318
330
|
include: IncEx | None = None,
|
|
319
331
|
exclude: IncEx | None = None,
|
|
332
|
+
context: Any | None = None,
|
|
320
333
|
by_alias: bool | None = None,
|
|
321
334
|
exclude_unset: bool = False,
|
|
322
335
|
exclude_defaults: bool = False,
|
|
323
336
|
exclude_none: bool = False,
|
|
337
|
+
exclude_computed_fields: bool = False,
|
|
324
338
|
round_trip: bool = False,
|
|
325
339
|
warnings: bool | Literal["none", "warn", "error"] = True,
|
|
326
|
-
context: dict[str, Any] | None = None,
|
|
327
340
|
fallback: Callable[[Any], Any] | None = None,
|
|
328
341
|
serialize_as_any: bool = False,
|
|
329
342
|
) -> str:
|
|
@@ -355,6 +368,10 @@ class BaseModel(pydantic.BaseModel):
|
|
|
355
368
|
raise ValueError("serialize_as_any is only supported in Pydantic v2")
|
|
356
369
|
if fallback is not None:
|
|
357
370
|
raise ValueError("fallback is only supported in Pydantic v2")
|
|
371
|
+
if ensure_ascii != False:
|
|
372
|
+
raise ValueError("ensure_ascii is only supported in Pydantic v2")
|
|
373
|
+
if exclude_computed_fields != False:
|
|
374
|
+
raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
|
|
358
375
|
return super().json( # type: ignore[reportDeprecated]
|
|
359
376
|
indent=indent,
|
|
360
377
|
include=include,
|
|
@@ -34,7 +34,7 @@ def is_typeddict(tp: Type[Any]) -> bool:
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def is_literal_type(tp: Type[Any]) -> bool:
|
|
37
|
-
return get_origin(tp) in _LITERAL_TYPES
|
|
37
|
+
return get_origin(tp) in _LITERAL_TYPES # type: ignore[comparison-overlap]
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
|
|
@@ -373,9 +373,9 @@ def get_required_header(headers: HeadersLike, header: str) -> str:
|
|
|
373
373
|
lower_header = header.lower()
|
|
374
374
|
if is_mapping_t(headers):
|
|
375
375
|
# mypy doesn't understand the type narrowing here
|
|
376
|
-
for k, v in headers.items(): # type: ignore
|
|
377
|
-
if k.lower() == lower_header and isinstance(v, str):
|
|
378
|
-
return v
|
|
376
|
+
for k, v in headers.items(): # type: ignore[misc, has-type, attr-defined]
|
|
377
|
+
if k.lower() == lower_header and isinstance(v, str): # type: ignore[has-type]
|
|
378
|
+
return v # type: ignore[has-type]
|
|
379
379
|
|
|
380
380
|
# to deal with the case where the header looks like Stainless-Event-Id
|
|
381
381
|
intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize())
|
checkout_intents/_version.py
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any, Union, TypeVar, Callable, Iterable, cast
|
|
7
|
+
from typing_extensions import TypeGuard
|
|
6
8
|
|
|
7
9
|
import httpx
|
|
8
10
|
|
|
@@ -21,14 +23,24 @@ from .._response import (
|
|
|
21
23
|
async_to_raw_response_wrapper,
|
|
22
24
|
async_to_streamed_response_wrapper,
|
|
23
25
|
)
|
|
26
|
+
from .._exceptions import PollTimeoutError
|
|
24
27
|
from .._base_client import make_request_options
|
|
25
28
|
from ..types.buyer_param import BuyerParam
|
|
26
|
-
from ..types.checkout_intent import
|
|
29
|
+
from ..types.checkout_intent import (
|
|
30
|
+
CheckoutIntent,
|
|
31
|
+
FailedCheckoutIntent,
|
|
32
|
+
CompletedCheckoutIntent,
|
|
33
|
+
AwaitingConfirmationCheckoutIntent,
|
|
34
|
+
)
|
|
27
35
|
from ..types.payment_method_param import PaymentMethodParam
|
|
28
36
|
from ..types.variant_selection_param import VariantSelectionParam
|
|
29
37
|
|
|
30
38
|
__all__ = ["CheckoutIntentsResource", "AsyncCheckoutIntentsResource"]
|
|
31
39
|
|
|
40
|
+
T = TypeVar("T", bound=CheckoutIntent)
|
|
41
|
+
|
|
42
|
+
logger: logging.Logger = logging.getLogger(__name__)
|
|
43
|
+
|
|
32
44
|
|
|
33
45
|
class CheckoutIntentsResource(SyncAPIResource):
|
|
34
46
|
@cached_property
|
|
@@ -218,6 +230,365 @@ class CheckoutIntentsResource(SyncAPIResource):
|
|
|
218
230
|
),
|
|
219
231
|
)
|
|
220
232
|
|
|
233
|
+
def _poll_until(
|
|
234
|
+
self,
|
|
235
|
+
id: str,
|
|
236
|
+
condition: Callable[[CheckoutIntent], TypeGuard[T]],
|
|
237
|
+
*,
|
|
238
|
+
poll_interval: float = 5.0,
|
|
239
|
+
max_attempts: int = 120,
|
|
240
|
+
extra_headers: Headers | None = None,
|
|
241
|
+
extra_query: Query | None = None,
|
|
242
|
+
extra_body: Body | None = None,
|
|
243
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
244
|
+
) -> T:
|
|
245
|
+
"""
|
|
246
|
+
A helper to poll a checkout intent until a specific condition is met.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
id: The checkout intent ID to poll
|
|
250
|
+
condition: A callable that returns True when the desired state is reached
|
|
251
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
252
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
253
|
+
extra_headers: Send extra headers
|
|
254
|
+
extra_query: Add additional query parameters to the request
|
|
255
|
+
extra_body: Add additional JSON properties to the request
|
|
256
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
The checkout intent once the condition is met
|
|
260
|
+
|
|
261
|
+
Raises:
|
|
262
|
+
CheckoutIntentsError: If the maximum number of attempts is reached without the condition being met
|
|
263
|
+
"""
|
|
264
|
+
if max_attempts < 1:
|
|
265
|
+
logger.warning(
|
|
266
|
+
"[Checkout Intents SDK] Invalid max_attempts value: %s. max_attempts must be >= 1. "
|
|
267
|
+
"Defaulting to 1 to ensure at least one polling attempt.",
|
|
268
|
+
max_attempts,
|
|
269
|
+
)
|
|
270
|
+
max_attempts = 1
|
|
271
|
+
|
|
272
|
+
attempts = 0
|
|
273
|
+
|
|
274
|
+
# Build headers for polling
|
|
275
|
+
poll_headers: dict[str, str] = {
|
|
276
|
+
"X-Stainless-Poll-Helper": "true",
|
|
277
|
+
"X-Stainless-Custom-Poll-Interval": str(int(poll_interval * 1000)),
|
|
278
|
+
}
|
|
279
|
+
if extra_headers:
|
|
280
|
+
for k, v in extra_headers.items():
|
|
281
|
+
if not isinstance(v, Omit):
|
|
282
|
+
poll_headers[k] = v # type: ignore[assignment]
|
|
283
|
+
|
|
284
|
+
while attempts < max_attempts:
|
|
285
|
+
# Use with_raw_response to access response headers
|
|
286
|
+
response = self.with_raw_response.retrieve(
|
|
287
|
+
id,
|
|
288
|
+
extra_headers=poll_headers,
|
|
289
|
+
extra_query=extra_query,
|
|
290
|
+
extra_body=extra_body,
|
|
291
|
+
timeout=timeout,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
intent = response.parse()
|
|
295
|
+
|
|
296
|
+
# Check if condition is met
|
|
297
|
+
if condition(intent):
|
|
298
|
+
return intent
|
|
299
|
+
|
|
300
|
+
attempts += 1
|
|
301
|
+
|
|
302
|
+
# If we've reached max attempts, throw an error
|
|
303
|
+
if attempts >= max_attempts:
|
|
304
|
+
raise PollTimeoutError(
|
|
305
|
+
intent_id=id,
|
|
306
|
+
attempts=attempts,
|
|
307
|
+
poll_interval=poll_interval,
|
|
308
|
+
max_attempts=max_attempts,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Check if server suggests a polling interval
|
|
312
|
+
sleep_interval = poll_interval
|
|
313
|
+
header_interval = response.headers.get("retry-after-ms")
|
|
314
|
+
if header_interval:
|
|
315
|
+
try:
|
|
316
|
+
header_interval_ms = int(header_interval)
|
|
317
|
+
sleep_interval = header_interval_ms / 1000.0
|
|
318
|
+
except ValueError:
|
|
319
|
+
pass # Ignore invalid header values
|
|
320
|
+
|
|
321
|
+
# Sleep before next poll
|
|
322
|
+
self._sleep(sleep_interval)
|
|
323
|
+
|
|
324
|
+
# This should never be reached due to the throw above, but TypeScript needs it
|
|
325
|
+
raise PollTimeoutError(
|
|
326
|
+
intent_id=id,
|
|
327
|
+
attempts=attempts,
|
|
328
|
+
poll_interval=poll_interval,
|
|
329
|
+
max_attempts=max_attempts,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def poll_until_completed(
|
|
333
|
+
self,
|
|
334
|
+
id: str,
|
|
335
|
+
*,
|
|
336
|
+
poll_interval: float = 5.0,
|
|
337
|
+
max_attempts: int = 120,
|
|
338
|
+
extra_headers: Headers | None = None,
|
|
339
|
+
extra_query: Query | None = None,
|
|
340
|
+
extra_body: Body | None = None,
|
|
341
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
342
|
+
) -> Union[CompletedCheckoutIntent, FailedCheckoutIntent]:
|
|
343
|
+
"""
|
|
344
|
+
A helper to poll a checkout intent until it reaches a completed state
|
|
345
|
+
(completed or failed).
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
id: The checkout intent ID to poll
|
|
349
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
350
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
351
|
+
extra_headers: Send extra headers
|
|
352
|
+
extra_query: Add additional query parameters to the request
|
|
353
|
+
extra_body: Add additional JSON properties to the request
|
|
354
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
The checkout intent once it reaches completed or failed state
|
|
358
|
+
|
|
359
|
+
Example:
|
|
360
|
+
```python
|
|
361
|
+
checkout_intent = client.checkout_intents.poll_until_completed("id")
|
|
362
|
+
if checkout_intent.state == "completed":
|
|
363
|
+
print("Order placed successfully!")
|
|
364
|
+
elif checkout_intent.state == "failed":
|
|
365
|
+
print("Order failed:", checkout_intent.failure_reason)
|
|
366
|
+
```
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
def is_completed(
|
|
370
|
+
intent: CheckoutIntent,
|
|
371
|
+
) -> TypeGuard[Union[CompletedCheckoutIntent, FailedCheckoutIntent]]:
|
|
372
|
+
return intent.state in ("completed", "failed")
|
|
373
|
+
|
|
374
|
+
return self._poll_until(
|
|
375
|
+
id,
|
|
376
|
+
is_completed,
|
|
377
|
+
poll_interval=poll_interval,
|
|
378
|
+
max_attempts=max_attempts,
|
|
379
|
+
extra_headers=extra_headers,
|
|
380
|
+
extra_query=extra_query,
|
|
381
|
+
extra_body=extra_body,
|
|
382
|
+
timeout=timeout,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
def poll_until_awaiting_confirmation(
|
|
386
|
+
self,
|
|
387
|
+
id: str,
|
|
388
|
+
*,
|
|
389
|
+
poll_interval: float = 5.0,
|
|
390
|
+
max_attempts: int = 120,
|
|
391
|
+
extra_headers: Headers | None = None,
|
|
392
|
+
extra_query: Query | None = None,
|
|
393
|
+
extra_body: Body | None = None,
|
|
394
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
395
|
+
) -> Union[AwaitingConfirmationCheckoutIntent, FailedCheckoutIntent]:
|
|
396
|
+
"""
|
|
397
|
+
A helper to poll a checkout intent until it's ready for confirmation
|
|
398
|
+
(awaiting_confirmation state) or has failed. This is typically used after
|
|
399
|
+
creating a checkout intent to wait for the offer to be retrieved from the merchant.
|
|
400
|
+
|
|
401
|
+
The intent can reach awaiting_confirmation (success - ready to confirm) or failed
|
|
402
|
+
(offer retrieval failed). Always check the state after polling.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
id: The checkout intent ID to poll
|
|
406
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
407
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
408
|
+
extra_headers: Send extra headers
|
|
409
|
+
extra_query: Add additional query parameters to the request
|
|
410
|
+
extra_body: Add additional JSON properties to the request
|
|
411
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
The checkout intent once it reaches awaiting_confirmation or failed state
|
|
415
|
+
|
|
416
|
+
Example:
|
|
417
|
+
```python
|
|
418
|
+
intent = client.checkout_intents.poll_until_awaiting_confirmation("id")
|
|
419
|
+
|
|
420
|
+
if intent.state == "awaiting_confirmation":
|
|
421
|
+
# Review the offer before confirming
|
|
422
|
+
print("Total:", intent.offer.cost.total)
|
|
423
|
+
elif intent.state == "failed":
|
|
424
|
+
# Handle failure (e.g., offer retrieval failed, product out of stock)
|
|
425
|
+
print("Failed:", intent.failure_reason)
|
|
426
|
+
```
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
def is_awaiting_confirmation(
|
|
430
|
+
intent: CheckoutIntent,
|
|
431
|
+
) -> TypeGuard[Union[AwaitingConfirmationCheckoutIntent, FailedCheckoutIntent]]:
|
|
432
|
+
return intent.state in ("awaiting_confirmation", "failed")
|
|
433
|
+
|
|
434
|
+
return self._poll_until(
|
|
435
|
+
id,
|
|
436
|
+
is_awaiting_confirmation,
|
|
437
|
+
poll_interval=poll_interval,
|
|
438
|
+
max_attempts=max_attempts,
|
|
439
|
+
extra_headers=extra_headers,
|
|
440
|
+
extra_query=extra_query,
|
|
441
|
+
extra_body=extra_body,
|
|
442
|
+
timeout=timeout,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
def create_and_poll(
|
|
446
|
+
self,
|
|
447
|
+
*,
|
|
448
|
+
buyer: BuyerParam,
|
|
449
|
+
product_url: str,
|
|
450
|
+
quantity: float,
|
|
451
|
+
variant_selections: Iterable[VariantSelectionParam] | Omit = omit,
|
|
452
|
+
poll_interval: float = 5.0,
|
|
453
|
+
max_attempts: int = 120,
|
|
454
|
+
extra_headers: Headers | None = None,
|
|
455
|
+
extra_query: Query | None = None,
|
|
456
|
+
extra_body: Body | None = None,
|
|
457
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
458
|
+
) -> Union[AwaitingConfirmationCheckoutIntent, FailedCheckoutIntent]:
|
|
459
|
+
"""
|
|
460
|
+
A helper to create a checkout intent and poll until it's ready for confirmation.
|
|
461
|
+
This follows the Rye documented flow: create → poll until awaiting_confirmation.
|
|
462
|
+
|
|
463
|
+
After this method completes, you should review the offer (pricing, shipping, taxes)
|
|
464
|
+
with the user before calling confirm().
|
|
465
|
+
|
|
466
|
+
Args:
|
|
467
|
+
buyer: Buyer information
|
|
468
|
+
product_url: URL of the product to purchase
|
|
469
|
+
quantity: Quantity of the product
|
|
470
|
+
variant_selections: Product variant selections (optional)
|
|
471
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
472
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
473
|
+
extra_headers: Send extra headers
|
|
474
|
+
extra_query: Add additional query parameters to the request
|
|
475
|
+
extra_body: Add additional JSON properties to the request
|
|
476
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
The checkout intent once it reaches awaiting_confirmation or failed state
|
|
480
|
+
|
|
481
|
+
Example:
|
|
482
|
+
```python
|
|
483
|
+
# Phase 1: Create and wait for offer
|
|
484
|
+
intent = client.checkout_intents.create_and_poll(
|
|
485
|
+
buyer={
|
|
486
|
+
"address1": "123 Main St",
|
|
487
|
+
"city": "New York",
|
|
488
|
+
"country": "United States",
|
|
489
|
+
"email": "john.doe@example.com",
|
|
490
|
+
"first_name": "John",
|
|
491
|
+
"last_name": "Doe",
|
|
492
|
+
"phone": "+1234567890",
|
|
493
|
+
"postal_code": "10001",
|
|
494
|
+
"province": "NY",
|
|
495
|
+
},
|
|
496
|
+
product_url="https://example.com/product",
|
|
497
|
+
quantity=1,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
# Review the offer with the user
|
|
501
|
+
print("Total:", intent.offer.cost.total)
|
|
502
|
+
|
|
503
|
+
# Phase 2: Confirm with payment
|
|
504
|
+
completed = client.checkout_intents.confirm_and_poll(
|
|
505
|
+
intent.id, payment_method={"type": "stripe_token", "stripe_token": "tok_visa"}
|
|
506
|
+
)
|
|
507
|
+
```
|
|
508
|
+
"""
|
|
509
|
+
intent = self.create(
|
|
510
|
+
buyer=buyer,
|
|
511
|
+
product_url=product_url,
|
|
512
|
+
quantity=quantity,
|
|
513
|
+
variant_selections=variant_selections,
|
|
514
|
+
extra_headers=extra_headers,
|
|
515
|
+
extra_query=extra_query,
|
|
516
|
+
extra_body=extra_body,
|
|
517
|
+
timeout=timeout,
|
|
518
|
+
)
|
|
519
|
+
return self.poll_until_awaiting_confirmation(
|
|
520
|
+
intent.id,
|
|
521
|
+
poll_interval=poll_interval,
|
|
522
|
+
max_attempts=max_attempts,
|
|
523
|
+
extra_headers=extra_headers,
|
|
524
|
+
extra_query=extra_query,
|
|
525
|
+
extra_body=extra_body,
|
|
526
|
+
timeout=timeout,
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
def confirm_and_poll(
|
|
530
|
+
self,
|
|
531
|
+
id: str,
|
|
532
|
+
*,
|
|
533
|
+
payment_method: PaymentMethodParam,
|
|
534
|
+
poll_interval: float = 5.0,
|
|
535
|
+
max_attempts: int = 120,
|
|
536
|
+
extra_headers: Headers | None = None,
|
|
537
|
+
extra_query: Query | None = None,
|
|
538
|
+
extra_body: Body | None = None,
|
|
539
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
540
|
+
) -> Union[CompletedCheckoutIntent, FailedCheckoutIntent]:
|
|
541
|
+
"""
|
|
542
|
+
A helper to confirm a checkout intent and poll until it reaches a completed state
|
|
543
|
+
(completed or failed).
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
id: The checkout intent ID to confirm
|
|
547
|
+
payment_method: Payment method information
|
|
548
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
549
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
550
|
+
extra_headers: Send extra headers
|
|
551
|
+
extra_query: Add additional query parameters to the request
|
|
552
|
+
extra_body: Add additional JSON properties to the request
|
|
553
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
The checkout intent once it reaches completed or failed state
|
|
557
|
+
|
|
558
|
+
Example:
|
|
559
|
+
```python
|
|
560
|
+
checkout_intent = client.checkout_intents.confirm_and_poll(
|
|
561
|
+
"id",
|
|
562
|
+
payment_method={
|
|
563
|
+
"stripe_token": "tok_1RkrWWHGDlstla3f1Fc7ZrhH",
|
|
564
|
+
"type": "stripe_token",
|
|
565
|
+
},
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
if checkout_intent.state == "completed":
|
|
569
|
+
print("Order placed successfully!")
|
|
570
|
+
elif checkout_intent.state == "failed":
|
|
571
|
+
print("Order failed:", checkout_intent.failure_reason)
|
|
572
|
+
```
|
|
573
|
+
"""
|
|
574
|
+
intent = self.confirm(
|
|
575
|
+
id,
|
|
576
|
+
payment_method=payment_method,
|
|
577
|
+
extra_headers=extra_headers,
|
|
578
|
+
extra_query=extra_query,
|
|
579
|
+
extra_body=extra_body,
|
|
580
|
+
timeout=timeout,
|
|
581
|
+
)
|
|
582
|
+
return self.poll_until_completed(
|
|
583
|
+
intent.id,
|
|
584
|
+
poll_interval=poll_interval,
|
|
585
|
+
max_attempts=max_attempts,
|
|
586
|
+
extra_headers=extra_headers,
|
|
587
|
+
extra_query=extra_query,
|
|
588
|
+
extra_body=extra_body,
|
|
589
|
+
timeout=timeout,
|
|
590
|
+
)
|
|
591
|
+
|
|
221
592
|
|
|
222
593
|
class AsyncCheckoutIntentsResource(AsyncAPIResource):
|
|
223
594
|
@cached_property
|
|
@@ -407,6 +778,365 @@ class AsyncCheckoutIntentsResource(AsyncAPIResource):
|
|
|
407
778
|
),
|
|
408
779
|
)
|
|
409
780
|
|
|
781
|
+
async def _poll_until(
|
|
782
|
+
self,
|
|
783
|
+
id: str,
|
|
784
|
+
condition: Callable[[CheckoutIntent], TypeGuard[T]],
|
|
785
|
+
*,
|
|
786
|
+
poll_interval: float = 5.0,
|
|
787
|
+
max_attempts: int = 120,
|
|
788
|
+
extra_headers: Headers | None = None,
|
|
789
|
+
extra_query: Query | None = None,
|
|
790
|
+
extra_body: Body | None = None,
|
|
791
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
792
|
+
) -> T:
|
|
793
|
+
"""
|
|
794
|
+
A helper to poll a checkout intent until a specific condition is met.
|
|
795
|
+
|
|
796
|
+
Args:
|
|
797
|
+
id: The checkout intent ID to poll
|
|
798
|
+
condition: A callable that returns True when the desired state is reached
|
|
799
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
800
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
801
|
+
extra_headers: Send extra headers
|
|
802
|
+
extra_query: Add additional query parameters to the request
|
|
803
|
+
extra_body: Add additional JSON properties to the request
|
|
804
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
805
|
+
|
|
806
|
+
Returns:
|
|
807
|
+
The checkout intent once the condition is met
|
|
808
|
+
|
|
809
|
+
Raises:
|
|
810
|
+
CheckoutIntentsError: If the maximum number of attempts is reached without the condition being met
|
|
811
|
+
"""
|
|
812
|
+
if max_attempts < 1:
|
|
813
|
+
logger.warning(
|
|
814
|
+
"[Checkout Intents SDK] Invalid max_attempts value: %s. max_attempts must be >= 1. "
|
|
815
|
+
"Defaulting to 1 to ensure at least one polling attempt.",
|
|
816
|
+
max_attempts,
|
|
817
|
+
)
|
|
818
|
+
max_attempts = 1
|
|
819
|
+
|
|
820
|
+
attempts = 0
|
|
821
|
+
|
|
822
|
+
# Build headers for polling
|
|
823
|
+
poll_headers: dict[str, str] = {
|
|
824
|
+
"X-Stainless-Poll-Helper": "true",
|
|
825
|
+
"X-Stainless-Custom-Poll-Interval": str(int(poll_interval * 1000)),
|
|
826
|
+
}
|
|
827
|
+
if extra_headers:
|
|
828
|
+
for k, v in extra_headers.items():
|
|
829
|
+
if not isinstance(v, Omit):
|
|
830
|
+
poll_headers[k] = v # type: ignore[assignment]
|
|
831
|
+
|
|
832
|
+
while attempts < max_attempts:
|
|
833
|
+
# Use with_raw_response to access response headers
|
|
834
|
+
response = await self.with_raw_response.retrieve(
|
|
835
|
+
id,
|
|
836
|
+
extra_headers=poll_headers,
|
|
837
|
+
extra_query=extra_query,
|
|
838
|
+
extra_body=extra_body,
|
|
839
|
+
timeout=timeout,
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
intent = await response.parse()
|
|
843
|
+
|
|
844
|
+
# Check if condition is met
|
|
845
|
+
if condition(intent):
|
|
846
|
+
return intent
|
|
847
|
+
|
|
848
|
+
attempts += 1
|
|
849
|
+
|
|
850
|
+
# If we've reached max attempts, throw an error
|
|
851
|
+
if attempts >= max_attempts:
|
|
852
|
+
raise PollTimeoutError(
|
|
853
|
+
intent_id=id,
|
|
854
|
+
attempts=attempts,
|
|
855
|
+
poll_interval=poll_interval,
|
|
856
|
+
max_attempts=max_attempts,
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
# Check if server suggests a polling interval
|
|
860
|
+
sleep_interval = poll_interval
|
|
861
|
+
header_interval = response.headers.get("retry-after-ms")
|
|
862
|
+
if header_interval:
|
|
863
|
+
try:
|
|
864
|
+
header_interval_ms = int(header_interval)
|
|
865
|
+
sleep_interval = header_interval_ms / 1000.0
|
|
866
|
+
except ValueError:
|
|
867
|
+
pass # Ignore invalid header values
|
|
868
|
+
|
|
869
|
+
# Sleep before next poll
|
|
870
|
+
await self._sleep(sleep_interval)
|
|
871
|
+
|
|
872
|
+
# This should never be reached due to the throw above, but TypeScript needs it
|
|
873
|
+
raise PollTimeoutError(
|
|
874
|
+
intent_id=id,
|
|
875
|
+
attempts=attempts,
|
|
876
|
+
poll_interval=poll_interval,
|
|
877
|
+
max_attempts=max_attempts,
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
async def poll_until_completed(
|
|
881
|
+
self,
|
|
882
|
+
id: str,
|
|
883
|
+
*,
|
|
884
|
+
poll_interval: float = 5.0,
|
|
885
|
+
max_attempts: int = 120,
|
|
886
|
+
extra_headers: Headers | None = None,
|
|
887
|
+
extra_query: Query | None = None,
|
|
888
|
+
extra_body: Body | None = None,
|
|
889
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
890
|
+
) -> Union[CompletedCheckoutIntent, FailedCheckoutIntent]:
|
|
891
|
+
"""
|
|
892
|
+
A helper to poll a checkout intent until it reaches a completed state
|
|
893
|
+
(completed or failed).
|
|
894
|
+
|
|
895
|
+
Args:
|
|
896
|
+
id: The checkout intent ID to poll
|
|
897
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
898
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
899
|
+
extra_headers: Send extra headers
|
|
900
|
+
extra_query: Add additional query parameters to the request
|
|
901
|
+
extra_body: Add additional JSON properties to the request
|
|
902
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
903
|
+
|
|
904
|
+
Returns:
|
|
905
|
+
The checkout intent once it reaches completed or failed state
|
|
906
|
+
|
|
907
|
+
Example:
|
|
908
|
+
```python
|
|
909
|
+
checkout_intent = await client.checkout_intents.poll_until_completed("id")
|
|
910
|
+
if checkout_intent.state == "completed":
|
|
911
|
+
print("Order placed successfully!")
|
|
912
|
+
elif checkout_intent.state == "failed":
|
|
913
|
+
print("Order failed:", checkout_intent.failure_reason)
|
|
914
|
+
```
|
|
915
|
+
"""
|
|
916
|
+
|
|
917
|
+
def is_completed(
|
|
918
|
+
intent: CheckoutIntent,
|
|
919
|
+
) -> TypeGuard[Union[CompletedCheckoutIntent, FailedCheckoutIntent]]:
|
|
920
|
+
return intent.state in ("completed", "failed")
|
|
921
|
+
|
|
922
|
+
return await self._poll_until(
|
|
923
|
+
id,
|
|
924
|
+
is_completed,
|
|
925
|
+
poll_interval=poll_interval,
|
|
926
|
+
max_attempts=max_attempts,
|
|
927
|
+
extra_headers=extra_headers,
|
|
928
|
+
extra_query=extra_query,
|
|
929
|
+
extra_body=extra_body,
|
|
930
|
+
timeout=timeout,
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
async def poll_until_awaiting_confirmation(
|
|
934
|
+
self,
|
|
935
|
+
id: str,
|
|
936
|
+
*,
|
|
937
|
+
poll_interval: float = 5.0,
|
|
938
|
+
max_attempts: int = 120,
|
|
939
|
+
extra_headers: Headers | None = None,
|
|
940
|
+
extra_query: Query | None = None,
|
|
941
|
+
extra_body: Body | None = None,
|
|
942
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
943
|
+
) -> Union[AwaitingConfirmationCheckoutIntent, FailedCheckoutIntent]:
|
|
944
|
+
"""
|
|
945
|
+
A helper to poll a checkout intent until it's ready for confirmation
|
|
946
|
+
(awaiting_confirmation state) or has failed. This is typically used after
|
|
947
|
+
creating a checkout intent to wait for the offer to be retrieved from the merchant.
|
|
948
|
+
|
|
949
|
+
The intent can reach awaiting_confirmation (success - ready to confirm) or failed
|
|
950
|
+
(offer retrieval failed). Always check the state after polling.
|
|
951
|
+
|
|
952
|
+
Args:
|
|
953
|
+
id: The checkout intent ID to poll
|
|
954
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
955
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
956
|
+
extra_headers: Send extra headers
|
|
957
|
+
extra_query: Add additional query parameters to the request
|
|
958
|
+
extra_body: Add additional JSON properties to the request
|
|
959
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
960
|
+
|
|
961
|
+
Returns:
|
|
962
|
+
The checkout intent once it reaches awaiting_confirmation or failed state
|
|
963
|
+
|
|
964
|
+
Example:
|
|
965
|
+
```python
|
|
966
|
+
intent = await client.checkout_intents.poll_until_awaiting_confirmation("id")
|
|
967
|
+
|
|
968
|
+
if intent.state == "awaiting_confirmation":
|
|
969
|
+
# Review the offer before confirming
|
|
970
|
+
print("Total:", intent.offer.cost.total)
|
|
971
|
+
elif intent.state == "failed":
|
|
972
|
+
# Handle failure (e.g., offer retrieval failed, product out of stock)
|
|
973
|
+
print("Failed:", intent.failure_reason)
|
|
974
|
+
```
|
|
975
|
+
"""
|
|
976
|
+
|
|
977
|
+
def is_awaiting_confirmation(
|
|
978
|
+
intent: CheckoutIntent,
|
|
979
|
+
) -> TypeGuard[Union[AwaitingConfirmationCheckoutIntent, FailedCheckoutIntent]]:
|
|
980
|
+
return intent.state in ("awaiting_confirmation", "failed")
|
|
981
|
+
|
|
982
|
+
return await self._poll_until(
|
|
983
|
+
id,
|
|
984
|
+
is_awaiting_confirmation,
|
|
985
|
+
poll_interval=poll_interval,
|
|
986
|
+
max_attempts=max_attempts,
|
|
987
|
+
extra_headers=extra_headers,
|
|
988
|
+
extra_query=extra_query,
|
|
989
|
+
extra_body=extra_body,
|
|
990
|
+
timeout=timeout,
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
async def create_and_poll(
|
|
994
|
+
self,
|
|
995
|
+
*,
|
|
996
|
+
buyer: BuyerParam,
|
|
997
|
+
product_url: str,
|
|
998
|
+
quantity: float,
|
|
999
|
+
variant_selections: Iterable[VariantSelectionParam] | Omit = omit,
|
|
1000
|
+
poll_interval: float = 5.0,
|
|
1001
|
+
max_attempts: int = 120,
|
|
1002
|
+
extra_headers: Headers | None = None,
|
|
1003
|
+
extra_query: Query | None = None,
|
|
1004
|
+
extra_body: Body | None = None,
|
|
1005
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
1006
|
+
) -> Union[AwaitingConfirmationCheckoutIntent, FailedCheckoutIntent]:
|
|
1007
|
+
"""
|
|
1008
|
+
A helper to create a checkout intent and poll until it's ready for confirmation.
|
|
1009
|
+
This follows the Rye documented flow: create → poll until awaiting_confirmation.
|
|
1010
|
+
|
|
1011
|
+
After this method completes, you should review the offer (pricing, shipping, taxes)
|
|
1012
|
+
with the user before calling confirm().
|
|
1013
|
+
|
|
1014
|
+
Args:
|
|
1015
|
+
buyer: Buyer information
|
|
1016
|
+
product_url: URL of the product to purchase
|
|
1017
|
+
quantity: Quantity of the product
|
|
1018
|
+
variant_selections: Product variant selections (optional)
|
|
1019
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
1020
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
1021
|
+
extra_headers: Send extra headers
|
|
1022
|
+
extra_query: Add additional query parameters to the request
|
|
1023
|
+
extra_body: Add additional JSON properties to the request
|
|
1024
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
1025
|
+
|
|
1026
|
+
Returns:
|
|
1027
|
+
The checkout intent once it reaches awaiting_confirmation or failed state
|
|
1028
|
+
|
|
1029
|
+
Example:
|
|
1030
|
+
```python
|
|
1031
|
+
# Phase 1: Create and wait for offer
|
|
1032
|
+
intent = await client.checkout_intents.create_and_poll(
|
|
1033
|
+
buyer={
|
|
1034
|
+
"address1": "123 Main St",
|
|
1035
|
+
"city": "New York",
|
|
1036
|
+
"country": "United States",
|
|
1037
|
+
"email": "john.doe@example.com",
|
|
1038
|
+
"first_name": "John",
|
|
1039
|
+
"last_name": "Doe",
|
|
1040
|
+
"phone": "+1234567890",
|
|
1041
|
+
"postal_code": "10001",
|
|
1042
|
+
"province": "NY",
|
|
1043
|
+
},
|
|
1044
|
+
product_url="https://example.com/product",
|
|
1045
|
+
quantity=1,
|
|
1046
|
+
)
|
|
1047
|
+
|
|
1048
|
+
# Review the offer with the user
|
|
1049
|
+
print("Total:", intent.offer.cost.total)
|
|
1050
|
+
|
|
1051
|
+
# Phase 2: Confirm with payment
|
|
1052
|
+
completed = await client.checkout_intents.confirm_and_poll(
|
|
1053
|
+
intent.id, payment_method={"type": "stripe_token", "stripe_token": "tok_visa"}
|
|
1054
|
+
)
|
|
1055
|
+
```
|
|
1056
|
+
"""
|
|
1057
|
+
intent = await self.create(
|
|
1058
|
+
buyer=buyer,
|
|
1059
|
+
product_url=product_url,
|
|
1060
|
+
quantity=quantity,
|
|
1061
|
+
variant_selections=variant_selections,
|
|
1062
|
+
extra_headers=extra_headers,
|
|
1063
|
+
extra_query=extra_query,
|
|
1064
|
+
extra_body=extra_body,
|
|
1065
|
+
timeout=timeout,
|
|
1066
|
+
)
|
|
1067
|
+
return await self.poll_until_awaiting_confirmation(
|
|
1068
|
+
intent.id,
|
|
1069
|
+
poll_interval=poll_interval,
|
|
1070
|
+
max_attempts=max_attempts,
|
|
1071
|
+
extra_headers=extra_headers,
|
|
1072
|
+
extra_query=extra_query,
|
|
1073
|
+
extra_body=extra_body,
|
|
1074
|
+
timeout=timeout,
|
|
1075
|
+
)
|
|
1076
|
+
|
|
1077
|
+
async def confirm_and_poll(
|
|
1078
|
+
self,
|
|
1079
|
+
id: str,
|
|
1080
|
+
*,
|
|
1081
|
+
payment_method: PaymentMethodParam,
|
|
1082
|
+
poll_interval: float = 5.0,
|
|
1083
|
+
max_attempts: int = 120,
|
|
1084
|
+
extra_headers: Headers | None = None,
|
|
1085
|
+
extra_query: Query | None = None,
|
|
1086
|
+
extra_body: Body | None = None,
|
|
1087
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
1088
|
+
) -> Union[CompletedCheckoutIntent, FailedCheckoutIntent]:
|
|
1089
|
+
"""
|
|
1090
|
+
A helper to confirm a checkout intent and poll until it reaches a completed state
|
|
1091
|
+
(completed or failed).
|
|
1092
|
+
|
|
1093
|
+
Args:
|
|
1094
|
+
id: The checkout intent ID to confirm
|
|
1095
|
+
payment_method: Payment method information
|
|
1096
|
+
poll_interval: The interval in seconds between polling attempts (default: 5.0)
|
|
1097
|
+
max_attempts: The maximum number of polling attempts before timing out (default: 120)
|
|
1098
|
+
extra_headers: Send extra headers
|
|
1099
|
+
extra_query: Add additional query parameters to the request
|
|
1100
|
+
extra_body: Add additional JSON properties to the request
|
|
1101
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
1102
|
+
|
|
1103
|
+
Returns:
|
|
1104
|
+
The checkout intent once it reaches completed or failed state
|
|
1105
|
+
|
|
1106
|
+
Example:
|
|
1107
|
+
```python
|
|
1108
|
+
checkout_intent = await client.checkout_intents.confirm_and_poll(
|
|
1109
|
+
"id",
|
|
1110
|
+
payment_method={
|
|
1111
|
+
"stripe_token": "tok_1RkrWWHGDlstla3f1Fc7ZrhH",
|
|
1112
|
+
"type": "stripe_token",
|
|
1113
|
+
},
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
if checkout_intent.state == "completed":
|
|
1117
|
+
print("Order placed successfully!")
|
|
1118
|
+
elif checkout_intent.state == "failed":
|
|
1119
|
+
print("Order failed:", checkout_intent.failure_reason)
|
|
1120
|
+
```
|
|
1121
|
+
"""
|
|
1122
|
+
intent = await self.confirm(
|
|
1123
|
+
id,
|
|
1124
|
+
payment_method=payment_method,
|
|
1125
|
+
extra_headers=extra_headers,
|
|
1126
|
+
extra_query=extra_query,
|
|
1127
|
+
extra_body=extra_body,
|
|
1128
|
+
timeout=timeout,
|
|
1129
|
+
)
|
|
1130
|
+
return await self.poll_until_completed(
|
|
1131
|
+
intent.id,
|
|
1132
|
+
poll_interval=poll_interval,
|
|
1133
|
+
max_attempts=max_attempts,
|
|
1134
|
+
extra_headers=extra_headers,
|
|
1135
|
+
extra_query=extra_query,
|
|
1136
|
+
extra_body=extra_body,
|
|
1137
|
+
timeout=timeout,
|
|
1138
|
+
)
|
|
1139
|
+
|
|
410
1140
|
|
|
411
1141
|
class CheckoutIntentsResourceWithRawResponse:
|
|
412
1142
|
def __init__(self, checkout_intents: CheckoutIntentsResource) -> None:
|
|
@@ -424,6 +1154,18 @@ class CheckoutIntentsResourceWithRawResponse:
|
|
|
424
1154
|
self.confirm = to_raw_response_wrapper(
|
|
425
1155
|
checkout_intents.confirm,
|
|
426
1156
|
)
|
|
1157
|
+
self.poll_until_completed = to_raw_response_wrapper(
|
|
1158
|
+
checkout_intents.poll_until_completed,
|
|
1159
|
+
)
|
|
1160
|
+
self.poll_until_awaiting_confirmation = to_raw_response_wrapper(
|
|
1161
|
+
checkout_intents.poll_until_awaiting_confirmation,
|
|
1162
|
+
)
|
|
1163
|
+
self.create_and_poll = to_raw_response_wrapper(
|
|
1164
|
+
checkout_intents.create_and_poll,
|
|
1165
|
+
)
|
|
1166
|
+
self.confirm_and_poll = to_raw_response_wrapper(
|
|
1167
|
+
checkout_intents.confirm_and_poll,
|
|
1168
|
+
)
|
|
427
1169
|
|
|
428
1170
|
|
|
429
1171
|
class AsyncCheckoutIntentsResourceWithRawResponse:
|
|
@@ -442,6 +1184,18 @@ class AsyncCheckoutIntentsResourceWithRawResponse:
|
|
|
442
1184
|
self.confirm = async_to_raw_response_wrapper(
|
|
443
1185
|
checkout_intents.confirm,
|
|
444
1186
|
)
|
|
1187
|
+
self.poll_until_completed = async_to_raw_response_wrapper(
|
|
1188
|
+
checkout_intents.poll_until_completed,
|
|
1189
|
+
)
|
|
1190
|
+
self.poll_until_awaiting_confirmation = async_to_raw_response_wrapper(
|
|
1191
|
+
checkout_intents.poll_until_awaiting_confirmation,
|
|
1192
|
+
)
|
|
1193
|
+
self.create_and_poll = async_to_raw_response_wrapper(
|
|
1194
|
+
checkout_intents.create_and_poll,
|
|
1195
|
+
)
|
|
1196
|
+
self.confirm_and_poll = async_to_raw_response_wrapper(
|
|
1197
|
+
checkout_intents.confirm_and_poll,
|
|
1198
|
+
)
|
|
445
1199
|
|
|
446
1200
|
|
|
447
1201
|
class CheckoutIntentsResourceWithStreamingResponse:
|
|
@@ -460,6 +1214,18 @@ class CheckoutIntentsResourceWithStreamingResponse:
|
|
|
460
1214
|
self.confirm = to_streamed_response_wrapper(
|
|
461
1215
|
checkout_intents.confirm,
|
|
462
1216
|
)
|
|
1217
|
+
self.poll_until_completed = to_streamed_response_wrapper(
|
|
1218
|
+
checkout_intents.poll_until_completed,
|
|
1219
|
+
)
|
|
1220
|
+
self.poll_until_awaiting_confirmation = to_streamed_response_wrapper(
|
|
1221
|
+
checkout_intents.poll_until_awaiting_confirmation,
|
|
1222
|
+
)
|
|
1223
|
+
self.create_and_poll = to_streamed_response_wrapper(
|
|
1224
|
+
checkout_intents.create_and_poll,
|
|
1225
|
+
)
|
|
1226
|
+
self.confirm_and_poll = to_streamed_response_wrapper(
|
|
1227
|
+
checkout_intents.confirm_and_poll,
|
|
1228
|
+
)
|
|
463
1229
|
|
|
464
1230
|
|
|
465
1231
|
class AsyncCheckoutIntentsResourceWithStreamingResponse:
|
|
@@ -478,3 +1244,15 @@ class AsyncCheckoutIntentsResourceWithStreamingResponse:
|
|
|
478
1244
|
self.confirm = async_to_streamed_response_wrapper(
|
|
479
1245
|
checkout_intents.confirm,
|
|
480
1246
|
)
|
|
1247
|
+
self.poll_until_completed = async_to_streamed_response_wrapper(
|
|
1248
|
+
checkout_intents.poll_until_completed,
|
|
1249
|
+
)
|
|
1250
|
+
self.poll_until_awaiting_confirmation = async_to_streamed_response_wrapper(
|
|
1251
|
+
checkout_intents.poll_until_awaiting_confirmation,
|
|
1252
|
+
)
|
|
1253
|
+
self.create_and_poll = async_to_streamed_response_wrapper(
|
|
1254
|
+
checkout_intents.create_and_poll,
|
|
1255
|
+
)
|
|
1256
|
+
self.confirm_and_poll = async_to_streamed_response_wrapper(
|
|
1257
|
+
checkout_intents.confirm_and_poll,
|
|
1258
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: checkout-intents
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: The official Python library for the Checkout Intents API
|
|
5
5
|
Project-URL: Homepage, https://github.com/rye-com/checkout-intents-python
|
|
6
6
|
Project-URL: Repository, https://github.com/rye-com/checkout-intents-python
|
|
@@ -88,6 +88,98 @@ we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
|
|
|
88
88
|
to add `CHECKOUT_INTENTS_API_KEY="My API Key"` to your `.env` file
|
|
89
89
|
so that your API Key is not stored in source control.
|
|
90
90
|
|
|
91
|
+
### Polling Helpers
|
|
92
|
+
|
|
93
|
+
This SDK includes helper methods for the asynchronous checkout flow. The recommended pattern follows Rye's two-phase checkout:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from checkout_intents import CheckoutIntents
|
|
97
|
+
|
|
98
|
+
client = CheckoutIntents()
|
|
99
|
+
|
|
100
|
+
# Phase 1: Create and wait for offer
|
|
101
|
+
intent = client.checkout_intents.create_and_poll(
|
|
102
|
+
buyer={
|
|
103
|
+
"address1": "123 Main St",
|
|
104
|
+
"city": "New York",
|
|
105
|
+
"country": "US",
|
|
106
|
+
"email": "john.doe@example.com",
|
|
107
|
+
"first_name": "John",
|
|
108
|
+
"last_name": "Doe",
|
|
109
|
+
"phone": "5555555555",
|
|
110
|
+
"postal_code": "10001",
|
|
111
|
+
"province": "NY",
|
|
112
|
+
},
|
|
113
|
+
product_url="https://example.com/product",
|
|
114
|
+
quantity=1,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Handle failure during offer retrieval
|
|
118
|
+
if intent.state == "failed":
|
|
119
|
+
print(f"Failed: {intent.failure_reason}")
|
|
120
|
+
else:
|
|
121
|
+
# Review pricing with user
|
|
122
|
+
print(f"Total: {intent.offer.cost.total}")
|
|
123
|
+
|
|
124
|
+
# Phase 2: Confirm and wait for completion
|
|
125
|
+
completed = client.checkout_intents.confirm_and_poll(
|
|
126
|
+
intent.id,
|
|
127
|
+
payment_method={
|
|
128
|
+
"type": "stripe_token",
|
|
129
|
+
"stripe_token": "tok_visa",
|
|
130
|
+
},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
print(f"Status: {completed.state}")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
For more examples, see the [`examples/`](https://github.com/rye-com/checkout-intents-python/tree/main/./examples) directory:
|
|
137
|
+
|
|
138
|
+
- [`complete-checkout-intent.py`](https://github.com/rye-com/checkout-intents-python/tree/main/./examples/complete-checkout-intent.py) - Recommended two-phase flow
|
|
139
|
+
- [`error-handling.py`](https://github.com/rye-com/checkout-intents-python/tree/main/./examples/error-handling.py) - Timeout and error handling with `PollTimeoutError`
|
|
140
|
+
|
|
141
|
+
Available polling methods:
|
|
142
|
+
|
|
143
|
+
- `create_and_poll()` - Create and poll until offer is ready (awaiting_confirmation or failed)
|
|
144
|
+
- `confirm_and_poll()` - Confirm and poll until completion (completed or failed)
|
|
145
|
+
- `poll_until_completed()` - Poll until completed or failed
|
|
146
|
+
- `poll_until_awaiting_confirmation()` - Poll until offer is ready or failed
|
|
147
|
+
|
|
148
|
+
All polling methods support customizable timeouts:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# Configure polling behavior
|
|
152
|
+
intent = client.checkout_intents.poll_until_completed(
|
|
153
|
+
intent_id,
|
|
154
|
+
poll_interval=5.0, # Poll every 5 seconds (default)
|
|
155
|
+
max_attempts=120, # Try up to 120 times, ~10 minutes (default)
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Handling Polling Timeouts
|
|
160
|
+
|
|
161
|
+
When polling operations exceed `max_attempts`, a `PollTimeoutError` is raised with helpful context:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from checkout_intents import CheckoutIntents, PollTimeoutError
|
|
165
|
+
|
|
166
|
+
client = CheckoutIntents()
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
intent = client.checkout_intents.poll_until_completed(
|
|
170
|
+
intent_id,
|
|
171
|
+
poll_interval=5.0,
|
|
172
|
+
max_attempts=60,
|
|
173
|
+
)
|
|
174
|
+
except PollTimeoutError as e:
|
|
175
|
+
print(f"Polling timed out for intent: {e.intent_id}")
|
|
176
|
+
print(f"Attempted {e.attempts} times over {(e.attempts * e.poll_interval) / 1000}s")
|
|
177
|
+
|
|
178
|
+
# You can retrieve the current state manually
|
|
179
|
+
current_intent = client.checkout_intents.retrieve(e.intent_id)
|
|
180
|
+
print(f"Current state: {current_intent.state}")
|
|
181
|
+
```
|
|
182
|
+
|
|
91
183
|
## Async usage
|
|
92
184
|
|
|
93
185
|
Simply import `AsyncCheckoutIntents` instead of `CheckoutIntents` and use `await` with each API call:
|
|
@@ -258,6 +350,26 @@ Error codes are as follows:
|
|
|
258
350
|
| 429 | `RateLimitError` |
|
|
259
351
|
| >=500 | `InternalServerError` |
|
|
260
352
|
| N/A | `APIConnectionError` |
|
|
353
|
+
| N/A | `PollTimeoutError` |
|
|
354
|
+
|
|
355
|
+
### Polling Timeout Errors
|
|
356
|
+
|
|
357
|
+
When using polling helper methods, if the operation exceeds the configured `max_attempts`, a `PollTimeoutError` is raised. This error includes detailed context about the timeout:
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
from checkout_intents import CheckoutIntents, PollTimeoutError
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
intent = client.checkout_intents.poll_until_completed("intent_id")
|
|
364
|
+
except PollTimeoutError as e:
|
|
365
|
+
# Access timeout details
|
|
366
|
+
print(f"Intent ID: {e.intent_id}")
|
|
367
|
+
print(f"Attempts: {e.attempts}")
|
|
368
|
+
print(f"Poll interval: {e.poll_interval}s")
|
|
369
|
+
print(f"Max attempts: {e.max_attempts}")
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
See the [error-handling.py example](https://github.com/rye-com/checkout-intents-python/tree/main/./examples/error-handling.py) for more detailed timeout handling patterns.
|
|
261
373
|
|
|
262
374
|
### Retries
|
|
263
375
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
checkout_intents/__init__.py,sha256=
|
|
1
|
+
checkout_intents/__init__.py,sha256=5m0_Ktnyya4CJ0vdWZbAv7qZMeR1VnXqN_MJtH3RJTM,2833
|
|
2
2
|
checkout_intents/_base_client.py,sha256=eB43s-6F0_5tyH_e9uX-HAovJJu6D9DdMGVcI2zP4qc,67057
|
|
3
|
-
checkout_intents/_client.py,sha256=
|
|
3
|
+
checkout_intents/_client.py,sha256=px36nDPT8_lNISKkVjZhI_AZvFJ5KwRyVjQjPycia0s,21798
|
|
4
4
|
checkout_intents/_compat.py,sha256=DQBVORjFb33zch24jzkhM14msvnzY7mmSmgDLaVFUM8,6562
|
|
5
5
|
checkout_intents/_constants.py,sha256=S14PFzyN9-I31wiV7SmIlL5Ga0MLHxdvegInGdXH7tM,462
|
|
6
|
-
checkout_intents/_exceptions.py,sha256=
|
|
6
|
+
checkout_intents/_exceptions.py,sha256=oQn7Y6LzccgRDOtdg20l68bXtgf2tgVNKIrufJPDsuk,4252
|
|
7
7
|
checkout_intents/_files.py,sha256=KnEzGi_O756MvKyJ4fOCW_u3JhOeWPQ4RsmDvqihDQU,3545
|
|
8
|
-
checkout_intents/_models.py,sha256=
|
|
8
|
+
checkout_intents/_models.py,sha256=m6hDet9vdfyY7TuzbLCBYhMPoIN6hddOZp9qDZ-zfVU,31995
|
|
9
9
|
checkout_intents/_qs.py,sha256=craIKyvPktJ94cvf9zn8j8ekG9dWJzhWv0ob34lIOv4,4828
|
|
10
10
|
checkout_intents/_resource.py,sha256=X-eWffEBAgzTZvakLqNUjwgidMKfBFRmCeeOiuko4lg,1154
|
|
11
11
|
checkout_intents/_response.py,sha256=sLqxWBxzmVqPsdr2x5z6d87h8jb1U-Huvh2YFg-U6nE,28876
|
|
12
12
|
checkout_intents/_streaming.py,sha256=XOY20ljJEmOSf-nT8_KvPZipmgllR5sO400ePyMVLIY,10185
|
|
13
13
|
checkout_intents/_types.py,sha256=4l2pVH65co-2pua9stzq-WV2VcGg9Hl4nRHd0lz5cko,7246
|
|
14
|
-
checkout_intents/_version.py,sha256=
|
|
14
|
+
checkout_intents/_version.py,sha256=bLy0NHnQnIHbjqtwiAOnmDFC0CDZ2s-DkgnwlFLRcps,168
|
|
15
15
|
checkout_intents/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
checkout_intents/_utils/__init__.py,sha256=7fch0GT9zpNnErbciSpUNa-SjTxxjY6kxHxKMOM4AGs,2305
|
|
17
|
-
checkout_intents/_utils/_compat.py,sha256=
|
|
17
|
+
checkout_intents/_utils/_compat.py,sha256=rN17SSvjMoQE1GmKFTLniRuG1sKj2WAD5VjdLPeRlF0,1231
|
|
18
18
|
checkout_intents/_utils/_datetime_parse.py,sha256=bABTs0Bc6rabdFvnIwXjEhWL15TcRgWZ_6XGTqN8xUk,4204
|
|
19
19
|
checkout_intents/_utils/_logs.py,sha256=dw1slZVPfWp0Z_jBGEQvr8TDY0uu3V7pJuexM_MG4pk,804
|
|
20
20
|
checkout_intents/_utils/_proxy.py,sha256=aglnj2yBTDyGX9Akk2crZHrl10oqRmceUy2Zp008XEs,1975
|
|
@@ -24,11 +24,11 @@ checkout_intents/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD
|
|
|
24
24
|
checkout_intents/_utils/_sync.py,sha256=HBnZkkBnzxtwOZe0212C4EyoRvxhTVtTrLFDz2_xVCg,1589
|
|
25
25
|
checkout_intents/_utils/_transform.py,sha256=NjCzmnfqYrsAikUHQig6N9QfuTVbKipuP3ur9mcNF-E,15951
|
|
26
26
|
checkout_intents/_utils/_typing.py,sha256=N_5PPuFNsaygbtA_npZd98SVN1LQQvFTKL6bkWPBZGU,4786
|
|
27
|
-
checkout_intents/_utils/_utils.py,sha256=
|
|
27
|
+
checkout_intents/_utils/_utils.py,sha256=g9ftElB09kVT6EVfCIlD_nUfANhDX5_vZO61FDWoIQI,12334
|
|
28
28
|
checkout_intents/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
|
|
29
29
|
checkout_intents/resources/__init__.py,sha256=sHRW0nwo1GnFBFPoTNohkEbvOk7au-R3MdfVn_yb8Ds,1120
|
|
30
30
|
checkout_intents/resources/brands.py,sha256=LylMXefUeP4G540jtG8QHRAKwTMZHKI7vJhWnACS5Dc,6349
|
|
31
|
-
checkout_intents/resources/checkout_intents.py,sha256=
|
|
31
|
+
checkout_intents/resources/checkout_intents.py,sha256=Q8BIh96n3nh6DbcBW7tSmCrJv7wF12QOTxyoD9Dit8s,49522
|
|
32
32
|
checkout_intents/types/__init__.py,sha256=QHuqIegrR01arvPOp6GI2C8VUw3o37KNxr2KDtjh0eE,1098
|
|
33
33
|
checkout_intents/types/base_checkout_intent.py,sha256=56Qp3ssWYmcdu6A9Z0pvL3ewm1LTvQHiboiJ7Aogx2M,644
|
|
34
34
|
checkout_intents/types/brand_retrieve_response.py,sha256=1Z7yjrXl3NO1u9NFO5lDBJ-sucpEH501MUfUS_XeqbY,550
|
|
@@ -44,7 +44,7 @@ checkout_intents/types/payment_method.py,sha256=Mw85dZNa0z7Sr5FoQ9_nOUOjjKEHoE2x
|
|
|
44
44
|
checkout_intents/types/payment_method_param.py,sha256=jbqAao2w7wxEHd_nshOthmZ53Ww7I4EiFmddpa_ejis,437
|
|
45
45
|
checkout_intents/types/variant_selection.py,sha256=M8O6CpcpIlFqfPlQW_wFcJ6rwt459n6dJkjLkLB1klk,493
|
|
46
46
|
checkout_intents/types/variant_selection_param.py,sha256=CMz4y1bDpuHNpnUmCcyeiYs6uwLBS9CJwo9iSpGMu-Q,589
|
|
47
|
-
checkout_intents-0.
|
|
48
|
-
checkout_intents-0.
|
|
49
|
-
checkout_intents-0.
|
|
50
|
-
checkout_intents-0.
|
|
47
|
+
checkout_intents-0.2.0.dist-info/METADATA,sha256=YDQk64h64eqBFNJjfG3vO_O4HvHQ686OmaTnzjPcfAY,21534
|
|
48
|
+
checkout_intents-0.2.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
49
|
+
checkout_intents-0.2.0.dist-info/licenses/LICENSE,sha256=dRDmL6lFnLaphTaman8kAc21qY1IQ_qsAETk1F-b6jY,1056
|
|
50
|
+
checkout_intents-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|