From b00918007e41c6cdb4923567cab949a628233aa7 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 14 May 2026 12:57:34 +0000
Subject: [PATCH 1/4] feat(api): stainless fixes
---
.stats.yml | 4 +-
README.md | 32 ++--
api.md | 2 +-
src/dodopayments/resources/subscriptions.py | 112 +-------------
src/dodopayments/types/__init__.py | 1 +
src/dodopayments/types/discount_detail.py | 67 +++++++++
src/dodopayments/types/payment.py | 64 +-------
src/dodopayments/types/subscription.py | 64 +-------
...bscription_update_payment_method_params.py | 12 +-
tests/api_resources/test_subscriptions.py | 142 ++++--------------
10 files changed, 133 insertions(+), 367 deletions(-)
create mode 100644 src/dodopayments/types/discount_detail.py
diff --git a/.stats.yml b/.stats.yml
index 99b4291e..5ce735e3 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 115
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/dodo-payments/dodo-payments-2ffbf67de53851e90df27e282f1728e26f502595ca18fc795bd85e52def12403.yml
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/dodo-payments/dodo-payments-dff7a713601915da806189ad3718e609ce2a9e5a8652e4f14f490ba6e5cdf7d7.yml
openapi_spec_hash: df1cb95258514ae75058a767d8b8f1f4
-config_hash: ef79873bb22333b064f390e64e9ddd49
+config_hash: 659f7bf2e55b94eba1282ed857478d74
diff --git a/README.md b/README.md
index d859baf9..7e0a6b71 100644
--- a/README.md
+++ b/README.md
@@ -46,8 +46,8 @@ client = DodoPayments(
checkout_session_response = client.checkout_sessions.create(
product_cart=[
{
- "product_id": "product_id",
- "quantity": 0,
+ "product_id": "pdt_example",
+ "quantity": 1,
}
],
)
@@ -79,8 +79,8 @@ async def main() -> None:
checkout_session_response = await client.checkout_sessions.create(
product_cart=[
{
- "product_id": "product_id",
- "quantity": 0,
+ "product_id": "pdt_example",
+ "quantity": 1,
}
],
)
@@ -122,8 +122,8 @@ async def main() -> None:
checkout_session_response = await client.checkout_sessions.create(
product_cart=[
{
- "product_id": "product_id",
- "quantity": 0,
+ "product_id": "pdt_example",
+ "quantity": 1,
}
],
)
@@ -243,8 +243,8 @@ try:
client.checkout_sessions.create(
product_cart=[
{
- "product_id": "product_id",
- "quantity": 0,
+ "product_id": "pdt_example",
+ "quantity": 1,
}
],
)
@@ -293,8 +293,8 @@ client = DodoPayments(
client.with_options(max_retries=5).checkout_sessions.create(
product_cart=[
{
- "product_id": "product_id",
- "quantity": 0,
+ "product_id": "pdt_example",
+ "quantity": 1,
}
],
)
@@ -323,8 +323,8 @@ client = DodoPayments(
client.with_options(timeout=5.0).checkout_sessions.create(
product_cart=[
{
- "product_id": "product_id",
- "quantity": 0,
+ "product_id": "pdt_example",
+ "quantity": 1,
}
],
)
@@ -370,8 +370,8 @@ from dodopayments import DodoPayments
client = DodoPayments()
response = client.checkout_sessions.with_raw_response.create(
product_cart=[{
- "product_id": "product_id",
- "quantity": 0,
+ "product_id": "pdt_example",
+ "quantity": 1,
}],
)
print(response.headers.get('X-My-Header'))
@@ -394,8 +394,8 @@ To stream the response body, use `.with_streaming_response` instead, which requi
with client.checkout_sessions.with_streaming_response.create(
product_cart=[
{
- "product_id": "product_id",
- "quantity": 0,
+ "product_id": "pdt_example",
+ "quantity": 1,
}
],
) as response:
diff --git a/api.md b/api.md
index c7320f2d..3542c908 100644
--- a/api.md
+++ b/api.md
@@ -350,7 +350,7 @@ Methods:
Types:
```python
-from dodopayments.types import Discount, DiscountType
+from dodopayments.types import Discount, DiscountDetail, DiscountType
```
Methods:
diff --git a/src/dodopayments/resources/subscriptions.py b/src/dodopayments/resources/subscriptions.py
index 72bd79ea..a292aca5 100644
--- a/src/dodopayments/resources/subscriptions.py
+++ b/src/dodopayments/resources/subscriptions.py
@@ -5,7 +5,7 @@
import typing_extensions
from typing import Dict, List, Union, Iterable, Optional
from datetime import datetime
-from typing_extensions import Literal, overload
+from typing_extensions import Literal
import httpx
@@ -23,7 +23,7 @@
subscription_retrieve_usage_history_params,
)
from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
-from .._utils import path_template, required_args, maybe_transform, async_maybe_transform
+from .._utils import path_template, maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -815,39 +815,11 @@ def retrieve_usage_history(
model=SubscriptionRetrieveUsageHistoryResponse,
)
- @overload
def update_payment_method(
self,
subscription_id: str,
*,
- type: Literal["new"],
- return_url: Optional[str] | Omit = omit,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> SubscriptionUpdatePaymentMethodResponse:
- """
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- ...
-
- @overload
- def update_payment_method(
- self,
- subscription_id: str,
- *,
- payment_method_id: str,
- type: Literal["existing"],
+ payment_method: subscription_update_payment_method_params.PaymentMethod,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -865,34 +837,12 @@ def update_payment_method(
timeout: Override the client-level default timeout for this request, in seconds
"""
- ...
-
- @required_args(["type"], ["payment_method_id", "type"])
- def update_payment_method(
- self,
- subscription_id: str,
- *,
- type: Literal["new"] | Literal["existing"],
- return_url: Optional[str] | Omit = omit,
- payment_method_id: str | Omit = omit,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> SubscriptionUpdatePaymentMethodResponse:
if not subscription_id:
raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}")
return self._post(
path_template("/subscriptions/{subscription_id}/update-payment-method", subscription_id=subscription_id),
body=maybe_transform(
- {
- "type": type,
- "return_url": return_url,
- "payment_method_id": payment_method_id,
- },
- subscription_update_payment_method_params.SubscriptionUpdatePaymentMethodParams,
+ payment_method, subscription_update_payment_method_params.SubscriptionUpdatePaymentMethodParams
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -1664,39 +1614,11 @@ def retrieve_usage_history(
model=SubscriptionRetrieveUsageHistoryResponse,
)
- @overload
async def update_payment_method(
self,
subscription_id: str,
*,
- type: Literal["new"],
- return_url: Optional[str] | Omit = omit,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> SubscriptionUpdatePaymentMethodResponse:
- """
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- ...
-
- @overload
- async def update_payment_method(
- self,
- subscription_id: str,
- *,
- payment_method_id: str,
- type: Literal["existing"],
+ payment_method: subscription_update_payment_method_params.PaymentMethod,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -1714,34 +1636,12 @@ async def update_payment_method(
timeout: Override the client-level default timeout for this request, in seconds
"""
- ...
-
- @required_args(["type"], ["payment_method_id", "type"])
- async def update_payment_method(
- self,
- subscription_id: str,
- *,
- type: Literal["new"] | Literal["existing"],
- return_url: Optional[str] | Omit = omit,
- payment_method_id: str | Omit = omit,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> SubscriptionUpdatePaymentMethodResponse:
if not subscription_id:
raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}")
return await self._post(
path_template("/subscriptions/{subscription_id}/update-payment-method", subscription_id=subscription_id),
body=await async_maybe_transform(
- {
- "type": type,
- "return_url": return_url,
- "payment_method_id": payment_method_id,
- },
- subscription_update_payment_method_params.SubscriptionUpdatePaymentMethodParams,
+ payment_method, subscription_update_payment_method_params.SubscriptionUpdatePaymentMethodParams
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
diff --git a/src/dodopayments/types/__init__.py b/src/dodopayments/types/__init__.py
index d99c508e..e47525a1 100644
--- a/src/dodopayments/types/__init__.py
+++ b/src/dodopayments/types/__init__.py
@@ -30,6 +30,7 @@
from .addon_response import AddonResponse as AddonResponse
from .dispute_status import DisputeStatus as DisputeStatus
from .billing_address import BillingAddress as BillingAddress
+from .discount_detail import DiscountDetail as DiscountDetail
from .filter_operator import FilterOperator as FilterOperator
from .webhook_details import WebhookDetails as WebhookDetails
from .refund_list_item import RefundListItem as RefundListItem
diff --git a/src/dodopayments/types/discount_detail.py b/src/dodopayments/types/discount_detail.py
new file mode 100644
index 00000000..1eb42f76
--- /dev/null
+++ b/src/dodopayments/types/discount_detail.py
@@ -0,0 +1,67 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+
+from .._models import BaseModel
+from .discount_type import DiscountType
+
+__all__ = ["DiscountDetail"]
+
+
+class DiscountDetail(BaseModel):
+ """
+ Response struct for a discount with its position in a stack and optional
+ cycle-tracking information (for subscriptions).
+ """
+
+ amount: int
+ """The discount amount (basis points for percentage, USD cents for flat)"""
+
+ business_id: str
+ """The business this discount belongs to"""
+
+ code: str
+ """The discount code"""
+
+ created_at: datetime
+ """Timestamp when the discount was created"""
+
+ discount_id: str
+ """The unique discount ID"""
+
+ metadata: Dict[str, str]
+ """Additional metadata"""
+
+ position: int
+ """Position of this discount in the stack (0-based)"""
+
+ preserve_on_plan_change: bool
+ """Whether this discount should be preserved when a subscription changes plans"""
+
+ restricted_to: List[str]
+ """List of product IDs to which this discount is restricted"""
+
+ times_used: int
+ """How many times this discount has been used"""
+
+ type: DiscountType
+ """The type of discount"""
+
+ cycles_remaining: Optional[int] = None
+ """
+ Remaining billing cycles for this discount on this subscription (None for
+ one-time payments)
+ """
+
+ expires_at: Optional[datetime] = None
+ """Optional date/time after which discount is expired"""
+
+ name: Optional[str] = None
+ """Name for the Discount"""
+
+ subscription_cycles: Optional[int] = None
+ """Number of subscription billing cycles this discount is valid for"""
+
+ usage_limit: Optional[int] = None
+ """Usage limit for this discount, if any"""
diff --git a/src/dodopayments/types/payment.py b/src/dodopayments/types/payment.py
index 0142487c..89669e50 100644
--- a/src/dodopayments/types/payment.py
+++ b/src/dodopayments/types/payment.py
@@ -7,73 +7,15 @@
from .._models import BaseModel
from .currency import Currency
from .country_code import CountryCode
-from .discount_type import DiscountType
from .intent_status import IntentStatus
from .billing_address import BillingAddress
+from .discount_detail import DiscountDetail
from .refund_list_item import RefundListItem
from .custom_field_response import CustomFieldResponse
from .payment_refund_status import PaymentRefundStatus
from .customer_limited_details import CustomerLimitedDetails
-__all__ = ["Payment", "Discount", "ProductCart"]
-
-
-class Discount(BaseModel):
- """
- Response struct for a discount with its position in a stack and optional
- cycle-tracking information (for subscriptions).
- """
-
- amount: int
- """The discount amount (basis points for percentage, USD cents for flat)"""
-
- business_id: str
- """The business this discount belongs to"""
-
- code: str
- """The discount code"""
-
- created_at: datetime
- """Timestamp when the discount was created"""
-
- discount_id: str
- """The unique discount ID"""
-
- metadata: Dict[str, str]
- """Additional metadata"""
-
- position: int
- """Position of this discount in the stack (0-based)"""
-
- preserve_on_plan_change: bool
- """Whether this discount should be preserved when a subscription changes plans"""
-
- restricted_to: List[str]
- """List of product IDs to which this discount is restricted"""
-
- times_used: int
- """How many times this discount has been used"""
-
- type: DiscountType
- """The type of discount"""
-
- cycles_remaining: Optional[int] = None
- """
- Remaining billing cycles for this discount on this subscription (None for
- one-time payments)
- """
-
- expires_at: Optional[datetime] = None
- """Optional date/time after which discount is expired"""
-
- name: Optional[str] = None
- """Name for the Discount"""
-
- subscription_cycles: Optional[int] = None
- """Number of subscription billing cycles this discount is valid for"""
-
- usage_limit: Optional[int] = None
- """Usage limit for this discount, if any"""
+__all__ = ["Payment", "ProductCart"]
class ProductCart(BaseModel):
@@ -163,7 +105,7 @@ class Payment(BaseModel):
discount_id: Optional[str] = None
"""DEPRECATED: Use discounts instead. Returns the first discount's ID if present."""
- discounts: Optional[List[Discount]] = None
+ discounts: Optional[List[DiscountDetail]] = None
"""All stacked discounts applied, ordered by position"""
error_code: Optional[str] = None
diff --git a/src/dodopayments/types/subscription.py b/src/dodopayments/types/subscription.py
index d99b0117..6109bd74 100644
--- a/src/dodopayments/types/subscription.py
+++ b/src/dodopayments/types/subscription.py
@@ -5,9 +5,9 @@
from .._models import BaseModel
from .currency import Currency
-from .discount_type import DiscountType
from .time_interval import TimeInterval
from .billing_address import BillingAddress
+from .discount_detail import DiscountDetail
from .subscription_status import SubscriptionStatus
from .cancellation_feedback import CancellationFeedback
from .custom_field_response import CustomFieldResponse
@@ -18,65 +18,7 @@
from .credit_entitlement_cart_response import CreditEntitlementCartResponse
from .meter_credit_entitlement_cart_response import MeterCreditEntitlementCartResponse
-__all__ = ["Subscription", "Discount"]
-
-
-class Discount(BaseModel):
- """
- Response struct for a discount with its position in a stack and optional
- cycle-tracking information (for subscriptions).
- """
-
- amount: int
- """The discount amount (basis points for percentage, USD cents for flat)"""
-
- business_id: str
- """The business this discount belongs to"""
-
- code: str
- """The discount code"""
-
- created_at: datetime
- """Timestamp when the discount was created"""
-
- discount_id: str
- """The unique discount ID"""
-
- metadata: Dict[str, str]
- """Additional metadata"""
-
- position: int
- """Position of this discount in the stack (0-based)"""
-
- preserve_on_plan_change: bool
- """Whether this discount should be preserved when a subscription changes plans"""
-
- restricted_to: List[str]
- """List of product IDs to which this discount is restricted"""
-
- times_used: int
- """How many times this discount has been used"""
-
- type: DiscountType
- """The type of discount"""
-
- cycles_remaining: Optional[int] = None
- """
- Remaining billing cycles for this discount on this subscription (None for
- one-time payments)
- """
-
- expires_at: Optional[datetime] = None
- """Optional date/time after which discount is expired"""
-
- name: Optional[str] = None
- """Name for the Discount"""
-
- subscription_cycles: Optional[int] = None
- """Number of subscription billing cycles this discount is valid for"""
-
- usage_limit: Optional[int] = None
- """Usage limit for this discount, if any"""
+__all__ = ["Subscription"]
class Subscription(BaseModel):
@@ -178,7 +120,7 @@ class Subscription(BaseModel):
discount_id: Optional[str] = None
"""DEPRECATED: Use discounts instead. Returns the first discount's ID if present."""
- discounts: Optional[List[Discount]] = None
+ discounts: Optional[List[DiscountDetail]] = None
"""All stacked discounts applied, ordered by position"""
expires_at: Optional[datetime] = None
diff --git a/src/dodopayments/types/subscription_update_payment_method_params.py b/src/dodopayments/types/subscription_update_payment_method_params.py
index 88f915b8..d51dd50e 100644
--- a/src/dodopayments/types/subscription_update_payment_method_params.py
+++ b/src/dodopayments/types/subscription_update_payment_method_params.py
@@ -5,19 +5,23 @@
from typing import Union, Optional
from typing_extensions import Literal, Required, TypeAlias, TypedDict
-__all__ = ["SubscriptionUpdatePaymentMethodParams", "New", "Existing"]
+__all__ = ["SubscriptionUpdatePaymentMethodParams", "PaymentMethod", "PaymentMethodNew", "PaymentMethodExisting"]
-class New(TypedDict, total=False):
+class SubscriptionUpdatePaymentMethodParams(TypedDict, total=False):
+ payment_method: Required[PaymentMethod]
+
+
+class PaymentMethodNew(TypedDict, total=False):
type: Required[Literal["new"]]
return_url: Optional[str]
-class Existing(TypedDict, total=False):
+class PaymentMethodExisting(TypedDict, total=False):
payment_method_id: Required[str]
type: Required[Literal["existing"]]
-SubscriptionUpdatePaymentMethodParams: TypeAlias = Union[New, Existing]
+PaymentMethod: TypeAlias = Union[PaymentMethodNew, PaymentMethodExisting]
diff --git a/tests/api_resources/test_subscriptions.py b/tests/api_resources/test_subscriptions.py
index aa4988f3..fdb10757 100644
--- a/tests/api_resources/test_subscriptions.py
+++ b/tests/api_resources/test_subscriptions.py
@@ -623,71 +623,29 @@ def test_path_params_retrieve_usage_history(self, client: DodoPayments) -> None:
)
@parametrize
- def test_method_update_payment_method_overload_1(self, client: DodoPayments) -> None:
+ def test_method_update_payment_method(self, client: DodoPayments) -> None:
subscription = client.subscriptions.update_payment_method(
subscription_id="subscription_id",
- type="new",
+ payment_method={"type": "new"},
)
assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
@parametrize
- def test_method_update_payment_method_with_all_params_overload_1(self, client: DodoPayments) -> None:
+ def test_method_update_payment_method_with_all_params(self, client: DodoPayments) -> None:
subscription = client.subscriptions.update_payment_method(
subscription_id="subscription_id",
- type="new",
- return_url="return_url",
- )
- assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
-
- @parametrize
- def test_raw_response_update_payment_method_overload_1(self, client: DodoPayments) -> None:
- response = client.subscriptions.with_raw_response.update_payment_method(
- subscription_id="subscription_id",
- type="new",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- subscription = response.parse()
- assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
-
- @parametrize
- def test_streaming_response_update_payment_method_overload_1(self, client: DodoPayments) -> None:
- with client.subscriptions.with_streaming_response.update_payment_method(
- subscription_id="subscription_id",
- type="new",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- subscription = response.parse()
- assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_update_payment_method_overload_1(self, client: DodoPayments) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `subscription_id` but received ''"):
- client.subscriptions.with_raw_response.update_payment_method(
- subscription_id="",
- type="new",
- )
-
- @parametrize
- def test_method_update_payment_method_overload_2(self, client: DodoPayments) -> None:
- subscription = client.subscriptions.update_payment_method(
- subscription_id="subscription_id",
- payment_method_id="payment_method_id",
- type="existing",
+ payment_method={
+ "type": "new",
+ "return_url": "return_url",
+ },
)
assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
@parametrize
- def test_raw_response_update_payment_method_overload_2(self, client: DodoPayments) -> None:
+ def test_raw_response_update_payment_method(self, client: DodoPayments) -> None:
response = client.subscriptions.with_raw_response.update_payment_method(
subscription_id="subscription_id",
- payment_method_id="payment_method_id",
- type="existing",
+ payment_method={"type": "new"},
)
assert response.is_closed is True
@@ -696,11 +654,10 @@ def test_raw_response_update_payment_method_overload_2(self, client: DodoPayment
assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
@parametrize
- def test_streaming_response_update_payment_method_overload_2(self, client: DodoPayments) -> None:
+ def test_streaming_response_update_payment_method(self, client: DodoPayments) -> None:
with client.subscriptions.with_streaming_response.update_payment_method(
subscription_id="subscription_id",
- payment_method_id="payment_method_id",
- type="existing",
+ payment_method={"type": "new"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -711,12 +668,11 @@ def test_streaming_response_update_payment_method_overload_2(self, client: DodoP
assert cast(Any, response.is_closed) is True
@parametrize
- def test_path_params_update_payment_method_overload_2(self, client: DodoPayments) -> None:
+ def test_path_params_update_payment_method(self, client: DodoPayments) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `subscription_id` but received ''"):
client.subscriptions.with_raw_response.update_payment_method(
subscription_id="",
- payment_method_id="payment_method_id",
- type="existing",
+ payment_method={"type": "new"},
)
@@ -1318,73 +1274,29 @@ async def test_path_params_retrieve_usage_history(self, async_client: AsyncDodoP
)
@parametrize
- async def test_method_update_payment_method_overload_1(self, async_client: AsyncDodoPayments) -> None:
+ async def test_method_update_payment_method(self, async_client: AsyncDodoPayments) -> None:
subscription = await async_client.subscriptions.update_payment_method(
subscription_id="subscription_id",
- type="new",
+ payment_method={"type": "new"},
)
assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
@parametrize
- async def test_method_update_payment_method_with_all_params_overload_1(
- self, async_client: AsyncDodoPayments
- ) -> None:
+ async def test_method_update_payment_method_with_all_params(self, async_client: AsyncDodoPayments) -> None:
subscription = await async_client.subscriptions.update_payment_method(
subscription_id="subscription_id",
- type="new",
- return_url="return_url",
- )
- assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
-
- @parametrize
- async def test_raw_response_update_payment_method_overload_1(self, async_client: AsyncDodoPayments) -> None:
- response = await async_client.subscriptions.with_raw_response.update_payment_method(
- subscription_id="subscription_id",
- type="new",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- subscription = await response.parse()
- assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
-
- @parametrize
- async def test_streaming_response_update_payment_method_overload_1(self, async_client: AsyncDodoPayments) -> None:
- async with async_client.subscriptions.with_streaming_response.update_payment_method(
- subscription_id="subscription_id",
- type="new",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- subscription = await response.parse()
- assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_update_payment_method_overload_1(self, async_client: AsyncDodoPayments) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `subscription_id` but received ''"):
- await async_client.subscriptions.with_raw_response.update_payment_method(
- subscription_id="",
- type="new",
- )
-
- @parametrize
- async def test_method_update_payment_method_overload_2(self, async_client: AsyncDodoPayments) -> None:
- subscription = await async_client.subscriptions.update_payment_method(
- subscription_id="subscription_id",
- payment_method_id="payment_method_id",
- type="existing",
+ payment_method={
+ "type": "new",
+ "return_url": "return_url",
+ },
)
assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
@parametrize
- async def test_raw_response_update_payment_method_overload_2(self, async_client: AsyncDodoPayments) -> None:
+ async def test_raw_response_update_payment_method(self, async_client: AsyncDodoPayments) -> None:
response = await async_client.subscriptions.with_raw_response.update_payment_method(
subscription_id="subscription_id",
- payment_method_id="payment_method_id",
- type="existing",
+ payment_method={"type": "new"},
)
assert response.is_closed is True
@@ -1393,11 +1305,10 @@ async def test_raw_response_update_payment_method_overload_2(self, async_client:
assert_matches_type(SubscriptionUpdatePaymentMethodResponse, subscription, path=["response"])
@parametrize
- async def test_streaming_response_update_payment_method_overload_2(self, async_client: AsyncDodoPayments) -> None:
+ async def test_streaming_response_update_payment_method(self, async_client: AsyncDodoPayments) -> None:
async with async_client.subscriptions.with_streaming_response.update_payment_method(
subscription_id="subscription_id",
- payment_method_id="payment_method_id",
- type="existing",
+ payment_method={"type": "new"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -1408,10 +1319,9 @@ async def test_streaming_response_update_payment_method_overload_2(self, async_c
assert cast(Any, response.is_closed) is True
@parametrize
- async def test_path_params_update_payment_method_overload_2(self, async_client: AsyncDodoPayments) -> None:
+ async def test_path_params_update_payment_method(self, async_client: AsyncDodoPayments) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `subscription_id` but received ''"):
await async_client.subscriptions.with_raw_response.update_payment_method(
subscription_id="",
- payment_method_id="payment_method_id",
- type="existing",
+ payment_method={"type": "new"},
)
From 73101fab3a997da1203fd0936339eabf7e609d6b Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 21 May 2026 13:19:37 +0000
Subject: [PATCH 2/4] feat(api): updated openapi spec to v1.99.0 and added
missing endpoints as well
---
.stats.yml | 8 +-
api.md | 69 +-
src/dodopayments/_client.py | 41 +
src/dodopayments/resources/__init__.py | 14 +
.../resources/checkout_sessions.py | 24 +
.../resources/invoices/payments.py | 80 ++
src/dodopayments/resources/payments.py | 17 +-
.../resources/product_collections/__init__.py | 33 +
.../product_collections/groups/__init__.py | 33 +
.../product_collections/groups/groups.py | 436 ++++++++++
.../product_collections/groups/items.py | 370 ++++++++
.../product_collections.py | 803 ++++++++++++++++++
src/dodopayments/resources/subscriptions.py | 31 +-
src/dodopayments/types/__init__.py | 31 +-
.../types/checkout_session_create_params.py | 7 +
.../types/checkout_session_flags_param.py | 11 +
.../types/checkout_session_preview_params.py | 7 +
.../types/checkout_session_response.py | 20 +
src/dodopayments/types/filter_type.py | 27 +
src/dodopayments/types/filter_type_param.py | 26 +
src/dodopayments/types/github_permission.py | 7 +
.../types/integration_config_param.py | 5 +-
.../types/integration_config_response.py | 5 +-
src/dodopayments/types/meter.py | 8 +-
src/dodopayments/types/meter_create_params.py | 6 +-
src/dodopayments/types/meter_filter.py | 100 +--
src/dodopayments/types/meter_filter_param.py | 103 +--
.../types/one_time_product_cart_item.py | 9 -
.../types/one_time_product_cart_item_param.py | 21 -
src/dodopayments/types/payment.py | 11 +-
.../types/payment_create_params.py | 25 +-
.../types/payment_create_response.py | 18 +-
.../types/payment_method_types.py | 1 +
src/dodopayments/types/product_collection.py | 35 +
.../types/product_collection_create_params.py | 24 +
.../types/product_collection_list_params.py | 21 +
.../types/product_collection_list_response.py | 31 +
.../product_collection_unarchive_response.py | 18 +
...product_collection_update_images_params.py | 13 +
...oduct_collection_update_images_response.py | 15 +
.../types/product_collection_update_params.py | 27 +
.../types/product_collections/__init__.py | 11 +
.../group_create_params.py | 25 +
.../group_product_param.py | 16 +
.../group_update_params.py | 29 +
.../product_collections/groups/__init__.py | 8 +
.../groups/item_create_params.py | 17 +
.../groups/item_create_response.py | 10 +
.../groups/item_update_params.py | 16 +
.../groups/product_collection_product.py | 50 ++
.../product_collection_group_details_param.py | 25 +
.../product_collection_group_response.py | 18 +
src/dodopayments/types/subscription.py | 6 +
.../types/subscription_create_params.py | 25 +-
.../types/subscription_create_response.py | 11 +-
.../types/subscription_list_response.py | 6 +
.../types/subscription_update_params.py | 8 +
src/dodopayments/types/webhook_event_type.py | 6 +-
tests/api_resources/invoices/test_payments.py | 102 +++
.../product_collections/__init__.py | 1 +
.../product_collections/groups/__init__.py | 1 +
.../product_collections/groups/test_items.py | 382 +++++++++
.../product_collections/test_groups.py | 354 ++++++++
tests/api_resources/test_checkout_sessions.py | 8 +
tests/api_resources/test_payments.py | 2 +
.../api_resources/test_product_collections.py | 646 ++++++++++++++
tests/api_resources/test_subscriptions.py | 4 +
67 files changed, 4106 insertions(+), 272 deletions(-)
create mode 100644 src/dodopayments/resources/product_collections/__init__.py
create mode 100644 src/dodopayments/resources/product_collections/groups/__init__.py
create mode 100644 src/dodopayments/resources/product_collections/groups/groups.py
create mode 100644 src/dodopayments/resources/product_collections/groups/items.py
create mode 100644 src/dodopayments/resources/product_collections/product_collections.py
create mode 100644 src/dodopayments/types/filter_type.py
create mode 100644 src/dodopayments/types/filter_type_param.py
create mode 100644 src/dodopayments/types/github_permission.py
delete mode 100644 src/dodopayments/types/one_time_product_cart_item_param.py
create mode 100644 src/dodopayments/types/product_collection.py
create mode 100644 src/dodopayments/types/product_collection_create_params.py
create mode 100644 src/dodopayments/types/product_collection_list_params.py
create mode 100644 src/dodopayments/types/product_collection_list_response.py
create mode 100644 src/dodopayments/types/product_collection_unarchive_response.py
create mode 100644 src/dodopayments/types/product_collection_update_images_params.py
create mode 100644 src/dodopayments/types/product_collection_update_images_response.py
create mode 100644 src/dodopayments/types/product_collection_update_params.py
create mode 100644 src/dodopayments/types/product_collections/__init__.py
create mode 100644 src/dodopayments/types/product_collections/group_create_params.py
create mode 100644 src/dodopayments/types/product_collections/group_product_param.py
create mode 100644 src/dodopayments/types/product_collections/group_update_params.py
create mode 100644 src/dodopayments/types/product_collections/groups/__init__.py
create mode 100644 src/dodopayments/types/product_collections/groups/item_create_params.py
create mode 100644 src/dodopayments/types/product_collections/groups/item_create_response.py
create mode 100644 src/dodopayments/types/product_collections/groups/item_update_params.py
create mode 100644 src/dodopayments/types/product_collections/groups/product_collection_product.py
create mode 100644 src/dodopayments/types/product_collections/product_collection_group_details_param.py
create mode 100644 src/dodopayments/types/product_collections/product_collection_group_response.py
create mode 100644 tests/api_resources/product_collections/__init__.py
create mode 100644 tests/api_resources/product_collections/groups/__init__.py
create mode 100644 tests/api_resources/product_collections/groups/test_items.py
create mode 100644 tests/api_resources/product_collections/test_groups.py
create mode 100644 tests/api_resources/test_product_collections.py
diff --git a/.stats.yml b/.stats.yml
index 5ce735e3..e02293cc 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 115
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/dodo-payments/dodo-payments-dff7a713601915da806189ad3718e609ce2a9e5a8652e4f14f490ba6e5cdf7d7.yml
-openapi_spec_hash: df1cb95258514ae75058a767d8b8f1f4
-config_hash: 659f7bf2e55b94eba1282ed857478d74
+configured_endpoints: 129
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/dodo-payments/dodo-payments-40cfcf3dec892796c176c3300b60660fa0372e2253e682d94196c496f7c9948a.yml
+openapi_spec_hash: 3ae2b45a65b68c25007835eafc758f3e
+config_hash: 4313eddd18b96948c4a83bac4564acb3
diff --git a/api.md b/api.md
index 3542c908..651f416b 100644
--- a/api.md
+++ b/api.md
@@ -106,6 +106,7 @@ Methods:
Methods:
- client.invoices.payments.retrieve(payment_id) -> BinaryAPIResponse
+- client.invoices.payments.retrieve_payout(payout_id) -> BinaryAPIResponse
- client.invoices.payments.retrieve_refund(refund_id) -> BinaryAPIResponse
# Licenses
@@ -534,7 +535,14 @@ Methods:
Types:
```python
-from dodopayments.types import Conjunction, FilterOperator, Meter, MeterAggregation, MeterFilter
+from dodopayments.types import (
+ Conjunction,
+ FilterOperator,
+ FilterType,
+ Meter,
+ MeterAggregation,
+ MeterFilter,
+)
```
Methods:
@@ -604,6 +612,7 @@ Types:
from dodopayments.types import (
Entitlement,
EntitlementIntegrationType,
+ GitHubPermission,
IntegrationConfig,
IntegrationConfigResponse,
)
@@ -642,3 +651,61 @@ Methods:
- client.entitlements.grants.list(id, \*\*params) -> SyncDefaultPageNumberPagination[EntitlementGrant]
- client.entitlements.grants.revoke(grant_id, \*, id) -> EntitlementGrant
+
+# ProductCollections
+
+Types:
+
+```python
+from dodopayments.types import (
+ ProductCollection,
+ ProductCollectionListResponse,
+ ProductCollectionUnarchiveResponse,
+ ProductCollectionUpdateImagesResponse,
+)
+```
+
+Methods:
+
+- client.product_collections.create(\*\*params) -> ProductCollection
+- client.product_collections.retrieve(id) -> ProductCollection
+- client.product_collections.update(id, \*\*params) -> None
+- client.product_collections.list(\*\*params) -> SyncDefaultPageNumberPagination[ProductCollectionListResponse]
+- client.product_collections.delete(id) -> None
+- client.product_collections.unarchive(id) -> ProductCollectionUnarchiveResponse
+- client.product_collections.update_images(id, \*\*params) -> ProductCollectionUpdateImagesResponse
+
+## Groups
+
+Types:
+
+```python
+from dodopayments.types.product_collections import (
+ GroupProduct,
+ ProductCollectionGroupDetails,
+ ProductCollectionGroupResponse,
+)
+```
+
+Methods:
+
+- client.product_collections.groups.create(id, \*\*params) -> ProductCollectionGroupResponse
+- client.product_collections.groups.update(group_id, \*, id, \*\*params) -> None
+- client.product_collections.groups.delete(group_id, \*, id) -> None
+
+### Items
+
+Types:
+
+```python
+from dodopayments.types.product_collections.groups import (
+ ProductCollectionProduct,
+ ItemCreateResponse,
+)
+```
+
+Methods:
+
+- client.product_collections.groups.items.create(group_id, \*, id, \*\*params) -> ItemCreateResponse
+- client.product_collections.groups.items.update(item_id, \*, id, group_id, \*\*params) -> None
+- client.product_collections.groups.items.delete(item_id, \*, id, group_id) -> None
diff --git a/src/dodopayments/_client.py b/src/dodopayments/_client.py
index 77863044..8df6d59b 100644
--- a/src/dodopayments/_client.py
+++ b/src/dodopayments/_client.py
@@ -57,6 +57,7 @@
subscriptions,
checkout_sessions,
credit_entitlements,
+ product_collections,
license_key_instances,
)
from .resources.misc import MiscResource, AsyncMiscResource
@@ -84,6 +85,10 @@
CreditEntitlementsResource,
AsyncCreditEntitlementsResource,
)
+ from .resources.product_collections.product_collections import (
+ ProductCollectionsResource,
+ AsyncProductCollectionsResource,
+ )
__all__ = [
"ENVIRONMENTS",
@@ -331,6 +336,12 @@ def entitlements(self) -> EntitlementsResource:
return EntitlementsResource(self)
+ @cached_property
+ def product_collections(self) -> ProductCollectionsResource:
+ from .resources.product_collections import ProductCollectionsResource
+
+ return ProductCollectionsResource(self)
+
@cached_property
def with_raw_response(self) -> DodoPaymentsWithRawResponse:
return DodoPaymentsWithRawResponse(self)
@@ -676,6 +687,12 @@ def entitlements(self) -> AsyncEntitlementsResource:
return AsyncEntitlementsResource(self)
+ @cached_property
+ def product_collections(self) -> AsyncProductCollectionsResource:
+ from .resources.product_collections import AsyncProductCollectionsResource
+
+ return AsyncProductCollectionsResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncDodoPaymentsWithRawResponse:
return AsyncDodoPaymentsWithRawResponse(self)
@@ -931,6 +948,12 @@ def entitlements(self) -> entitlements.EntitlementsResourceWithRawResponse:
return EntitlementsResourceWithRawResponse(self._client.entitlements)
+ @cached_property
+ def product_collections(self) -> product_collections.ProductCollectionsResourceWithRawResponse:
+ from .resources.product_collections import ProductCollectionsResourceWithRawResponse
+
+ return ProductCollectionsResourceWithRawResponse(self._client.product_collections)
+
class AsyncDodoPaymentsWithRawResponse:
_client: AsyncDodoPayments
@@ -1070,6 +1093,12 @@ def entitlements(self) -> entitlements.AsyncEntitlementsResourceWithRawResponse:
return AsyncEntitlementsResourceWithRawResponse(self._client.entitlements)
+ @cached_property
+ def product_collections(self) -> product_collections.AsyncProductCollectionsResourceWithRawResponse:
+ from .resources.product_collections import AsyncProductCollectionsResourceWithRawResponse
+
+ return AsyncProductCollectionsResourceWithRawResponse(self._client.product_collections)
+
class DodoPaymentsWithStreamedResponse:
_client: DodoPayments
@@ -1209,6 +1238,12 @@ def entitlements(self) -> entitlements.EntitlementsResourceWithStreamingResponse
return EntitlementsResourceWithStreamingResponse(self._client.entitlements)
+ @cached_property
+ def product_collections(self) -> product_collections.ProductCollectionsResourceWithStreamingResponse:
+ from .resources.product_collections import ProductCollectionsResourceWithStreamingResponse
+
+ return ProductCollectionsResourceWithStreamingResponse(self._client.product_collections)
+
class AsyncDodoPaymentsWithStreamedResponse:
_client: AsyncDodoPayments
@@ -1348,6 +1383,12 @@ def entitlements(self) -> entitlements.AsyncEntitlementsResourceWithStreamingRes
return AsyncEntitlementsResourceWithStreamingResponse(self._client.entitlements)
+ @cached_property
+ def product_collections(self) -> product_collections.AsyncProductCollectionsResourceWithStreamingResponse:
+ from .resources.product_collections import AsyncProductCollectionsResourceWithStreamingResponse
+
+ return AsyncProductCollectionsResourceWithStreamingResponse(self._client.product_collections)
+
Client = DodoPayments
diff --git a/src/dodopayments/resources/__init__.py b/src/dodopayments/resources/__init__.py
index 27b19406..d6fd6e60 100644
--- a/src/dodopayments/resources/__init__.py
+++ b/src/dodopayments/resources/__init__.py
@@ -168,6 +168,14 @@
CreditEntitlementsResourceWithStreamingResponse,
AsyncCreditEntitlementsResourceWithStreamingResponse,
)
+from .product_collections import (
+ ProductCollectionsResource,
+ AsyncProductCollectionsResource,
+ ProductCollectionsResourceWithRawResponse,
+ AsyncProductCollectionsResourceWithRawResponse,
+ ProductCollectionsResourceWithStreamingResponse,
+ AsyncProductCollectionsResourceWithStreamingResponse,
+)
from .license_key_instances import (
LicenseKeyInstancesResource,
AsyncLicenseKeyInstancesResource,
@@ -310,4 +318,10 @@
"AsyncEntitlementsResourceWithRawResponse",
"EntitlementsResourceWithStreamingResponse",
"AsyncEntitlementsResourceWithStreamingResponse",
+ "ProductCollectionsResource",
+ "AsyncProductCollectionsResource",
+ "ProductCollectionsResourceWithRawResponse",
+ "AsyncProductCollectionsResourceWithRawResponse",
+ "ProductCollectionsResourceWithStreamingResponse",
+ "AsyncProductCollectionsResourceWithStreamingResponse",
]
diff --git a/src/dodopayments/resources/checkout_sessions.py b/src/dodopayments/resources/checkout_sessions.py
index 29e09930..e84c5726 100644
--- a/src/dodopayments/resources/checkout_sessions.py
+++ b/src/dodopayments/resources/checkout_sessions.py
@@ -69,6 +69,7 @@ def create(
confirm: bool | Omit = omit,
custom_fields: Optional[Iterable[CustomFieldParam]] | Omit = omit,
customer: Optional[CustomerRequestParam] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
customization: CheckoutSessionCustomizationParam | Omit = omit,
discount_code: Optional[str] | Omit = omit,
discount_codes: Optional[SequenceNotStr[str]] | Omit = omit,
@@ -114,6 +115,10 @@ def create(
customer: Customer details for the session
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B purchase, this name is rendered on the
+ invoice instead of the customer's personal name.
+
customization: Customization for the checkout session page
discount_code: DEPRECATED: Use discount_codes instead. Cannot be used together with
@@ -171,6 +176,7 @@ def create(
"confirm": confirm,
"custom_fields": custom_fields,
"customer": customer,
+ "customer_business_name": customer_business_name,
"customization": customization,
"discount_code": discount_code,
"discount_codes": discount_codes,
@@ -237,6 +243,7 @@ def preview(
confirm: bool | Omit = omit,
custom_fields: Optional[Iterable[CustomFieldParam]] | Omit = omit,
customer: Optional[CustomerRequestParam] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
customization: CheckoutSessionCustomizationParam | Omit = omit,
discount_code: Optional[str] | Omit = omit,
discount_codes: Optional[SequenceNotStr[str]] | Omit = omit,
@@ -282,6 +289,10 @@ def preview(
customer: Customer details for the session
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B purchase, this name is rendered on the
+ invoice instead of the customer's personal name.
+
customization: Customization for the checkout session page
discount_code: DEPRECATED: Use discount_codes instead. Cannot be used together with
@@ -339,6 +350,7 @@ def preview(
"confirm": confirm,
"custom_fields": custom_fields,
"customer": customer,
+ "customer_business_name": customer_business_name,
"customization": customization,
"discount_code": discount_code,
"discount_codes": discount_codes,
@@ -395,6 +407,7 @@ async def create(
confirm: bool | Omit = omit,
custom_fields: Optional[Iterable[CustomFieldParam]] | Omit = omit,
customer: Optional[CustomerRequestParam] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
customization: CheckoutSessionCustomizationParam | Omit = omit,
discount_code: Optional[str] | Omit = omit,
discount_codes: Optional[SequenceNotStr[str]] | Omit = omit,
@@ -440,6 +453,10 @@ async def create(
customer: Customer details for the session
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B purchase, this name is rendered on the
+ invoice instead of the customer's personal name.
+
customization: Customization for the checkout session page
discount_code: DEPRECATED: Use discount_codes instead. Cannot be used together with
@@ -497,6 +514,7 @@ async def create(
"confirm": confirm,
"custom_fields": custom_fields,
"customer": customer,
+ "customer_business_name": customer_business_name,
"customization": customization,
"discount_code": discount_code,
"discount_codes": discount_codes,
@@ -563,6 +581,7 @@ async def preview(
confirm: bool | Omit = omit,
custom_fields: Optional[Iterable[CustomFieldParam]] | Omit = omit,
customer: Optional[CustomerRequestParam] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
customization: CheckoutSessionCustomizationParam | Omit = omit,
discount_code: Optional[str] | Omit = omit,
discount_codes: Optional[SequenceNotStr[str]] | Omit = omit,
@@ -608,6 +627,10 @@ async def preview(
customer: Customer details for the session
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B purchase, this name is rendered on the
+ invoice instead of the customer's personal name.
+
customization: Customization for the checkout session page
discount_code: DEPRECATED: Use discount_codes instead. Cannot be used together with
@@ -665,6 +688,7 @@ async def preview(
"confirm": confirm,
"custom_fields": custom_fields,
"customer": customer,
+ "customer_business_name": customer_business_name,
"customization": customization,
"discount_code": discount_code,
"discount_codes": discount_codes,
diff --git a/src/dodopayments/resources/invoices/payments.py b/src/dodopayments/resources/invoices/payments.py
index a30cf23a..2324abcd 100644
--- a/src/dodopayments/resources/invoices/payments.py
+++ b/src/dodopayments/resources/invoices/payments.py
@@ -75,6 +75,38 @@ def retrieve(
cast_to=BinaryAPIResponse,
)
+ def retrieve_payout(
+ self,
+ payout_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BinaryAPIResponse:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not payout_id:
+ raise ValueError(f"Expected a non-empty value for `payout_id` but received {payout_id!r}")
+ extra_headers = {"Accept": "application/pdf", **(extra_headers or {})}
+ return self._get(
+ path_template("/invoices/payouts/{payout_id}", payout_id=payout_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BinaryAPIResponse,
+ )
+
def retrieve_refund(
self,
refund_id: str,
@@ -160,6 +192,38 @@ async def retrieve(
cast_to=AsyncBinaryAPIResponse,
)
+ async def retrieve_payout(
+ self,
+ payout_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AsyncBinaryAPIResponse:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not payout_id:
+ raise ValueError(f"Expected a non-empty value for `payout_id` but received {payout_id!r}")
+ extra_headers = {"Accept": "application/pdf", **(extra_headers or {})}
+ return await self._get(
+ path_template("/invoices/payouts/{payout_id}", payout_id=payout_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AsyncBinaryAPIResponse,
+ )
+
async def retrieve_refund(
self,
refund_id: str,
@@ -201,6 +265,10 @@ def __init__(self, payments: PaymentsResource) -> None:
payments.retrieve,
BinaryAPIResponse,
)
+ self.retrieve_payout = to_custom_raw_response_wrapper(
+ payments.retrieve_payout,
+ BinaryAPIResponse,
+ )
self.retrieve_refund = to_custom_raw_response_wrapper(
payments.retrieve_refund,
BinaryAPIResponse,
@@ -215,6 +283,10 @@ def __init__(self, payments: AsyncPaymentsResource) -> None:
payments.retrieve,
AsyncBinaryAPIResponse,
)
+ self.retrieve_payout = async_to_custom_raw_response_wrapper(
+ payments.retrieve_payout,
+ AsyncBinaryAPIResponse,
+ )
self.retrieve_refund = async_to_custom_raw_response_wrapper(
payments.retrieve_refund,
AsyncBinaryAPIResponse,
@@ -229,6 +301,10 @@ def __init__(self, payments: PaymentsResource) -> None:
payments.retrieve,
StreamedBinaryAPIResponse,
)
+ self.retrieve_payout = to_custom_streamed_response_wrapper(
+ payments.retrieve_payout,
+ StreamedBinaryAPIResponse,
+ )
self.retrieve_refund = to_custom_streamed_response_wrapper(
payments.retrieve_refund,
StreamedBinaryAPIResponse,
@@ -243,6 +319,10 @@ def __init__(self, payments: AsyncPaymentsResource) -> None:
payments.retrieve,
AsyncStreamedBinaryAPIResponse,
)
+ self.retrieve_payout = async_to_custom_streamed_response_wrapper(
+ payments.retrieve_payout,
+ AsyncStreamedBinaryAPIResponse,
+ )
self.retrieve_refund = async_to_custom_streamed_response_wrapper(
payments.retrieve_refund,
AsyncStreamedBinaryAPIResponse,
diff --git a/src/dodopayments/resources/payments.py b/src/dodopayments/resources/payments.py
index 98472f08..4cadfc98 100644
--- a/src/dodopayments/resources/payments.py
+++ b/src/dodopayments/resources/payments.py
@@ -29,7 +29,6 @@
from ..types.payment_list_response import PaymentListResponse
from ..types.customer_request_param import CustomerRequestParam
from ..types.payment_create_response import PaymentCreateResponse
-from ..types.one_time_product_cart_item_param import OneTimeProductCartItemParam
from ..types.payment_retrieve_line_items_response import PaymentRetrieveLineItemsResponse
__all__ = ["PaymentsResource", "AsyncPaymentsResource"]
@@ -61,10 +60,11 @@ def create(
*,
billing: BillingAddressParam,
customer: CustomerRequestParam,
- product_cart: Iterable[OneTimeProductCartItemParam],
+ product_cart: Iterable[payment_create_params.ProductCart],
adaptive_currency_fees_inclusive: Optional[bool] | Omit = omit,
allowed_payment_method_types: Optional[List[PaymentMethodTypes]] | Omit = omit,
billing_currency: Optional[Currency] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
discount_code: Optional[str] | Omit = omit,
discount_codes: Optional[SequenceNotStr[str]] | Omit = omit,
force_3ds: Optional[bool] | Omit = omit,
@@ -105,6 +105,10 @@ def create(
billing_currency: Fix the currency in which the end customer is billed. If Dodo Payments cannot
support that currency for this transaction, it will not proceed
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B purchase, this name is rendered on the
+ invoice instead of the customer's personal name.
+
discount_code: DEPRECATED: Use discount_codes instead. Cannot be used together with
discount_codes.
@@ -157,6 +161,7 @@ def create(
"adaptive_currency_fees_inclusive": adaptive_currency_fees_inclusive,
"allowed_payment_method_types": allowed_payment_method_types,
"billing_currency": billing_currency,
+ "customer_business_name": customer_business_name,
"discount_code": discount_code,
"discount_codes": discount_codes,
"force_3ds": force_3ds,
@@ -353,10 +358,11 @@ async def create(
*,
billing: BillingAddressParam,
customer: CustomerRequestParam,
- product_cart: Iterable[OneTimeProductCartItemParam],
+ product_cart: Iterable[payment_create_params.ProductCart],
adaptive_currency_fees_inclusive: Optional[bool] | Omit = omit,
allowed_payment_method_types: Optional[List[PaymentMethodTypes]] | Omit = omit,
billing_currency: Optional[Currency] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
discount_code: Optional[str] | Omit = omit,
discount_codes: Optional[SequenceNotStr[str]] | Omit = omit,
force_3ds: Optional[bool] | Omit = omit,
@@ -397,6 +403,10 @@ async def create(
billing_currency: Fix the currency in which the end customer is billed. If Dodo Payments cannot
support that currency for this transaction, it will not proceed
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B purchase, this name is rendered on the
+ invoice instead of the customer's personal name.
+
discount_code: DEPRECATED: Use discount_codes instead. Cannot be used together with
discount_codes.
@@ -449,6 +459,7 @@ async def create(
"adaptive_currency_fees_inclusive": adaptive_currency_fees_inclusive,
"allowed_payment_method_types": allowed_payment_method_types,
"billing_currency": billing_currency,
+ "customer_business_name": customer_business_name,
"discount_code": discount_code,
"discount_codes": discount_codes,
"force_3ds": force_3ds,
diff --git a/src/dodopayments/resources/product_collections/__init__.py b/src/dodopayments/resources/product_collections/__init__.py
new file mode 100644
index 00000000..1ab45bd6
--- /dev/null
+++ b/src/dodopayments/resources/product_collections/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .groups import (
+ GroupsResource,
+ AsyncGroupsResource,
+ GroupsResourceWithRawResponse,
+ AsyncGroupsResourceWithRawResponse,
+ GroupsResourceWithStreamingResponse,
+ AsyncGroupsResourceWithStreamingResponse,
+)
+from .product_collections import (
+ ProductCollectionsResource,
+ AsyncProductCollectionsResource,
+ ProductCollectionsResourceWithRawResponse,
+ AsyncProductCollectionsResourceWithRawResponse,
+ ProductCollectionsResourceWithStreamingResponse,
+ AsyncProductCollectionsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "GroupsResource",
+ "AsyncGroupsResource",
+ "GroupsResourceWithRawResponse",
+ "AsyncGroupsResourceWithRawResponse",
+ "GroupsResourceWithStreamingResponse",
+ "AsyncGroupsResourceWithStreamingResponse",
+ "ProductCollectionsResource",
+ "AsyncProductCollectionsResource",
+ "ProductCollectionsResourceWithRawResponse",
+ "AsyncProductCollectionsResourceWithRawResponse",
+ "ProductCollectionsResourceWithStreamingResponse",
+ "AsyncProductCollectionsResourceWithStreamingResponse",
+]
diff --git a/src/dodopayments/resources/product_collections/groups/__init__.py b/src/dodopayments/resources/product_collections/groups/__init__.py
new file mode 100644
index 00000000..7c869102
--- /dev/null
+++ b/src/dodopayments/resources/product_collections/groups/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .items import (
+ ItemsResource,
+ AsyncItemsResource,
+ ItemsResourceWithRawResponse,
+ AsyncItemsResourceWithRawResponse,
+ ItemsResourceWithStreamingResponse,
+ AsyncItemsResourceWithStreamingResponse,
+)
+from .groups import (
+ GroupsResource,
+ AsyncGroupsResource,
+ GroupsResourceWithRawResponse,
+ AsyncGroupsResourceWithRawResponse,
+ GroupsResourceWithStreamingResponse,
+ AsyncGroupsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "ItemsResource",
+ "AsyncItemsResource",
+ "ItemsResourceWithRawResponse",
+ "AsyncItemsResourceWithRawResponse",
+ "ItemsResourceWithStreamingResponse",
+ "AsyncItemsResourceWithStreamingResponse",
+ "GroupsResource",
+ "AsyncGroupsResource",
+ "GroupsResourceWithRawResponse",
+ "AsyncGroupsResourceWithRawResponse",
+ "GroupsResourceWithStreamingResponse",
+ "AsyncGroupsResourceWithStreamingResponse",
+]
diff --git a/src/dodopayments/resources/product_collections/groups/groups.py b/src/dodopayments/resources/product_collections/groups/groups.py
new file mode 100644
index 00000000..0da2d460
--- /dev/null
+++ b/src/dodopayments/resources/product_collections/groups/groups.py
@@ -0,0 +1,436 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+
+import httpx
+
+from .items import (
+ ItemsResource,
+ AsyncItemsResource,
+ ItemsResourceWithRawResponse,
+ AsyncItemsResourceWithRawResponse,
+ ItemsResourceWithStreamingResponse,
+ AsyncItemsResourceWithStreamingResponse,
+)
+from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.product_collections import group_create_params, group_update_params
+from ....types.product_collections.group_product_param import GroupProductParam
+from ....types.product_collections.product_collection_group_response import ProductCollectionGroupResponse
+
+__all__ = ["GroupsResource", "AsyncGroupsResource"]
+
+
+class GroupsResource(SyncAPIResource):
+ @cached_property
+ def items(self) -> ItemsResource:
+ return ItemsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> GroupsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#accessing-raw-response-data-eg-headers
+ """
+ return GroupsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> GroupsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#with_streaming_response
+ """
+ return GroupsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ id: str,
+ *,
+ products: Iterable[GroupProductParam],
+ group_name: Optional[str] | Omit = omit,
+ status: Optional[bool] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollectionGroupResponse:
+ """Args:
+ products: Products in this group
+
+ group_name: Optional group name.
+
+ Multiple groups can have null names, but named groups must
+ be unique per collection
+
+ status: Status of the group (defaults to true if not provided)
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ path_template("/product-collections/{id}/groups", id=id),
+ body=maybe_transform(
+ {
+ "products": products,
+ "group_name": group_name,
+ "status": status,
+ },
+ group_create_params.GroupCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProductCollectionGroupResponse,
+ )
+
+ def update(
+ self,
+ group_id: str,
+ *,
+ id: str,
+ group_name: Optional[str] | Omit = omit,
+ product_order: Optional[SequenceNotStr[str]] | Omit = omit,
+ status: Optional[bool] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ group_name: Optional group name update: Some(Some(name)) = set name, Some(None) = clear
+ name, None = no change
+
+ product_order: Optional new order for products in this group (array of
+ product_collection_group_pdts UUIDs)
+
+ status: Optional status update
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._patch(
+ path_template("/product-collections/{id}/groups/{group_id}", id=id, group_id=group_id),
+ body=maybe_transform(
+ {
+ "group_name": group_name,
+ "product_order": product_order,
+ "status": status,
+ },
+ group_update_params.GroupUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def delete(
+ self,
+ group_id: str,
+ *,
+ id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/product-collections/{id}/groups/{group_id}", id=id, group_id=group_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncGroupsResource(AsyncAPIResource):
+ @cached_property
+ def items(self) -> AsyncItemsResource:
+ return AsyncItemsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncGroupsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncGroupsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncGroupsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#with_streaming_response
+ """
+ return AsyncGroupsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ id: str,
+ *,
+ products: Iterable[GroupProductParam],
+ group_name: Optional[str] | Omit = omit,
+ status: Optional[bool] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollectionGroupResponse:
+ """Args:
+ products: Products in this group
+
+ group_name: Optional group name.
+
+ Multiple groups can have null names, but named groups must
+ be unique per collection
+
+ status: Status of the group (defaults to true if not provided)
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ path_template("/product-collections/{id}/groups", id=id),
+ body=await async_maybe_transform(
+ {
+ "products": products,
+ "group_name": group_name,
+ "status": status,
+ },
+ group_create_params.GroupCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProductCollectionGroupResponse,
+ )
+
+ async def update(
+ self,
+ group_id: str,
+ *,
+ id: str,
+ group_name: Optional[str] | Omit = omit,
+ product_order: Optional[SequenceNotStr[str]] | Omit = omit,
+ status: Optional[bool] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ group_name: Optional group name update: Some(Some(name)) = set name, Some(None) = clear
+ name, None = no change
+
+ product_order: Optional new order for products in this group (array of
+ product_collection_group_pdts UUIDs)
+
+ status: Optional status update
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._patch(
+ path_template("/product-collections/{id}/groups/{group_id}", id=id, group_id=group_id),
+ body=await async_maybe_transform(
+ {
+ "group_name": group_name,
+ "product_order": product_order,
+ "status": status,
+ },
+ group_update_params.GroupUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def delete(
+ self,
+ group_id: str,
+ *,
+ id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/product-collections/{id}/groups/{group_id}", id=id, group_id=group_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class GroupsResourceWithRawResponse:
+ def __init__(self, groups: GroupsResource) -> None:
+ self._groups = groups
+
+ self.create = to_raw_response_wrapper(
+ groups.create,
+ )
+ self.update = to_raw_response_wrapper(
+ groups.update,
+ )
+ self.delete = to_raw_response_wrapper(
+ groups.delete,
+ )
+
+ @cached_property
+ def items(self) -> ItemsResourceWithRawResponse:
+ return ItemsResourceWithRawResponse(self._groups.items)
+
+
+class AsyncGroupsResourceWithRawResponse:
+ def __init__(self, groups: AsyncGroupsResource) -> None:
+ self._groups = groups
+
+ self.create = async_to_raw_response_wrapper(
+ groups.create,
+ )
+ self.update = async_to_raw_response_wrapper(
+ groups.update,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ groups.delete,
+ )
+
+ @cached_property
+ def items(self) -> AsyncItemsResourceWithRawResponse:
+ return AsyncItemsResourceWithRawResponse(self._groups.items)
+
+
+class GroupsResourceWithStreamingResponse:
+ def __init__(self, groups: GroupsResource) -> None:
+ self._groups = groups
+
+ self.create = to_streamed_response_wrapper(
+ groups.create,
+ )
+ self.update = to_streamed_response_wrapper(
+ groups.update,
+ )
+ self.delete = to_streamed_response_wrapper(
+ groups.delete,
+ )
+
+ @cached_property
+ def items(self) -> ItemsResourceWithStreamingResponse:
+ return ItemsResourceWithStreamingResponse(self._groups.items)
+
+
+class AsyncGroupsResourceWithStreamingResponse:
+ def __init__(self, groups: AsyncGroupsResource) -> None:
+ self._groups = groups
+
+ self.create = async_to_streamed_response_wrapper(
+ groups.create,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ groups.update,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ groups.delete,
+ )
+
+ @cached_property
+ def items(self) -> AsyncItemsResourceWithStreamingResponse:
+ return AsyncItemsResourceWithStreamingResponse(self._groups.items)
diff --git a/src/dodopayments/resources/product_collections/groups/items.py b/src/dodopayments/resources/product_collections/groups/items.py
new file mode 100644
index 00000000..7a8d295b
--- /dev/null
+++ b/src/dodopayments/resources/product_collections/groups/items.py
@@ -0,0 +1,370 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+
+import httpx
+
+from ...._types import Body, Query, Headers, NoneType, NotGiven, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.product_collections.groups import item_create_params, item_update_params
+from ....types.product_collections.group_product_param import GroupProductParam
+from ....types.product_collections.groups.item_create_response import ItemCreateResponse
+
+__all__ = ["ItemsResource", "AsyncItemsResource"]
+
+
+class ItemsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ItemsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#accessing-raw-response-data-eg-headers
+ """
+ return ItemsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ItemsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#with_streaming_response
+ """
+ return ItemsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ group_id: str,
+ *,
+ id: str,
+ products: Iterable[GroupProductParam],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ItemCreateResponse:
+ """
+ Args:
+ products: Products to add to the group
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return self._post(
+ path_template("/product-collections/{id}/groups/{group_id}/items", id=id, group_id=group_id),
+ body=maybe_transform({"products": products}, item_create_params.ItemCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ItemCreateResponse,
+ )
+
+ def update(
+ self,
+ item_id: str,
+ *,
+ id: str,
+ group_id: str,
+ status: bool,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ status: Status of the product in the group
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ if not item_id:
+ raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._patch(
+ path_template(
+ "/product-collections/{id}/groups/{group_id}/items/{item_id}", id=id, group_id=group_id, item_id=item_id
+ ),
+ body=maybe_transform({"status": status}, item_update_params.ItemUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def delete(
+ self,
+ item_id: str,
+ *,
+ id: str,
+ group_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ if not item_id:
+ raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template(
+ "/product-collections/{id}/groups/{group_id}/items/{item_id}", id=id, group_id=group_id, item_id=item_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncItemsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncItemsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncItemsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncItemsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#with_streaming_response
+ """
+ return AsyncItemsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ group_id: str,
+ *,
+ id: str,
+ products: Iterable[GroupProductParam],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ItemCreateResponse:
+ """
+ Args:
+ products: Products to add to the group
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return await self._post(
+ path_template("/product-collections/{id}/groups/{group_id}/items", id=id, group_id=group_id),
+ body=await async_maybe_transform({"products": products}, item_create_params.ItemCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ItemCreateResponse,
+ )
+
+ async def update(
+ self,
+ item_id: str,
+ *,
+ id: str,
+ group_id: str,
+ status: bool,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ status: Status of the product in the group
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ if not item_id:
+ raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._patch(
+ path_template(
+ "/product-collections/{id}/groups/{group_id}/items/{item_id}", id=id, group_id=group_id, item_id=item_id
+ ),
+ body=await async_maybe_transform({"status": status}, item_update_params.ItemUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def delete(
+ self,
+ item_id: str,
+ *,
+ id: str,
+ group_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ if not item_id:
+ raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template(
+ "/product-collections/{id}/groups/{group_id}/items/{item_id}", id=id, group_id=group_id, item_id=item_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class ItemsResourceWithRawResponse:
+ def __init__(self, items: ItemsResource) -> None:
+ self._items = items
+
+ self.create = to_raw_response_wrapper(
+ items.create,
+ )
+ self.update = to_raw_response_wrapper(
+ items.update,
+ )
+ self.delete = to_raw_response_wrapper(
+ items.delete,
+ )
+
+
+class AsyncItemsResourceWithRawResponse:
+ def __init__(self, items: AsyncItemsResource) -> None:
+ self._items = items
+
+ self.create = async_to_raw_response_wrapper(
+ items.create,
+ )
+ self.update = async_to_raw_response_wrapper(
+ items.update,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ items.delete,
+ )
+
+
+class ItemsResourceWithStreamingResponse:
+ def __init__(self, items: ItemsResource) -> None:
+ self._items = items
+
+ self.create = to_streamed_response_wrapper(
+ items.create,
+ )
+ self.update = to_streamed_response_wrapper(
+ items.update,
+ )
+ self.delete = to_streamed_response_wrapper(
+ items.delete,
+ )
+
+
+class AsyncItemsResourceWithStreamingResponse:
+ def __init__(self, items: AsyncItemsResource) -> None:
+ self._items = items
+
+ self.create = async_to_streamed_response_wrapper(
+ items.create,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ items.update,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ items.delete,
+ )
diff --git a/src/dodopayments/resources/product_collections/product_collections.py b/src/dodopayments/resources/product_collections/product_collections.py
new file mode 100644
index 00000000..4398c32c
--- /dev/null
+++ b/src/dodopayments/resources/product_collections/product_collections.py
@@ -0,0 +1,803 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+
+import httpx
+
+from ...types import (
+ product_collection_list_params,
+ product_collection_create_params,
+ product_collection_update_params,
+ product_collection_update_images_params,
+)
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...pagination import SyncDefaultPageNumberPagination, AsyncDefaultPageNumberPagination
+from .groups.groups import (
+ GroupsResource,
+ AsyncGroupsResource,
+ GroupsResourceWithRawResponse,
+ AsyncGroupsResourceWithRawResponse,
+ GroupsResourceWithStreamingResponse,
+ AsyncGroupsResourceWithStreamingResponse,
+)
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.product_collection import ProductCollection
+from ...types.product_collection_list_response import ProductCollectionListResponse
+from ...types.product_collection_unarchive_response import ProductCollectionUnarchiveResponse
+from ...types.product_collection_update_images_response import ProductCollectionUpdateImagesResponse
+from ...types.product_collections.product_collection_group_details_param import ProductCollectionGroupDetailsParam
+
+__all__ = ["ProductCollectionsResource", "AsyncProductCollectionsResource"]
+
+
+class ProductCollectionsResource(SyncAPIResource):
+ @cached_property
+ def groups(self) -> GroupsResource:
+ return GroupsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> ProductCollectionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#accessing-raw-response-data-eg-headers
+ """
+ return ProductCollectionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ProductCollectionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#with_streaming_response
+ """
+ return ProductCollectionsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ groups: Iterable[ProductCollectionGroupDetailsParam],
+ name: str,
+ brand_id: Optional[str] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollection:
+ """
+ Args:
+ groups: Groups of products in this collection
+
+ name: Name of the product collection
+
+ brand_id: Brand id for the collection, if not provided will default to primary brand
+
+ description: Optional description of the product collection
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/product-collections",
+ body=maybe_transform(
+ {
+ "groups": groups,
+ "name": name,
+ "brand_id": brand_id,
+ "description": description,
+ },
+ product_collection_create_params.ProductCollectionCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProductCollection,
+ )
+
+ def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollection:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ path_template("/product-collections/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProductCollection,
+ )
+
+ def update(
+ self,
+ id: str,
+ *,
+ brand_id: Optional[str] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ group_order: Optional[SequenceNotStr[str]] | Omit = omit,
+ image_id: Optional[str] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ brand_id: Optional brand_id update
+
+ description: Optional description update - pass null to remove, omit to keep unchanged
+
+ group_order: Optional new order for groups (array of group UUIDs in desired order)
+
+ image_id: Optional image update - pass null to remove, omit to keep unchanged
+
+ name: Optional new name for the collection
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._patch(
+ path_template("/product-collections/{id}", id=id),
+ body=maybe_transform(
+ {
+ "brand_id": brand_id,
+ "description": description,
+ "group_order": group_order,
+ "image_id": image_id,
+ "name": name,
+ },
+ product_collection_update_params.ProductCollectionUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def list(
+ self,
+ *,
+ archived: bool | Omit = omit,
+ brand_id: str | Omit = omit,
+ page_number: int | Omit = omit,
+ page_size: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SyncDefaultPageNumberPagination[ProductCollectionListResponse]:
+ """
+ Args:
+ archived: List archived collections
+
+ brand_id: Filter by Brand id
+
+ page_number: Page number default is 0
+
+ page_size: Page size default is 10 max is 100
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/product-collections",
+ page=SyncDefaultPageNumberPagination[ProductCollectionListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "archived": archived,
+ "brand_id": brand_id,
+ "page_number": page_number,
+ "page_size": page_size,
+ },
+ product_collection_list_params.ProductCollectionListParams,
+ ),
+ ),
+ model=ProductCollectionListResponse,
+ )
+
+ def delete(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/product-collections/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def unarchive(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollectionUnarchiveResponse:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ path_template("/product-collections/{id}/unarchive", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProductCollectionUnarchiveResponse,
+ )
+
+ def update_images(
+ self,
+ id: str,
+ *,
+ force_update: Optional[bool] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollectionUpdateImagesResponse:
+ """
+ Args:
+ force_update: If true, generates a new image ID to force cache invalidation
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._put(
+ path_template("/product-collections/{id}/images", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {"force_update": force_update},
+ product_collection_update_images_params.ProductCollectionUpdateImagesParams,
+ ),
+ ),
+ cast_to=ProductCollectionUpdateImagesResponse,
+ )
+
+
+class AsyncProductCollectionsResource(AsyncAPIResource):
+ @cached_property
+ def groups(self) -> AsyncGroupsResource:
+ return AsyncGroupsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncProductCollectionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncProductCollectionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncProductCollectionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/dodopayments/dodopayments-python#with_streaming_response
+ """
+ return AsyncProductCollectionsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ groups: Iterable[ProductCollectionGroupDetailsParam],
+ name: str,
+ brand_id: Optional[str] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollection:
+ """
+ Args:
+ groups: Groups of products in this collection
+
+ name: Name of the product collection
+
+ brand_id: Brand id for the collection, if not provided will default to primary brand
+
+ description: Optional description of the product collection
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/product-collections",
+ body=await async_maybe_transform(
+ {
+ "groups": groups,
+ "name": name,
+ "brand_id": brand_id,
+ "description": description,
+ },
+ product_collection_create_params.ProductCollectionCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProductCollection,
+ )
+
+ async def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollection:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ path_template("/product-collections/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProductCollection,
+ )
+
+ async def update(
+ self,
+ id: str,
+ *,
+ brand_id: Optional[str] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ group_order: Optional[SequenceNotStr[str]] | Omit = omit,
+ image_id: Optional[str] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ brand_id: Optional brand_id update
+
+ description: Optional description update - pass null to remove, omit to keep unchanged
+
+ group_order: Optional new order for groups (array of group UUIDs in desired order)
+
+ image_id: Optional image update - pass null to remove, omit to keep unchanged
+
+ name: Optional new name for the collection
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._patch(
+ path_template("/product-collections/{id}", id=id),
+ body=await async_maybe_transform(
+ {
+ "brand_id": brand_id,
+ "description": description,
+ "group_order": group_order,
+ "image_id": image_id,
+ "name": name,
+ },
+ product_collection_update_params.ProductCollectionUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def list(
+ self,
+ *,
+ archived: bool | Omit = omit,
+ brand_id: str | Omit = omit,
+ page_number: int | Omit = omit,
+ page_size: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[ProductCollectionListResponse, AsyncDefaultPageNumberPagination[ProductCollectionListResponse]]:
+ """
+ Args:
+ archived: List archived collections
+
+ brand_id: Filter by Brand id
+
+ page_number: Page number default is 0
+
+ page_size: Page size default is 10 max is 100
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/product-collections",
+ page=AsyncDefaultPageNumberPagination[ProductCollectionListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "archived": archived,
+ "brand_id": brand_id,
+ "page_number": page_number,
+ "page_size": page_size,
+ },
+ product_collection_list_params.ProductCollectionListParams,
+ ),
+ ),
+ model=ProductCollectionListResponse,
+ )
+
+ async def delete(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/product-collections/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def unarchive(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollectionUnarchiveResponse:
+ """
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ path_template("/product-collections/{id}/unarchive", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProductCollectionUnarchiveResponse,
+ )
+
+ async def update_images(
+ self,
+ id: str,
+ *,
+ force_update: Optional[bool] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProductCollectionUpdateImagesResponse:
+ """
+ Args:
+ force_update: If true, generates a new image ID to force cache invalidation
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._put(
+ path_template("/product-collections/{id}/images", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"force_update": force_update},
+ product_collection_update_images_params.ProductCollectionUpdateImagesParams,
+ ),
+ ),
+ cast_to=ProductCollectionUpdateImagesResponse,
+ )
+
+
+class ProductCollectionsResourceWithRawResponse:
+ def __init__(self, product_collections: ProductCollectionsResource) -> None:
+ self._product_collections = product_collections
+
+ self.create = to_raw_response_wrapper(
+ product_collections.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ product_collections.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ product_collections.update,
+ )
+ self.list = to_raw_response_wrapper(
+ product_collections.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ product_collections.delete,
+ )
+ self.unarchive = to_raw_response_wrapper(
+ product_collections.unarchive,
+ )
+ self.update_images = to_raw_response_wrapper(
+ product_collections.update_images,
+ )
+
+ @cached_property
+ def groups(self) -> GroupsResourceWithRawResponse:
+ return GroupsResourceWithRawResponse(self._product_collections.groups)
+
+
+class AsyncProductCollectionsResourceWithRawResponse:
+ def __init__(self, product_collections: AsyncProductCollectionsResource) -> None:
+ self._product_collections = product_collections
+
+ self.create = async_to_raw_response_wrapper(
+ product_collections.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ product_collections.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ product_collections.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ product_collections.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ product_collections.delete,
+ )
+ self.unarchive = async_to_raw_response_wrapper(
+ product_collections.unarchive,
+ )
+ self.update_images = async_to_raw_response_wrapper(
+ product_collections.update_images,
+ )
+
+ @cached_property
+ def groups(self) -> AsyncGroupsResourceWithRawResponse:
+ return AsyncGroupsResourceWithRawResponse(self._product_collections.groups)
+
+
+class ProductCollectionsResourceWithStreamingResponse:
+ def __init__(self, product_collections: ProductCollectionsResource) -> None:
+ self._product_collections = product_collections
+
+ self.create = to_streamed_response_wrapper(
+ product_collections.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ product_collections.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ product_collections.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ product_collections.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ product_collections.delete,
+ )
+ self.unarchive = to_streamed_response_wrapper(
+ product_collections.unarchive,
+ )
+ self.update_images = to_streamed_response_wrapper(
+ product_collections.update_images,
+ )
+
+ @cached_property
+ def groups(self) -> GroupsResourceWithStreamingResponse:
+ return GroupsResourceWithStreamingResponse(self._product_collections.groups)
+
+
+class AsyncProductCollectionsResourceWithStreamingResponse:
+ def __init__(self, product_collections: AsyncProductCollectionsResource) -> None:
+ self._product_collections = product_collections
+
+ self.create = async_to_streamed_response_wrapper(
+ product_collections.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ product_collections.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ product_collections.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ product_collections.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ product_collections.delete,
+ )
+ self.unarchive = async_to_streamed_response_wrapper(
+ product_collections.unarchive,
+ )
+ self.update_images = async_to_streamed_response_wrapper(
+ product_collections.update_images,
+ )
+
+ @cached_property
+ def groups(self) -> AsyncGroupsResourceWithStreamingResponse:
+ return AsyncGroupsResourceWithStreamingResponse(self._product_collections.groups)
diff --git a/src/dodopayments/resources/subscriptions.py b/src/dodopayments/resources/subscriptions.py
index a292aca5..addbadba 100644
--- a/src/dodopayments/resources/subscriptions.py
+++ b/src/dodopayments/resources/subscriptions.py
@@ -46,7 +46,6 @@
from ..types.on_demand_subscription_param import OnDemandSubscriptionParam
from ..types.subscription_charge_response import SubscriptionChargeResponse
from ..types.subscription_create_response import SubscriptionCreateResponse
-from ..types.one_time_product_cart_item_param import OneTimeProductCartItemParam
from ..types.subscription_preview_change_plan_response import SubscriptionPreviewChangePlanResponse
from ..types.subscription_retrieve_credit_usage_response import SubscriptionRetrieveCreditUsageResponse
from ..types.subscription_update_payment_method_response import SubscriptionUpdatePaymentMethodResponse
@@ -86,13 +85,14 @@ def create(
addons: Optional[Iterable[AttachAddonParam]] | Omit = omit,
allowed_payment_method_types: Optional[List[PaymentMethodTypes]] | Omit = omit,
billing_currency: Optional[Currency] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
discount_code: Optional[str] | Omit = omit,
discount_codes: Optional[SequenceNotStr[str]] | Omit = omit,
force_3ds: Optional[bool] | Omit = omit,
mandate_min_amount_inr_paise: Optional[int] | Omit = omit,
metadata: Dict[str, str] | Omit = omit,
on_demand: Optional[OnDemandSubscriptionParam] | Omit = omit,
- one_time_product_cart: Optional[Iterable[OneTimeProductCartItemParam]] | Omit = omit,
+ one_time_product_cart: Optional[Iterable[subscription_create_params.OneTimeProductCart]] | Omit = omit,
payment_link: Optional[bool] | Omit = omit,
payment_method_id: Optional[str] | Omit = omit,
redirect_immediately: bool | Omit = omit,
@@ -131,6 +131,10 @@ def create(
billing_currency: Fix the currency in which the end customer is billed. If Dodo Payments cannot
support that currency for this transaction, it will not proceed
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B purchase, this name is rendered on the
+ invoice instead of the customer's personal name.
+
discount_code: DEPRECATED: Use discount_codes instead. Cannot be used together with
discount_codes.
@@ -195,6 +199,7 @@ def create(
"addons": addons,
"allowed_payment_method_types": allowed_payment_method_types,
"billing_currency": billing_currency,
+ "customer_business_name": customer_business_name,
"discount_code": discount_code,
"discount_codes": discount_codes,
"force_3ds": force_3ds,
@@ -264,6 +269,7 @@ def update(
cancellation_comment: Optional[str] | Omit = omit,
cancellation_feedback: Optional[CancellationFeedback] | Omit = omit,
credit_entitlement_cart: Optional[Iterable[subscription_update_params.CreditEntitlementCart]] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
customer_name: Optional[str] | Omit = omit,
disable_on_demand: Optional[subscription_update_params.DisableOnDemand] | Omit = omit,
metadata: Optional[Dict[str, str]] | Omit = omit,
@@ -289,6 +295,11 @@ def update(
credit_entitlement_cart: Update credit entitlement cart settings
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B subscription, this name is rendered on
+ the invoice instead of the customer's personal name. Send `null` to explicitly
+ clear the business name.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -309,6 +320,7 @@ def update(
"cancellation_comment": cancellation_comment,
"cancellation_feedback": cancellation_feedback,
"credit_entitlement_cart": credit_entitlement_cart,
+ "customer_business_name": customer_business_name,
"customer_name": customer_name,
"disable_on_demand": disable_on_demand,
"metadata": metadata,
@@ -882,13 +894,14 @@ async def create(
addons: Optional[Iterable[AttachAddonParam]] | Omit = omit,
allowed_payment_method_types: Optional[List[PaymentMethodTypes]] | Omit = omit,
billing_currency: Optional[Currency] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
discount_code: Optional[str] | Omit = omit,
discount_codes: Optional[SequenceNotStr[str]] | Omit = omit,
force_3ds: Optional[bool] | Omit = omit,
mandate_min_amount_inr_paise: Optional[int] | Omit = omit,
metadata: Dict[str, str] | Omit = omit,
on_demand: Optional[OnDemandSubscriptionParam] | Omit = omit,
- one_time_product_cart: Optional[Iterable[OneTimeProductCartItemParam]] | Omit = omit,
+ one_time_product_cart: Optional[Iterable[subscription_create_params.OneTimeProductCart]] | Omit = omit,
payment_link: Optional[bool] | Omit = omit,
payment_method_id: Optional[str] | Omit = omit,
redirect_immediately: bool | Omit = omit,
@@ -927,6 +940,10 @@ async def create(
billing_currency: Fix the currency in which the end customer is billed. If Dodo Payments cannot
support that currency for this transaction, it will not proceed
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B purchase, this name is rendered on the
+ invoice instead of the customer's personal name.
+
discount_code: DEPRECATED: Use discount_codes instead. Cannot be used together with
discount_codes.
@@ -991,6 +1008,7 @@ async def create(
"addons": addons,
"allowed_payment_method_types": allowed_payment_method_types,
"billing_currency": billing_currency,
+ "customer_business_name": customer_business_name,
"discount_code": discount_code,
"discount_codes": discount_codes,
"force_3ds": force_3ds,
@@ -1060,6 +1078,7 @@ async def update(
cancellation_comment: Optional[str] | Omit = omit,
cancellation_feedback: Optional[CancellationFeedback] | Omit = omit,
credit_entitlement_cart: Optional[Iterable[subscription_update_params.CreditEntitlementCart]] | Omit = omit,
+ customer_business_name: Optional[str] | Omit = omit,
customer_name: Optional[str] | Omit = omit,
disable_on_demand: Optional[subscription_update_params.DisableOnDemand] | Omit = omit,
metadata: Optional[Dict[str, str]] | Omit = omit,
@@ -1085,6 +1104,11 @@ async def update(
credit_entitlement_cart: Update credit entitlement cart settings
+ customer_business_name: Optional business / legal name associated with the tax id. When provided
+ together with a valid tax id for a B2B subscription, this name is rendered on
+ the invoice instead of the customer's personal name. Send `null` to explicitly
+ clear the business name.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -1105,6 +1129,7 @@ async def update(
"cancellation_comment": cancellation_comment,
"cancellation_feedback": cancellation_feedback,
"credit_entitlement_cart": credit_entitlement_cart,
+ "customer_business_name": customer_business_name,
"customer_name": customer_name,
"disable_on_demand": disable_on_demand,
"metadata": metadata,
diff --git a/src/dodopayments/types/__init__.py b/src/dodopayments/types/__init__.py
index e47525a1..7ea86a47 100644
--- a/src/dodopayments/types/__init__.py
+++ b/src/dodopayments/types/__init__.py
@@ -2,6 +2,8 @@
from __future__ import annotations
+from . import meter, meter_filter
+from .. import _compat
from .brand import Brand as Brand
from .event import Event as Event
from .meter import Meter as Meter
@@ -15,6 +17,7 @@
from .discount import Discount as Discount
from .conjunction import Conjunction as Conjunction
from .entitlement import Entitlement as Entitlement
+from .filter_type import FilterType as FilterType
from .get_dispute import GetDispute as GetDispute
from .license_key import LicenseKey as LicenseKey
from .price_param import PriceParam as PriceParam
@@ -36,6 +39,8 @@
from .refund_list_item import RefundListItem as RefundListItem
from .addon_list_params import AddonListParams as AddonListParams
from .event_input_param import EventInputParam as EventInputParam
+from .filter_type_param import FilterTypeParam as FilterTypeParam
+from .github_permission import GitHubPermission as GitHubPermission
from .meter_aggregation import MeterAggregation as MeterAggregation
from .meter_list_params import MeterListParams as MeterListParams
from .add_meter_to_price import AddMeterToPrice as AddMeterToPrice
@@ -46,6 +51,7 @@
from .meter_filter_param import MeterFilterParam as MeterFilterParam
from .new_customer_param import NewCustomerParam as NewCustomerParam
from .payout_list_params import PayoutListParams as PayoutListParams
+from .product_collection import ProductCollection as ProductCollection
from .refund_list_params import RefundListParams as RefundListParams
from .theme_config_param import ThemeConfigParam as ThemeConfigParam
from .webhook_event_type import WebhookEventType as WebhookEventType
@@ -152,6 +158,7 @@
from .checkout_session_create_params import CheckoutSessionCreateParams as CheckoutSessionCreateParams
from .credit_entitlement_list_params import CreditEntitlementListParams as CreditEntitlementListParams
from .dispute_accepted_webhook_event import DisputeAcceptedWebhookEvent as DisputeAcceptedWebhookEvent
+from .product_collection_list_params import ProductCollectionListParams as ProductCollectionListParams
from .refund_succeeded_webhook_event import RefundSucceededWebhookEvent as RefundSucceededWebhookEvent
from .attach_credit_entitlement_param import AttachCreditEntitlementParam as AttachCreditEntitlementParam
from .checkout_session_preview_params import CheckoutSessionPreviewParams as CheckoutSessionPreviewParams
@@ -168,8 +175,10 @@
from .credit_rolled_over_webhook_event import CreditRolledOverWebhookEvent as CreditRolledOverWebhookEvent
from .dispute_challenged_webhook_event import DisputeChallengedWebhookEvent as DisputeChallengedWebhookEvent
from .license_key_instance_list_params import LicenseKeyInstanceListParams as LicenseKeyInstanceListParams
-from .one_time_product_cart_item_param import OneTimeProductCartItemParam as OneTimeProductCartItemParam
from .payment_processing_webhook_event import PaymentProcessingWebhookEvent as PaymentProcessingWebhookEvent
+from .product_collection_create_params import ProductCollectionCreateParams as ProductCollectionCreateParams
+from .product_collection_list_response import ProductCollectionListResponse as ProductCollectionListResponse
+from .product_collection_update_params import ProductCollectionUpdateParams as ProductCollectionUpdateParams
from .webhook_retrieve_secret_response import WebhookRetrieveSecretResponse as WebhookRetrieveSecretResponse
from .checkout_session_preview_response import CheckoutSessionPreviewResponse as CheckoutSessionPreviewResponse
from .license_key_created_webhook_event import LicenseKeyCreatedWebhookEvent as LicenseKeyCreatedWebhookEvent
@@ -187,6 +196,9 @@
from .credit_overage_charged_webhook_event import CreditOverageChargedWebhookEvent as CreditOverageChargedWebhookEvent
from .payment_retrieve_line_items_response import PaymentRetrieveLineItemsResponse as PaymentRetrieveLineItemsResponse
from .subscription_cancelled_webhook_event import SubscriptionCancelledWebhookEvent as SubscriptionCancelledWebhookEvent
+from .product_collection_unarchive_response import (
+ ProductCollectionUnarchiveResponse as ProductCollectionUnarchiveResponse,
+)
from .checkout_session_billing_address_param import (
CheckoutSessionBillingAddressParam as CheckoutSessionBillingAddressParam,
)
@@ -211,6 +223,9 @@
from .entitlement_grant_revoked_webhook_event import (
EntitlementGrantRevokedWebhookEvent as EntitlementGrantRevokedWebhookEvent,
)
+from .product_collection_update_images_params import (
+ ProductCollectionUpdateImagesParams as ProductCollectionUpdateImagesParams,
+)
from .subscription_plan_changed_webhook_event import (
SubscriptionPlanChangedWebhookEvent as SubscriptionPlanChangedWebhookEvent,
)
@@ -223,6 +238,9 @@
from .entitlement_grant_delivered_webhook_event import (
EntitlementGrantDeliveredWebhookEvent as EntitlementGrantDeliveredWebhookEvent,
)
+from .product_collection_update_images_response import (
+ ProductCollectionUpdateImagesResponse as ProductCollectionUpdateImagesResponse,
+)
from .subscription_preview_change_plan_response import (
SubscriptionPreviewChangePlanResponse as SubscriptionPreviewChangePlanResponse,
)
@@ -250,3 +268,14 @@
from .subscription_retrieve_usage_history_response import (
SubscriptionRetrieveUsageHistoryResponse as SubscriptionRetrieveUsageHistoryResponse,
)
+
+# Rebuild cyclical models only after all modules are imported.
+# This ensures that, when building the deferred (due to cyclical references) model schema,
+# Pydantic can resolve the necessary references.
+# See: https://github.com/pydantic/pydantic/issues/11250 for more context.
+if _compat.PYDANTIC_V1:
+ meter.Meter.update_forward_refs() # type: ignore
+ meter_filter.MeterFilter.update_forward_refs() # type: ignore
+else:
+ meter.Meter.model_rebuild(_parent_namespace_depth=0)
+ meter_filter.MeterFilter.model_rebuild(_parent_namespace_depth=0)
diff --git a/src/dodopayments/types/checkout_session_create_params.py b/src/dodopayments/types/checkout_session_create_params.py
index d7fd15ca..46e1b4c8 100644
--- a/src/dodopayments/types/checkout_session_create_params.py
+++ b/src/dodopayments/types/checkout_session_create_params.py
@@ -56,6 +56,13 @@ class CheckoutSessionCreateParams(TypedDict, total=False):
customer: Optional[CustomerRequestParam]
"""Customer details for the session"""
+ customer_business_name: Optional[str]
+ """Optional business / legal name associated with the tax id.
+
+ When provided together with a valid tax id for a B2B purchase, this name is
+ rendered on the invoice instead of the customer's personal name.
+ """
+
customization: CheckoutSessionCustomizationParam
"""Customization for the checkout session page"""
diff --git a/src/dodopayments/types/checkout_session_flags_param.py b/src/dodopayments/types/checkout_session_flags_param.py
index 85a8f437..432369c2 100644
--- a/src/dodopayments/types/checkout_session_flags_param.py
+++ b/src/dodopayments/types/checkout_session_flags_param.py
@@ -14,6 +14,17 @@ class CheckoutSessionFlagsParam(TypedDict, total=False):
Default is true
"""
+ allow_customer_editing_business_name: bool
+ """
+ If true, the customer can supply or edit the business name associated with the
+ tax id during checkout. Works independently of `allow_customer_editing_tax_id` —
+ either flag (or `allow_tax_id`) is sufficient to let the customer override the
+ session's business name. Typically set together with
+ `allow_customer_editing_tax_id`.
+
+ Default is false
+ """
+
allow_customer_editing_city: bool
allow_customer_editing_country: bool
diff --git a/src/dodopayments/types/checkout_session_preview_params.py b/src/dodopayments/types/checkout_session_preview_params.py
index 902e60d8..be97f012 100644
--- a/src/dodopayments/types/checkout_session_preview_params.py
+++ b/src/dodopayments/types/checkout_session_preview_params.py
@@ -56,6 +56,13 @@ class CheckoutSessionPreviewParams(TypedDict, total=False):
customer: Optional[CustomerRequestParam]
"""Customer details for the session"""
+ customer_business_name: Optional[str]
+ """Optional business / legal name associated with the tax id.
+
+ When provided together with a valid tax id for a B2B purchase, this name is
+ rendered on the invoice instead of the customer's personal name.
+ """
+
customization: CheckoutSessionCustomizationParam
"""Customization for the checkout session page"""
diff --git a/src/dodopayments/types/checkout_session_response.py b/src/dodopayments/types/checkout_session_response.py
index 0a33ebe7..42754f0c 100644
--- a/src/dodopayments/types/checkout_session_response.py
+++ b/src/dodopayments/types/checkout_session_response.py
@@ -13,3 +13,23 @@ class CheckoutSessionResponse(BaseModel):
checkout_url: Optional[str] = None
"""Checkout url (None when payment_method_id is provided)"""
+
+ client_secret: Optional[str] = None
+ """Client secret used to load the Dodo Payments checkout SDK.
+
+ Returned when `confirm: true` was passed and a PaymentIntent was created at
+ session-creation time. `None` otherwise.
+ """
+
+ payment_id: Optional[str] = None
+ """
+ Underlying payment id when `confirm: true` was passed and a PaymentIntent was
+ created at session-creation time. `None` otherwise.
+ """
+
+ publishable_key: Optional[str] = None
+ """Publishable key for the Dodo Payments checkout SDK.
+
+ Returned when `confirm: true` was passed and a PaymentIntent was created at
+ session-creation time. `None` otherwise.
+ """
diff --git a/src/dodopayments/types/filter_type.py b/src/dodopayments/types/filter_type.py
new file mode 100644
index 00000000..34c5674d
--- /dev/null
+++ b/src/dodopayments/types/filter_type.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List, Union
+from typing_extensions import TypeAlias
+
+from .._models import BaseModel
+from .filter_operator import FilterOperator
+
+__all__ = ["FilterType", "MeterFilterConditionList"]
+
+
+class MeterFilterConditionList(BaseModel):
+ key: str
+ """Filter key to apply"""
+
+ operator: FilterOperator
+ """Filter operator"""
+
+ value: Union[str, float, bool]
+ """Filter value - can be string, number, or boolean"""
+
+
+FilterType: TypeAlias = Union[List[MeterFilterConditionList], List["MeterFilter"]]
+
+from .meter_filter import MeterFilter
diff --git a/src/dodopayments/types/filter_type_param.py b/src/dodopayments/types/filter_type_param.py
new file mode 100644
index 00000000..2f5d28e4
--- /dev/null
+++ b/src/dodopayments/types/filter_type_param.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable
+from typing_extensions import Required, TypeAlias, TypedDict
+
+from .filter_operator import FilterOperator
+
+__all__ = ["FilterTypeParam", "MeterFilterConditionList"]
+
+
+class MeterFilterConditionList(TypedDict, total=False):
+ key: Required[str]
+ """Filter key to apply"""
+
+ operator: Required[FilterOperator]
+ """Filter operator"""
+
+ value: Required[Union[str, float, bool]]
+ """Filter value - can be string, number, or boolean"""
+
+
+FilterTypeParam: TypeAlias = Union[Iterable[MeterFilterConditionList], Iterable["MeterFilterParam"]]
+
+from .meter_filter_param import MeterFilterParam
diff --git a/src/dodopayments/types/github_permission.py b/src/dodopayments/types/github_permission.py
new file mode 100644
index 00000000..10424581
--- /dev/null
+++ b/src/dodopayments/types/github_permission.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["GitHubPermission"]
+
+GitHubPermission: TypeAlias = Literal["pull", "push", "admin", "maintain", "triage"]
diff --git a/src/dodopayments/types/integration_config_param.py b/src/dodopayments/types/integration_config_param.py
index 2b3de974..5d3910d4 100644
--- a/src/dodopayments/types/integration_config_param.py
+++ b/src/dodopayments/types/integration_config_param.py
@@ -3,10 +3,11 @@
from __future__ import annotations
from typing import Union, Optional
-from typing_extensions import Literal, Required, TypeAlias, TypedDict
+from typing_extensions import Required, TypeAlias, TypedDict
from .._types import SequenceNotStr
from .time_interval import TimeInterval
+from .github_permission import GitHubPermission
__all__ = [
"IntegrationConfigParam",
@@ -22,7 +23,7 @@
class GitHubConfig(TypedDict, total=False):
- permission: Required[Literal["pull", "push", "admin", "maintain", "triage"]]
+ permission: Required[GitHubPermission]
"""Permission to grant on the repository."""
target_id: Required[str]
diff --git a/src/dodopayments/types/integration_config_response.py b/src/dodopayments/types/integration_config_response.py
index 667bca75..d62e93b2 100644
--- a/src/dodopayments/types/integration_config_response.py
+++ b/src/dodopayments/types/integration_config_response.py
@@ -1,10 +1,11 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
-from typing_extensions import Literal, TypeAlias
+from typing_extensions import TypeAlias
from .._models import BaseModel
from .time_interval import TimeInterval
+from .github_permission import GitHubPermission
__all__ = [
"IntegrationConfigResponse",
@@ -22,7 +23,7 @@
class GitHubConfig(BaseModel):
- permission: Literal["pull", "push", "admin", "maintain", "triage"]
+ permission: GitHubPermission
"""Permission to grant on the repository."""
target_id: str
diff --git a/src/dodopayments/types/meter.py b/src/dodopayments/types/meter.py
index 4c40853a..5a9b98e8 100644
--- a/src/dodopayments/types/meter.py
+++ b/src/dodopayments/types/meter.py
@@ -1,10 +1,11 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from __future__ import annotations
+
from typing import Optional
from datetime import datetime
from .._models import BaseModel
-from .meter_filter import MeterFilter
from .meter_aggregation import MeterAggregation
__all__ = ["Meter"]
@@ -29,7 +30,7 @@ class Meter(BaseModel):
description: Optional[str] = None
- filter: Optional[MeterFilter] = None
+ filter: Optional["MeterFilter"] = None
"""
A filter structure that combines multiple conditions with logical conjunctions
(AND/OR).
@@ -38,3 +39,6 @@ class Meter(BaseModel):
filter has a conjunction (and/or) and clauses that can be either direct
conditions or nested filters.
"""
+
+
+from .meter_filter import MeterFilter
diff --git a/src/dodopayments/types/meter_create_params.py b/src/dodopayments/types/meter_create_params.py
index ab1e5b85..31905410 100644
--- a/src/dodopayments/types/meter_create_params.py
+++ b/src/dodopayments/types/meter_create_params.py
@@ -5,7 +5,6 @@
from typing import Optional
from typing_extensions import Required, TypedDict
-from .meter_filter_param import MeterFilterParam
from .meter_aggregation_param import MeterAggregationParam
__all__ = ["MeterCreateParams"]
@@ -27,5 +26,8 @@ class MeterCreateParams(TypedDict, total=False):
description: Optional[str]
"""Optional description of the meter"""
- filter: Optional[MeterFilterParam]
+ filter: Optional["MeterFilterParam"]
"""Optional filter to apply to the meter"""
+
+
+from .meter_filter_param import MeterFilterParam
diff --git a/src/dodopayments/types/meter_filter.py b/src/dodopayments/types/meter_filter.py
index 755b4812..8c83c3a7 100644
--- a/src/dodopayments/types/meter_filter.py
+++ b/src/dodopayments/types/meter_filter.py
@@ -1,102 +1,11 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List, Union
+from __future__ import annotations
from .._models import BaseModel
from .conjunction import Conjunction
-from .filter_operator import FilterOperator
-__all__ = [
- "MeterFilter",
- "ClausesDirectFilterCondition",
- "ClausesNestedMeterFilter",
- "ClausesNestedMeterFilterClausesLevel1FilterCondition",
- "ClausesNestedMeterFilterClausesLevel1NestedFilter",
- "ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2FilterCondition",
- "ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilter",
- "ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilterClause",
-]
-
-
-class ClausesDirectFilterCondition(BaseModel):
- """Filter condition with key, operator, and value"""
-
- key: str
- """Filter key to apply"""
-
- operator: FilterOperator
-
- value: Union[str, float, bool]
- """Filter value - can be string, number, or boolean"""
-
-
-class ClausesNestedMeterFilterClausesLevel1FilterCondition(BaseModel):
- """Filter condition with key, operator, and value"""
-
- key: str
- """Filter key to apply"""
-
- operator: FilterOperator
-
- value: Union[str, float, bool]
- """Filter value - can be string, number, or boolean"""
-
-
-class ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2FilterCondition(BaseModel):
- """Filter condition with key, operator, and value"""
-
- key: str
- """Filter key to apply"""
-
- operator: FilterOperator
-
- value: Union[str, float, bool]
- """Filter value - can be string, number, or boolean"""
-
-
-class ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilterClause(BaseModel):
- """Filter condition with key, operator, and value"""
-
- key: str
- """Filter key to apply"""
-
- operator: FilterOperator
-
- value: Union[str, float, bool]
- """Filter value - can be string, number, or boolean"""
-
-
-class ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilter(BaseModel):
- """Level 3 nested filter (final nesting level)"""
-
- clauses: List[ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilterClause]
- """Level 3: Filter conditions only (max depth reached)"""
-
- conjunction: Conjunction
-
-
-class ClausesNestedMeterFilterClausesLevel1NestedFilter(BaseModel):
- """Level 2 nested filter"""
-
- clauses: Union[
- List[ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2FilterCondition],
- List[ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilter],
- ]
- """Level 2: Can be conditions or nested filters (1 more level allowed)"""
-
- conjunction: Conjunction
-
-
-class ClausesNestedMeterFilter(BaseModel):
- """Level 1 nested filter - can contain Level 2 filters"""
-
- clauses: Union[
- List[ClausesNestedMeterFilterClausesLevel1FilterCondition],
- List[ClausesNestedMeterFilterClausesLevel1NestedFilter],
- ]
- """Level 1: Can be conditions or nested filters (2 more levels allowed)"""
-
- conjunction: Conjunction
+__all__ = ["MeterFilter"]
class MeterFilter(BaseModel):
@@ -107,7 +16,7 @@ class MeterFilter(BaseModel):
Each filter has a conjunction (and/or) and clauses that can be either direct conditions or nested filters.
"""
- clauses: Union[List[ClausesDirectFilterCondition], List[ClausesNestedMeterFilter]]
+ clauses: "FilterType"
"""
Filter clauses - can be direct conditions or nested filters (up to 3 levels
deep)
@@ -115,3 +24,6 @@ class MeterFilter(BaseModel):
conjunction: Conjunction
"""Logical conjunction to apply between clauses (and/or)"""
+
+
+from .filter_type import FilterType
diff --git a/src/dodopayments/types/meter_filter_param.py b/src/dodopayments/types/meter_filter_param.py
index 1c11a0ba..ce2511bb 100644
--- a/src/dodopayments/types/meter_filter_param.py
+++ b/src/dodopayments/types/meter_filter_param.py
@@ -2,107 +2,11 @@
from __future__ import annotations
-from typing import Union, Iterable
from typing_extensions import Required, TypedDict
from .conjunction import Conjunction
-from .filter_operator import FilterOperator
-__all__ = [
- "MeterFilterParam",
- "ClausesDirectFilterCondition",
- "ClausesNestedMeterFilter",
- "ClausesNestedMeterFilterClausesLevel1FilterCondition",
- "ClausesNestedMeterFilterClausesLevel1NestedFilter",
- "ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2FilterCondition",
- "ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilter",
- "ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilterClause",
-]
-
-
-class ClausesDirectFilterCondition(TypedDict, total=False):
- """Filter condition with key, operator, and value"""
-
- key: Required[str]
- """Filter key to apply"""
-
- operator: Required[FilterOperator]
-
- value: Required[Union[str, float, bool]]
- """Filter value - can be string, number, or boolean"""
-
-
-class ClausesNestedMeterFilterClausesLevel1FilterCondition(TypedDict, total=False):
- """Filter condition with key, operator, and value"""
-
- key: Required[str]
- """Filter key to apply"""
-
- operator: Required[FilterOperator]
-
- value: Required[Union[str, float, bool]]
- """Filter value - can be string, number, or boolean"""
-
-
-class ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2FilterCondition(TypedDict, total=False):
- """Filter condition with key, operator, and value"""
-
- key: Required[str]
- """Filter key to apply"""
-
- operator: Required[FilterOperator]
-
- value: Required[Union[str, float, bool]]
- """Filter value - can be string, number, or boolean"""
-
-
-class ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilterClause(TypedDict, total=False):
- """Filter condition with key, operator, and value"""
-
- key: Required[str]
- """Filter key to apply"""
-
- operator: Required[FilterOperator]
-
- value: Required[Union[str, float, bool]]
- """Filter value - can be string, number, or boolean"""
-
-
-class ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilter(TypedDict, total=False):
- """Level 3 nested filter (final nesting level)"""
-
- clauses: Required[Iterable[ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilterClause]]
- """Level 3: Filter conditions only (max depth reached)"""
-
- conjunction: Required[Conjunction]
-
-
-class ClausesNestedMeterFilterClausesLevel1NestedFilter(TypedDict, total=False):
- """Level 2 nested filter"""
-
- clauses: Required[
- Union[
- Iterable[ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2FilterCondition],
- Iterable[ClausesNestedMeterFilterClausesLevel1NestedFilterClausesLevel2NestedFilter],
- ]
- ]
- """Level 2: Can be conditions or nested filters (1 more level allowed)"""
-
- conjunction: Required[Conjunction]
-
-
-class ClausesNestedMeterFilter(TypedDict, total=False):
- """Level 1 nested filter - can contain Level 2 filters"""
-
- clauses: Required[
- Union[
- Iterable[ClausesNestedMeterFilterClausesLevel1FilterCondition],
- Iterable[ClausesNestedMeterFilterClausesLevel1NestedFilter],
- ]
- ]
- """Level 1: Can be conditions or nested filters (2 more levels allowed)"""
-
- conjunction: Required[Conjunction]
+__all__ = ["MeterFilterParam"]
class MeterFilterParam(TypedDict, total=False):
@@ -113,7 +17,7 @@ class MeterFilterParam(TypedDict, total=False):
Each filter has a conjunction (and/or) and clauses that can be either direct conditions or nested filters.
"""
- clauses: Required[Union[Iterable[ClausesDirectFilterCondition], Iterable[ClausesNestedMeterFilter]]]
+ clauses: Required["FilterTypeParam"]
"""
Filter clauses - can be direct conditions or nested filters (up to 3 levels
deep)
@@ -121,3 +25,6 @@ class MeterFilterParam(TypedDict, total=False):
conjunction: Required[Conjunction]
"""Logical conjunction to apply between clauses (and/or)"""
+
+
+from .filter_type_param import FilterTypeParam
diff --git a/src/dodopayments/types/one_time_product_cart_item.py b/src/dodopayments/types/one_time_product_cart_item.py
index c868be06..661648e6 100644
--- a/src/dodopayments/types/one_time_product_cart_item.py
+++ b/src/dodopayments/types/one_time_product_cart_item.py
@@ -1,7 +1,5 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
-
from .._models import BaseModel
__all__ = ["OneTimeProductCartItem"]
@@ -11,10 +9,3 @@ class OneTimeProductCartItem(BaseModel):
product_id: str
quantity: int
-
- amount: Optional[int] = None
- """Amount the customer pays if pay_what_you_want is enabled.
-
- If disabled then amount will be ignored Represented in the lowest denomination
- of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
- """
diff --git a/src/dodopayments/types/one_time_product_cart_item_param.py b/src/dodopayments/types/one_time_product_cart_item_param.py
deleted file mode 100644
index 35944838..00000000
--- a/src/dodopayments/types/one_time_product_cart_item_param.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import Optional
-from typing_extensions import Required, TypedDict
-
-__all__ = ["OneTimeProductCartItemParam"]
-
-
-class OneTimeProductCartItemParam(TypedDict, total=False):
- product_id: Required[str]
-
- quantity: Required[int]
-
- amount: Optional[int]
- """Amount the customer pays if pay_what_you_want is enabled.
-
- If disabled then amount will be ignored Represented in the lowest denomination
- of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
- """
diff --git a/src/dodopayments/types/payment.py b/src/dodopayments/types/payment.py
index 89669e50..d0656cc4 100644
--- a/src/dodopayments/types/payment.py
+++ b/src/dodopayments/types/payment.py
@@ -14,14 +14,9 @@
from .custom_field_response import CustomFieldResponse
from .payment_refund_status import PaymentRefundStatus
from .customer_limited_details import CustomerLimitedDetails
+from .one_time_product_cart_item import OneTimeProductCartItem
-__all__ = ["Payment", "ProductCart"]
-
-
-class ProductCart(BaseModel):
- product_id: str
-
- quantity: int
+__all__ = ["Payment"]
class Payment(BaseModel):
@@ -129,7 +124,7 @@ class Payment(BaseModel):
payment_method_type: Optional[str] = None
"""Specific type of payment method (e.g. "visa", "mastercard")"""
- product_cart: Optional[List[ProductCart]] = None
+ product_cart: Optional[List[OneTimeProductCartItem]] = None
"""List of products purchased in a one-time payment"""
refund_status: Optional[PaymentRefundStatus] = None
diff --git a/src/dodopayments/types/payment_create_params.py b/src/dodopayments/types/payment_create_params.py
index e7c19df0..86296e13 100644
--- a/src/dodopayments/types/payment_create_params.py
+++ b/src/dodopayments/types/payment_create_params.py
@@ -10,9 +10,8 @@
from .payment_method_types import PaymentMethodTypes
from .billing_address_param import BillingAddressParam
from .customer_request_param import CustomerRequestParam
-from .one_time_product_cart_item_param import OneTimeProductCartItemParam
-__all__ = ["PaymentCreateParams"]
+__all__ = ["PaymentCreateParams", "ProductCart"]
class PaymentCreateParams(TypedDict, total=False):
@@ -22,7 +21,7 @@ class PaymentCreateParams(TypedDict, total=False):
customer: Required[CustomerRequestParam]
"""Customer information for the payment"""
- product_cart: Required[Iterable[OneTimeProductCartItemParam]]
+ product_cart: Required[Iterable[ProductCart]]
"""List of products in the cart. Must contain at least 1 and at most 100 items."""
adaptive_currency_fees_inclusive: Optional[bool]
@@ -46,6 +45,13 @@ class PaymentCreateParams(TypedDict, total=False):
support that currency for this transaction, it will not proceed
"""
+ customer_business_name: Optional[str]
+ """Optional business / legal name associated with the tax id.
+
+ When provided together with a valid tax id for a B2B purchase, this name is
+ rendered on the invoice instead of the customer's personal name.
+ """
+
discount_code: Optional[str]
"""DEPRECATED: Use discount_codes instead.
@@ -107,3 +113,16 @@ class PaymentCreateParams(TypedDict, total=False):
If tax id validation fails the payment creation will fail
"""
+
+
+class ProductCart(TypedDict, total=False):
+ product_id: Required[str]
+
+ quantity: Required[int]
+
+ amount: Optional[int]
+ """Amount the customer pays if pay_what_you_want is enabled.
+
+ If disabled then amount will be ignored Represented in the lowest denomination
+ of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
+ """
diff --git a/src/dodopayments/types/payment_create_response.py b/src/dodopayments/types/payment_create_response.py
index f090f836..a1164de8 100644
--- a/src/dodopayments/types/payment_create_response.py
+++ b/src/dodopayments/types/payment_create_response.py
@@ -5,9 +5,21 @@
from .._models import BaseModel
from .customer_limited_details import CustomerLimitedDetails
-from .one_time_product_cart_item import OneTimeProductCartItem
-__all__ = ["PaymentCreateResponse"]
+__all__ = ["PaymentCreateResponse", "ProductCart"]
+
+
+class ProductCart(BaseModel):
+ product_id: str
+
+ quantity: int
+
+ amount: Optional[int] = None
+ """Amount the customer pays if pay_what_you_want is enabled.
+
+ If disabled then amount will be ignored Represented in the lowest denomination
+ of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
+ """
class PaymentCreateResponse(BaseModel):
@@ -44,5 +56,5 @@ class PaymentCreateResponse(BaseModel):
payment_link: Optional[str] = None
"""Optional URL to a hosted payment page"""
- product_cart: Optional[List[OneTimeProductCartItem]] = None
+ product_cart: Optional[List[ProductCart]] = None
"""Optional list of products included in the payment"""
diff --git a/src/dodopayments/types/payment_method_types.py b/src/dodopayments/types/payment_method_types.py
index e767e3f7..a28d2779 100644
--- a/src/dodopayments/types/payment_method_types.py
+++ b/src/dodopayments/types/payment_method_types.py
@@ -82,6 +82,7 @@
"sepa",
"sepa_bank_transfer",
"sofort",
+ "sunbit",
"swish",
"touch_n_go",
"trustly",
diff --git a/src/dodopayments/types/product_collection.py b/src/dodopayments/types/product_collection.py
new file mode 100644
index 00000000..9fa1e329
--- /dev/null
+++ b/src/dodopayments/types/product_collection.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from .._models import BaseModel
+from .product_collections.product_collection_group_response import ProductCollectionGroupResponse
+
+__all__ = ["ProductCollection"]
+
+
+class ProductCollection(BaseModel):
+ id: str
+ """Unique identifier for the product collection"""
+
+ brand_id: str
+ """Brand ID for the collection"""
+
+ created_at: datetime
+ """Timestamp when the collection was created"""
+
+ groups: List[ProductCollectionGroupResponse]
+ """Groups in this collection"""
+
+ name: str
+ """Name of the collection"""
+
+ updated_at: datetime
+ """Timestamp when the collection was last updated"""
+
+ description: Optional[str] = None
+ """Description of the collection"""
+
+ image: Optional[str] = None
+ """URL of the collection image"""
diff --git a/src/dodopayments/types/product_collection_create_params.py b/src/dodopayments/types/product_collection_create_params.py
new file mode 100644
index 00000000..134e47ed
--- /dev/null
+++ b/src/dodopayments/types/product_collection_create_params.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Required, TypedDict
+
+from .product_collections.product_collection_group_details_param import ProductCollectionGroupDetailsParam
+
+__all__ = ["ProductCollectionCreateParams"]
+
+
+class ProductCollectionCreateParams(TypedDict, total=False):
+ groups: Required[Iterable[ProductCollectionGroupDetailsParam]]
+ """Groups of products in this collection"""
+
+ name: Required[str]
+ """Name of the product collection"""
+
+ brand_id: Optional[str]
+ """Brand id for the collection, if not provided will default to primary brand"""
+
+ description: Optional[str]
+ """Optional description of the product collection"""
diff --git a/src/dodopayments/types/product_collection_list_params.py b/src/dodopayments/types/product_collection_list_params.py
new file mode 100644
index 00000000..73624792
--- /dev/null
+++ b/src/dodopayments/types/product_collection_list_params.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["ProductCollectionListParams"]
+
+
+class ProductCollectionListParams(TypedDict, total=False):
+ archived: bool
+ """List archived collections"""
+
+ brand_id: str
+ """Filter by Brand id"""
+
+ page_number: int
+ """Page number default is 0"""
+
+ page_size: int
+ """Page size default is 10 max is 100"""
diff --git a/src/dodopayments/types/product_collection_list_response.py b/src/dodopayments/types/product_collection_list_response.py
new file mode 100644
index 00000000..d161ef46
--- /dev/null
+++ b/src/dodopayments/types/product_collection_list_response.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from .._models import BaseModel
+
+__all__ = ["ProductCollectionListResponse"]
+
+
+class ProductCollectionListResponse(BaseModel):
+ id: str
+ """Collection ID"""
+
+ created_at: datetime
+ """Timestamp when created"""
+
+ name: str
+ """Collection name"""
+
+ products_count: int
+ """Number of products in the collection"""
+
+ updated_at: datetime
+ """Timestamp when last updated"""
+
+ description: Optional[str] = None
+ """Collection description"""
+
+ image: Optional[str] = None
+ """Collection image URL"""
diff --git a/src/dodopayments/types/product_collection_unarchive_response.py b/src/dodopayments/types/product_collection_unarchive_response.py
new file mode 100644
index 00000000..0802cd6a
--- /dev/null
+++ b/src/dodopayments/types/product_collection_unarchive_response.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from .._models import BaseModel
+
+__all__ = ["ProductCollectionUnarchiveResponse"]
+
+
+class ProductCollectionUnarchiveResponse(BaseModel):
+ collection_id: str
+ """Collection ID that was unarchived"""
+
+ excluded_product_ids: List[str]
+ """Product IDs that were excluded because they are archived"""
+
+ message: str
+ """Success message"""
diff --git a/src/dodopayments/types/product_collection_update_images_params.py b/src/dodopayments/types/product_collection_update_images_params.py
new file mode 100644
index 00000000..da23ed66
--- /dev/null
+++ b/src/dodopayments/types/product_collection_update_images_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+__all__ = ["ProductCollectionUpdateImagesParams"]
+
+
+class ProductCollectionUpdateImagesParams(TypedDict, total=False):
+ force_update: Optional[bool]
+ """If true, generates a new image ID to force cache invalidation"""
diff --git a/src/dodopayments/types/product_collection_update_images_response.py b/src/dodopayments/types/product_collection_update_images_response.py
new file mode 100644
index 00000000..18d87db3
--- /dev/null
+++ b/src/dodopayments/types/product_collection_update_images_response.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+
+__all__ = ["ProductCollectionUpdateImagesResponse"]
+
+
+class ProductCollectionUpdateImagesResponse(BaseModel):
+ url: str
+ """Presigned S3 URL for uploading the image"""
+
+ image_id: Optional[str] = None
+ """Optional image ID (present when force_update is true)"""
diff --git a/src/dodopayments/types/product_collection_update_params.py b/src/dodopayments/types/product_collection_update_params.py
new file mode 100644
index 00000000..9f4935d9
--- /dev/null
+++ b/src/dodopayments/types/product_collection_update_params.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+from .._types import SequenceNotStr
+
+__all__ = ["ProductCollectionUpdateParams"]
+
+
+class ProductCollectionUpdateParams(TypedDict, total=False):
+ brand_id: Optional[str]
+ """Optional brand_id update"""
+
+ description: Optional[str]
+ """Optional description update - pass null to remove, omit to keep unchanged"""
+
+ group_order: Optional[SequenceNotStr[str]]
+ """Optional new order for groups (array of group UUIDs in desired order)"""
+
+ image_id: Optional[str]
+ """Optional image update - pass null to remove, omit to keep unchanged"""
+
+ name: Optional[str]
+ """Optional new name for the collection"""
diff --git a/src/dodopayments/types/product_collections/__init__.py b/src/dodopayments/types/product_collections/__init__.py
new file mode 100644
index 00000000..20cd4bdb
--- /dev/null
+++ b/src/dodopayments/types/product_collections/__init__.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .group_create_params import GroupCreateParams as GroupCreateParams
+from .group_product_param import GroupProductParam as GroupProductParam
+from .group_update_params import GroupUpdateParams as GroupUpdateParams
+from .product_collection_group_response import ProductCollectionGroupResponse as ProductCollectionGroupResponse
+from .product_collection_group_details_param import (
+ ProductCollectionGroupDetailsParam as ProductCollectionGroupDetailsParam,
+)
diff --git a/src/dodopayments/types/product_collections/group_create_params.py b/src/dodopayments/types/product_collections/group_create_params.py
new file mode 100644
index 00000000..f1f74324
--- /dev/null
+++ b/src/dodopayments/types/product_collections/group_create_params.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Required, TypedDict
+
+from .group_product_param import GroupProductParam
+
+__all__ = ["GroupCreateParams"]
+
+
+class GroupCreateParams(TypedDict, total=False):
+ products: Required[Iterable[GroupProductParam]]
+ """Products in this group"""
+
+ group_name: Optional[str]
+ """Optional group name.
+
+ Multiple groups can have null names, but named groups must be unique per
+ collection
+ """
+
+ status: Optional[bool]
+ """Status of the group (defaults to true if not provided)"""
diff --git a/src/dodopayments/types/product_collections/group_product_param.py b/src/dodopayments/types/product_collections/group_product_param.py
new file mode 100644
index 00000000..ca3eb8e8
--- /dev/null
+++ b/src/dodopayments/types/product_collections/group_product_param.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["GroupProductParam"]
+
+
+class GroupProductParam(TypedDict, total=False):
+ product_id: Required[str]
+ """Product ID to include in the group"""
+
+ status: Optional[bool]
+ """Status of the product in this group (defaults to true if not provided)"""
diff --git a/src/dodopayments/types/product_collections/group_update_params.py b/src/dodopayments/types/product_collections/group_update_params.py
new file mode 100644
index 00000000..7d83d8e6
--- /dev/null
+++ b/src/dodopayments/types/product_collections/group_update_params.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+from ..._types import SequenceNotStr
+
+__all__ = ["GroupUpdateParams"]
+
+
+class GroupUpdateParams(TypedDict, total=False):
+ id: Required[str]
+
+ group_name: Optional[str]
+ """
+ Optional group name update: Some(Some(name)) = set name, Some(None) = clear
+ name, None = no change
+ """
+
+ product_order: Optional[SequenceNotStr[str]]
+ """
+ Optional new order for products in this group (array of
+ product_collection_group_pdts UUIDs)
+ """
+
+ status: Optional[bool]
+ """Optional status update"""
diff --git a/src/dodopayments/types/product_collections/groups/__init__.py b/src/dodopayments/types/product_collections/groups/__init__.py
new file mode 100644
index 00000000..60959cb1
--- /dev/null
+++ b/src/dodopayments/types/product_collections/groups/__init__.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .item_create_params import ItemCreateParams as ItemCreateParams
+from .item_update_params import ItemUpdateParams as ItemUpdateParams
+from .item_create_response import ItemCreateResponse as ItemCreateResponse
+from .product_collection_product import ProductCollectionProduct as ProductCollectionProduct
diff --git a/src/dodopayments/types/product_collections/groups/item_create_params.py b/src/dodopayments/types/product_collections/groups/item_create_params.py
new file mode 100644
index 00000000..4cd309b9
--- /dev/null
+++ b/src/dodopayments/types/product_collections/groups/item_create_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Required, TypedDict
+
+from ..group_product_param import GroupProductParam
+
+__all__ = ["ItemCreateParams"]
+
+
+class ItemCreateParams(TypedDict, total=False):
+ id: Required[str]
+
+ products: Required[Iterable[GroupProductParam]]
+ """Products to add to the group"""
diff --git a/src/dodopayments/types/product_collections/groups/item_create_response.py b/src/dodopayments/types/product_collections/groups/item_create_response.py
new file mode 100644
index 00000000..d64e5270
--- /dev/null
+++ b/src/dodopayments/types/product_collections/groups/item_create_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from .product_collection_product import ProductCollectionProduct
+
+__all__ = ["ItemCreateResponse"]
+
+ItemCreateResponse: TypeAlias = List[ProductCollectionProduct]
diff --git a/src/dodopayments/types/product_collections/groups/item_update_params.py b/src/dodopayments/types/product_collections/groups/item_update_params.py
new file mode 100644
index 00000000..708361ad
--- /dev/null
+++ b/src/dodopayments/types/product_collections/groups/item_update_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ItemUpdateParams"]
+
+
+class ItemUpdateParams(TypedDict, total=False):
+ id: Required[str]
+
+ group_id: Required[str]
+
+ status: Required[bool]
+ """Status of the product in the group"""
diff --git a/src/dodopayments/types/product_collections/groups/product_collection_product.py b/src/dodopayments/types/product_collections/groups/product_collection_product.py
new file mode 100644
index 00000000..bbe420d2
--- /dev/null
+++ b/src/dodopayments/types/product_collections/groups/product_collection_product.py
@@ -0,0 +1,50 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ...price import Price
+from ...._models import BaseModel
+from ...currency import Currency
+from ...tax_category import TaxCategory
+
+__all__ = ["ProductCollectionProduct"]
+
+
+class ProductCollectionProduct(BaseModel):
+ id: str
+
+ addons_count: int
+
+ files_count: int
+
+ has_credit_entitlements: bool
+ """Whether this product has any credit entitlements attached"""
+
+ is_recurring: bool
+
+ license_key_enabled: bool
+
+ meters_count: int
+
+ product_id: str
+
+ status: bool
+
+ currency: Optional[Currency] = None
+
+ description: Optional[str] = None
+
+ name: Optional[str] = None
+
+ price: Optional[int] = None
+
+ price_detail: Optional[Price] = None
+ """One-time price details."""
+
+ tax_category: Optional[TaxCategory] = None
+ """
+ Represents the different categories of taxation applicable to various products
+ and services.
+ """
+
+ tax_inclusive: Optional[bool] = None
diff --git a/src/dodopayments/types/product_collections/product_collection_group_details_param.py b/src/dodopayments/types/product_collections/product_collection_group_details_param.py
new file mode 100644
index 00000000..721043c0
--- /dev/null
+++ b/src/dodopayments/types/product_collections/product_collection_group_details_param.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Required, TypedDict
+
+from .group_product_param import GroupProductParam
+
+__all__ = ["ProductCollectionGroupDetailsParam"]
+
+
+class ProductCollectionGroupDetailsParam(TypedDict, total=False):
+ products: Required[Iterable[GroupProductParam]]
+ """Products in this group"""
+
+ group_name: Optional[str]
+ """Optional group name.
+
+ Multiple groups can have null names, but named groups must be unique per
+ collection
+ """
+
+ status: Optional[bool]
+ """Status of the group (defaults to true if not provided)"""
diff --git a/src/dodopayments/types/product_collections/product_collection_group_response.py b/src/dodopayments/types/product_collections/product_collection_group_response.py
new file mode 100644
index 00000000..6efc2e44
--- /dev/null
+++ b/src/dodopayments/types/product_collections/product_collection_group_response.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ..._models import BaseModel
+from .groups.product_collection_product import ProductCollectionProduct
+
+__all__ = ["ProductCollectionGroupResponse"]
+
+
+class ProductCollectionGroupResponse(BaseModel):
+ group_id: str
+
+ products: List[ProductCollectionProduct]
+
+ status: bool
+
+ group_name: Optional[str] = None
diff --git a/src/dodopayments/types/subscription.py b/src/dodopayments/types/subscription.py
index 6109bd74..ae2ed122 100644
--- a/src/dodopayments/types/subscription.py
+++ b/src/dodopayments/types/subscription.py
@@ -114,6 +114,12 @@ class Subscription(BaseModel):
custom_field_responses: Optional[List[CustomFieldResponse]] = None
"""Customer's responses to custom fields collected during checkout"""
+ customer_business_name: Optional[str] = None
+ """Business / legal name associated with the tax id (B2B).
+
+ When set this is used on the invoice in place of the customer's personal name.
+ """
+
discount_cycles_remaining: Optional[int] = None
"""DEPRECATED: Use discounts[].cycles_remaining instead."""
diff --git a/src/dodopayments/types/subscription_create_params.py b/src/dodopayments/types/subscription_create_params.py
index 132c8185..bbb1ff7e 100644
--- a/src/dodopayments/types/subscription_create_params.py
+++ b/src/dodopayments/types/subscription_create_params.py
@@ -12,9 +12,8 @@
from .billing_address_param import BillingAddressParam
from .customer_request_param import CustomerRequestParam
from .on_demand_subscription_param import OnDemandSubscriptionParam
-from .one_time_product_cart_item_param import OneTimeProductCartItemParam
-__all__ = ["SubscriptionCreateParams"]
+__all__ = ["SubscriptionCreateParams", "OneTimeProductCart"]
class SubscriptionCreateParams(TypedDict, total=False):
@@ -48,6 +47,13 @@ class SubscriptionCreateParams(TypedDict, total=False):
support that currency for this transaction, it will not proceed
"""
+ customer_business_name: Optional[str]
+ """Optional business / legal name associated with the tax id.
+
+ When provided together with a valid tax id for a B2B purchase, this name is
+ rendered on the invoice instead of the customer's personal name.
+ """
+
discount_code: Optional[str]
"""DEPRECATED: Use discount_codes instead.
@@ -78,7 +84,7 @@ class SubscriptionCreateParams(TypedDict, total=False):
on_demand: Optional[OnDemandSubscriptionParam]
- one_time_product_cart: Optional[Iterable[OneTimeProductCartItemParam]]
+ one_time_product_cart: Optional[Iterable[OneTimeProductCart]]
"""
List of one time products that will be bundled with the first payment for this
subscription
@@ -127,3 +133,16 @@ class SubscriptionCreateParams(TypedDict, total=False):
Optional trial period in days If specified, this value overrides the trial
period set in the product's price Must be between 0 and 10000 days
"""
+
+
+class OneTimeProductCart(TypedDict, total=False):
+ product_id: Required[str]
+
+ quantity: Required[int]
+
+ amount: Optional[int]
+ """Amount the customer pays if pay_what_you_want is enabled.
+
+ If disabled then amount will be ignored Represented in the lowest denomination
+ of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
+ """
diff --git a/src/dodopayments/types/subscription_create_response.py b/src/dodopayments/types/subscription_create_response.py
index 886b4239..972da4fc 100644
--- a/src/dodopayments/types/subscription_create_response.py
+++ b/src/dodopayments/types/subscription_create_response.py
@@ -6,14 +6,9 @@
from .._models import BaseModel
from .addon_cart_response_item import AddonCartResponseItem
from .customer_limited_details import CustomerLimitedDetails
+from .one_time_product_cart_item import OneTimeProductCartItem
-__all__ = ["SubscriptionCreateResponse", "OneTimeProductCart"]
-
-
-class OneTimeProductCart(BaseModel):
- product_id: str
-
- quantity: int
+__all__ = ["SubscriptionCreateResponse"]
class SubscriptionCreateResponse(BaseModel):
@@ -56,7 +51,7 @@ class SubscriptionCreateResponse(BaseModel):
expires_on: Optional[datetime] = None
"""Expiry timestamp of the payment link"""
- one_time_product_cart: Optional[List[OneTimeProductCart]] = None
+ one_time_product_cart: Optional[List[OneTimeProductCartItem]] = None
"""One time products associated with the purchase of subscription"""
payment_link: Optional[str] = None
diff --git a/src/dodopayments/types/subscription_list_response.py b/src/dodopayments/types/subscription_list_response.py
index 454f8aff..7a066cd1 100644
--- a/src/dodopayments/types/subscription_list_response.py
+++ b/src/dodopayments/types/subscription_list_response.py
@@ -102,6 +102,12 @@ class SubscriptionListResponse(BaseModel):
cancelled_at: Optional[datetime] = None
"""Cancelled timestamp if the subscription is cancelled"""
+ customer_business_name: Optional[str] = None
+ """Business / legal name associated with the tax id (B2B).
+
+ When set this is used on the invoice in place of the customer's personal name.
+ """
+
discount_cycles_remaining: Optional[int] = None
"""DEPRECATED: Use discounts[].cycles_remaining instead."""
diff --git a/src/dodopayments/types/subscription_update_params.py b/src/dodopayments/types/subscription_update_params.py
index 9b775768..6ae9cd96 100644
--- a/src/dodopayments/types/subscription_update_params.py
+++ b/src/dodopayments/types/subscription_update_params.py
@@ -40,6 +40,14 @@ class SubscriptionUpdateParams(TypedDict, total=False):
credit_entitlement_cart: Optional[Iterable[CreditEntitlementCart]]
"""Update credit entitlement cart settings"""
+ customer_business_name: Optional[str]
+ """Optional business / legal name associated with the tax id.
+
+ When provided together with a valid tax id for a B2B subscription, this name is
+ rendered on the invoice instead of the customer's personal name. Send `null` to
+ explicitly clear the business name.
+ """
+
customer_name: Optional[str]
disable_on_demand: Optional[DisableOnDemand]
diff --git a/src/dodopayments/types/webhook_event_type.py b/src/dodopayments/types/webhook_event_type.py
index 731cdfb2..c4c6fcb3 100644
--- a/src/dodopayments/types/webhook_event_type.py
+++ b/src/dodopayments/types/webhook_event_type.py
@@ -21,10 +21,8 @@
"subscription.active",
"subscription.renewed",
"subscription.on_hold",
+ "subscription.paused",
"subscription.cancelled",
- "subscription.cancellation_scheduled",
- "subscription.trial_ending",
- "subscription.upcoming_renewal",
"subscription.failed",
"subscription.expired",
"subscription.plan_changed",
@@ -48,8 +46,6 @@
"abandoned_checkout.recovered",
"dunning.started",
"dunning.recovered",
- "acr.email",
- "dunning.email",
"entitlement_grant.created",
"entitlement_grant.delivered",
"entitlement_grant.failed",
diff --git a/tests/api_resources/invoices/test_payments.py b/tests/api_resources/invoices/test_payments.py
index 05671ebc..0cdac55f 100644
--- a/tests/api_resources/invoices/test_payments.py
+++ b/tests/api_resources/invoices/test_payments.py
@@ -73,6 +73,56 @@ def test_path_params_retrieve(self, client: DodoPayments) -> None:
"",
)
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_retrieve_payout(self, client: DodoPayments, respx_mock: MockRouter) -> None:
+ respx_mock.get("/invoices/payouts/payout_id").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ payment = client.invoices.payments.retrieve_payout(
+ "payout_id",
+ )
+ assert payment.is_closed
+ assert payment.json() == {"foo": "bar"}
+ assert cast(Any, payment.is_closed) is True
+ assert isinstance(payment, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_raw_response_retrieve_payout(self, client: DodoPayments, respx_mock: MockRouter) -> None:
+ respx_mock.get("/invoices/payouts/payout_id").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ payment = client.invoices.payments.with_raw_response.retrieve_payout(
+ "payout_id",
+ )
+
+ assert payment.is_closed is True
+ assert payment.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert payment.json() == {"foo": "bar"}
+ assert isinstance(payment, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_streaming_response_retrieve_payout(self, client: DodoPayments, respx_mock: MockRouter) -> None:
+ respx_mock.get("/invoices/payouts/payout_id").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ with client.invoices.payments.with_streaming_response.retrieve_payout(
+ "payout_id",
+ ) as payment:
+ assert not payment.is_closed
+ assert payment.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert payment.json() == {"foo": "bar"}
+ assert cast(Any, payment.is_closed) is True
+ assert isinstance(payment, StreamedBinaryAPIResponse)
+
+ assert cast(Any, payment.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_path_params_retrieve_payout(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `payout_id` but received ''"):
+ client.invoices.payments.with_raw_response.retrieve_payout(
+ "",
+ )
+
@parametrize
@pytest.mark.respx(base_url=base_url)
def test_method_retrieve_refund(self, client: DodoPayments, respx_mock: MockRouter) -> None:
@@ -179,6 +229,58 @@ async def test_path_params_retrieve(self, async_client: AsyncDodoPayments) -> No
"",
)
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_retrieve_payout(self, async_client: AsyncDodoPayments, respx_mock: MockRouter) -> None:
+ respx_mock.get("/invoices/payouts/payout_id").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ payment = await async_client.invoices.payments.retrieve_payout(
+ "payout_id",
+ )
+ assert payment.is_closed
+ assert await payment.json() == {"foo": "bar"}
+ assert cast(Any, payment.is_closed) is True
+ assert isinstance(payment, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_raw_response_retrieve_payout(self, async_client: AsyncDodoPayments, respx_mock: MockRouter) -> None:
+ respx_mock.get("/invoices/payouts/payout_id").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ payment = await async_client.invoices.payments.with_raw_response.retrieve_payout(
+ "payout_id",
+ )
+
+ assert payment.is_closed is True
+ assert payment.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert await payment.json() == {"foo": "bar"}
+ assert isinstance(payment, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_streaming_response_retrieve_payout(
+ self, async_client: AsyncDodoPayments, respx_mock: MockRouter
+ ) -> None:
+ respx_mock.get("/invoices/payouts/payout_id").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ async with async_client.invoices.payments.with_streaming_response.retrieve_payout(
+ "payout_id",
+ ) as payment:
+ assert not payment.is_closed
+ assert payment.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert await payment.json() == {"foo": "bar"}
+ assert cast(Any, payment.is_closed) is True
+ assert isinstance(payment, AsyncStreamedBinaryAPIResponse)
+
+ assert cast(Any, payment.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_path_params_retrieve_payout(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `payout_id` but received ''"):
+ await async_client.invoices.payments.with_raw_response.retrieve_payout(
+ "",
+ )
+
@parametrize
@pytest.mark.respx(base_url=base_url)
async def test_method_retrieve_refund(self, async_client: AsyncDodoPayments, respx_mock: MockRouter) -> None:
diff --git a/tests/api_resources/product_collections/__init__.py b/tests/api_resources/product_collections/__init__.py
new file mode 100644
index 00000000..fd8019a9
--- /dev/null
+++ b/tests/api_resources/product_collections/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/product_collections/groups/__init__.py b/tests/api_resources/product_collections/groups/__init__.py
new file mode 100644
index 00000000..fd8019a9
--- /dev/null
+++ b/tests/api_resources/product_collections/groups/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/product_collections/groups/test_items.py b/tests/api_resources/product_collections/groups/test_items.py
new file mode 100644
index 00000000..aefc5b7c
--- /dev/null
+++ b/tests/api_resources/product_collections/groups/test_items.py
@@ -0,0 +1,382 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from dodopayments import DodoPayments, AsyncDodoPayments
+from dodopayments.types.product_collections.groups import ItemCreateResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestItems:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: DodoPayments) -> None:
+ item = client.product_collections.groups.items.create(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+ assert_matches_type(ItemCreateResponse, item, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: DodoPayments) -> None:
+ response = client.product_collections.groups.items.with_raw_response.create(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert_matches_type(ItemCreateResponse, item, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: DodoPayments) -> None:
+ with client.product_collections.groups.items.with_streaming_response.create(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ products=[{"product_id": "product_id"}],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = response.parse()
+ assert_matches_type(ItemCreateResponse, item, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.groups.items.with_raw_response.create(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ products=[{"product_id": "product_id"}],
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ client.product_collections.groups.items.with_raw_response.create(
+ group_id="",
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+
+ @parametrize
+ def test_method_update(self, client: DodoPayments) -> None:
+ item = client.product_collections.groups.items.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ )
+ assert item is None
+
+ @parametrize
+ def test_raw_response_update(self, client: DodoPayments) -> None:
+ response = client.product_collections.groups.items.with_raw_response.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert item is None
+
+ @parametrize
+ def test_streaming_response_update(self, client: DodoPayments) -> None:
+ with client.product_collections.groups.items.with_streaming_response.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = response.parse()
+ assert item is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.groups.items.with_raw_response.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ client.product_collections.groups.items.with_raw_response.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="",
+ status=True,
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `item_id` but received ''"):
+ client.product_collections.groups.items.with_raw_response.update(
+ item_id="",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ )
+
+ @parametrize
+ def test_method_delete(self, client: DodoPayments) -> None:
+ item = client.product_collections.groups.items.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert item is None
+
+ @parametrize
+ def test_raw_response_delete(self, client: DodoPayments) -> None:
+ response = client.product_collections.groups.items.with_raw_response.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert item is None
+
+ @parametrize
+ def test_streaming_response_delete(self, client: DodoPayments) -> None:
+ with client.product_collections.groups.items.with_streaming_response.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = response.parse()
+ assert item is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.groups.items.with_raw_response.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ client.product_collections.groups.items.with_raw_response.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `item_id` but received ''"):
+ client.product_collections.groups.items.with_raw_response.delete(
+ item_id="",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+
+class TestAsyncItems:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncDodoPayments) -> None:
+ item = await async_client.product_collections.groups.items.create(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+ assert_matches_type(ItemCreateResponse, item, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.groups.items.with_raw_response.create(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = await response.parse()
+ assert_matches_type(ItemCreateResponse, item, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.groups.items.with_streaming_response.create(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ products=[{"product_id": "product_id"}],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = await response.parse()
+ assert_matches_type(ItemCreateResponse, item, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.groups.items.with_raw_response.create(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ products=[{"product_id": "product_id"}],
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ await async_client.product_collections.groups.items.with_raw_response.create(
+ group_id="",
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncDodoPayments) -> None:
+ item = await async_client.product_collections.groups.items.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ )
+ assert item is None
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.groups.items.with_raw_response.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = await response.parse()
+ assert item is None
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.groups.items.with_streaming_response.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = await response.parse()
+ assert item is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.groups.items.with_raw_response.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ await async_client.product_collections.groups.items.with_raw_response.update(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="",
+ status=True,
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `item_id` but received ''"):
+ await async_client.product_collections.groups.items.with_raw_response.update(
+ item_id="",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status=True,
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncDodoPayments) -> None:
+ item = await async_client.product_collections.groups.items.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert item is None
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.groups.items.with_raw_response.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = await response.parse()
+ assert item is None
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.groups.items.with_streaming_response.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = await response.parse()
+ assert item is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.groups.items.with_raw_response.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ await async_client.product_collections.groups.items.with_raw_response.delete(
+ item_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `item_id` but received ''"):
+ await async_client.product_collections.groups.items.with_raw_response.delete(
+ item_id="",
+ id="id",
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
diff --git a/tests/api_resources/product_collections/test_groups.py b/tests/api_resources/product_collections/test_groups.py
new file mode 100644
index 00000000..d950cc8e
--- /dev/null
+++ b/tests/api_resources/product_collections/test_groups.py
@@ -0,0 +1,354 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from dodopayments import DodoPayments, AsyncDodoPayments
+from dodopayments.types.product_collections import (
+ ProductCollectionGroupResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestGroups:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: DodoPayments) -> None:
+ group = client.product_collections.groups.create(
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+ assert_matches_type(ProductCollectionGroupResponse, group, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: DodoPayments) -> None:
+ group = client.product_collections.groups.create(
+ id="id",
+ products=[
+ {
+ "product_id": "product_id",
+ "status": True,
+ }
+ ],
+ group_name="group_name",
+ status=True,
+ )
+ assert_matches_type(ProductCollectionGroupResponse, group, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: DodoPayments) -> None:
+ response = client.product_collections.groups.with_raw_response.create(
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ group = response.parse()
+ assert_matches_type(ProductCollectionGroupResponse, group, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: DodoPayments) -> None:
+ with client.product_collections.groups.with_streaming_response.create(
+ id="id",
+ products=[{"product_id": "product_id"}],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ group = response.parse()
+ assert_matches_type(ProductCollectionGroupResponse, group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.groups.with_raw_response.create(
+ id="",
+ products=[{"product_id": "product_id"}],
+ )
+
+ @parametrize
+ def test_method_update(self, client: DodoPayments) -> None:
+ group = client.product_collections.groups.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+ assert group is None
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: DodoPayments) -> None:
+ group = client.product_collections.groups.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_name="group_name",
+ product_order=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ status=True,
+ )
+ assert group is None
+
+ @parametrize
+ def test_raw_response_update(self, client: DodoPayments) -> None:
+ response = client.product_collections.groups.with_raw_response.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ group = response.parse()
+ assert group is None
+
+ @parametrize
+ def test_streaming_response_update(self, client: DodoPayments) -> None:
+ with client.product_collections.groups.with_streaming_response.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ group = response.parse()
+ assert group is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.groups.with_raw_response.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ client.product_collections.groups.with_raw_response.update(
+ group_id="",
+ id="id",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: DodoPayments) -> None:
+ group = client.product_collections.groups.delete(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+ assert group is None
+
+ @parametrize
+ def test_raw_response_delete(self, client: DodoPayments) -> None:
+ response = client.product_collections.groups.with_raw_response.delete(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ group = response.parse()
+ assert group is None
+
+ @parametrize
+ def test_streaming_response_delete(self, client: DodoPayments) -> None:
+ with client.product_collections.groups.with_streaming_response.delete(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ group = response.parse()
+ assert group is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.groups.with_raw_response.delete(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ client.product_collections.groups.with_raw_response.delete(
+ group_id="",
+ id="id",
+ )
+
+
+class TestAsyncGroups:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncDodoPayments) -> None:
+ group = await async_client.product_collections.groups.create(
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+ assert_matches_type(ProductCollectionGroupResponse, group, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncDodoPayments) -> None:
+ group = await async_client.product_collections.groups.create(
+ id="id",
+ products=[
+ {
+ "product_id": "product_id",
+ "status": True,
+ }
+ ],
+ group_name="group_name",
+ status=True,
+ )
+ assert_matches_type(ProductCollectionGroupResponse, group, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.groups.with_raw_response.create(
+ id="id",
+ products=[{"product_id": "product_id"}],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ group = await response.parse()
+ assert_matches_type(ProductCollectionGroupResponse, group, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.groups.with_streaming_response.create(
+ id="id",
+ products=[{"product_id": "product_id"}],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ group = await response.parse()
+ assert_matches_type(ProductCollectionGroupResponse, group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.groups.with_raw_response.create(
+ id="",
+ products=[{"product_id": "product_id"}],
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncDodoPayments) -> None:
+ group = await async_client.product_collections.groups.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+ assert group is None
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncDodoPayments) -> None:
+ group = await async_client.product_collections.groups.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ group_name="group_name",
+ product_order=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ status=True,
+ )
+ assert group is None
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.groups.with_raw_response.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ group = await response.parse()
+ assert group is None
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.groups.with_streaming_response.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ group = await response.parse()
+ assert group is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.groups.with_raw_response.update(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ await async_client.product_collections.groups.with_raw_response.update(
+ group_id="",
+ id="id",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncDodoPayments) -> None:
+ group = await async_client.product_collections.groups.delete(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+ assert group is None
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.groups.with_raw_response.delete(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ group = await response.parse()
+ assert group is None
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.groups.with_streaming_response.delete(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ group = await response.parse()
+ assert group is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.groups.with_raw_response.delete(
+ group_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ await async_client.product_collections.groups.with_raw_response.delete(
+ group_id="",
+ id="id",
+ )
diff --git a/tests/api_resources/test_checkout_sessions.py b/tests/api_resources/test_checkout_sessions.py
index 772fdee8..0d414092 100644
--- a/tests/api_resources/test_checkout_sessions.py
+++ b/tests/api_resources/test_checkout_sessions.py
@@ -77,6 +77,7 @@ def test_method_create_with_all_params(self, client: DodoPayments) -> None:
}
],
customer={"customer_id": "customer_id"},
+ customer_business_name="customer_business_name",
customization={
"force_language": "force_language",
"show_on_demand_tag": True,
@@ -131,6 +132,7 @@ def test_method_create_with_all_params(self, client: DodoPayments) -> None:
discount_codes=["string"],
feature_flags={
"allow_currency_selection": True,
+ "allow_customer_editing_business_name": True,
"allow_customer_editing_city": True,
"allow_customer_editing_country": True,
"allow_customer_editing_email": True,
@@ -297,6 +299,7 @@ def test_method_preview_with_all_params(self, client: DodoPayments) -> None:
}
],
customer={"customer_id": "customer_id"},
+ customer_business_name="customer_business_name",
customization={
"force_language": "force_language",
"show_on_demand_tag": True,
@@ -351,6 +354,7 @@ def test_method_preview_with_all_params(self, client: DodoPayments) -> None:
discount_codes=["string"],
feature_flags={
"allow_currency_selection": True,
+ "allow_customer_editing_business_name": True,
"allow_customer_editing_city": True,
"allow_customer_editing_country": True,
"allow_customer_editing_email": True,
@@ -485,6 +489,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncDodoPaymen
}
],
customer={"customer_id": "customer_id"},
+ customer_business_name="customer_business_name",
customization={
"force_language": "force_language",
"show_on_demand_tag": True,
@@ -539,6 +544,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncDodoPaymen
discount_codes=["string"],
feature_flags={
"allow_currency_selection": True,
+ "allow_customer_editing_business_name": True,
"allow_customer_editing_city": True,
"allow_customer_editing_country": True,
"allow_customer_editing_email": True,
@@ -705,6 +711,7 @@ async def test_method_preview_with_all_params(self, async_client: AsyncDodoPayme
}
],
customer={"customer_id": "customer_id"},
+ customer_business_name="customer_business_name",
customization={
"force_language": "force_language",
"show_on_demand_tag": True,
@@ -759,6 +766,7 @@ async def test_method_preview_with_all_params(self, async_client: AsyncDodoPayme
discount_codes=["string"],
feature_flags={
"allow_currency_selection": True,
+ "allow_customer_editing_business_name": True,
"allow_customer_editing_city": True,
"allow_customer_editing_country": True,
"allow_customer_editing_email": True,
diff --git a/tests/api_resources/test_payments.py b/tests/api_resources/test_payments.py
index 078efa60..dad0d440 100644
--- a/tests/api_resources/test_payments.py
+++ b/tests/api_resources/test_payments.py
@@ -64,6 +64,7 @@ def test_method_create_with_all_params(self, client: DodoPayments) -> None:
adaptive_currency_fees_inclusive=True,
allowed_payment_method_types=["ach"],
billing_currency="AED",
+ customer_business_name="customer_business_name",
discount_code="discount_code",
discount_codes=["string"],
force_3ds=True,
@@ -280,6 +281,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncDodoPaymen
adaptive_currency_fees_inclusive=True,
allowed_payment_method_types=["ach"],
billing_currency="AED",
+ customer_business_name="customer_business_name",
discount_code="discount_code",
discount_codes=["string"],
force_3ds=True,
diff --git a/tests/api_resources/test_product_collections.py b/tests/api_resources/test_product_collections.py
new file mode 100644
index 00000000..62dade1a
--- /dev/null
+++ b/tests/api_resources/test_product_collections.py
@@ -0,0 +1,646 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from dodopayments import DodoPayments, AsyncDodoPayments
+from dodopayments.types import (
+ ProductCollection,
+ ProductCollectionListResponse,
+ ProductCollectionUnarchiveResponse,
+ ProductCollectionUpdateImagesResponse,
+)
+from dodopayments.pagination import SyncDefaultPageNumberPagination, AsyncDefaultPageNumberPagination
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestProductCollections:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.create(
+ groups=[{"products": [{"product_id": "product_id"}]}],
+ name="name",
+ )
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.create(
+ groups=[
+ {
+ "products": [
+ {
+ "product_id": "product_id",
+ "status": True,
+ }
+ ],
+ "group_name": "group_name",
+ "status": True,
+ }
+ ],
+ name="name",
+ brand_id="brand_id",
+ description="description",
+ )
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: DodoPayments) -> None:
+ response = client.product_collections.with_raw_response.create(
+ groups=[{"products": [{"product_id": "product_id"}]}],
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = response.parse()
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: DodoPayments) -> None:
+ with client.product_collections.with_streaming_response.create(
+ groups=[{"products": [{"product_id": "product_id"}]}],
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = response.parse()
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.retrieve(
+ "id",
+ )
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: DodoPayments) -> None:
+ response = client.product_collections.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = response.parse()
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: DodoPayments) -> None:
+ with client.product_collections.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = response.parse()
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.update(
+ id="id",
+ )
+ assert product_collection is None
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.update(
+ id="id",
+ brand_id="brand_id",
+ description="description",
+ group_order=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ image_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+ assert product_collection is None
+
+ @parametrize
+ def test_raw_response_update(self, client: DodoPayments) -> None:
+ response = client.product_collections.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = response.parse()
+ assert product_collection is None
+
+ @parametrize
+ def test_streaming_response_update(self, client: DodoPayments) -> None:
+ with client.product_collections.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = response.parse()
+ assert product_collection is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.with_raw_response.update(
+ id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.list()
+ assert_matches_type(
+ SyncDefaultPageNumberPagination[ProductCollectionListResponse], product_collection, path=["response"]
+ )
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.list(
+ archived=True,
+ brand_id="brand_id",
+ page_number=0,
+ page_size=0,
+ )
+ assert_matches_type(
+ SyncDefaultPageNumberPagination[ProductCollectionListResponse], product_collection, path=["response"]
+ )
+
+ @parametrize
+ def test_raw_response_list(self, client: DodoPayments) -> None:
+ response = client.product_collections.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = response.parse()
+ assert_matches_type(
+ SyncDefaultPageNumberPagination[ProductCollectionListResponse], product_collection, path=["response"]
+ )
+
+ @parametrize
+ def test_streaming_response_list(self, client: DodoPayments) -> None:
+ with client.product_collections.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = response.parse()
+ assert_matches_type(
+ SyncDefaultPageNumberPagination[ProductCollectionListResponse], product_collection, path=["response"]
+ )
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.delete(
+ "id",
+ )
+ assert product_collection is None
+
+ @parametrize
+ def test_raw_response_delete(self, client: DodoPayments) -> None:
+ response = client.product_collections.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = response.parse()
+ assert product_collection is None
+
+ @parametrize
+ def test_streaming_response_delete(self, client: DodoPayments) -> None:
+ with client.product_collections.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = response.parse()
+ assert product_collection is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ def test_method_unarchive(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.unarchive(
+ "id",
+ )
+ assert_matches_type(ProductCollectionUnarchiveResponse, product_collection, path=["response"])
+
+ @parametrize
+ def test_raw_response_unarchive(self, client: DodoPayments) -> None:
+ response = client.product_collections.with_raw_response.unarchive(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = response.parse()
+ assert_matches_type(ProductCollectionUnarchiveResponse, product_collection, path=["response"])
+
+ @parametrize
+ def test_streaming_response_unarchive(self, client: DodoPayments) -> None:
+ with client.product_collections.with_streaming_response.unarchive(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = response.parse()
+ assert_matches_type(ProductCollectionUnarchiveResponse, product_collection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_unarchive(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.with_raw_response.unarchive(
+ "",
+ )
+
+ @parametrize
+ def test_method_update_images(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.update_images(
+ id="id",
+ )
+ assert_matches_type(ProductCollectionUpdateImagesResponse, product_collection, path=["response"])
+
+ @parametrize
+ def test_method_update_images_with_all_params(self, client: DodoPayments) -> None:
+ product_collection = client.product_collections.update_images(
+ id="id",
+ force_update=True,
+ )
+ assert_matches_type(ProductCollectionUpdateImagesResponse, product_collection, path=["response"])
+
+ @parametrize
+ def test_raw_response_update_images(self, client: DodoPayments) -> None:
+ response = client.product_collections.with_raw_response.update_images(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = response.parse()
+ assert_matches_type(ProductCollectionUpdateImagesResponse, product_collection, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update_images(self, client: DodoPayments) -> None:
+ with client.product_collections.with_streaming_response.update_images(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = response.parse()
+ assert_matches_type(ProductCollectionUpdateImagesResponse, product_collection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update_images(self, client: DodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.product_collections.with_raw_response.update_images(
+ id="",
+ )
+
+
+class TestAsyncProductCollections:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.create(
+ groups=[{"products": [{"product_id": "product_id"}]}],
+ name="name",
+ )
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.create(
+ groups=[
+ {
+ "products": [
+ {
+ "product_id": "product_id",
+ "status": True,
+ }
+ ],
+ "group_name": "group_name",
+ "status": True,
+ }
+ ],
+ name="name",
+ brand_id="brand_id",
+ description="description",
+ )
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.with_raw_response.create(
+ groups=[{"products": [{"product_id": "product_id"}]}],
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = await response.parse()
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.with_streaming_response.create(
+ groups=[{"products": [{"product_id": "product_id"}]}],
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = await response.parse()
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.retrieve(
+ "id",
+ )
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = await response.parse()
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = await response.parse()
+ assert_matches_type(ProductCollection, product_collection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.update(
+ id="id",
+ )
+ assert product_collection is None
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.update(
+ id="id",
+ brand_id="brand_id",
+ description="description",
+ group_order=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ image_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+ assert product_collection is None
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = await response.parse()
+ assert product_collection is None
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = await response.parse()
+ assert product_collection is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.with_raw_response.update(
+ id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.list()
+ assert_matches_type(
+ AsyncDefaultPageNumberPagination[ProductCollectionListResponse], product_collection, path=["response"]
+ )
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.list(
+ archived=True,
+ brand_id="brand_id",
+ page_number=0,
+ page_size=0,
+ )
+ assert_matches_type(
+ AsyncDefaultPageNumberPagination[ProductCollectionListResponse], product_collection, path=["response"]
+ )
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = await response.parse()
+ assert_matches_type(
+ AsyncDefaultPageNumberPagination[ProductCollectionListResponse], product_collection, path=["response"]
+ )
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = await response.parse()
+ assert_matches_type(
+ AsyncDefaultPageNumberPagination[ProductCollectionListResponse], product_collection, path=["response"]
+ )
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.delete(
+ "id",
+ )
+ assert product_collection is None
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = await response.parse()
+ assert product_collection is None
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = await response.parse()
+ assert product_collection is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ async def test_method_unarchive(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.unarchive(
+ "id",
+ )
+ assert_matches_type(ProductCollectionUnarchiveResponse, product_collection, path=["response"])
+
+ @parametrize
+ async def test_raw_response_unarchive(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.with_raw_response.unarchive(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = await response.parse()
+ assert_matches_type(ProductCollectionUnarchiveResponse, product_collection, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_unarchive(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.with_streaming_response.unarchive(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = await response.parse()
+ assert_matches_type(ProductCollectionUnarchiveResponse, product_collection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_unarchive(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.with_raw_response.unarchive(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update_images(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.update_images(
+ id="id",
+ )
+ assert_matches_type(ProductCollectionUpdateImagesResponse, product_collection, path=["response"])
+
+ @parametrize
+ async def test_method_update_images_with_all_params(self, async_client: AsyncDodoPayments) -> None:
+ product_collection = await async_client.product_collections.update_images(
+ id="id",
+ force_update=True,
+ )
+ assert_matches_type(ProductCollectionUpdateImagesResponse, product_collection, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update_images(self, async_client: AsyncDodoPayments) -> None:
+ response = await async_client.product_collections.with_raw_response.update_images(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ product_collection = await response.parse()
+ assert_matches_type(ProductCollectionUpdateImagesResponse, product_collection, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update_images(self, async_client: AsyncDodoPayments) -> None:
+ async with async_client.product_collections.with_streaming_response.update_images(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ product_collection = await response.parse()
+ assert_matches_type(ProductCollectionUpdateImagesResponse, product_collection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update_images(self, async_client: AsyncDodoPayments) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.product_collections.with_raw_response.update_images(
+ id="",
+ )
diff --git a/tests/api_resources/test_subscriptions.py b/tests/api_resources/test_subscriptions.py
index fdb10757..5f95113b 100644
--- a/tests/api_resources/test_subscriptions.py
+++ b/tests/api_resources/test_subscriptions.py
@@ -64,6 +64,7 @@ def test_method_create_with_all_params(self, client: DodoPayments) -> None:
],
allowed_payment_method_types=["ach"],
billing_currency="AED",
+ customer_business_name="customer_business_name",
discount_code="discount_code",
discount_codes=["string"],
force_3ds=True,
@@ -203,6 +204,7 @@ def test_method_update_with_all_params(self, client: DodoPayments) -> None:
"rollover_timeframe_interval": "Day",
}
],
+ customer_business_name="customer_business_name",
customer_name="customer_name",
disable_on_demand={"next_billing_date": parse_datetime("2019-12-27T18:11:19.117Z")},
metadata={"foo": "string"},
@@ -715,6 +717,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncDodoPaymen
],
allowed_payment_method_types=["ach"],
billing_currency="AED",
+ customer_business_name="customer_business_name",
discount_code="discount_code",
discount_codes=["string"],
force_3ds=True,
@@ -854,6 +857,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncDodoPaymen
"rollover_timeframe_interval": "Day",
}
],
+ customer_business_name="customer_business_name",
customer_name="customer_name",
disable_on_demand={"next_billing_date": parse_datetime("2019-12-27T18:11:19.117Z")},
metadata={"foo": "string"},
From f1234a0b6d96cacf419634fefbc39e173d8d87b1 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 21 May 2026 13:20:38 +0000
Subject: [PATCH 3/4] feat(api): fixed a breaking modal change
---
.stats.yml | 4 ++--
src/dodopayments/resources/payments.py | 5 +++--
src/dodopayments/resources/subscriptions.py | 5 +++--
src/dodopayments/types/__init__.py | 1 +
.../types/one_time_product_cart_item.py | 9 ++++++++
.../types/one_time_product_cart_item_param.py | 21 +++++++++++++++++++
src/dodopayments/types/payment.py | 11 +++++++---
.../types/payment_create_params.py | 18 +++-------------
.../types/payment_create_response.py | 18 +++-------------
.../types/subscription_create_params.py | 18 +++-------------
.../types/subscription_create_response.py | 11 +++++++---
11 files changed, 64 insertions(+), 57 deletions(-)
create mode 100644 src/dodopayments/types/one_time_product_cart_item_param.py
diff --git a/.stats.yml b/.stats.yml
index e02293cc..02540ce6 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 129
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/dodo-payments/dodo-payments-40cfcf3dec892796c176c3300b60660fa0372e2253e682d94196c496f7c9948a.yml
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/dodo-payments/dodo-payments-1b34d3f97b8b78222aaa5ad0e32add5aa49307a8cc563a556bc3e5ea78585175.yml
openapi_spec_hash: 3ae2b45a65b68c25007835eafc758f3e
-config_hash: 4313eddd18b96948c4a83bac4564acb3
+config_hash: b9fa2634135996e7865bc69f4d985177
diff --git a/src/dodopayments/resources/payments.py b/src/dodopayments/resources/payments.py
index 4cadfc98..8e41dee5 100644
--- a/src/dodopayments/resources/payments.py
+++ b/src/dodopayments/resources/payments.py
@@ -29,6 +29,7 @@
from ..types.payment_list_response import PaymentListResponse
from ..types.customer_request_param import CustomerRequestParam
from ..types.payment_create_response import PaymentCreateResponse
+from ..types.one_time_product_cart_item_param import OneTimeProductCartItemParam
from ..types.payment_retrieve_line_items_response import PaymentRetrieveLineItemsResponse
__all__ = ["PaymentsResource", "AsyncPaymentsResource"]
@@ -60,7 +61,7 @@ def create(
*,
billing: BillingAddressParam,
customer: CustomerRequestParam,
- product_cart: Iterable[payment_create_params.ProductCart],
+ product_cart: Iterable[OneTimeProductCartItemParam],
adaptive_currency_fees_inclusive: Optional[bool] | Omit = omit,
allowed_payment_method_types: Optional[List[PaymentMethodTypes]] | Omit = omit,
billing_currency: Optional[Currency] | Omit = omit,
@@ -358,7 +359,7 @@ async def create(
*,
billing: BillingAddressParam,
customer: CustomerRequestParam,
- product_cart: Iterable[payment_create_params.ProductCart],
+ product_cart: Iterable[OneTimeProductCartItemParam],
adaptive_currency_fees_inclusive: Optional[bool] | Omit = omit,
allowed_payment_method_types: Optional[List[PaymentMethodTypes]] | Omit = omit,
billing_currency: Optional[Currency] | Omit = omit,
diff --git a/src/dodopayments/resources/subscriptions.py b/src/dodopayments/resources/subscriptions.py
index addbadba..550c5bbe 100644
--- a/src/dodopayments/resources/subscriptions.py
+++ b/src/dodopayments/resources/subscriptions.py
@@ -46,6 +46,7 @@
from ..types.on_demand_subscription_param import OnDemandSubscriptionParam
from ..types.subscription_charge_response import SubscriptionChargeResponse
from ..types.subscription_create_response import SubscriptionCreateResponse
+from ..types.one_time_product_cart_item_param import OneTimeProductCartItemParam
from ..types.subscription_preview_change_plan_response import SubscriptionPreviewChangePlanResponse
from ..types.subscription_retrieve_credit_usage_response import SubscriptionRetrieveCreditUsageResponse
from ..types.subscription_update_payment_method_response import SubscriptionUpdatePaymentMethodResponse
@@ -92,7 +93,7 @@ def create(
mandate_min_amount_inr_paise: Optional[int] | Omit = omit,
metadata: Dict[str, str] | Omit = omit,
on_demand: Optional[OnDemandSubscriptionParam] | Omit = omit,
- one_time_product_cart: Optional[Iterable[subscription_create_params.OneTimeProductCart]] | Omit = omit,
+ one_time_product_cart: Optional[Iterable[OneTimeProductCartItemParam]] | Omit = omit,
payment_link: Optional[bool] | Omit = omit,
payment_method_id: Optional[str] | Omit = omit,
redirect_immediately: bool | Omit = omit,
@@ -901,7 +902,7 @@ async def create(
mandate_min_amount_inr_paise: Optional[int] | Omit = omit,
metadata: Dict[str, str] | Omit = omit,
on_demand: Optional[OnDemandSubscriptionParam] | Omit = omit,
- one_time_product_cart: Optional[Iterable[subscription_create_params.OneTimeProductCart]] | Omit = omit,
+ one_time_product_cart: Optional[Iterable[OneTimeProductCartItemParam]] | Omit = omit,
payment_link: Optional[bool] | Omit = omit,
payment_method_id: Optional[str] | Omit = omit,
redirect_immediately: bool | Omit = omit,
diff --git a/src/dodopayments/types/__init__.py b/src/dodopayments/types/__init__.py
index 7ea86a47..eed80a06 100644
--- a/src/dodopayments/types/__init__.py
+++ b/src/dodopayments/types/__init__.py
@@ -175,6 +175,7 @@
from .credit_rolled_over_webhook_event import CreditRolledOverWebhookEvent as CreditRolledOverWebhookEvent
from .dispute_challenged_webhook_event import DisputeChallengedWebhookEvent as DisputeChallengedWebhookEvent
from .license_key_instance_list_params import LicenseKeyInstanceListParams as LicenseKeyInstanceListParams
+from .one_time_product_cart_item_param import OneTimeProductCartItemParam as OneTimeProductCartItemParam
from .payment_processing_webhook_event import PaymentProcessingWebhookEvent as PaymentProcessingWebhookEvent
from .product_collection_create_params import ProductCollectionCreateParams as ProductCollectionCreateParams
from .product_collection_list_response import ProductCollectionListResponse as ProductCollectionListResponse
diff --git a/src/dodopayments/types/one_time_product_cart_item.py b/src/dodopayments/types/one_time_product_cart_item.py
index 661648e6..c868be06 100644
--- a/src/dodopayments/types/one_time_product_cart_item.py
+++ b/src/dodopayments/types/one_time_product_cart_item.py
@@ -1,5 +1,7 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from typing import Optional
+
from .._models import BaseModel
__all__ = ["OneTimeProductCartItem"]
@@ -9,3 +11,10 @@ class OneTimeProductCartItem(BaseModel):
product_id: str
quantity: int
+
+ amount: Optional[int] = None
+ """Amount the customer pays if pay_what_you_want is enabled.
+
+ If disabled then amount will be ignored Represented in the lowest denomination
+ of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
+ """
diff --git a/src/dodopayments/types/one_time_product_cart_item_param.py b/src/dodopayments/types/one_time_product_cart_item_param.py
new file mode 100644
index 00000000..35944838
--- /dev/null
+++ b/src/dodopayments/types/one_time_product_cart_item_param.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["OneTimeProductCartItemParam"]
+
+
+class OneTimeProductCartItemParam(TypedDict, total=False):
+ product_id: Required[str]
+
+ quantity: Required[int]
+
+ amount: Optional[int]
+ """Amount the customer pays if pay_what_you_want is enabled.
+
+ If disabled then amount will be ignored Represented in the lowest denomination
+ of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
+ """
diff --git a/src/dodopayments/types/payment.py b/src/dodopayments/types/payment.py
index d0656cc4..89669e50 100644
--- a/src/dodopayments/types/payment.py
+++ b/src/dodopayments/types/payment.py
@@ -14,9 +14,14 @@
from .custom_field_response import CustomFieldResponse
from .payment_refund_status import PaymentRefundStatus
from .customer_limited_details import CustomerLimitedDetails
-from .one_time_product_cart_item import OneTimeProductCartItem
-__all__ = ["Payment"]
+__all__ = ["Payment", "ProductCart"]
+
+
+class ProductCart(BaseModel):
+ product_id: str
+
+ quantity: int
class Payment(BaseModel):
@@ -124,7 +129,7 @@ class Payment(BaseModel):
payment_method_type: Optional[str] = None
"""Specific type of payment method (e.g. "visa", "mastercard")"""
- product_cart: Optional[List[OneTimeProductCartItem]] = None
+ product_cart: Optional[List[ProductCart]] = None
"""List of products purchased in a one-time payment"""
refund_status: Optional[PaymentRefundStatus] = None
diff --git a/src/dodopayments/types/payment_create_params.py b/src/dodopayments/types/payment_create_params.py
index 86296e13..0e0e7315 100644
--- a/src/dodopayments/types/payment_create_params.py
+++ b/src/dodopayments/types/payment_create_params.py
@@ -10,8 +10,9 @@
from .payment_method_types import PaymentMethodTypes
from .billing_address_param import BillingAddressParam
from .customer_request_param import CustomerRequestParam
+from .one_time_product_cart_item_param import OneTimeProductCartItemParam
-__all__ = ["PaymentCreateParams", "ProductCart"]
+__all__ = ["PaymentCreateParams"]
class PaymentCreateParams(TypedDict, total=False):
@@ -21,7 +22,7 @@ class PaymentCreateParams(TypedDict, total=False):
customer: Required[CustomerRequestParam]
"""Customer information for the payment"""
- product_cart: Required[Iterable[ProductCart]]
+ product_cart: Required[Iterable[OneTimeProductCartItemParam]]
"""List of products in the cart. Must contain at least 1 and at most 100 items."""
adaptive_currency_fees_inclusive: Optional[bool]
@@ -113,16 +114,3 @@ class PaymentCreateParams(TypedDict, total=False):
If tax id validation fails the payment creation will fail
"""
-
-
-class ProductCart(TypedDict, total=False):
- product_id: Required[str]
-
- quantity: Required[int]
-
- amount: Optional[int]
- """Amount the customer pays if pay_what_you_want is enabled.
-
- If disabled then amount will be ignored Represented in the lowest denomination
- of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
- """
diff --git a/src/dodopayments/types/payment_create_response.py b/src/dodopayments/types/payment_create_response.py
index a1164de8..f090f836 100644
--- a/src/dodopayments/types/payment_create_response.py
+++ b/src/dodopayments/types/payment_create_response.py
@@ -5,21 +5,9 @@
from .._models import BaseModel
from .customer_limited_details import CustomerLimitedDetails
+from .one_time_product_cart_item import OneTimeProductCartItem
-__all__ = ["PaymentCreateResponse", "ProductCart"]
-
-
-class ProductCart(BaseModel):
- product_id: str
-
- quantity: int
-
- amount: Optional[int] = None
- """Amount the customer pays if pay_what_you_want is enabled.
-
- If disabled then amount will be ignored Represented in the lowest denomination
- of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
- """
+__all__ = ["PaymentCreateResponse"]
class PaymentCreateResponse(BaseModel):
@@ -56,5 +44,5 @@ class PaymentCreateResponse(BaseModel):
payment_link: Optional[str] = None
"""Optional URL to a hosted payment page"""
- product_cart: Optional[List[ProductCart]] = None
+ product_cart: Optional[List[OneTimeProductCartItem]] = None
"""Optional list of products included in the payment"""
diff --git a/src/dodopayments/types/subscription_create_params.py b/src/dodopayments/types/subscription_create_params.py
index bbb1ff7e..3ac55add 100644
--- a/src/dodopayments/types/subscription_create_params.py
+++ b/src/dodopayments/types/subscription_create_params.py
@@ -12,8 +12,9 @@
from .billing_address_param import BillingAddressParam
from .customer_request_param import CustomerRequestParam
from .on_demand_subscription_param import OnDemandSubscriptionParam
+from .one_time_product_cart_item_param import OneTimeProductCartItemParam
-__all__ = ["SubscriptionCreateParams", "OneTimeProductCart"]
+__all__ = ["SubscriptionCreateParams"]
class SubscriptionCreateParams(TypedDict, total=False):
@@ -84,7 +85,7 @@ class SubscriptionCreateParams(TypedDict, total=False):
on_demand: Optional[OnDemandSubscriptionParam]
- one_time_product_cart: Optional[Iterable[OneTimeProductCart]]
+ one_time_product_cart: Optional[Iterable[OneTimeProductCartItemParam]]
"""
List of one time products that will be bundled with the first payment for this
subscription
@@ -133,16 +134,3 @@ class SubscriptionCreateParams(TypedDict, total=False):
Optional trial period in days If specified, this value overrides the trial
period set in the product's price Must be between 0 and 10000 days
"""
-
-
-class OneTimeProductCart(TypedDict, total=False):
- product_id: Required[str]
-
- quantity: Required[int]
-
- amount: Optional[int]
- """Amount the customer pays if pay_what_you_want is enabled.
-
- If disabled then amount will be ignored Represented in the lowest denomination
- of the currency (e.g., cents for USD). For example, to charge $1.00, pass `100`.
- """
diff --git a/src/dodopayments/types/subscription_create_response.py b/src/dodopayments/types/subscription_create_response.py
index 972da4fc..886b4239 100644
--- a/src/dodopayments/types/subscription_create_response.py
+++ b/src/dodopayments/types/subscription_create_response.py
@@ -6,9 +6,14 @@
from .._models import BaseModel
from .addon_cart_response_item import AddonCartResponseItem
from .customer_limited_details import CustomerLimitedDetails
-from .one_time_product_cart_item import OneTimeProductCartItem
-__all__ = ["SubscriptionCreateResponse"]
+__all__ = ["SubscriptionCreateResponse", "OneTimeProductCart"]
+
+
+class OneTimeProductCart(BaseModel):
+ product_id: str
+
+ quantity: int
class SubscriptionCreateResponse(BaseModel):
@@ -51,7 +56,7 @@ class SubscriptionCreateResponse(BaseModel):
expires_on: Optional[datetime] = None
"""Expiry timestamp of the payment link"""
- one_time_product_cart: Optional[List[OneTimeProductCartItem]] = None
+ one_time_product_cart: Optional[List[OneTimeProductCart]] = None
"""One time products associated with the purchase of subscription"""
payment_link: Optional[str] = None
From c9ee7a55e8017382e5838acada0eaddf4c3d887b Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 21 May 2026 13:21:19 +0000
Subject: [PATCH 4/4] release: 1.99.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 10 ++++++++++
pyproject.toml | 2 +-
src/dodopayments/_version.py | 2 +-
4 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 0f1932b5..5c9b107c 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "1.98.2"
+ ".": "1.99.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1b0f48e..4a02b790 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Changelog
+## 1.99.0 (2026-05-21)
+
+Full Changelog: [v1.98.2...v1.99.0](https://github.com/dodopayments/dodopayments-python/compare/v1.98.2...v1.99.0)
+
+### Features
+
+* **api:** fixed a breaking modal change ([f1234a0](https://github.com/dodopayments/dodopayments-python/commit/f1234a0b6d96cacf419634fefbc39e173d8d87b1))
+* **api:** stainless fixes ([b009180](https://github.com/dodopayments/dodopayments-python/commit/b00918007e41c6cdb4923567cab949a628233aa7))
+* **api:** updated openapi spec to v1.99.0 and added missing endpoints as well ([73101fa](https://github.com/dodopayments/dodopayments-python/commit/73101fab3a997da1203fd0936339eabf7e609d6b))
+
## 1.98.2 (2026-05-14)
Full Changelog: [v1.98.1...v1.98.2](https://github.com/dodopayments/dodopayments-python/compare/v1.98.1...v1.98.2)
diff --git a/pyproject.toml b/pyproject.toml
index 236da819..eefdde7f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "dodopayments"
-version = "1.98.2"
+version = "1.99.0"
description = "The official Python library for the Dodo Payments API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/dodopayments/_version.py b/src/dodopayments/_version.py
index 0a434820..cac7fab3 100644
--- a/src/dodopayments/_version.py
+++ b/src/dodopayments/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "dodopayments"
-__version__ = "1.98.2" # x-release-please-version
+__version__ = "1.99.0" # x-release-please-version