diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7d0480c96..e6cb33b1e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.249.0" + ".": "0.250.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index bccf31fb0..a78c7eb65 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 199 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-0ecd4933d3a1f2e6363dc40b619d296a033732ec245018effd3e5f687f60bb04.yml -openapi_spec_hash: 41f0a23615d13ed80758208130da6abd -config_hash: 0c284b69f3dccb22b24877f61d0d8a9a +configured_endpoints: 201 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-101cab80ae65141fb306c316367abab195616ae8f2ce61d87e0d66e3caa4ef2f.yml +openapi_spec_hash: 9338ab9453d1a0f426e9998e574bff67 +config_hash: 97774f946585cecb19181a1817870d0b diff --git a/CHANGELOG.md b/CHANGELOG.md index 2edbf023f..28fd8e727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.250.0 (2025-06-11) + +Full Changelog: [v0.249.0...v0.250.0](https://github.com/Increase/increase-python/compare/v0.249.0...v0.250.0) + +### Features + +* **api:** api update ([8783771](https://github.com/Increase/increase-python/commit/8783771d6f3528c2e1af0931b19752af8f603838)) + ## 0.249.0 (2025-06-08) Full Changelog: [v0.248.0...v0.249.0](https://github.com/Increase/increase-python/compare/v0.248.0...v0.249.0) diff --git a/api.md b/api.md index 90805a762..2a4dab211 100644 --- a/api.md +++ b/api.md @@ -169,8 +169,10 @@ from increase.types import PendingTransaction Methods: +- client.pending_transactions.create(\*\*params) -> PendingTransaction - client.pending_transactions.retrieve(pending_transaction_id) -> PendingTransaction - client.pending_transactions.list(\*\*params) -> SyncPage[PendingTransaction] +- client.pending_transactions.release(pending_transaction_id) -> PendingTransaction # DeclinedTransactions diff --git a/pyproject.toml b/pyproject.toml index e64a060a7..049d24718 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "increase" -version = "0.249.0" +version = "0.250.0" description = "The official Python library for the increase API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/increase/_version.py b/src/increase/_version.py index 9fea6c936..331b94da9 100644 --- a/src/increase/_version.py +++ b/src/increase/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "increase" -__version__ = "0.249.0" # x-release-please-version +__version__ = "0.250.0" # x-release-please-version diff --git a/src/increase/resources/pending_transactions.py b/src/increase/resources/pending_transactions.py index e15e4a663..e431de9ce 100644 --- a/src/increase/resources/pending_transactions.py +++ b/src/increase/resources/pending_transactions.py @@ -4,9 +4,9 @@ import httpx -from ..types import pending_transaction_list_params +from ..types import pending_transaction_list_params, pending_transaction_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -42,6 +42,66 @@ def with_streaming_response(self) -> PendingTransactionsResourceWithStreamingRes """ return PendingTransactionsResourceWithStreamingResponse(self) + def create( + self, + *, + account_id: str, + amount: int, + description: str | NotGiven = NOT_GIVEN, + # 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, + idempotency_key: str | None = None, + ) -> PendingTransaction: + """Creates a pending transaction on an account. + + This can be useful to hold funds + for an external payment or known future transaction outside of Increase. The + resulting Pending Transaction will have a `category` of `user_initiated_hold` + and can be released via the API to unlock the held funds. + + Args: + account_id: The Account to place the hold on. + + amount: The amount to hold in the minor unit of the account's currency. For dollars, for + example, this is cents. This should be a negative amount - to hold $1.00 from + the account, you would pass -100. + + description: The description you choose to give the hold. + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + return self._post( + "/pending_transactions", + body=maybe_transform( + { + "account_id": account_id, + "amount": amount, + "description": description, + }, + pending_transaction_create_params.PendingTransactionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=PendingTransaction, + ) + def retrieve( self, pending_transaction_id: str, @@ -141,6 +201,54 @@ def list( model=PendingTransaction, ) + def release( + self, + pending_transaction_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, + idempotency_key: str | None = None, + ) -> PendingTransaction: + """Release a Pending Transaction you had previously created. + + The Pending + Transaction must have a `category` of `user_initiated_hold` and a `status` of + `pending`. This will unlock the held funds and mark the Pending Transaction as + complete. + + Args: + pending_transaction_id: The identifier of the Pending Transaction to release. + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + if not pending_transaction_id: + raise ValueError( + f"Expected a non-empty value for `pending_transaction_id` but received {pending_transaction_id!r}" + ) + return self._post( + f"/pending_transactions/{pending_transaction_id}/release", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=PendingTransaction, + ) + class AsyncPendingTransactionsResource(AsyncAPIResource): @cached_property @@ -162,6 +270,66 @@ def with_streaming_response(self) -> AsyncPendingTransactionsResourceWithStreami """ return AsyncPendingTransactionsResourceWithStreamingResponse(self) + async def create( + self, + *, + account_id: str, + amount: int, + description: str | NotGiven = NOT_GIVEN, + # 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, + idempotency_key: str | None = None, + ) -> PendingTransaction: + """Creates a pending transaction on an account. + + This can be useful to hold funds + for an external payment or known future transaction outside of Increase. The + resulting Pending Transaction will have a `category` of `user_initiated_hold` + and can be released via the API to unlock the held funds. + + Args: + account_id: The Account to place the hold on. + + amount: The amount to hold in the minor unit of the account's currency. For dollars, for + example, this is cents. This should be a negative amount - to hold $1.00 from + the account, you would pass -100. + + description: The description you choose to give the hold. + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + return await self._post( + "/pending_transactions", + body=await async_maybe_transform( + { + "account_id": account_id, + "amount": amount, + "description": description, + }, + pending_transaction_create_params.PendingTransactionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=PendingTransaction, + ) + async def retrieve( self, pending_transaction_id: str, @@ -261,50 +429,122 @@ def list( model=PendingTransaction, ) + async def release( + self, + pending_transaction_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, + idempotency_key: str | None = None, + ) -> PendingTransaction: + """Release a Pending Transaction you had previously created. + + The Pending + Transaction must have a `category` of `user_initiated_hold` and a `status` of + `pending`. This will unlock the held funds and mark the Pending Transaction as + complete. + + Args: + pending_transaction_id: The identifier of the Pending Transaction to release. + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + if not pending_transaction_id: + raise ValueError( + f"Expected a non-empty value for `pending_transaction_id` but received {pending_transaction_id!r}" + ) + return await self._post( + f"/pending_transactions/{pending_transaction_id}/release", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=PendingTransaction, + ) + class PendingTransactionsResourceWithRawResponse: def __init__(self, pending_transactions: PendingTransactionsResource) -> None: self._pending_transactions = pending_transactions + self.create = to_raw_response_wrapper( + pending_transactions.create, + ) self.retrieve = to_raw_response_wrapper( pending_transactions.retrieve, ) self.list = to_raw_response_wrapper( pending_transactions.list, ) + self.release = to_raw_response_wrapper( + pending_transactions.release, + ) class AsyncPendingTransactionsResourceWithRawResponse: def __init__(self, pending_transactions: AsyncPendingTransactionsResource) -> None: self._pending_transactions = pending_transactions + self.create = async_to_raw_response_wrapper( + pending_transactions.create, + ) self.retrieve = async_to_raw_response_wrapper( pending_transactions.retrieve, ) self.list = async_to_raw_response_wrapper( pending_transactions.list, ) + self.release = async_to_raw_response_wrapper( + pending_transactions.release, + ) class PendingTransactionsResourceWithStreamingResponse: def __init__(self, pending_transactions: PendingTransactionsResource) -> None: self._pending_transactions = pending_transactions + self.create = to_streamed_response_wrapper( + pending_transactions.create, + ) self.retrieve = to_streamed_response_wrapper( pending_transactions.retrieve, ) self.list = to_streamed_response_wrapper( pending_transactions.list, ) + self.release = to_streamed_response_wrapper( + pending_transactions.release, + ) class AsyncPendingTransactionsResourceWithStreamingResponse: def __init__(self, pending_transactions: AsyncPendingTransactionsResource) -> None: self._pending_transactions = pending_transactions + self.create = async_to_streamed_response_wrapper( + pending_transactions.create, + ) self.retrieve = async_to_streamed_response_wrapper( pending_transactions.retrieve, ) self.list = async_to_streamed_response_wrapper( pending_transactions.list, ) + self.release = async_to_streamed_response_wrapper( + pending_transactions.release, + ) diff --git a/src/increase/types/__init__.py b/src/increase/types/__init__.py index 6b67834ca..82f6cda1a 100644 --- a/src/increase/types/__init__.py +++ b/src/increase/types/__init__.py @@ -128,6 +128,7 @@ from .digital_card_profile_clone_params import DigitalCardProfileCloneParams as DigitalCardProfileCloneParams from .inbound_check_deposit_list_params import InboundCheckDepositListParams as InboundCheckDepositListParams from .inbound_wire_transfer_list_params import InboundWireTransferListParams as InboundWireTransferListParams +from .pending_transaction_create_params import PendingTransactionCreateParams as PendingTransactionCreateParams from .physical_card_profile_list_params import PhysicalCardProfileListParams as PhysicalCardProfileListParams from .supplemental_document_list_params import SupplementalDocumentListParams as SupplementalDocumentListParams from .wire_drawdown_request_list_params import WireDrawdownRequestListParams as WireDrawdownRequestListParams diff --git a/src/increase/types/pending_transaction.py b/src/increase/types/pending_transaction.py index 009304ddd..1e1bd94c6 100644 --- a/src/increase/types/pending_transaction.py +++ b/src/increase/types/pending_transaction.py @@ -20,7 +20,6 @@ "SourceCardAuthorizationVerificationCardholderAddress", "SourceCheckDepositInstruction", "SourceCheckTransferInstruction", - "SourceGroupInitiatedHold", "SourceInboundFundsHold", "SourceInboundWireTransferReversal", "SourceOutboundCardPushTransferInstruction", @@ -502,11 +501,6 @@ class SourceCheckTransferInstruction(BaseModel): """The identifier of the Check Transfer that led to this Pending Transaction.""" -class SourceGroupInitiatedHold(BaseModel): - id: str - """The Group Initiated Hold identifier.""" - - class SourceInboundFundsHold(BaseModel): id: str """The Inbound Funds Hold identifier.""" @@ -647,7 +641,7 @@ class Source(BaseModel): "check_deposit_instruction", "check_transfer_instruction", "inbound_funds_hold", - "group_initiated_hold", + "user_initiated_hold", "real_time_payments_transfer_instruction", "wire_transfer_instruction", "inbound_wire_transfer_reversal", @@ -672,8 +666,8 @@ class Source(BaseModel): under the `check_transfer_instruction` object. - `inbound_funds_hold` - Inbound Funds Hold: details will be under the `inbound_funds_hold` object. - - `group_initiated_hold` - Group Initiated Hold Source: details will be under - the `group_initiated_hold` object. + - `user_initiated_hold` - User Initiated Hold: details will be under the + `user_initiated_hold` object. - `real_time_payments_transfer_instruction` - Real-Time Payments Transfer Instruction: details will be under the `real_time_payments_transfer_instruction` object. @@ -704,13 +698,6 @@ class Source(BaseModel): equal to `check_transfer_instruction`. """ - group_initiated_hold: Optional[SourceGroupInitiatedHold] = None - """A Group Initiated Hold Source object. - - This field will be present in the JSON response if and only if `category` is - equal to `group_initiated_hold`. - """ - inbound_funds_hold: Optional[SourceInboundFundsHold] = None """An Inbound Funds Hold object. @@ -756,6 +743,14 @@ class Source(BaseModel): equal to `swift_transfer_instruction`. """ + user_initiated_hold: Optional[object] = None + """An User Initiated Hold object. + + This field will be present in the JSON response if and only if `category` is + equal to `user_initiated_hold`. Created when a user initiates a hold on funds in + their account. + """ + wire_transfer_instruction: Optional[SourceWireTransferInstruction] = None """A Wire Transfer Instruction object. diff --git a/src/increase/types/pending_transaction_create_params.py b/src/increase/types/pending_transaction_create_params.py new file mode 100644 index 000000000..3d3f2f798 --- /dev/null +++ b/src/increase/types/pending_transaction_create_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["PendingTransactionCreateParams"] + + +class PendingTransactionCreateParams(TypedDict, total=False): + account_id: Required[str] + """The Account to place the hold on.""" + + amount: Required[int] + """The amount to hold in the minor unit of the account's currency. + + For dollars, for example, this is cents. This should be a negative amount - to + hold $1.00 from the account, you would pass -100. + """ + + description: str + """The description you choose to give the hold.""" diff --git a/src/increase/types/pending_transaction_list_params.py b/src/increase/types/pending_transaction_list_params.py index 2979d4bde..8cec9832d 100644 --- a/src/increase/types/pending_transaction_list_params.py +++ b/src/increase/types/pending_transaction_list_params.py @@ -45,7 +45,7 @@ class PendingTransactionListParams(TypedDict, total=False): "check_deposit_instruction", "check_transfer_instruction", "inbound_funds_hold", - "group_initiated_hold", + "user_initiated_hold", "real_time_payments_transfer_instruction", "wire_transfer_instruction", "inbound_wire_transfer_reversal", diff --git a/tests/api_resources/test_pending_transactions.py b/tests/api_resources/test_pending_transactions.py index 7130bf502..11a7b8a06 100644 --- a/tests/api_resources/test_pending_transactions.py +++ b/tests/api_resources/test_pending_transactions.py @@ -19,6 +19,49 @@ class TestPendingTransactions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @parametrize + def test_method_create(self, client: Increase) -> None: + pending_transaction = client.pending_transactions.create( + account_id="account_in71c4amph0vgo2qllky", + amount=-1000, + ) + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Increase) -> None: + pending_transaction = client.pending_transactions.create( + account_id="account_in71c4amph0vgo2qllky", + amount=-1000, + description="Hold for pending transaction", + ) + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Increase) -> None: + response = client.pending_transactions.with_raw_response.create( + account_id="account_in71c4amph0vgo2qllky", + amount=-1000, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + pending_transaction = response.parse() + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Increase) -> None: + with client.pending_transactions.with_streaming_response.create( + account_id="account_in71c4amph0vgo2qllky", + amount=-1000, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + pending_transaction = response.parse() + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: Increase) -> None: pending_transaction = client.pending_transactions.retrieve( @@ -102,10 +145,93 @@ def test_streaming_response_list(self, client: Increase) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_release(self, client: Increase) -> None: + pending_transaction = client.pending_transactions.release( + "pending_transaction_id", + ) + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + def test_raw_response_release(self, client: Increase) -> None: + response = client.pending_transactions.with_raw_response.release( + "pending_transaction_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + pending_transaction = response.parse() + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + def test_streaming_response_release(self, client: Increase) -> None: + with client.pending_transactions.with_streaming_response.release( + "pending_transaction_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + pending_transaction = response.parse() + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_release(self, client: Increase) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `pending_transaction_id` but received ''" + ): + client.pending_transactions.with_raw_response.release( + "", + ) + class TestAsyncPendingTransactions: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + @parametrize + async def test_method_create(self, async_client: AsyncIncrease) -> None: + pending_transaction = await async_client.pending_transactions.create( + account_id="account_in71c4amph0vgo2qllky", + amount=-1000, + ) + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncIncrease) -> None: + pending_transaction = await async_client.pending_transactions.create( + account_id="account_in71c4amph0vgo2qllky", + amount=-1000, + description="Hold for pending transaction", + ) + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncIncrease) -> None: + response = await async_client.pending_transactions.with_raw_response.create( + account_id="account_in71c4amph0vgo2qllky", + amount=-1000, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + pending_transaction = await response.parse() + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncIncrease) -> None: + async with async_client.pending_transactions.with_streaming_response.create( + account_id="account_in71c4amph0vgo2qllky", + amount=-1000, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + pending_transaction = await response.parse() + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, async_client: AsyncIncrease) -> None: pending_transaction = await async_client.pending_transactions.retrieve( @@ -188,3 +314,43 @@ async def test_streaming_response_list(self, async_client: AsyncIncrease) -> Non assert_matches_type(AsyncPage[PendingTransaction], pending_transaction, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_release(self, async_client: AsyncIncrease) -> None: + pending_transaction = await async_client.pending_transactions.release( + "pending_transaction_id", + ) + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + async def test_raw_response_release(self, async_client: AsyncIncrease) -> None: + response = await async_client.pending_transactions.with_raw_response.release( + "pending_transaction_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + pending_transaction = await response.parse() + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + @parametrize + async def test_streaming_response_release(self, async_client: AsyncIncrease) -> None: + async with async_client.pending_transactions.with_streaming_response.release( + "pending_transaction_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + pending_transaction = await response.parse() + assert_matches_type(PendingTransaction, pending_transaction, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_release(self, async_client: AsyncIncrease) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `pending_transaction_id` but received ''" + ): + await async_client.pending_transactions.with_raw_response.release( + "", + )