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