diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index dd939620..ff261bad 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode -RUN curl -sSf https://rye-up.com/get | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash +RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.44.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH -RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc +RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bbeb30b1..c17fdc16 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -24,6 +24,9 @@ } } } + }, + "features": { + "ghcr.io/devcontainers/features/node:1": {} } // Features to add to the dev container. More info: https://containers.dev/features. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65ca1794..c7ac8cf4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,36 +6,71 @@ on: pull_request: branches: - main + - next jobs: lint: name: lint runs-on: ubuntu-latest - if: github.repository == 'lithic-com/lithic-python' steps: - uses: actions/checkout@v4 - name: Install Rye run: | - curl -sSf https://rye-up.com/get | bash + curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: 0.24.0 - RYE_INSTALL_OPTION: "--yes" + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' - name: Install dependencies - run: | - rye sync --all-features + run: rye sync --all-features + + - name: Run lints + run: ./scripts/lint - - name: Run ruff + test: + name: test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rye run: | - rye run check:ruff + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Bootstrap + run: ./scripts/bootstrap - - name: Run type checking + - name: Run tests + run: ./scripts/test + + examples: + name: examples + runs-on: ubuntu-latest + if: github.repository == 'lithic-com/lithic-python' + + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + - name: Install dependencies run: | - rye run typecheck + rye sync --all-features - - name: Ensure importable + - env: + LITHIC_API_KEY: ${{ secrets.LITHIC_API_KEY }} run: | - rye run python -c 'import lithic' + rye run python ./examples/transactions.py diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 6a3f9363..b7160d6a 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -14,15 +14,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rye run: | - curl -sSf https://rye-up.com/get | bash + curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: 0.24.0 - RYE_INSTALL_OPTION: "--yes" + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' - name: Publish to PyPI run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 20d60569..82eba48b 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -1,6 +1,8 @@ name: Release Doctor on: pull_request: + branches: + - main workflow_dispatch: jobs: @@ -10,7 +12,7 @@ jobs: if: github.repository == 'lithic-com/lithic-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check release environment run: | diff --git a/.gitignore b/.gitignore index a4b2f8c0..87797408 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.prism.log .vscode _dev @@ -12,3 +13,4 @@ dist .env .envrc codegen.log +Brewfile.lock.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1b5dc400..d80a91e2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.39.0" + ".": "0.88.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 8bdf2ea0..1d9cb822 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1,4 @@ -configured_endpoints: 112 +configured_endpoints: 156 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-179992b114ffada2cdd2d9d56a8a25e0683bec2297606d32d0f0006b9eb9f21d.yml +openapi_spec_hash: a111418d378ea248892306c81b00f8c8 +config_hash: 6729d695e399d14fff4891b6b82ec86c diff --git a/Brewfile b/Brewfile new file mode 100644 index 00000000..492ca37b --- /dev/null +++ b/Brewfile @@ -0,0 +1,2 @@ +brew "rye" + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 45814ef0..d0b5db1c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,13 @@ ### With Rye -We use [Rye](https://rye-up.com/) to manage dependencies so we highly recommend [installing it](https://rye-up.com/guide/installation/) as it will automatically provision a Python environment with the expected Python version. +We use [Rye](https://rye.astral.sh/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: -After installing Rye, you'll just have to run this command: +```sh +$ ./scripts/bootstrap +``` + +Or [install Rye manually](https://rye.astral.sh/guide/installation/) and run: ```sh $ rye sync --all-features @@ -31,25 +35,25 @@ $ pip install -r requirements-dev.lock ## Modifying/Adding code -Most of the SDK is generated code, and any modified code will be overridden on the next generation. The -`src/lithic/lib/` and `examples/` directories are exceptions and will never be overridden. +Most of the SDK is generated code. Modifications to code will be persisted between generations, but may +result in merge conflicts between manual patches and changes from the generator. The generator will never +modify the contents of the `src/lithic/lib/` and `examples/` directories. ## Adding and running examples -All files in the `examples/` directory are not modified by the Stainless generator and can be freely edited or -added to. +All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. -```bash +```py # add an example to examples/.py #!/usr/bin/env -S rye run python … ``` -``` -chmod +x examples/.py +```sh +$ chmod +x examples/.py # run the example against your api -./examples/.py +$ ./examples/.py ``` ## Using the repository from source @@ -58,8 +62,8 @@ If you’d like to use the repository from source, you can either install from g To install via git: -```bash -pip install git+ssh://git@github.com:lithic-com/lithic-python.git +```sh +$ pip install git+ssh://git@github.com/lithic-com/lithic-python.git ``` Alternatively, you can build from source and install the wheel file: @@ -68,29 +72,29 @@ Building this package will create two files in the `dist/` directory, a `.tar.gz To create a distributable version of the library, all you have to do is run this command: -```bash -rye build +```sh +$ rye build # or -python -m build +$ python -m build ``` Then to install: ```sh -pip install ./path-to-wheel-file.whl +$ pip install ./path-to-wheel-file.whl ``` ## Running tests -Most tests will require you to [setup a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. -```bash +```sh # you will need npm installed -npx prism path/to/your/openapi.yml +$ npx prism mock path/to/your/openapi.yml ``` -```bash -rye run pytest +```sh +$ ./scripts/test ``` ## Linting and formatting @@ -100,14 +104,14 @@ This repository uses [ruff](https://github.com/astral-sh/ruff) and To lint: -```bash -rye run lint +```sh +$ ./scripts/lint ``` To format and fix all ruff issues automatically: -```bash -rye run format +```sh +$ ./scripts/format ``` ## Publishing and releases @@ -117,9 +121,9 @@ the changes aren't made through the automated pipeline, you may want to make rel ### Publish with a GitHub workflow -You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/lithic-com/lithic-python/actions/workflows/publish-pypi.yml). This will require a setup organization or repository secret to be set up. +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/lithic-com/lithic-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. ### Publish manually -If you need to manually release a package, you can run the `bin/publish-pypi` script with an `PYPI_TOKEN` set on +If you need to manually release a package, you can run the `bin/publish-pypi` script with a `PYPI_TOKEN` set on the environment. diff --git a/LICENSE b/LICENSE index bca930a9..06eee64e 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2024 Lithic + Copyright 2025 Lithic Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 1b6bf7a5..eb044efd 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,18 @@ [![PyPI version](https://img.shields.io/pypi/v/lithic.svg)](https://pypi.org/project/lithic/) -The Lithic Python library provides convenient access to the Lithic REST API from any Python 3.7+ +The Lithic Python library provides convenient access to the Lithic REST API from any Python 3.8+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). ## Documentation -The REST API documentation can be found [on docs.lithic.com](https://docs.lithic.com). The full API of this library can be found in [api.md](api.md). +The REST API documentation can be found on [docs.lithic.com](https://docs.lithic.com). The full API of this library can be found in [api.md](api.md). ## Installation ```sh +# install from PyPI pip install lithic ``` @@ -25,8 +26,7 @@ import os from lithic import Lithic client = Lithic( - # This is the default and can be omitted - api_key=os.environ.get("LITHIC_API_KEY"), + api_key=os.environ.get("LITHIC_API_KEY"), # This is the default and can be omitted # defaults to "production". environment="sandbox", ) @@ -52,8 +52,7 @@ import asyncio from lithic import AsyncLithic client = AsyncLithic( - # This is the default and can be omitted - api_key=os.environ.get("LITHIC_API_KEY"), + api_key=os.environ.get("LITHIC_API_KEY"), # This is the default and can be omitted # defaults to "production". environment="sandbox", ) @@ -73,10 +72,10 @@ Functionality between the synchronous and asynchronous clients is otherwise iden ## Using types -Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev), which provide helper methods for things like: +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: -- Serializing back into JSON, `model.model_dump_json(indent=2, exclude_unset=True)` -- Converting to a dictionary, `model.model_dump(exclude_unset=True)` +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. @@ -87,7 +86,7 @@ List methods in the Lithic API are paginated. This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: ```python -import lithic +from lithic import Lithic client = Lithic() @@ -103,7 +102,7 @@ Or, asynchronously: ```python import asyncio -import lithic +from lithic import AsyncLithic client = AsyncLithic() @@ -153,34 +152,11 @@ from lithic import Lithic client = Lithic() card = client.cards.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", ) print(card.product_id) ``` -## Webhook Verification - -We provide helper methods for verifying that a webhook request came from Lithic, and not a malicious third party. - -You can use `lithic.webhooks.verify_signature(body: string, headers, secret?) -> None` or `lithic.webhooks.unwrap(body: string, headers, secret?) -> Payload`, -both of which will raise an error if the signature is invalid. - -Note that the "body" parameter must be the raw JSON string sent from the server (do not parse it first). -The `.unwrap()` method can parse this JSON for you into a `Payload` object. - -For example, in [FastAPI](https://fastapi.tiangolo.com/): - -```py -@app.post('/my-webhook-handler') -async def handler(request: Request): - body = await request.body() - secret = os.environ['LITHIC_WEBHOOK_SECRET'] # env var used by default; explicit here. - payload = client.webhooks.unwrap(body, request.headers, secret) - print(payload) - - return {'ok': True} -``` - ## Handling errors When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `lithic.APIConnectionError` is raised. @@ -211,7 +187,7 @@ except lithic.APIStatusError as e: print(e.response) ``` -Error codes are as followed: +Error codes are as follows: | Status Code | Error Type | | ----------- | -------------------------- | @@ -267,7 +243,7 @@ client = Lithic( ) # Override per-request: -client.with_options(timeout=5 * 1000).cards.list( +client.with_options(timeout=5.0).cards.list( page_size=10, ) ``` @@ -296,12 +272,14 @@ client = Lithic( We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. -You can enable logging by setting the environment variable `LITHIC_LOG` to `debug`. +You can enable logging by setting the environment variable `LITHIC_LOG` to `info`. ```shell -$ export LITHIC_LOG=debug +$ export LITHIC_LOG=info ``` +Or to `debug` for more verbose logging. + ### How to tell whether `None` means `null` or missing In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: @@ -331,7 +309,7 @@ card = response.parse() # get the object that `cards.create()` would have retur print(card.token) ``` -These methods return an [`LegacyAPIResponse`](https://github.com/lithic-com/lithic-python/tree/main/src/lithic/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. +These methods return a [`LegacyAPIResponse`](https://github.com/lithic-com/lithic-python/tree/main/src/lithic/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. For the sync client this will mostly be the same with the exception of `content` & `text` will be methods instead of properties. In the @@ -360,44 +338,109 @@ with client.cards.with_streaming_response.create( The context manager is required so that the response will reliably be closed. +### Making custom/undocumented requests + +This library is typed for convenient access to the documented API. + +If you need to access undocumented endpoints, params, or response properties, the library can still be used. + +#### Undocumented endpoints + +To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other +http verbs. Options on the client will be respected (such as retries) when making this request. + +```py +import httpx + +response = client.post( + "/foo", + cast_to=httpx.Response, + body={"my_param": True}, +) + +print(response.headers.get("x-foo")) +``` + +#### Undocumented request params + +If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request +options. + +#### Undocumented response properties + +To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You +can also get all the extra fields on the Pydantic model as a dict with +[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra). + ### Configuring the HTTP client You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: -- Support for proxies -- Custom transports -- Additional [advanced](https://www.python-httpx.org/advanced/#client-instances) functionality +- Support for [proxies](https://www.python-httpx.org/advanced/proxies/) +- Custom [transports](https://www.python-httpx.org/advanced/transports/) +- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality ```python import httpx -from lithic import Lithic +from lithic import Lithic, DefaultHttpxClient client = Lithic( # Or use the `LITHIC_BASE_URL` env var base_url="http://my.test.server.example.com:8083", - http_client=httpx.Client( - proxies="http://my.test.proxy.example.com", + http_client=DefaultHttpxClient( + proxy="http://my.test.proxy.example.com", transport=httpx.HTTPTransport(local_address="0.0.0.0"), ), ) ``` +You can also customize the client on a per-request basis by using `with_options()`: + +```python +client.with_options(http_client=DefaultHttpxClient(...)) +``` + ### Managing HTTP resources By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. +```py +from lithic import Lithic + +with Lithic() as client: + # make requests here + ... + +# HTTP client is now closed +``` + ## Versioning This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: 1. Changes that only affect static types, without breaking runtime behavior. -2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals)_. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ 3. Changes that we do not expect to impact the vast majority of users in practice. We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. We are keen for your feedback; please open an [issue](https://www.github.com/lithic-com/lithic-python/issues) with questions, bugs, or suggestions. +### Determining the installed version + +If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version. + +You can determine the version that is being used at runtime with: + +```py +import lithic +print(lithic.__version__) +``` + ## Requirements -Python 3.7 or higher. +Python 3.8 or higher. + +## Contributing + +See [the contributing documentation](./CONTRIBUTING.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..eae5ea4d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainless.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by Lithic please follow the respective company's security reporting guidelines. + +### Lithic Terms and Policies + +Please contact sdk-feedback@lithic.com for any questions or concerns regarding security of our services. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/api.md b/api.md index 21359f56..d1dca42b 100644 --- a/api.md +++ b/api.md @@ -1,7 +1,15 @@ # Shared Types ```python -from lithic.types import Address, Carrier, ShippingAddress +from lithic.types import ( + AccountFinancialAccountType, + Address, + Carrier, + Currency, + Document, + InstanceFinancialAccountType, + ShippingAddress, +) ``` # Lithic @@ -14,29 +22,22 @@ from lithic.types import APIStatus Methods: -- client.api_status() -> APIStatus +- client.api_status() -> APIStatus # Accounts Types: ```python -from lithic.types import Account, AccountSpendLimits, BusinessAccount +from lithic.types import Account, AccountSpendLimits ``` Methods: -- client.accounts.retrieve(account_token) -> Account -- client.accounts.update(account_token, \*\*params) -> Account -- client.accounts.list(\*\*params) -> SyncCursorPage[Account] -- client.accounts.retrieve_spend_limits(account_token) -> AccountSpendLimits - -## CreditConfigurations - -Methods: - -- client.accounts.credit_configurations.retrieve(account_token) -> BusinessAccount -- client.accounts.credit_configurations.update(account_token, \*\*params) -> BusinessAccount +- client.accounts.retrieve(account_token) -> Account +- client.accounts.update(account_token, \*\*params) -> Account +- client.accounts.list(\*\*params) -> SyncCursorPage[Account] +- client.accounts.retrieve_spend_limits(account_token) -> AccountSpendLimits # AccountHolders @@ -45,43 +46,80 @@ Types: ```python from lithic.types import ( AccountHolder, - AccountHolderDocument, + AddressUpdate, KYB, + KYBBusinessEntity, KYC, KYCExempt, + RequiredDocument, AccountHolderCreateResponse, AccountHolderUpdateResponse, AccountHolderListDocumentsResponse, + AccountHolderSimulateEnrollmentReviewResponse, ) ``` Methods: -- client.account_holders.create(\*\*params) -> AccountHolderCreateResponse -- client.account_holders.retrieve(account_holder_token) -> AccountHolder -- client.account_holders.update(account_holder_token, \*\*params) -> AccountHolderUpdateResponse -- client.account_holders.list(\*\*params) -> SyncSinglePage[AccountHolder] -- client.account_holders.list_documents(account_holder_token) -> AccountHolderListDocumentsResponse -- client.account_holders.resubmit(account_holder_token, \*\*params) -> AccountHolder -- client.account_holders.retrieve_document(document_token, \*, account_holder_token) -> AccountHolderDocument -- client.account_holders.upload_document(account_holder_token, \*\*params) -> AccountHolderDocument +- client.account_holders.create(\*\*params) -> AccountHolderCreateResponse +- client.account_holders.retrieve(account_holder_token) -> AccountHolder +- client.account_holders.update(account_holder_token, \*\*params) -> AccountHolderUpdateResponse +- client.account_holders.list(\*\*params) -> SyncSinglePage[AccountHolder] +- client.account_holders.list_documents(account_holder_token) -> AccountHolderListDocumentsResponse +- client.account_holders.retrieve_document(document_token, \*, account_holder_token) -> Document +- client.account_holders.simulate_enrollment_document_review(\*\*params) -> Document +- client.account_holders.simulate_enrollment_review(\*\*params) -> AccountHolderSimulateEnrollmentReviewResponse +- client.account_holders.upload_document(account_holder_token, \*\*params) -> Document # AuthRules +## V2 + +Types: + +```python +from lithic.types.auth_rules import ( + AuthRule, + AuthRuleCondition, + ConditionalAttribute, + ConditionalBlockParameters, + VelocityLimitParams, + VelocityLimitParamsPeriodWindow, + V2CreateResponse, + V2RetrieveResponse, + V2UpdateResponse, + V2ListResponse, + V2ApplyResponse, + V2DraftResponse, + V2PromoteResponse, + V2ReportResponse, +) +``` + +Methods: + +- client.auth_rules.v2.create(\*\*params) -> V2CreateResponse +- client.auth_rules.v2.retrieve(auth_rule_token) -> V2RetrieveResponse +- client.auth_rules.v2.update(auth_rule_token, \*\*params) -> V2UpdateResponse +- client.auth_rules.v2.list(\*\*params) -> SyncCursorPage[V2ListResponse] +- client.auth_rules.v2.delete(auth_rule_token) -> None +- client.auth_rules.v2.apply(auth_rule_token, \*\*params) -> V2ApplyResponse +- client.auth_rules.v2.draft(auth_rule_token, \*\*params) -> V2DraftResponse +- client.auth_rules.v2.promote(auth_rule_token) -> V2PromoteResponse +- client.auth_rules.v2.report(auth_rule_token) -> V2ReportResponse + +### Backtests + Types: ```python -from lithic.types import AuthRule, AuthRuleRetrieveResponse, AuthRuleRemoveResponse +from lithic.types.auth_rules.v2 import BacktestResults, BacktestCreateResponse ``` Methods: -- client.auth_rules.create(\*\*params) -> AuthRule -- client.auth_rules.retrieve(auth_rule_token) -> AuthRuleRetrieveResponse -- client.auth_rules.update(auth_rule_token, \*\*params) -> AuthRule -- client.auth_rules.list(\*\*params) -> SyncCursorPage[AuthRule] -- client.auth_rules.apply(auth_rule_token, \*\*params) -> AuthRule -- client.auth_rules.remove(\*\*params) -> AuthRuleRemoveResponse +- client.auth_rules.v2.backtests.create(auth_rule_token, \*\*params) -> BacktestCreateResponse +- client.auth_rules.v2.backtests.retrieve(auth_rule_backtest_token, \*, auth_rule_token) -> BacktestResults # AuthStreamEnrollment @@ -93,8 +131,8 @@ from lithic.types import AuthStreamSecret Methods: -- client.auth_stream_enrollment.retrieve_secret() -> AuthStreamSecret -- client.auth_stream_enrollment.rotate_secret() -> None +- client.auth_stream_enrollment.retrieve_secret() -> AuthStreamSecret +- client.auth_stream_enrollment.rotate_secret() -> None # TokenizationDecisioning @@ -106,22 +144,33 @@ from lithic.types import TokenizationSecret, TokenizationDecisioningRotateSecret Methods: -- client.tokenization_decisioning.retrieve_secret() -> TokenizationSecret -- client.tokenization_decisioning.rotate_secret() -> TokenizationDecisioningRotateSecretResponse +- client.tokenization_decisioning.retrieve_secret() -> TokenizationSecret +- client.tokenization_decisioning.rotate_secret() -> TokenizationDecisioningRotateSecretResponse # Tokenizations Types: ```python -from lithic.types import Tokenization, TokenizationRetrieveResponse, TokenizationSimulateResponse +from lithic.types import ( + Tokenization, + TokenizationRetrieveResponse, + TokenizationSimulateResponse, + TokenizationUpdateDigitalCardArtResponse, +) ``` Methods: -- client.tokenizations.retrieve(tokenization_token) -> TokenizationRetrieveResponse -- client.tokenizations.list(\*\*params) -> SyncCursorPage[Tokenization] -- client.tokenizations.simulate(\*\*params) -> TokenizationSimulateResponse +- client.tokenizations.retrieve(tokenization_token) -> TokenizationRetrieveResponse +- client.tokenizations.list(\*\*params) -> SyncCursorPage[Tokenization] +- client.tokenizations.activate(tokenization_token) -> None +- client.tokenizations.deactivate(tokenization_token) -> None +- client.tokenizations.pause(tokenization_token) -> None +- client.tokenizations.resend_activation_code(tokenization_token, \*\*params) -> None +- client.tokenizations.simulate(\*\*params) -> TokenizationSimulateResponse +- client.tokenizations.unpause(tokenization_token) -> None +- client.tokenizations.update_digital_card_art(tokenization_token, \*\*params) -> TokenizationUpdateDigitalCardArtResponse # Cards @@ -131,7 +180,6 @@ Types: from lithic.types import ( Card, CardSpendLimits, - EmbedRequestParams, SpendLimitDuration, CardEmbedResponse, CardProvisionResponse, @@ -140,18 +188,17 @@ from lithic.types import ( Methods: -- client.cards.create(\*\*params) -> Card -- client.cards.retrieve(card_token) -> Card -- client.cards.update(card_token, \*\*params) -> Card -- client.cards.list(\*\*params) -> SyncCursorPage[Card] -- client.cards.embed(\*\*params) -> str -- client.cards.provision(card_token, \*\*params) -> CardProvisionResponse -- client.cards.reissue(card_token, \*\*params) -> Card -- client.cards.renew(card_token, \*\*params) -> Card -- client.cards.retrieve_spend_limits(card_token) -> CardSpendLimits -- client.cards.search_by_pan(\*\*params) -> Card -- client.cards.get_embed_html(\*args) -> str -- client.cards.get_embed_url(\*args) -> URL +- client.cards.create(\*\*params) -> Card +- client.cards.retrieve(card_token) -> Card +- client.cards.update(card_token, \*\*params) -> Card +- client.cards.list(\*\*params) -> SyncCursorPage[Card] +- client.cards.convert_physical(card_token, \*\*params) -> Card +- client.cards.embed(\*\*params) -> str +- client.cards.provision(card_token, \*\*params) -> CardProvisionResponse +- client.cards.reissue(card_token, \*\*params) -> Card +- client.cards.renew(card_token, \*\*params) -> Card +- client.cards.retrieve_spend_limits(card_token) -> CardSpendLimits +- client.cards.search_by_pan(\*\*params) -> Card ## AggregateBalances @@ -163,20 +210,26 @@ from lithic.types.cards import AggregateBalanceListResponse Methods: -- client.cards.aggregate_balances.list(\*\*params) -> SyncSinglePage[AggregateBalanceListResponse] +- client.cards.aggregate_balances.list(\*\*params) -> SyncSinglePage[AggregateBalanceListResponse] ## Balances +Types: + +```python +from lithic.types.cards import BalanceListResponse +``` + Methods: -- client.cards.balances.list(card_token, \*\*params) -> SyncSinglePage[Balance] +- client.cards.balances.list(card_token, \*\*params) -> SyncSinglePage[BalanceListResponse] ## FinancialTransactions Methods: -- client.cards.financial_transactions.retrieve(financial_transaction_token, \*, card_token) -> FinancialTransaction -- client.cards.financial_transactions.list(card_token, \*\*params) -> SyncSinglePage[FinancialTransaction] +- client.cards.financial_transactions.retrieve(financial_transaction_token, \*, card_token) -> FinancialTransaction +- client.cards.financial_transactions.list(card_token, \*\*params) -> SyncSinglePage[FinancialTransaction] # Balances @@ -188,7 +241,7 @@ from lithic.types import Balance Methods: -- client.balances.list(\*\*params) -> SyncSinglePage[Balance] +- client.balances.list(\*\*params) -> SyncSinglePage[Balance] # AggregateBalances @@ -200,7 +253,7 @@ from lithic.types import AggregateBalance Methods: -- client.aggregate_balances.list(\*\*params) -> SyncSinglePage[AggregateBalance] +- client.aggregate_balances.list(\*\*params) -> SyncSinglePage[AggregateBalance] # Disputes @@ -212,16 +265,15 @@ from lithic.types import Dispute, DisputeEvidence Methods: -- client.disputes.create(\*\*params) -> Dispute -- client.disputes.retrieve(dispute_token) -> Dispute -- client.disputes.update(dispute_token, \*\*params) -> Dispute -- client.disputes.list(\*\*params) -> SyncCursorPage[Dispute] -- client.disputes.delete(dispute_token) -> Dispute -- client.disputes.delete_evidence(evidence_token, \*, dispute_token) -> DisputeEvidence -- client.disputes.initiate_evidence_upload(dispute_token, \*\*params) -> DisputeEvidence -- client.disputes.list_evidences(dispute_token, \*\*params) -> SyncCursorPage[DisputeEvidence] -- client.disputes.retrieve_evidence(evidence_token, \*, dispute_token) -> DisputeEvidence -- client.disputes.upload_evidence(\*args) -> None +- client.disputes.create(\*\*params) -> Dispute +- client.disputes.retrieve(dispute_token) -> Dispute +- client.disputes.update(dispute_token, \*\*params) -> Dispute +- client.disputes.list(\*\*params) -> SyncCursorPage[Dispute] +- client.disputes.delete(dispute_token) -> Dispute +- client.disputes.delete_evidence(evidence_token, \*, dispute_token) -> DisputeEvidence +- client.disputes.initiate_evidence_upload(dispute_token, \*\*params) -> DisputeEvidence +- client.disputes.list_evidences(dispute_token, \*\*params) -> SyncCursorPage[DisputeEvidence] +- client.disputes.retrieve_evidence(evidence_token, \*, dispute_token) -> DisputeEvidence # Events @@ -233,10 +285,9 @@ from lithic.types import Event, EventSubscription, MessageAttempt Methods: -- client.events.retrieve(event_token) -> Event -- client.events.list(\*\*params) -> SyncCursorPage[Event] -- client.events.list_attempts(event_token, \*\*params) -> SyncCursorPage[MessageAttempt] -- client.events.resend(\*args) -> None +- client.events.retrieve(event_token) -> Event +- client.events.list(\*\*params) -> SyncCursorPage[Event] +- client.events.list_attempts(event_token, \*\*params) -> SyncCursorPage[MessageAttempt] ## Subscriptions @@ -248,17 +299,23 @@ from lithic.types.events import SubscriptionRetrieveSecretResponse Methods: -- client.events.subscriptions.create(\*\*params) -> EventSubscription -- client.events.subscriptions.retrieve(event_subscription_token) -> EventSubscription -- client.events.subscriptions.update(event_subscription_token, \*\*params) -> EventSubscription -- client.events.subscriptions.list(\*\*params) -> SyncCursorPage[EventSubscription] -- client.events.subscriptions.delete(event_subscription_token) -> None -- client.events.subscriptions.list_attempts(event_subscription_token, \*\*params) -> SyncCursorPage[MessageAttempt] -- client.events.subscriptions.recover(event_subscription_token, \*\*params) -> None -- client.events.subscriptions.replay_missing(event_subscription_token, \*\*params) -> None -- client.events.subscriptions.retrieve_secret(event_subscription_token) -> SubscriptionRetrieveSecretResponse -- client.events.subscriptions.rotate_secret(event_subscription_token) -> None -- client.events.subscriptions.send_simulated_example(event_subscription_token, \*\*params) -> None +- client.events.subscriptions.create(\*\*params) -> EventSubscription +- client.events.subscriptions.retrieve(event_subscription_token) -> EventSubscription +- client.events.subscriptions.update(event_subscription_token, \*\*params) -> EventSubscription +- client.events.subscriptions.list(\*\*params) -> SyncCursorPage[EventSubscription] +- client.events.subscriptions.delete(event_subscription_token) -> None +- client.events.subscriptions.list_attempts(event_subscription_token, \*\*params) -> SyncCursorPage[MessageAttempt] +- client.events.subscriptions.recover(event_subscription_token, \*\*params) -> None +- client.events.subscriptions.replay_missing(event_subscription_token, \*\*params) -> None +- client.events.subscriptions.retrieve_secret(event_subscription_token) -> SubscriptionRetrieveSecretResponse +- client.events.subscriptions.rotate_secret(event_subscription_token) -> None +- client.events.subscriptions.send_simulated_example(event_subscription_token, \*\*params) -> None + +## EventSubscriptions + +Methods: + +- client.events.event_subscriptions.resend(event_subscription_token, \*, event_token) -> None # FinancialAccounts @@ -270,48 +327,81 @@ from lithic.types import FinancialAccount, FinancialTransaction Methods: -- client.financial_accounts.create(\*\*params) -> FinancialAccount -- client.financial_accounts.retrieve(financial_account_token) -> FinancialAccount -- client.financial_accounts.update(financial_account_token, \*\*params) -> FinancialAccount -- client.financial_accounts.list(\*\*params) -> SyncSinglePage[FinancialAccount] +- client.financial_accounts.create(\*\*params) -> FinancialAccount +- client.financial_accounts.retrieve(financial_account_token) -> FinancialAccount +- client.financial_accounts.update(financial_account_token, \*\*params) -> FinancialAccount +- client.financial_accounts.list(\*\*params) -> SyncSinglePage[FinancialAccount] +- client.financial_accounts.update_status(financial_account_token, \*\*params) -> FinancialAccount ## Balances +Types: + +```python +from lithic.types.financial_accounts import BalanceListResponse +``` + Methods: -- client.financial_accounts.balances.list(financial_account_token, \*\*params) -> SyncSinglePage[Balance] +- client.financial_accounts.balances.list(financial_account_token, \*\*params) -> SyncSinglePage[BalanceListResponse] ## FinancialTransactions Methods: -- client.financial_accounts.financial_transactions.retrieve(financial_transaction_token, \*, financial_account_token) -> FinancialTransaction -- client.financial_accounts.financial_transactions.list(financial_account_token, \*\*params) -> SyncSinglePage[FinancialTransaction] +- client.financial_accounts.financial_transactions.retrieve(financial_transaction_token, \*, financial_account_token) -> FinancialTransaction +- client.financial_accounts.financial_transactions.list(financial_account_token, \*\*params) -> SyncSinglePage[FinancialTransaction] + +## CreditConfiguration + +Types: + +```python +from lithic.types.financial_accounts import FinancialAccountCreditConfig +``` + +Methods: + +- client.financial_accounts.credit_configuration.retrieve(financial_account_token) -> FinancialAccountCreditConfig +- client.financial_accounts.credit_configuration.update(financial_account_token, \*\*params) -> FinancialAccountCreditConfig ## Statements Types: ```python -from lithic.types.financial_accounts import Statement +from lithic.types.financial_accounts import Statement, Statements ``` Methods: -- client.financial_accounts.statements.retrieve(statement_token, \*, financial_account_token) -> Statement -- client.financial_accounts.statements.list(financial_account_token, \*\*params) -> SyncCursorPage[Statement] +- client.financial_accounts.statements.retrieve(statement_token, \*, financial_account_token) -> Statement +- client.financial_accounts.statements.list(financial_account_token, \*\*params) -> SyncCursorPage[Statement] ### LineItems Types: ```python -from lithic.types.financial_accounts.statements import LineItemListResponse +from lithic.types.financial_accounts.statements import StatementLineItems ``` Methods: -- client.financial_accounts.statements.line_items.list(statement_token, \*, financial_account_token, \*\*params) -> SyncCursorPage[LineItemListResponse] +- client.financial_accounts.statements.line_items.list(statement_token, \*, financial_account_token, \*\*params) -> SyncCursorPage[Data] + +## LoanTapes + +Types: + +```python +from lithic.types.financial_accounts import LoanTape +``` + +Methods: + +- client.financial_accounts.loan_tapes.retrieve(loan_tape_token, \*, financial_account_token) -> LoanTape +- client.financial_accounts.loan_tapes.list(financial_account_token, \*\*params) -> SyncCursorPage[LoanTape] # Transactions @@ -332,36 +422,56 @@ from lithic.types import ( Methods: -- client.transactions.retrieve(transaction_token) -> Transaction -- client.transactions.list(\*\*params) -> SyncCursorPage[Transaction] -- client.transactions.simulate_authorization(\*\*params) -> TransactionSimulateAuthorizationResponse -- client.transactions.simulate_authorization_advice(\*\*params) -> TransactionSimulateAuthorizationAdviceResponse -- client.transactions.simulate_clearing(\*\*params) -> TransactionSimulateClearingResponse -- client.transactions.simulate_credit_authorization(\*\*params) -> TransactionSimulateCreditAuthorizationResponse -- client.transactions.simulate_return(\*\*params) -> TransactionSimulateReturnResponse -- client.transactions.simulate_return_reversal(\*\*params) -> TransactionSimulateReturnReversalResponse -- client.transactions.simulate_void(\*\*params) -> TransactionSimulateVoidResponse +- client.transactions.retrieve(transaction_token) -> Transaction +- client.transactions.list(\*\*params) -> SyncCursorPage[Transaction] +- client.transactions.expire_authorization(transaction_token) -> None +- client.transactions.simulate_authorization(\*\*params) -> TransactionSimulateAuthorizationResponse +- client.transactions.simulate_authorization_advice(\*\*params) -> TransactionSimulateAuthorizationAdviceResponse +- client.transactions.simulate_clearing(\*\*params) -> TransactionSimulateClearingResponse +- client.transactions.simulate_credit_authorization(\*\*params) -> TransactionSimulateCreditAuthorizationResponse +- client.transactions.simulate_return(\*\*params) -> TransactionSimulateReturnResponse +- client.transactions.simulate_return_reversal(\*\*params) -> TransactionSimulateReturnReversalResponse +- client.transactions.simulate_void(\*\*params) -> TransactionSimulateVoidResponse -# ResponderEndpoints +## EnhancedCommercialData Types: ```python -from lithic.types import ResponderEndpointStatus, ResponderEndpointCreateResponse +from lithic.types.transactions import EnhancedCommercialDataRetrieveResponse ``` Methods: -- client.responder_endpoints.create(\*\*params) -> ResponderEndpointCreateResponse -- client.responder_endpoints.delete(\*\*params) -> None -- client.responder_endpoints.check_status(\*\*params) -> ResponderEndpointStatus +- client.transactions.enhanced_commercial_data.retrieve(transaction_token) -> EnhancedCommercialDataRetrieveResponse + +## Events -# Webhooks +### EnhancedCommercialData + +Types: + +```python +from lithic.types.transactions.events import EnhancedData +``` Methods: -- client.webhooks.unwrap(\*args) -> object -- client.webhooks.verify_signature(\*args) -> None +- client.transactions.events.enhanced_commercial_data.retrieve(event_token) -> EnhancedData + +# ResponderEndpoints + +Types: + +```python +from lithic.types import ResponderEndpointStatus, ResponderEndpointCreateResponse +``` + +Methods: + +- client.responder_endpoints.create(\*\*params) -> ResponderEndpointCreateResponse +- client.responder_endpoints.delete(\*\*params) -> None +- client.responder_endpoints.check_status(\*\*params) -> ResponderEndpointStatus # ExternalBankAccounts @@ -377,16 +487,18 @@ from lithic.types import ( ExternalBankAccountUpdateResponse, ExternalBankAccountListResponse, ExternalBankAccountRetryMicroDepositsResponse, + ExternalBankAccountRetryPrenoteResponse, ) ``` Methods: -- client.external_bank_accounts.create(\*\*params) -> ExternalBankAccountCreateResponse -- client.external_bank_accounts.retrieve(external_bank_account_token) -> ExternalBankAccountRetrieveResponse -- client.external_bank_accounts.update(external_bank_account_token, \*\*params) -> ExternalBankAccountUpdateResponse -- client.external_bank_accounts.list(\*\*params) -> SyncCursorPage[ExternalBankAccountListResponse] -- client.external_bank_accounts.retry_micro_deposits(external_bank_account_token) -> ExternalBankAccountRetryMicroDepositsResponse +- client.external_bank_accounts.create(\*\*params) -> ExternalBankAccountCreateResponse +- client.external_bank_accounts.retrieve(external_bank_account_token) -> ExternalBankAccountRetrieveResponse +- client.external_bank_accounts.update(external_bank_account_token, \*\*params) -> ExternalBankAccountUpdateResponse +- client.external_bank_accounts.list(\*\*params) -> SyncCursorPage[ExternalBankAccountListResponse] +- client.external_bank_accounts.retry_micro_deposits(external_bank_account_token, \*\*params) -> ExternalBankAccountRetryMicroDepositsResponse +- client.external_bank_accounts.retry_prenote(external_bank_account_token, \*\*params) -> ExternalBankAccountRetryPrenoteResponse ## MicroDeposits @@ -398,7 +510,7 @@ from lithic.types.external_bank_accounts import MicroDepositCreateResponse Methods: -- client.external_bank_accounts.micro_deposits.create(external_bank_account_token, \*\*params) -> MicroDepositCreateResponse +- client.external_bank_accounts.micro_deposits.create(external_bank_account_token, \*\*params) -> MicroDepositCreateResponse # Payments @@ -409,6 +521,8 @@ from lithic.types import ( Payment, PaymentCreateResponse, PaymentRetryResponse, + PaymentSimulateActionResponse, + PaymentSimulateReceiptResponse, PaymentSimulateReleaseResponse, PaymentSimulateReturnResponse, ) @@ -416,12 +530,14 @@ from lithic.types import ( Methods: -- client.payments.create(\*\*params) -> PaymentCreateResponse -- client.payments.retrieve(payment_token) -> Payment -- client.payments.list(\*\*params) -> SyncCursorPage[Payment] -- client.payments.retry(payment_token) -> PaymentRetryResponse -- client.payments.simulate_release(\*\*params) -> PaymentSimulateReleaseResponse -- client.payments.simulate_return(\*\*params) -> PaymentSimulateReturnResponse +- client.payments.create(\*\*params) -> PaymentCreateResponse +- client.payments.retrieve(payment_token) -> Payment +- client.payments.list(\*\*params) -> SyncCursorPage[Payment] +- client.payments.retry(payment_token) -> PaymentRetryResponse +- client.payments.simulate_action(payment_token, \*\*params) -> PaymentSimulateActionResponse +- client.payments.simulate_receipt(\*\*params) -> PaymentSimulateReceiptResponse +- client.payments.simulate_release(\*\*params) -> PaymentSimulateReleaseResponse +- client.payments.simulate_return(\*\*params) -> PaymentSimulateReturnResponse # ThreeDS @@ -435,21 +551,27 @@ from lithic.types.three_ds import AuthenticationRetrieveResponse, Authentication Methods: -- client.three_ds.authentication.retrieve(three_ds_authentication_token) -> AuthenticationRetrieveResponse -- client.three_ds.authentication.simulate(\*\*params) -> AuthenticationSimulateResponse +- client.three_ds.authentication.retrieve(three_ds_authentication_token) -> AuthenticationRetrieveResponse +- client.three_ds.authentication.simulate(\*\*params) -> AuthenticationSimulateResponse +- client.three_ds.authentication.simulate_otp_entry(\*\*params) -> None ## Decisioning Types: ```python -from lithic.types.three_ds import DecisioningRetrieveSecretResponse +from lithic.types.three_ds import ( + ChallengeResponse, + ChallengeResult, + DecisioningRetrieveSecretResponse, +) ``` Methods: -- client.three_ds.decisioning.retrieve_secret() -> DecisioningRetrieveSecretResponse -- client.three_ds.decisioning.rotate_secret() -> None +- client.three_ds.decisioning.challenge_response(\*\*params) -> None +- client.three_ds.decisioning.retrieve_secret() -> DecisioningRetrieveSecretResponse +- client.three_ds.decisioning.rotate_secret() -> None # Reports @@ -463,20 +585,21 @@ from lithic.types import SettlementDetail, SettlementReport, SettlementSummaryDe Methods: -- client.reports.settlement.list_details(report_date, \*\*params) -> SyncCursorPage[SettlementDetail] -- client.reports.settlement.summary(report_date) -> SettlementReport +- client.reports.settlement.list_details(report_date, \*\*params) -> SyncCursorPage[SettlementDetail] +- client.reports.settlement.summary(report_date) -> SettlementReport -# CardProduct +### NetworkTotals Types: ```python -from lithic.types import CardProductCreditDetailResponse +from lithic.types.reports.settlement import NetworkTotalRetrieveResponse, NetworkTotalListResponse ``` Methods: -- client.card_product.credit_detail() -> CardProductCreditDetailResponse +- client.reports.settlement.network_totals.retrieve(token) -> NetworkTotalRetrieveResponse +- client.reports.settlement.network_totals.list(\*\*params) -> SyncCursorPage[NetworkTotalListResponse] # CardPrograms @@ -488,10 +611,10 @@ from lithic.types import CardProgram Methods: -- client.card_programs.retrieve(card_program_token) -> CardProgram -- client.card_programs.list(\*\*params) -> SyncCursorPage[CardProgram] +- client.card_programs.retrieve(card_program_token) -> CardProgram +- client.card_programs.list(\*\*params) -> SyncCursorPage[CardProgram] -# DigitalCardArtResource +# DigitalCardArt Types: @@ -501,5 +624,80 @@ from lithic.types import DigitalCardArt Methods: -- client.digital_card_art.retrieve(digital_card_art_token) -> DigitalCardArt -- client.digital_card_art.list(\*\*params) -> SyncCursorPage[DigitalCardArt] +- client.digital_card_art.retrieve(digital_card_art_token) -> DigitalCardArt +- client.digital_card_art.list(\*\*params) -> SyncCursorPage[DigitalCardArt] + +# BookTransfers + +Types: + +```python +from lithic.types import BookTransferResponse +``` + +Methods: + +- client.book_transfers.create(\*\*params) -> BookTransferResponse +- client.book_transfers.retrieve(book_transfer_token) -> BookTransferResponse +- client.book_transfers.list(\*\*params) -> SyncCursorPage[BookTransferResponse] +- client.book_transfers.reverse(book_transfer_token, \*\*params) -> BookTransferResponse + +# CreditProducts + +## ExtendedCredit + +Types: + +```python +from lithic.types.credit_products import ExtendedCredit +``` + +Methods: + +- client.credit_products.extended_credit.retrieve(credit_product_token) -> ExtendedCredit + +## PrimeRates + +Types: + +```python +from lithic.types.credit_products import PrimeRateRetrieveResponse +``` + +Methods: + +- client.credit_products.prime_rates.create(credit_product_token, \*\*params) -> None +- client.credit_products.prime_rates.retrieve(credit_product_token, \*\*params) -> PrimeRateRetrieveResponse + +# ExternalPayments + +Types: + +```python +from lithic.types import ExternalPayment +``` + +Methods: + +- client.external_payments.create(\*\*params) -> ExternalPayment +- client.external_payments.retrieve(external_payment_token) -> ExternalPayment +- client.external_payments.list(\*\*params) -> SyncCursorPage[ExternalPayment] +- client.external_payments.cancel(external_payment_token, \*\*params) -> ExternalPayment +- client.external_payments.release(external_payment_token, \*\*params) -> ExternalPayment +- client.external_payments.reverse(external_payment_token, \*\*params) -> ExternalPayment +- client.external_payments.settle(external_payment_token, \*\*params) -> ExternalPayment + +# ManagementOperations + +Types: + +```python +from lithic.types import ManagementOperationTransaction +``` + +Methods: + +- client.management_operations.create(\*\*params) -> ManagementOperationTransaction +- client.management_operations.retrieve(management_operation_token) -> ManagementOperationTransaction +- client.management_operations.list(\*\*params) -> SyncCursorPage[ManagementOperationTransaction] +- client.management_operations.reverse(management_operation_token, \*\*params) -> ManagementOperationTransaction diff --git a/bin/check-env-state.py b/bin/check-env-state.py deleted file mode 100644 index e1b8b6cb..00000000 --- a/bin/check-env-state.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Script that exits 1 if the current environment is not -in sync with the `requirements-dev.lock` file. -""" - -from pathlib import Path - -import importlib_metadata - - -def should_run_sync() -> bool: - dev_lock = Path(__file__).parent.parent.joinpath("requirements-dev.lock") - - for line in dev_lock.read_text().splitlines(): - if not line or line.startswith("#") or line.startswith("-e"): - continue - - dep, lock_version = line.split("==") - - try: - version = importlib_metadata.version(dep) - - if lock_version != version: - print(f"mismatch for {dep} current={version} lock={lock_version}") - return True - except Exception: - print(f"could not import {dep}") - return True - - return False - - -def main() -> None: - if should_run_sync(): - exit(1) - else: - exit(0) - - -if __name__ == "__main__": - main() diff --git a/bin/check-test-server b/bin/check-test-server deleted file mode 100755 index a6fa3495..00000000 --- a/bin/check-test-server +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -NC='\033[0m' # No Color - -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 -} - -function is_overriding_api_base_url() { - [ -n "$TEST_API_BASE_URL" ] -} - -if is_overriding_api_base_url ; then - # If someone is running the tests against the live API, we can trust they know - # what they're doing and exit early. - echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" - - exit 0 -elif prism_is_running ; then - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" - echo - - exit 0 -else - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" - echo -e "running against your OpenAPI spec." - echo - echo -e "${YELLOW}To fix:${NC}" - echo - echo -e "1. Install Prism (requires Node 16+):" - echo - echo -e " With npm:" - echo -e " \$ ${YELLOW}npm install -g @stoplight/prism-cli${NC}" - echo - echo -e " With yarn:" - echo -e " \$ ${YELLOW}yarn global add @stoplight/prism-cli${NC}" - echo - echo -e "2. Run the mock server" - echo - echo -e " To run the server, pass in the path of your OpenAPI" - echo -e " spec to the prism command:" - echo - echo -e " \$ ${YELLOW}prism mock path/to/your.openapi.yml${NC}" - echo - - exit 1 -fi diff --git a/bin/test b/bin/test deleted file mode 100755 index 60ede7a8..00000000 --- a/bin/test +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -bin/check-test-server && rye run pytest "$@" diff --git a/examples/datetime_usage.py b/examples/datetime_usage.py deleted file mode 100644 index a01df811..00000000 --- a/examples/datetime_usage.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env -S poetry run python - -from datetime import datetime - -from lithic import Lithic - -client = Lithic(environment="sandbox") - -now = datetime.now() - -# datetime responses will always be instances of `datetime` -card = client.cards.create(type="VIRTUAL") -assert isinstance(card.created, datetime) -assert card.created.year == now.year -assert card.created.month == now.month -assert card.created.tzname() == "UTC" - -dt = datetime.fromisoformat("2022-07-25T21:34:45+00:00") - -# # both `datetime` instances or datetime strings can be passed as a request param -page = client.cards.list(begin=dt, page_size=1) -assert len(page.data) == 1 - -page = client.cards.list(begin=dt.isoformat(), page_size=1) -assert len(page.data) == 1 diff --git a/examples/hello_sailor.txt b/examples/hello_sailor.txt deleted file mode 100644 index cc034734..00000000 --- a/examples/hello_sailor.txt +++ /dev/null @@ -1 +0,0 @@ -Hello, Sailor! diff --git a/examples/hello_world.txt b/examples/hello_world.txt deleted file mode 100644 index 8ab686ea..00000000 --- a/examples/hello_world.txt +++ /dev/null @@ -1 +0,0 @@ -Hello, World! diff --git a/examples/upload_evidence.py b/examples/upload_evidence.py deleted file mode 100644 index 440a1666..00000000 --- a/examples/upload_evidence.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env -S poetry run python - -# -# Run with: LITHIC_API_KEY= poetry run python examples/upload_evidence.py -# - -from lithic import Lithic, file_from_path - -client = Lithic(environment="sandbox") - -transactions_page = client.transactions.list() -assert len(transactions_page.data) > 0, "No transactions found" - -transaction = transactions_page.data[0] -assert transaction.token, "Transaction must have a token" - -disputes_page = client.disputes.list() -dispute = disputes_page.data[0] -if not dispute: - dispute = client.disputes.create( - amount=42, - reason="ATM_CASH_MISDISPENSE", - transaction_token=transaction.token, - ) - -print(dispute) -assert dispute, "Could not find or create a dispute" - -my_file = file_from_path("hello_world.txt") - -upload = client.disputes.upload_evidence(dispute.token, my_file) -print(upload) - -print("Done!") diff --git a/integrations/pagination.py b/integrations/pagination.py deleted file mode 100755 index e874414f..00000000 --- a/integrations/pagination.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env -S rye run python integrations/pagination.py - -from __future__ import annotations - -import json - -from lithic import Lithic - -client = Lithic(environment="sandbox") - - -def main() -> None: - page = client.transactions.list() - assert len(page.data) > 0, "No transactions found" - - if not page.has_more or not page.has_next_page(): - raise RuntimeError(f"Expected multiple pages to be present, only got {len(page.data)} items") - - tokens: dict[str, int] = {} - - for transaction in page: - tokens[transaction.token] = tokens.get(transaction.token, 0) + 1 - - duplicates = {token: count for token, count in tokens.items() if count > 1} - if duplicates: - print(json.dumps(duplicates, indent=2)) # noqa: T201 - raise RuntimeError(f"Found {len(duplicates)} duplicate entries!") - - print("Success!") # noqa: T201 - - -main() diff --git a/mypy.ini b/mypy.ini index b5862c11..c0bb47f6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -5,7 +5,10 @@ show_error_codes = True # Exclude _files.py because mypy isn't smart enough to apply # the correct type narrowing and as this is an internal module # it's fine to just use Pyright. -exclude = ^(src/lithic/_files\.py|_dev/.*\.py)$ +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ^(src/lithic/_files\.py|_dev/.*\.py|tests/.*|src/lithic/resources/external_bank_accounts/external_bank_accounts\.py)$ strict_equality = True implicit_reexport = True @@ -38,7 +41,7 @@ cache_fine_grained = True # ``` # Changing this codegen to make mypy happy would increase complexity # and would not be worth it. -disable_error_code = func-returns-value +disable_error_code = func-returns-value,overload-cannot-match # https://github.com/python/mypy/issues/12162 [mypy.overrides] diff --git a/pyproject.toml b/pyproject.toml index 059f64fd..88de6f19 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [project] name = "lithic" -version = "0.39.0" +version = "0.88.0" description = "The official Python library for the lithic API" -readme = "README.md" +dynamic = ["readme"] license = "Apache-2.0" authors = [ { name = "Lithic", email = "sdk-feedback@lithic.com" }, @@ -10,18 +10,15 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.7, <5", + "typing-extensions>=4.10, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", - "cached-property; python_version < '3.8'", - ] -requires-python = ">= 3.7" +requires-python = ">= 3.8" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -36,19 +33,16 @@ classifiers = [ "License :: OSI Approved :: Apache Software License" ] - - [project.urls] Homepage = "https://github.com/lithic-com/lithic-python" Repository = "https://github.com/lithic-com/lithic-python" - [tool.rye] managed = true # version pins are in requirements-dev.lock dev-dependencies = [ - "pyright", + "pyright>=1.1.359", "mypy", "respx", "pytest", @@ -58,7 +52,8 @@ dev-dependencies = [ "nox", "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", - + "rich>=13.7.1", + "nest_asyncio==1.6.0", ] [tool.rye.scripts] @@ -66,18 +61,21 @@ format = { chain = [ "format:ruff", "format:docs", "fix:ruff", + # run formatting again to fix any inconsistencies when imports are stripped + "format:ruff", ]} -"format:black" = "black ." -"format:docs" = "python bin/ruffen-docs.py README.md api.md" +"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" "format:ruff" = "ruff format" -"format:isort" = "isort ." "lint" = { chain = [ "check:ruff", "typecheck", + "check:importable", ]} -"check:ruff" = "ruff ." -"fix:ruff" = "ruff --fix ." +"check:ruff" = "ruff check ." +"fix:ruff" = "ruff check --fix ." + +"check:importable" = "python -c 'import lithic'" typecheck = { chain = [ "typecheck:pyright", @@ -88,7 +86,7 @@ typecheck = { chain = [ "typecheck:mypy" = "mypy ." [build-system] -requires = ["hatchling"] +requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"] build-backend = "hatchling.build" [tool.hatch.build] @@ -99,15 +97,38 @@ include = [ [tool.hatch.build.targets.wheel] packages = ["src/lithic"] -[tool.black] -line-length = 120 -target-version = ["py37"] +[tool.hatch.build.targets.sdist] +# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc) +include = [ + "/*.toml", + "/*.json", + "/*.lock", + "/*.md", + "/mypy.ini", + "/noxfile.py", + "bin/*", + "examples/*", + "src/*", + "tests/*", +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] +path = "README.md" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] +# replace relative links with absolute links +pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' +replacement = '[\1](https://github.com/lithic-com/lithic-python/tree/main/\g<2>)' [tool.pytest.ini_options] testpaths = ["tests"] addopts = "--tb=short" xfail_strict = true asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" filterwarnings = [ "error" ] @@ -117,7 +138,7 @@ filterwarnings = [ # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" -pythonVersion = "3.7" +pythonVersion = "3.8" exclude = [ "_dev", @@ -129,11 +150,18 @@ reportImplicitOverride = true reportImportCycles = false reportPrivateUsage = false +reportOverlappingOverload = false + [tool.ruff] line-length = 120 output-format = "grouped" target-version = "py37" + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint] select = [ # isort "I", @@ -149,7 +177,9 @@ select = [ "T201", "T203", # misuse of typing.TYPE_CHECKING - "TCH004" + "TC004", + # import rules + "TID251", ] ignore = [ # mutable defaults @@ -160,10 +190,9 @@ unfixable = [ "T201", "T203", ] -ignore-init-module-imports = true -[tool.ruff.format] -docstring-code-format = true +[tool.ruff.lint.flake8-tidy-imports.banned-api] +"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" [tool.ruff.lint.isort] length-sort = true @@ -172,7 +201,8 @@ combine-as-imports = true extra-standard-library = ["typing_extensions"] known-first-party = ["lithic", "tests"] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "bin/**.py" = ["T201", "T203"] +"scripts/**.py" = ["T201", "T203"] "tests/**.py" = ["T201", "T203"] "examples/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 6078baa3..196ee452 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -6,17 +6,17 @@ # features: [] # all-features: true # with-sources: false +# generate-hashes: false +# universal: false -e file:. annotated-types==0.6.0 # via pydantic -anyio==4.1.0 +anyio==4.4.0 # via httpx # via lithic argcomplete==3.1.2 # via nox -attrs==23.1.0 - # via pytest certifi==2023.7.22 # via httpcore # via httpx @@ -27,15 +27,16 @@ distlib==0.3.7 # via virtualenv distro==1.8.0 # via lithic -exceptiongroup==1.1.3 +exceptiongroup==1.2.2 # via anyio + # via pytest filelock==3.12.4 # via virtualenv h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx -httpx==0.25.2 +httpx==0.28.1 # via lithic # via respx idna==3.4 @@ -44,9 +45,14 @@ idna==3.4 importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest -mypy==1.7.1 +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py +mypy==1.14.1 mypy-extensions==1.0.0 # via mypy +nest-asyncio==1.6.0 nodeenv==1.8.0 # via pyright nox==2023.4.22 @@ -55,41 +61,43 @@ packaging==23.2 # via pytest platformdirs==3.11.0 # via virtualenv -pluggy==1.3.0 - # via pytest -py==1.11.0 +pluggy==1.5.0 # via pytest -pydantic==2.4.2 +pydantic==2.10.3 # via lithic -pydantic-core==2.10.1 +pydantic-core==2.27.1 # via pydantic -pyright==1.1.351 -pytest==7.1.1 +pygments==2.18.0 + # via rich +pyright==1.1.392.post0 +pytest==8.3.3 # via pytest-asyncio -pytest-asyncio==0.21.1 +pytest-asyncio==0.24.0 python-dateutil==2.8.2 # via time-machine pytz==2023.3.post1 # via dirty-equals -respx==0.20.2 -ruff==0.1.9 +respx==0.22.0 +rich==13.7.1 +ruff==0.9.4 setuptools==68.2.2 # via nodeenv six==1.16.0 # via python-dateutil sniffio==1.3.0 # via anyio - # via httpx # via lithic time-machine==2.9.0 -tomli==2.0.1 +tomli==2.0.2 # via mypy # via pytest -typing-extensions==4.8.0 +typing-extensions==4.12.2 + # via anyio # via lithic # via mypy # via pydantic # via pydantic-core + # via pyright virtualenv==20.24.5 # via nox zipp==3.17.0 diff --git a/requirements.lock b/requirements.lock index 7fbcaa73..0745afc3 100644 --- a/requirements.lock +++ b/requirements.lock @@ -6,11 +6,13 @@ # features: [] # all-features: true # with-sources: false +# generate-hashes: false +# universal: false -e file:. annotated-types==0.6.0 # via pydantic -anyio==4.1.0 +anyio==4.4.0 # via httpx # via lithic certifi==2023.7.22 @@ -18,26 +20,26 @@ certifi==2023.7.22 # via httpx distro==1.8.0 # via lithic -exceptiongroup==1.1.3 +exceptiongroup==1.2.2 # via anyio h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx -httpx==0.25.2 +httpx==0.28.1 # via lithic idna==3.4 # via anyio # via httpx -pydantic==2.4.2 +pydantic==2.10.3 # via lithic -pydantic-core==2.10.1 +pydantic-core==2.27.1 # via pydantic sniffio==1.3.0 # via anyio - # via httpx # via lithic -typing-extensions==4.8.0 +typing-extensions==4.12.2 + # via anyio # via lithic # via pydantic # via pydantic-core diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 00000000..e84fe62c --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then + brew bundle check >/dev/null 2>&1 || { + echo "==> Installing Homebrew dependencies…" + brew bundle + } +fi + +echo "==> Installing Python dependencies…" + +# experimental uv support makes installations significantly faster +rye config --set-bool behavior.use-uv=true + +rye sync --all-features diff --git a/scripts/format b/scripts/format new file mode 100755 index 00000000..667ec2d7 --- /dev/null +++ b/scripts/format @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running formatters" +rye run format diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 00000000..c997f8ca --- /dev/null +++ b/scripts/lint @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running lints" +rye run lint + +echo "==> Making sure it imports" +rye run python -c 'import lithic' diff --git a/scripts/mock b/scripts/mock new file mode 100755 index 00000000..d2814ae6 --- /dev/null +++ b/scripts/mock @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [[ -n "$1" && "$1" != '--'* ]]; then + URL="$1" + shift +else + URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" +fi + +# Check if the URL is empty +if [ -z "$URL" ]; then + echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" + exit 1 +fi + +echo "==> Starting mock server with URL ${URL}" + +# Run prism mock on the given spec +if [ "$1" == "--daemon" ]; then + npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log & + + # Wait for server to come online + echo -n "Waiting for server" + while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + echo -n "." + sleep 0.1 + done + + if grep -q "✖ fatal" ".prism.log"; then + cat .prism.log + exit 1 + fi + + echo +else + npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" +fi diff --git a/scripts/test b/scripts/test new file mode 100755 index 00000000..2b878456 --- /dev/null +++ b/scripts/test @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +function prism_is_running() { + curl --silent "http://localhost:4010" >/dev/null 2>&1 +} + +kill_server_on_port() { + pids=$(lsof -t -i tcp:"$1" || echo "") + if [ "$pids" != "" ]; then + kill "$pids" + echo "Stopped $pids." + fi +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if ! is_overriding_api_base_url && ! prism_is_running ; then + # When we exit this script, make sure to kill the background mock server process + trap 'kill_server_on_port 4010' EXIT + + # Start the dev server + ./scripts/mock --daemon +fi + +if is_overriding_api_base_url ; then + echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" + echo +elif ! prism_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "To run the server, pass in the path or url of your OpenAPI" + echo -e "spec to the prism command:" + echo + echo -e " \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +else + echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo +fi + +export DEFER_PYDANTIC_BUILD=false + +echo "==> Running tests" +rye run pytest "$@" + +echo "==> Running Pydantic v1 tests" +rye run nox -s test-pydantic-v1 -- "$@" diff --git a/bin/ruffen-docs.py b/scripts/utils/ruffen-docs.py similarity index 97% rename from bin/ruffen-docs.py rename to scripts/utils/ruffen-docs.py index 37b3d94f..0cf2bd2f 100644 --- a/bin/ruffen-docs.py +++ b/scripts/utils/ruffen-docs.py @@ -47,7 +47,7 @@ def _md_match(match: Match[str]) -> str: with _collect_error(match): code = format_code_block(code) code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' + return f"{match['before']}{code}{match['after']}" def _pycon_match(match: Match[str]) -> str: code = "" @@ -97,7 +97,7 @@ def finish_fragment() -> None: def _md_pycon_match(match: Match[str]) -> str: code = _pycon_match(match) code = textwrap.indent(code, match["indent"]) - return f'{match["before"]}{code}{match["after"]}' + return f"{match['before']}{code}{match['after']}" src = MD_RE.sub(_md_match, src) src = MD_PYCON_RE.sub(_md_pycon_match, src) diff --git a/src/lithic/__init__.py b/src/lithic/__init__.py index 8b7691d9..f847e185 100644 --- a/src/lithic/__init__.py +++ b/src/lithic/__init__.py @@ -1,7 +1,7 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from . import types -from ._types import NoneType, Transport, ProxiesTypes +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes from ._utils import file_from_path from ._client import ( ENVIRONMENTS, @@ -18,6 +18,7 @@ from ._models import BaseModel from ._version import __title__, __version__ from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse +from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS from ._exceptions import ( APIError, LithicError, @@ -34,6 +35,7 @@ UnprocessableEntityError, APIResponseValidationError, ) +from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -43,6 +45,9 @@ "NoneType", "Transport", "ProxiesTypes", + "NotGiven", + "NOT_GIVEN", + "Omit", "LithicError", "APIError", "APIStatusError", @@ -68,6 +73,11 @@ "ENVIRONMENTS", "file_from_path", "BaseModel", + "DEFAULT_TIMEOUT", + "DEFAULT_MAX_RETRIES", + "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", ] _setup_logging() diff --git a/src/lithic/_base_client.py b/src/lithic/_base_client.py index 73bd2411..c3a3eb97 100644 --- a/src/lithic/_base_client.py +++ b/src/lithic/_base_client.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys import json import time import uuid @@ -8,7 +9,6 @@ import inspect import logging import platform -import warnings import email.utils from types import TracebackType from random import random @@ -29,14 +29,13 @@ cast, overload, ) -from functools import lru_cache from typing_extensions import Literal, override, get_origin import anyio import httpx import distro import pydantic -from httpx import URL, Limits +from httpx import URL from pydantic import PrivateAttr from . import _exceptions @@ -51,18 +50,16 @@ Timeout, NotGiven, ResponseT, - Transport, AnyMapping, PostParser, - ProxiesTypes, RequestFiles, HttpxSendArgs, - AsyncTransport, RequestOptions, + HttpxRequestFiles, ModelBuilderProtocol, ) -from ._utils import is_dict, is_list, is_given, is_mapping -from ._compat import model_copy, model_dump +from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping +from ._compat import PYDANTIC_V2, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -71,15 +68,15 @@ extract_response_type, ) from ._constants import ( - DEFAULT_LIMITS, DEFAULT_TIMEOUT, MAX_RETRY_DELAY, DEFAULT_MAX_RETRIES, INITIAL_RETRY_DELAY, RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER, + DEFAULT_CONNECTION_LIMITS, ) -from ._streaming import Stream, AsyncStream +from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder from ._exceptions import ( APIStatusError, APITimeoutError, @@ -125,16 +122,14 @@ def __init__( self, *, url: URL, - ) -> None: - ... + ) -> None: ... @overload def __init__( self, *, params: Query, - ) -> None: - ... + ) -> None: ... def __init__( self, @@ -145,6 +140,12 @@ def __init__( self.url = url self.params = params + @override + def __repr__(self) -> str: + if self.url: + return f"{self.__class__.__name__}(url={self.url})" + return f"{self.__class__.__name__}(params={self.params})" + class BasePage(GenericModel, Generic[_T]): """ @@ -167,8 +168,7 @@ def has_next_page(self) -> bool: return False return self.next_page_info() is not None - def next_page_info(self) -> Optional[PageInfo]: - ... + def next_page_info(self) -> Optional[PageInfo]: ... def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] ... @@ -204,6 +204,9 @@ def _set_private_attributes( model: Type[_T], options: FinalRequestOptions, ) -> None: + if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options @@ -289,6 +292,9 @@ def _set_private_attributes( client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: + if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options @@ -328,9 +334,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): _base_url: URL max_retries: int timeout: Union[float, Timeout, None] - _limits: httpx.Limits - _proxies: ProxiesTypes | None - _transport: Transport | AsyncTransport | None _strict_response_validation: bool _idempotency_header: str | None _default_stream_cls: type[_DefaultStreamT] | None = None @@ -343,9 +346,6 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None = DEFAULT_TIMEOUT, - limits: httpx.Limits, - transport: Transport | AsyncTransport | None, - proxies: ProxiesTypes | None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: @@ -353,13 +353,16 @@ def __init__( self._base_url = self._enforce_trailing_slash(URL(base_url)) self.max_retries = max_retries self.timeout = timeout - self._limits = limits - self._proxies = proxies - self._transport = transport self._custom_headers = custom_headers or {} self._custom_query = custom_query or {} self._strict_response_validation = _strict_response_validation self._idempotency_header = None + self._platform: Platform | None = None + + if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] + raise TypeError( + "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `lithic.DEFAULT_MAX_RETRIES`" + ) def _enforce_trailing_slash(self, url: URL) -> URL: if url.raw_path.endswith(b"/"): @@ -397,14 +400,7 @@ def _make_status_error( ) -> _exceptions.APIStatusError: raise NotImplementedError() - def _remaining_retries( - self, - remaining_retries: Optional[int], - options: FinalRequestOptions, - ) -> int: - return remaining_retries if remaining_retries is not None else options.get_max_retries(self.max_retries) - - def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers: + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: custom_headers = options.headers or {} headers_dict = _merge_mappings(self.default_headers, custom_headers) self._validate_headers(headers_dict, custom_headers) @@ -416,6 +412,18 @@ def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers: if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: headers[idempotency_header] = options.idempotency_key or self._idempotency_key() + # Don't set these headers if they were already set or removed by the caller. We check + # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: + headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) + return headers def _prepare_url(self, url: str) -> URL: @@ -431,9 +439,14 @@ def _prepare_url(self, url: str) -> URL: return merge_url + def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: + return SSEDecoder() + def _build_request( self, options: FinalRequestOptions, + *, + retries_taken: int = 0, ) -> httpx.Request: if log.isEnabledFor(logging.DEBUG): log.debug("Request options: %s", model_dump(options, exclude_unset=True)) @@ -449,9 +462,10 @@ def _build_request( else: raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") - headers = self._build_headers(options) - params = _merge_mappings(self._custom_query, options.params) + headers = self._build_headers(options, retries_taken=retries_taken) + params = _merge_mappings(self.default_query, options.params) content_type = headers.get("Content-Type") + files = options.files # If the given Content-Type header is multipart/form-data then it # has to be removed so that httpx can generate the header with @@ -465,7 +479,7 @@ def _build_request( headers.pop("Content-Type") # As we are now sending multipart/form-data instead of application/json - # we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding if json_data: if not is_dict(json_data): raise TypeError( @@ -473,19 +487,33 @@ def _build_request( ) kwargs["data"] = self._serialize_multipartform(json_data) + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + + prepared_url = self._prepare_url(options.url) + if "_" in prepared_url.host: + # work around https://github.com/encode/httpx/discussions/2880 + kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, method=options.method, - url=self._prepare_url(options.url), + url=prepared_url, # the `Query` type that we use is incompatible with qs' # `Params` type as it needs to be typed as `Mapping[str, object]` # so that passing a `TypedDict` doesn't cause an error. # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, - json=json_data, - files=options.files, + json=json_data if is_given(json_data) else None, + files=files, **kwargs, ) @@ -586,6 +614,12 @@ def default_headers(self) -> dict[str, str | Omit]: **self._custom_headers, } + @property + def default_query(self) -> dict[str, object]: + return { + **self._custom_query, + } + def _validate_headers( self, headers: Headers, # noqa: ARG002 @@ -610,7 +644,10 @@ def base_url(self, url: URL | str) -> None: self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) def platform_headers(self) -> Dict[str, str]: - return platform_headers(self._version) + # the actual implementation is in a separate `lru_cache` decorated + # function because adding `lru_cache` to methods will leak memory + # https://github.com/python/cpython/issues/88476 + return platform_headers(self._version, platform=self._platform) def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. @@ -659,7 +696,8 @@ def _calculate_retry_timeout( if retry_after is not None and 0 < retry_after <= 60: return retry_after - nb_retries = max_retries - remaining_retries + # Also cap retry count to 1000 to avoid any potential overflows with `pow` + nb_retries = min(max_retries - remaining_retries, 1000) # Apply exponential backoff, but not more than the max. sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) @@ -708,8 +746,31 @@ def _idempotency_key(self) -> str: return f"stainless-python-retry-{uuid.uuid4()}" -class SyncHttpxClientWrapper(httpx.Client): +class _DefaultHttpxClient(httpx.Client): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultHttpxClient = httpx.Client + """An alias to `httpx.Client` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.Client` will result in httpx's defaults being used, not ours. + """ +else: + DefaultHttpxClient = _DefaultHttpxClient + + +class SyncHttpxClientWrapper(DefaultHttpxClient): def __del__(self) -> None: + if self.is_closed: + return + try: self.close() except Exception: @@ -727,43 +788,11 @@ def __init__( base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: Transport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, _strict_response_validation: bool, ) -> None: - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_LIMITS - - if transport is not None: - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -777,14 +806,16 @@ def __init__( else: timeout = DEFAULT_TIMEOUT + if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" + ) + super().__init__( version=version, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, base_url=base_url, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -794,10 +825,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, - limits=limits, - follow_redirects=True, ) def is_closed(self) -> bool: @@ -827,9 +854,9 @@ def __exit__( def _prepare_options( self, options: FinalRequestOptions, # noqa: ARG002 - ) -> None: + ) -> FinalRequestOptions: """Hook for mutating the given options""" - return None + return options def _prepare_request( self, @@ -851,8 +878,7 @@ def request( *, stream: Literal[True], stream_cls: Type[_StreamT], - ) -> _StreamT: - ... + ) -> _StreamT: ... @overload def request( @@ -862,8 +888,7 @@ def request( remaining_retries: Optional[int] = None, *, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload def request( @@ -874,8 +899,7 @@ def request( *, stream: bool = False, stream_cls: Type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - ... + ) -> ResponseT | _StreamT: ... def request( self, @@ -886,12 +910,17 @@ def request( stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: + if remaining_retries is not None: + retries_taken = options.get_max_retries(self.max_retries) - remaining_retries + else: + retries_taken = 0 + return self._request( cast_to=cast_to, options=options, stream=stream, stream_cls=stream_cls, - remaining_retries=remaining_retries, + retries_taken=retries_taken, ) def _request( @@ -899,21 +928,28 @@ def _request( *, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: int | None, + retries_taken: int, stream: bool, stream_cls: type[_StreamT] | None, ) -> ResponseT | _StreamT: + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + cast_to = self._maybe_override_cast_to(cast_to, options) - self._prepare_options(options) + options = self._prepare_options(options) - retries = self._remaining_retries(remaining_retries, options) - request = self._build_request(options) + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + request = self._build_request(options, retries_taken=retries_taken) self._prepare_request(request) kwargs: HttpxSendArgs = {} if self.custom_auth is not None: kwargs["auth"] = self.custom_auth + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + try: response = self._client.send( request, @@ -923,11 +959,11 @@ def _request( except httpx.TimeoutException as err: log.debug("Encountered httpx.TimeoutException", exc_info=True) - if retries > 0: + if remaining_retries > 0: return self._retry_request( - options, + input_options, cast_to, - retries, + retries_taken=retries_taken, stream=stream, stream_cls=stream_cls, response_headers=None, @@ -938,11 +974,11 @@ def _request( except Exception as err: log.debug("Encountered Exception", exc_info=True) - if retries > 0: + if remaining_retries > 0: return self._retry_request( - options, + input_options, cast_to, - retries, + retries_taken=retries_taken, stream=stream, stream_cls=stream_cls, response_headers=None, @@ -952,7 +988,12 @@ def _request( raise APIConnectionError(request=request) from err log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, ) try: @@ -960,13 +1001,13 @@ def _request( except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - if retries > 0 and self._should_retry(err.response): + if remaining_retries > 0 and self._should_retry(err.response): err.response.close() return self._retry_request( - options, + input_options, cast_to, - retries, - err.response.headers, + retries_taken=retries_taken, + response_headers=err.response.headers, stream=stream, stream_cls=stream_cls, ) @@ -985,25 +1026,26 @@ def _request( response=response, stream=stream, stream_cls=stream_cls, + retries_taken=retries_taken, ) def _retry_request( self, options: FinalRequestOptions, cast_to: Type[ResponseT], - remaining_retries: int, - response_headers: httpx.Headers | None, *, + retries_taken: int, + response_headers: httpx.Headers | None, stream: bool, stream_cls: type[_StreamT] | None, ) -> ResponseT | _StreamT: - remaining = remaining_retries - 1 - if remaining == 1: + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + if remaining_retries == 1: log.debug("1 retry left") else: - log.debug("%i retries left", remaining) + log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout(remaining, options, response_headers) + timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) log.info("Retrying request to %s in %f seconds", options.url, timeout) # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a @@ -1013,7 +1055,7 @@ def _retry_request( return self._request( options=options, cast_to=cast_to, - remaining_retries=remaining, + retries_taken=retries_taken + 1, stream=stream, stream_cls=stream_cls, ) @@ -1026,6 +1068,7 @@ def _process_response( response: httpx.Response, stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, ) -> ResponseT: if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": return cast( @@ -1037,6 +1080,7 @@ def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ), ) @@ -1056,6 +1100,7 @@ def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ), ) @@ -1069,6 +1114,7 @@ def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ) if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): return cast(ResponseT, api_response) @@ -1101,8 +1147,7 @@ def get( cast_to: Type[ResponseT], options: RequestOptions = {}, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload def get( @@ -1113,8 +1158,7 @@ def get( options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_StreamT], - ) -> _StreamT: - ... + ) -> _StreamT: ... @overload def get( @@ -1125,8 +1169,7 @@ def get( options: RequestOptions = {}, stream: bool, stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - ... + ) -> ResponseT | _StreamT: ... def get( self, @@ -1152,8 +1195,7 @@ def post( options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload def post( @@ -1166,8 +1208,7 @@ def post( files: RequestFiles | None = None, stream: Literal[True], stream_cls: type[_StreamT], - ) -> _StreamT: - ... + ) -> _StreamT: ... @overload def post( @@ -1180,8 +1221,7 @@ def post( files: RequestFiles | None = None, stream: bool, stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - ... + ) -> ResponseT | _StreamT: ... def post( self, @@ -1249,8 +1289,31 @@ def get_api_list( return self._request_api_list(model, page, opts) -class AsyncHttpxClientWrapper(httpx.AsyncClient): +class _DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultAsyncHttpxClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.AsyncClient` will result in httpx's defaults being used, not ours. + """ +else: + DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + + +class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): def __del__(self) -> None: + if self.is_closed: + return + try: # TODO(someday): support non asyncio runtimes here asyncio.get_running_loop().create_task(self.aclose()) @@ -1270,42 +1333,10 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: AsyncTransport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_LIMITS - - if transport is not None: - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -1319,14 +1350,16 @@ def __init__( else: timeout = DEFAULT_TIMEOUT + if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" + ) + super().__init__( version=version, base_url=base_url, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -1336,10 +1369,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, - limits=limits, - follow_redirects=True, ) def is_closed(self) -> bool: @@ -1366,9 +1395,9 @@ async def __aexit__( async def _prepare_options( self, options: FinalRequestOptions, # noqa: ARG002 - ) -> None: + ) -> FinalRequestOptions: """Hook for mutating the given options""" - return None + return options async def _prepare_request( self, @@ -1389,8 +1418,7 @@ async def request( *, stream: Literal[False] = False, remaining_retries: Optional[int] = None, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload async def request( @@ -1401,8 +1429,7 @@ async def request( stream: Literal[True], stream_cls: type[_AsyncStreamT], remaining_retries: Optional[int] = None, - ) -> _AsyncStreamT: - ... + ) -> _AsyncStreamT: ... @overload async def request( @@ -1413,8 +1440,7 @@ async def request( stream: bool, stream_cls: type[_AsyncStreamT] | None = None, remaining_retries: Optional[int] = None, - ) -> ResponseT | _AsyncStreamT: - ... + ) -> ResponseT | _AsyncStreamT: ... async def request( self, @@ -1425,12 +1451,17 @@ async def request( stream_cls: type[_AsyncStreamT] | None = None, remaining_retries: Optional[int] = None, ) -> ResponseT | _AsyncStreamT: + if remaining_retries is not None: + retries_taken = options.get_max_retries(self.max_retries) - remaining_retries + else: + retries_taken = 0 + return await self._request( cast_to=cast_to, options=options, stream=stream, stream_cls=stream_cls, - remaining_retries=remaining_retries, + retries_taken=retries_taken, ) async def _request( @@ -1440,13 +1471,23 @@ async def _request( *, stream: bool, stream_cls: type[_AsyncStreamT] | None, - remaining_retries: int | None, + retries_taken: int, ) -> ResponseT | _AsyncStreamT: + if self._platform is None: + # `get_platform` can make blocking IO calls so we + # execute it earlier while we are in an async context + self._platform = await asyncify(get_platform)() + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + cast_to = self._maybe_override_cast_to(cast_to, options) - await self._prepare_options(options) + options = await self._prepare_options(options) - retries = self._remaining_retries(remaining_retries, options) - request = self._build_request(options) + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + request = self._build_request(options, retries_taken=retries_taken) await self._prepare_request(request) kwargs: HttpxSendArgs = {} @@ -1462,11 +1503,11 @@ async def _request( except httpx.TimeoutException as err: log.debug("Encountered httpx.TimeoutException", exc_info=True) - if retries > 0: + if remaining_retries > 0: return await self._retry_request( - options, + input_options, cast_to, - retries, + retries_taken=retries_taken, stream=stream, stream_cls=stream_cls, response_headers=None, @@ -1477,11 +1518,11 @@ async def _request( except Exception as err: log.debug("Encountered Exception", exc_info=True) - if retries > 0: + if remaining_retries > 0: return await self._retry_request( - options, + input_options, cast_to, - retries, + retries_taken=retries_taken, stream=stream, stream_cls=stream_cls, response_headers=None, @@ -1499,13 +1540,13 @@ async def _request( except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - if retries > 0 and self._should_retry(err.response): + if remaining_retries > 0 and self._should_retry(err.response): await err.response.aclose() return await self._retry_request( - options, + input_options, cast_to, - retries, - err.response.headers, + retries_taken=retries_taken, + response_headers=err.response.headers, stream=stream, stream_cls=stream_cls, ) @@ -1524,25 +1565,26 @@ async def _request( response=response, stream=stream, stream_cls=stream_cls, + retries_taken=retries_taken, ) async def _retry_request( self, options: FinalRequestOptions, cast_to: Type[ResponseT], - remaining_retries: int, - response_headers: httpx.Headers | None, *, + retries_taken: int, + response_headers: httpx.Headers | None, stream: bool, stream_cls: type[_AsyncStreamT] | None, ) -> ResponseT | _AsyncStreamT: - remaining = remaining_retries - 1 - if remaining == 1: + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + if remaining_retries == 1: log.debug("1 retry left") else: - log.debug("%i retries left", remaining) + log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout(remaining, options, response_headers) + timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) log.info("Retrying request to %s in %f seconds", options.url, timeout) await anyio.sleep(timeout) @@ -1550,7 +1592,7 @@ async def _retry_request( return await self._request( options=options, cast_to=cast_to, - remaining_retries=remaining, + retries_taken=retries_taken + 1, stream=stream, stream_cls=stream_cls, ) @@ -1563,6 +1605,7 @@ async def _process_response( response: httpx.Response, stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, ) -> ResponseT: if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": return cast( @@ -1574,6 +1617,7 @@ async def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ), ) @@ -1593,6 +1637,7 @@ async def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ), ) @@ -1606,6 +1651,7 @@ async def _process_response( stream=stream, stream_cls=stream_cls, options=options, + retries_taken=retries_taken, ) if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): return cast(ResponseT, api_response) @@ -1628,8 +1674,7 @@ async def get( cast_to: Type[ResponseT], options: RequestOptions = {}, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload async def get( @@ -1640,8 +1685,7 @@ async def get( options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_AsyncStreamT], - ) -> _AsyncStreamT: - ... + ) -> _AsyncStreamT: ... @overload async def get( @@ -1652,8 +1696,7 @@ async def get( options: RequestOptions = {}, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: - ... + ) -> ResponseT | _AsyncStreamT: ... async def get( self, @@ -1677,8 +1720,7 @@ async def post( files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[False] = False, - ) -> ResponseT: - ... + ) -> ResponseT: ... @overload async def post( @@ -1691,8 +1733,7 @@ async def post( options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_AsyncStreamT], - ) -> _AsyncStreamT: - ... + ) -> _AsyncStreamT: ... @overload async def post( @@ -1705,8 +1746,7 @@ async def post( options: RequestOptions = {}, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: - ... + ) -> ResponseT | _AsyncStreamT: ... async def post( self, @@ -1811,6 +1851,11 @@ def make_request_options( return options +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + class OtherPlatform: def __init__(self, name: str) -> None: self.name = name @@ -1878,11 +1923,11 @@ def get_platform() -> Platform: @lru_cache(maxsize=None) -def platform_headers(version: str) -> Dict[str, str]: +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: return { "X-Stainless-Lang": "python", "X-Stainless-Package-Version": version, - "X-Stainless-OS": str(get_platform()), + "X-Stainless-OS": str(platform or get_platform()), "X-Stainless-Arch": str(get_architecture()), "X-Stainless-Runtime": get_python_runtime(), "X-Stainless-Runtime-Version": get_python_version(), @@ -1917,7 +1962,6 @@ def get_python_version() -> str: def get_architecture() -> Arch: try: - python_bitness, _ = platform.architecture() machine = platform.machine().lower() except Exception: return "unknown" @@ -1933,7 +1977,7 @@ def get_architecture() -> Arch: return "x64" # TODO: untested - if python_bitness == "32bit": + if sys.maxsize <= 2**32: return "x32" if machine: diff --git a/src/lithic/_client.py b/src/lithic/_client.py index 3c0fda1a..65d0fc97 100644 --- a/src/lithic/_client.py +++ b/src/lithic/_client.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -8,9 +8,8 @@ import httpx -from . import resources, _exceptions, _legacy_response +from . import _exceptions, _legacy_response from ._qs import Querystring -from .types import APIStatus from ._types import ( NOT_GIVEN, Body, @@ -21,7 +20,6 @@ NotGiven, Transport, ProxiesTypes, - AsyncTransport, RequestOptions, ) from ._utils import ( @@ -30,17 +28,41 @@ ) from ._version import __version__ from ._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .resources import ( + accounts, + balances, + disputes, + payments, + card_programs, + tokenizations, + book_transfers, + account_holders, + digital_card_art, + external_payments, + aggregate_balances, + responder_endpoints, + management_operations, + auth_stream_enrollment, + tokenization_decisioning, +) from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import LithicError, APIStatusError from ._base_client import ( - DEFAULT_LIMITS, DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient, - SyncHttpxClientWrapper, - AsyncHttpxClientWrapper, make_request_options, ) +from .resources.cards import cards +from .resources.events import events +from .types.api_status import APIStatus +from .resources.reports import reports +from .resources.three_ds import three_ds +from .resources.auth_rules import auth_rules +from .resources.transactions import transactions +from .resources.credit_products import credit_products +from .resources.financial_accounts import financial_accounts +from .resources.external_bank_accounts import external_bank_accounts __all__ = [ "ENVIRONMENTS", @@ -48,7 +70,6 @@ "Transport", "ProxiesTypes", "RequestOptions", - "resources", "Lithic", "AsyncLithic", "Client", @@ -56,34 +77,36 @@ ] ENVIRONMENTS: Dict[str, str] = { - "production": "https://api.lithic.com/v1", - "sandbox": "https://sandbox.lithic.com/v1", + "production": "https://api.lithic.com", + "sandbox": "https://sandbox.lithic.com", } class Lithic(SyncAPIClient): - accounts: resources.Accounts - account_holders: resources.AccountHolders - auth_rules: resources.AuthRules - auth_stream_enrollment: resources.AuthStreamEnrollment - tokenization_decisioning: resources.TokenizationDecisioning - tokenizations: resources.Tokenizations - cards: resources.Cards - balances: resources.Balances - aggregate_balances: resources.AggregateBalances - disputes: resources.Disputes - events: resources.Events - financial_accounts: resources.FinancialAccounts - transactions: resources.Transactions - responder_endpoints: resources.ResponderEndpoints - webhooks: resources.Webhooks - external_bank_accounts: resources.ExternalBankAccounts - payments: resources.Payments - three_ds: resources.ThreeDS - reports: resources.Reports - card_product: resources.CardProduct - card_programs: resources.CardPrograms - digital_card_art: resources.DigitalCardArtResource + accounts: accounts.Accounts + account_holders: account_holders.AccountHolders + auth_rules: auth_rules.AuthRules + auth_stream_enrollment: auth_stream_enrollment.AuthStreamEnrollment + tokenization_decisioning: tokenization_decisioning.TokenizationDecisioning + tokenizations: tokenizations.Tokenizations + cards: cards.Cards + balances: balances.Balances + aggregate_balances: aggregate_balances.AggregateBalances + disputes: disputes.Disputes + events: events.Events + financial_accounts: financial_accounts.FinancialAccounts + transactions: transactions.Transactions + responder_endpoints: responder_endpoints.ResponderEndpoints + external_bank_accounts: external_bank_accounts.ExternalBankAccounts + payments: payments.Payments + three_ds: three_ds.ThreeDS + reports: reports.Reports + card_programs: card_programs.CardPrograms + digital_card_art: digital_card_art.DigitalCardArtResource + book_transfers: book_transfers.BookTransfers + credit_products: credit_products.CreditProducts + external_payments: external_payments.ExternalPayments + management_operations: management_operations.ManagementOperations with_raw_response: LithicWithRawResponse with_streaming_response: LithicWithStreamedResponse @@ -104,14 +127,10 @@ def __init__( max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + # Configure a custom httpx client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. http_client: httpx.Client | None = None, - # See httpx documentation for [custom transports](https://www.python-httpx.org/advanced/#custom-transports) - transport: Transport | None = None, - # See httpx documentation for [proxies](https://www.python-httpx.org/advanced/#http-proxying) - proxies: ProxiesTypes | None = None, - # See httpx documentation for [limits](https://www.python-httpx.org/advanced/#pool-limit-configuration) - connection_pool_limits: httpx.Limits | None = None, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised # if the API responds with invalid data for the expected schema. @@ -122,7 +141,7 @@ def __init__( # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: - """Construct a new synchronous lithic client instance. + """Construct a new synchronous Lithic client instance. This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `LITHIC_API_KEY` @@ -172,36 +191,35 @@ def __init__( max_retries=max_retries, timeout=timeout, http_client=http_client, - transport=transport, - proxies=proxies, - limits=connection_pool_limits, custom_headers=default_headers, custom_query=default_query, _strict_response_validation=_strict_response_validation, ) - self.accounts = resources.Accounts(self) - self.account_holders = resources.AccountHolders(self) - self.auth_rules = resources.AuthRules(self) - self.auth_stream_enrollment = resources.AuthStreamEnrollment(self) - self.tokenization_decisioning = resources.TokenizationDecisioning(self) - self.tokenizations = resources.Tokenizations(self) - self.cards = resources.Cards(self) - self.balances = resources.Balances(self) - self.aggregate_balances = resources.AggregateBalances(self) - self.disputes = resources.Disputes(self) - self.events = resources.Events(self) - self.financial_accounts = resources.FinancialAccounts(self) - self.transactions = resources.Transactions(self) - self.responder_endpoints = resources.ResponderEndpoints(self) - self.webhooks = resources.Webhooks(self) - self.external_bank_accounts = resources.ExternalBankAccounts(self) - self.payments = resources.Payments(self) - self.three_ds = resources.ThreeDS(self) - self.reports = resources.Reports(self) - self.card_product = resources.CardProduct(self) - self.card_programs = resources.CardPrograms(self) - self.digital_card_art = resources.DigitalCardArtResource(self) + self.accounts = accounts.Accounts(self) + self.account_holders = account_holders.AccountHolders(self) + self.auth_rules = auth_rules.AuthRules(self) + self.auth_stream_enrollment = auth_stream_enrollment.AuthStreamEnrollment(self) + self.tokenization_decisioning = tokenization_decisioning.TokenizationDecisioning(self) + self.tokenizations = tokenizations.Tokenizations(self) + self.cards = cards.Cards(self) + self.balances = balances.Balances(self) + self.aggregate_balances = aggregate_balances.AggregateBalances(self) + self.disputes = disputes.Disputes(self) + self.events = events.Events(self) + self.financial_accounts = financial_accounts.FinancialAccounts(self) + self.transactions = transactions.Transactions(self) + self.responder_endpoints = responder_endpoints.ResponderEndpoints(self) + self.external_bank_accounts = external_bank_accounts.ExternalBankAccounts(self) + self.payments = payments.Payments(self) + self.three_ds = three_ds.ThreeDS(self) + self.reports = reports.Reports(self) + self.card_programs = card_programs.CardPrograms(self) + self.digital_card_art = digital_card_art.DigitalCardArtResource(self) + self.book_transfers = book_transfers.BookTransfers(self) + self.credit_products = credit_products.CreditProducts(self) + self.external_payments = external_payments.ExternalPayments(self) + self.management_operations = management_operations.ManagementOperations(self) self.with_raw_response = LithicWithRawResponse(self) self.with_streaming_response = LithicWithStreamedResponse(self) @@ -235,7 +253,6 @@ def copy( base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, - connection_pool_limits: httpx.Limits | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, @@ -264,24 +281,7 @@ def copy( elif set_default_query is not None: params = set_default_query - if connection_pool_limits is not None: - if http_client is not None: - raise ValueError("The 'http_client' argument is mutually exclusive with 'connection_pool_limits'") - - if not isinstance(self._client, SyncHttpxClientWrapper): - raise ValueError( - "A custom HTTP client has been set and is mutually exclusive with the 'connection_pool_limits' argument" - ) - - http_client = None - else: - if self._limits is not DEFAULT_LIMITS: - connection_pool_limits = self._limits - else: - connection_pool_limits = None - - http_client = http_client or self._client - + http_client = http_client or self._client return self.__class__( api_key=api_key or self.api_key, webhook_secret=webhook_secret or self.webhook_secret, @@ -289,7 +289,6 @@ def copy( environment=environment or self._environment, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, - connection_pool_limits=connection_pool_limits, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, @@ -312,7 +311,7 @@ def api_status( ) -> APIStatus: """Status of api""" return self.get( - "/status", + "/v1/status", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -354,28 +353,30 @@ def _make_status_error( class AsyncLithic(AsyncAPIClient): - accounts: resources.AsyncAccounts - account_holders: resources.AsyncAccountHolders - auth_rules: resources.AsyncAuthRules - auth_stream_enrollment: resources.AsyncAuthStreamEnrollment - tokenization_decisioning: resources.AsyncTokenizationDecisioning - tokenizations: resources.AsyncTokenizations - cards: resources.AsyncCards - balances: resources.AsyncBalances - aggregate_balances: resources.AsyncAggregateBalances - disputes: resources.AsyncDisputes - events: resources.AsyncEvents - financial_accounts: resources.AsyncFinancialAccounts - transactions: resources.AsyncTransactions - responder_endpoints: resources.AsyncResponderEndpoints - webhooks: resources.AsyncWebhooks - external_bank_accounts: resources.AsyncExternalBankAccounts - payments: resources.AsyncPayments - three_ds: resources.AsyncThreeDS - reports: resources.AsyncReports - card_product: resources.AsyncCardProduct - card_programs: resources.AsyncCardPrograms - digital_card_art: resources.AsyncDigitalCardArtResource + accounts: accounts.AsyncAccounts + account_holders: account_holders.AsyncAccountHolders + auth_rules: auth_rules.AsyncAuthRules + auth_stream_enrollment: auth_stream_enrollment.AsyncAuthStreamEnrollment + tokenization_decisioning: tokenization_decisioning.AsyncTokenizationDecisioning + tokenizations: tokenizations.AsyncTokenizations + cards: cards.AsyncCards + balances: balances.AsyncBalances + aggregate_balances: aggregate_balances.AsyncAggregateBalances + disputes: disputes.AsyncDisputes + events: events.AsyncEvents + financial_accounts: financial_accounts.AsyncFinancialAccounts + transactions: transactions.AsyncTransactions + responder_endpoints: responder_endpoints.AsyncResponderEndpoints + external_bank_accounts: external_bank_accounts.AsyncExternalBankAccounts + payments: payments.AsyncPayments + three_ds: three_ds.AsyncThreeDS + reports: reports.AsyncReports + card_programs: card_programs.AsyncCardPrograms + digital_card_art: digital_card_art.AsyncDigitalCardArtResource + book_transfers: book_transfers.AsyncBookTransfers + credit_products: credit_products.AsyncCreditProducts + external_payments: external_payments.AsyncExternalPayments + management_operations: management_operations.AsyncManagementOperations with_raw_response: AsyncLithicWithRawResponse with_streaming_response: AsyncLithicWithStreamedResponse @@ -396,14 +397,10 @@ def __init__( max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + # Configure a custom httpx client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. http_client: httpx.AsyncClient | None = None, - # See httpx documentation for [custom transports](https://www.python-httpx.org/advanced/#custom-transports) - transport: AsyncTransport | None = None, - # See httpx documentation for [proxies](https://www.python-httpx.org/advanced/#http-proxying) - proxies: ProxiesTypes | None = None, - # See httpx documentation for [limits](https://www.python-httpx.org/advanced/#pool-limit-configuration) - connection_pool_limits: httpx.Limits | None = None, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised # if the API responds with invalid data for the expected schema. @@ -414,7 +411,7 @@ def __init__( # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: - """Construct a new async lithic client instance. + """Construct a new async AsyncLithic client instance. This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `LITHIC_API_KEY` @@ -464,36 +461,35 @@ def __init__( max_retries=max_retries, timeout=timeout, http_client=http_client, - transport=transport, - proxies=proxies, - limits=connection_pool_limits, custom_headers=default_headers, custom_query=default_query, _strict_response_validation=_strict_response_validation, ) - self.accounts = resources.AsyncAccounts(self) - self.account_holders = resources.AsyncAccountHolders(self) - self.auth_rules = resources.AsyncAuthRules(self) - self.auth_stream_enrollment = resources.AsyncAuthStreamEnrollment(self) - self.tokenization_decisioning = resources.AsyncTokenizationDecisioning(self) - self.tokenizations = resources.AsyncTokenizations(self) - self.cards = resources.AsyncCards(self) - self.balances = resources.AsyncBalances(self) - self.aggregate_balances = resources.AsyncAggregateBalances(self) - self.disputes = resources.AsyncDisputes(self) - self.events = resources.AsyncEvents(self) - self.financial_accounts = resources.AsyncFinancialAccounts(self) - self.transactions = resources.AsyncTransactions(self) - self.responder_endpoints = resources.AsyncResponderEndpoints(self) - self.webhooks = resources.AsyncWebhooks(self) - self.external_bank_accounts = resources.AsyncExternalBankAccounts(self) - self.payments = resources.AsyncPayments(self) - self.three_ds = resources.AsyncThreeDS(self) - self.reports = resources.AsyncReports(self) - self.card_product = resources.AsyncCardProduct(self) - self.card_programs = resources.AsyncCardPrograms(self) - self.digital_card_art = resources.AsyncDigitalCardArtResource(self) + self.accounts = accounts.AsyncAccounts(self) + self.account_holders = account_holders.AsyncAccountHolders(self) + self.auth_rules = auth_rules.AsyncAuthRules(self) + self.auth_stream_enrollment = auth_stream_enrollment.AsyncAuthStreamEnrollment(self) + self.tokenization_decisioning = tokenization_decisioning.AsyncTokenizationDecisioning(self) + self.tokenizations = tokenizations.AsyncTokenizations(self) + self.cards = cards.AsyncCards(self) + self.balances = balances.AsyncBalances(self) + self.aggregate_balances = aggregate_balances.AsyncAggregateBalances(self) + self.disputes = disputes.AsyncDisputes(self) + self.events = events.AsyncEvents(self) + self.financial_accounts = financial_accounts.AsyncFinancialAccounts(self) + self.transactions = transactions.AsyncTransactions(self) + self.responder_endpoints = responder_endpoints.AsyncResponderEndpoints(self) + self.external_bank_accounts = external_bank_accounts.AsyncExternalBankAccounts(self) + self.payments = payments.AsyncPayments(self) + self.three_ds = three_ds.AsyncThreeDS(self) + self.reports = reports.AsyncReports(self) + self.card_programs = card_programs.AsyncCardPrograms(self) + self.digital_card_art = digital_card_art.AsyncDigitalCardArtResource(self) + self.book_transfers = book_transfers.AsyncBookTransfers(self) + self.credit_products = credit_products.AsyncCreditProducts(self) + self.external_payments = external_payments.AsyncExternalPayments(self) + self.management_operations = management_operations.AsyncManagementOperations(self) self.with_raw_response = AsyncLithicWithRawResponse(self) self.with_streaming_response = AsyncLithicWithStreamedResponse(self) @@ -527,7 +523,6 @@ def copy( base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, - connection_pool_limits: httpx.Limits | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, @@ -556,24 +551,7 @@ def copy( elif set_default_query is not None: params = set_default_query - if connection_pool_limits is not None: - if http_client is not None: - raise ValueError("The 'http_client' argument is mutually exclusive with 'connection_pool_limits'") - - if not isinstance(self._client, AsyncHttpxClientWrapper): - raise ValueError( - "A custom HTTP client has been set and is mutually exclusive with the 'connection_pool_limits' argument" - ) - - http_client = None - else: - if self._limits is not DEFAULT_LIMITS: - connection_pool_limits = self._limits - else: - connection_pool_limits = None - - http_client = http_client or self._client - + http_client = http_client or self._client return self.__class__( api_key=api_key or self.api_key, webhook_secret=webhook_secret or self.webhook_secret, @@ -581,7 +559,6 @@ def copy( environment=environment or self._environment, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, - connection_pool_limits=connection_pool_limits, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, @@ -604,7 +581,7 @@ async def api_status( ) -> APIStatus: """Status of api""" return await self.get( - "/status", + "/v1/status", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -647,29 +624,38 @@ def _make_status_error( class LithicWithRawResponse: def __init__(self, client: Lithic) -> None: - self.accounts = resources.AccountsWithRawResponse(client.accounts) - self.account_holders = resources.AccountHoldersWithRawResponse(client.account_holders) - self.auth_rules = resources.AuthRulesWithRawResponse(client.auth_rules) - self.auth_stream_enrollment = resources.AuthStreamEnrollmentWithRawResponse(client.auth_stream_enrollment) - self.tokenization_decisioning = resources.TokenizationDecisioningWithRawResponse( + self.accounts = accounts.AccountsWithRawResponse(client.accounts) + self.account_holders = account_holders.AccountHoldersWithRawResponse(client.account_holders) + self.auth_rules = auth_rules.AuthRulesWithRawResponse(client.auth_rules) + self.auth_stream_enrollment = auth_stream_enrollment.AuthStreamEnrollmentWithRawResponse( + client.auth_stream_enrollment + ) + self.tokenization_decisioning = tokenization_decisioning.TokenizationDecisioningWithRawResponse( client.tokenization_decisioning ) - self.tokenizations = resources.TokenizationsWithRawResponse(client.tokenizations) - self.cards = resources.CardsWithRawResponse(client.cards) - self.balances = resources.BalancesWithRawResponse(client.balances) - self.aggregate_balances = resources.AggregateBalancesWithRawResponse(client.aggregate_balances) - self.disputes = resources.DisputesWithRawResponse(client.disputes) - self.events = resources.EventsWithRawResponse(client.events) - self.financial_accounts = resources.FinancialAccountsWithRawResponse(client.financial_accounts) - self.transactions = resources.TransactionsWithRawResponse(client.transactions) - self.responder_endpoints = resources.ResponderEndpointsWithRawResponse(client.responder_endpoints) - self.external_bank_accounts = resources.ExternalBankAccountsWithRawResponse(client.external_bank_accounts) - self.payments = resources.PaymentsWithRawResponse(client.payments) - self.three_ds = resources.ThreeDSWithRawResponse(client.three_ds) - self.reports = resources.ReportsWithRawResponse(client.reports) - self.card_product = resources.CardProductWithRawResponse(client.card_product) - self.card_programs = resources.CardProgramsWithRawResponse(client.card_programs) - self.digital_card_art = resources.DigitalCardArtResourceWithRawResponse(client.digital_card_art) + self.tokenizations = tokenizations.TokenizationsWithRawResponse(client.tokenizations) + self.cards = cards.CardsWithRawResponse(client.cards) + self.balances = balances.BalancesWithRawResponse(client.balances) + self.aggregate_balances = aggregate_balances.AggregateBalancesWithRawResponse(client.aggregate_balances) + self.disputes = disputes.DisputesWithRawResponse(client.disputes) + self.events = events.EventsWithRawResponse(client.events) + self.financial_accounts = financial_accounts.FinancialAccountsWithRawResponse(client.financial_accounts) + self.transactions = transactions.TransactionsWithRawResponse(client.transactions) + self.responder_endpoints = responder_endpoints.ResponderEndpointsWithRawResponse(client.responder_endpoints) + self.external_bank_accounts = external_bank_accounts.ExternalBankAccountsWithRawResponse( + client.external_bank_accounts + ) + self.payments = payments.PaymentsWithRawResponse(client.payments) + self.three_ds = three_ds.ThreeDSWithRawResponse(client.three_ds) + self.reports = reports.ReportsWithRawResponse(client.reports) + self.card_programs = card_programs.CardProgramsWithRawResponse(client.card_programs) + self.digital_card_art = digital_card_art.DigitalCardArtResourceWithRawResponse(client.digital_card_art) + self.book_transfers = book_transfers.BookTransfersWithRawResponse(client.book_transfers) + self.credit_products = credit_products.CreditProductsWithRawResponse(client.credit_products) + self.external_payments = external_payments.ExternalPaymentsWithRawResponse(client.external_payments) + self.management_operations = management_operations.ManagementOperationsWithRawResponse( + client.management_operations + ) self.api_status = _legacy_response.to_raw_response_wrapper( client.api_status, @@ -678,29 +664,40 @@ def __init__(self, client: Lithic) -> None: class AsyncLithicWithRawResponse: def __init__(self, client: AsyncLithic) -> None: - self.accounts = resources.AsyncAccountsWithRawResponse(client.accounts) - self.account_holders = resources.AsyncAccountHoldersWithRawResponse(client.account_holders) - self.auth_rules = resources.AsyncAuthRulesWithRawResponse(client.auth_rules) - self.auth_stream_enrollment = resources.AsyncAuthStreamEnrollmentWithRawResponse(client.auth_stream_enrollment) - self.tokenization_decisioning = resources.AsyncTokenizationDecisioningWithRawResponse( + self.accounts = accounts.AsyncAccountsWithRawResponse(client.accounts) + self.account_holders = account_holders.AsyncAccountHoldersWithRawResponse(client.account_holders) + self.auth_rules = auth_rules.AsyncAuthRulesWithRawResponse(client.auth_rules) + self.auth_stream_enrollment = auth_stream_enrollment.AsyncAuthStreamEnrollmentWithRawResponse( + client.auth_stream_enrollment + ) + self.tokenization_decisioning = tokenization_decisioning.AsyncTokenizationDecisioningWithRawResponse( client.tokenization_decisioning ) - self.tokenizations = resources.AsyncTokenizationsWithRawResponse(client.tokenizations) - self.cards = resources.AsyncCardsWithRawResponse(client.cards) - self.balances = resources.AsyncBalancesWithRawResponse(client.balances) - self.aggregate_balances = resources.AsyncAggregateBalancesWithRawResponse(client.aggregate_balances) - self.disputes = resources.AsyncDisputesWithRawResponse(client.disputes) - self.events = resources.AsyncEventsWithRawResponse(client.events) - self.financial_accounts = resources.AsyncFinancialAccountsWithRawResponse(client.financial_accounts) - self.transactions = resources.AsyncTransactionsWithRawResponse(client.transactions) - self.responder_endpoints = resources.AsyncResponderEndpointsWithRawResponse(client.responder_endpoints) - self.external_bank_accounts = resources.AsyncExternalBankAccountsWithRawResponse(client.external_bank_accounts) - self.payments = resources.AsyncPaymentsWithRawResponse(client.payments) - self.three_ds = resources.AsyncThreeDSWithRawResponse(client.three_ds) - self.reports = resources.AsyncReportsWithRawResponse(client.reports) - self.card_product = resources.AsyncCardProductWithRawResponse(client.card_product) - self.card_programs = resources.AsyncCardProgramsWithRawResponse(client.card_programs) - self.digital_card_art = resources.AsyncDigitalCardArtResourceWithRawResponse(client.digital_card_art) + self.tokenizations = tokenizations.AsyncTokenizationsWithRawResponse(client.tokenizations) + self.cards = cards.AsyncCardsWithRawResponse(client.cards) + self.balances = balances.AsyncBalancesWithRawResponse(client.balances) + self.aggregate_balances = aggregate_balances.AsyncAggregateBalancesWithRawResponse(client.aggregate_balances) + self.disputes = disputes.AsyncDisputesWithRawResponse(client.disputes) + self.events = events.AsyncEventsWithRawResponse(client.events) + self.financial_accounts = financial_accounts.AsyncFinancialAccountsWithRawResponse(client.financial_accounts) + self.transactions = transactions.AsyncTransactionsWithRawResponse(client.transactions) + self.responder_endpoints = responder_endpoints.AsyncResponderEndpointsWithRawResponse( + client.responder_endpoints + ) + self.external_bank_accounts = external_bank_accounts.AsyncExternalBankAccountsWithRawResponse( + client.external_bank_accounts + ) + self.payments = payments.AsyncPaymentsWithRawResponse(client.payments) + self.three_ds = three_ds.AsyncThreeDSWithRawResponse(client.three_ds) + self.reports = reports.AsyncReportsWithRawResponse(client.reports) + self.card_programs = card_programs.AsyncCardProgramsWithRawResponse(client.card_programs) + self.digital_card_art = digital_card_art.AsyncDigitalCardArtResourceWithRawResponse(client.digital_card_art) + self.book_transfers = book_transfers.AsyncBookTransfersWithRawResponse(client.book_transfers) + self.credit_products = credit_products.AsyncCreditProductsWithRawResponse(client.credit_products) + self.external_payments = external_payments.AsyncExternalPaymentsWithRawResponse(client.external_payments) + self.management_operations = management_operations.AsyncManagementOperationsWithRawResponse( + client.management_operations + ) self.api_status = _legacy_response.async_to_raw_response_wrapper( client.api_status, @@ -709,29 +706,40 @@ def __init__(self, client: AsyncLithic) -> None: class LithicWithStreamedResponse: def __init__(self, client: Lithic) -> None: - self.accounts = resources.AccountsWithStreamingResponse(client.accounts) - self.account_holders = resources.AccountHoldersWithStreamingResponse(client.account_holders) - self.auth_rules = resources.AuthRulesWithStreamingResponse(client.auth_rules) - self.auth_stream_enrollment = resources.AuthStreamEnrollmentWithStreamingResponse(client.auth_stream_enrollment) - self.tokenization_decisioning = resources.TokenizationDecisioningWithStreamingResponse( + self.accounts = accounts.AccountsWithStreamingResponse(client.accounts) + self.account_holders = account_holders.AccountHoldersWithStreamingResponse(client.account_holders) + self.auth_rules = auth_rules.AuthRulesWithStreamingResponse(client.auth_rules) + self.auth_stream_enrollment = auth_stream_enrollment.AuthStreamEnrollmentWithStreamingResponse( + client.auth_stream_enrollment + ) + self.tokenization_decisioning = tokenization_decisioning.TokenizationDecisioningWithStreamingResponse( client.tokenization_decisioning ) - self.tokenizations = resources.TokenizationsWithStreamingResponse(client.tokenizations) - self.cards = resources.CardsWithStreamingResponse(client.cards) - self.balances = resources.BalancesWithStreamingResponse(client.balances) - self.aggregate_balances = resources.AggregateBalancesWithStreamingResponse(client.aggregate_balances) - self.disputes = resources.DisputesWithStreamingResponse(client.disputes) - self.events = resources.EventsWithStreamingResponse(client.events) - self.financial_accounts = resources.FinancialAccountsWithStreamingResponse(client.financial_accounts) - self.transactions = resources.TransactionsWithStreamingResponse(client.transactions) - self.responder_endpoints = resources.ResponderEndpointsWithStreamingResponse(client.responder_endpoints) - self.external_bank_accounts = resources.ExternalBankAccountsWithStreamingResponse(client.external_bank_accounts) - self.payments = resources.PaymentsWithStreamingResponse(client.payments) - self.three_ds = resources.ThreeDSWithStreamingResponse(client.three_ds) - self.reports = resources.ReportsWithStreamingResponse(client.reports) - self.card_product = resources.CardProductWithStreamingResponse(client.card_product) - self.card_programs = resources.CardProgramsWithStreamingResponse(client.card_programs) - self.digital_card_art = resources.DigitalCardArtResourceWithStreamingResponse(client.digital_card_art) + self.tokenizations = tokenizations.TokenizationsWithStreamingResponse(client.tokenizations) + self.cards = cards.CardsWithStreamingResponse(client.cards) + self.balances = balances.BalancesWithStreamingResponse(client.balances) + self.aggregate_balances = aggregate_balances.AggregateBalancesWithStreamingResponse(client.aggregate_balances) + self.disputes = disputes.DisputesWithStreamingResponse(client.disputes) + self.events = events.EventsWithStreamingResponse(client.events) + self.financial_accounts = financial_accounts.FinancialAccountsWithStreamingResponse(client.financial_accounts) + self.transactions = transactions.TransactionsWithStreamingResponse(client.transactions) + self.responder_endpoints = responder_endpoints.ResponderEndpointsWithStreamingResponse( + client.responder_endpoints + ) + self.external_bank_accounts = external_bank_accounts.ExternalBankAccountsWithStreamingResponse( + client.external_bank_accounts + ) + self.payments = payments.PaymentsWithStreamingResponse(client.payments) + self.three_ds = three_ds.ThreeDSWithStreamingResponse(client.three_ds) + self.reports = reports.ReportsWithStreamingResponse(client.reports) + self.card_programs = card_programs.CardProgramsWithStreamingResponse(client.card_programs) + self.digital_card_art = digital_card_art.DigitalCardArtResourceWithStreamingResponse(client.digital_card_art) + self.book_transfers = book_transfers.BookTransfersWithStreamingResponse(client.book_transfers) + self.credit_products = credit_products.CreditProductsWithStreamingResponse(client.credit_products) + self.external_payments = external_payments.ExternalPaymentsWithStreamingResponse(client.external_payments) + self.management_operations = management_operations.ManagementOperationsWithStreamingResponse( + client.management_operations + ) self.api_status = to_streamed_response_wrapper( client.api_status, @@ -740,33 +748,46 @@ def __init__(self, client: Lithic) -> None: class AsyncLithicWithStreamedResponse: def __init__(self, client: AsyncLithic) -> None: - self.accounts = resources.AsyncAccountsWithStreamingResponse(client.accounts) - self.account_holders = resources.AsyncAccountHoldersWithStreamingResponse(client.account_holders) - self.auth_rules = resources.AsyncAuthRulesWithStreamingResponse(client.auth_rules) - self.auth_stream_enrollment = resources.AsyncAuthStreamEnrollmentWithStreamingResponse( + self.accounts = accounts.AsyncAccountsWithStreamingResponse(client.accounts) + self.account_holders = account_holders.AsyncAccountHoldersWithStreamingResponse(client.account_holders) + self.auth_rules = auth_rules.AsyncAuthRulesWithStreamingResponse(client.auth_rules) + self.auth_stream_enrollment = auth_stream_enrollment.AsyncAuthStreamEnrollmentWithStreamingResponse( client.auth_stream_enrollment ) - self.tokenization_decisioning = resources.AsyncTokenizationDecisioningWithStreamingResponse( + self.tokenization_decisioning = tokenization_decisioning.AsyncTokenizationDecisioningWithStreamingResponse( client.tokenization_decisioning ) - self.tokenizations = resources.AsyncTokenizationsWithStreamingResponse(client.tokenizations) - self.cards = resources.AsyncCardsWithStreamingResponse(client.cards) - self.balances = resources.AsyncBalancesWithStreamingResponse(client.balances) - self.aggregate_balances = resources.AsyncAggregateBalancesWithStreamingResponse(client.aggregate_balances) - self.disputes = resources.AsyncDisputesWithStreamingResponse(client.disputes) - self.events = resources.AsyncEventsWithStreamingResponse(client.events) - self.financial_accounts = resources.AsyncFinancialAccountsWithStreamingResponse(client.financial_accounts) - self.transactions = resources.AsyncTransactionsWithStreamingResponse(client.transactions) - self.responder_endpoints = resources.AsyncResponderEndpointsWithStreamingResponse(client.responder_endpoints) - self.external_bank_accounts = resources.AsyncExternalBankAccountsWithStreamingResponse( + self.tokenizations = tokenizations.AsyncTokenizationsWithStreamingResponse(client.tokenizations) + self.cards = cards.AsyncCardsWithStreamingResponse(client.cards) + self.balances = balances.AsyncBalancesWithStreamingResponse(client.balances) + self.aggregate_balances = aggregate_balances.AsyncAggregateBalancesWithStreamingResponse( + client.aggregate_balances + ) + self.disputes = disputes.AsyncDisputesWithStreamingResponse(client.disputes) + self.events = events.AsyncEventsWithStreamingResponse(client.events) + self.financial_accounts = financial_accounts.AsyncFinancialAccountsWithStreamingResponse( + client.financial_accounts + ) + self.transactions = transactions.AsyncTransactionsWithStreamingResponse(client.transactions) + self.responder_endpoints = responder_endpoints.AsyncResponderEndpointsWithStreamingResponse( + client.responder_endpoints + ) + self.external_bank_accounts = external_bank_accounts.AsyncExternalBankAccountsWithStreamingResponse( client.external_bank_accounts ) - self.payments = resources.AsyncPaymentsWithStreamingResponse(client.payments) - self.three_ds = resources.AsyncThreeDSWithStreamingResponse(client.three_ds) - self.reports = resources.AsyncReportsWithStreamingResponse(client.reports) - self.card_product = resources.AsyncCardProductWithStreamingResponse(client.card_product) - self.card_programs = resources.AsyncCardProgramsWithStreamingResponse(client.card_programs) - self.digital_card_art = resources.AsyncDigitalCardArtResourceWithStreamingResponse(client.digital_card_art) + self.payments = payments.AsyncPaymentsWithStreamingResponse(client.payments) + self.three_ds = three_ds.AsyncThreeDSWithStreamingResponse(client.three_ds) + self.reports = reports.AsyncReportsWithStreamingResponse(client.reports) + self.card_programs = card_programs.AsyncCardProgramsWithStreamingResponse(client.card_programs) + self.digital_card_art = digital_card_art.AsyncDigitalCardArtResourceWithStreamingResponse( + client.digital_card_art + ) + self.book_transfers = book_transfers.AsyncBookTransfersWithStreamingResponse(client.book_transfers) + self.credit_products = credit_products.AsyncCreditProductsWithStreamingResponse(client.credit_products) + self.external_payments = external_payments.AsyncExternalPaymentsWithStreamingResponse(client.external_payments) + self.management_operations = management_operations.AsyncManagementOperationsWithStreamingResponse( + client.management_operations + ) self.api_status = async_to_streamed_response_wrapper( client.api_status, diff --git a/src/lithic/_compat.py b/src/lithic/_compat.py index 74c7639b..92d9ee61 100644 --- a/src/lithic/_compat.py +++ b/src/lithic/_compat.py @@ -2,12 +2,12 @@ from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload from datetime import date, datetime -from typing_extensions import Self +from typing_extensions import Self, Literal import pydantic from pydantic.fields import FieldInfo -from ._types import StrBytesIntFloat +from ._types import IncEx, StrBytesIntFloat _T = TypeVar("_T") _ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) @@ -118,10 +118,10 @@ def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: return model.__fields__ # type: ignore -def model_copy(model: _ModelT) -> _ModelT: +def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: if PYDANTIC_V2: - return model.model_copy() - return model.copy() # type: ignore + return model.model_copy(deep=deep) + return model.copy(deep=deep) # type: ignore def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: @@ -133,17 +133,25 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: def model_dump( model: pydantic.BaseModel, *, + exclude: IncEx | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, + warnings: bool = True, + mode: Literal["json", "python"] = "python", ) -> dict[str, Any]: - if PYDANTIC_V2: + if PYDANTIC_V2 or hasattr(model, "model_dump"): return model.model_dump( + mode=mode, + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, + # warnings are not supported in Pydantic v1 + warnings=warnings if PYDANTIC_V2 else True, ) return cast( "dict[str, Any]", model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, ), @@ -159,22 +167,19 @@ def model_parse(model: type[_ModelT], data: Any) -> _ModelT: # generic models if TYPE_CHECKING: - class GenericModel(pydantic.BaseModel): - ... + class GenericModel(pydantic.BaseModel): ... else: if PYDANTIC_V2: # there no longer needs to be a distinction in v2 but # we still have to create our own subclass to avoid # inconsistent MRO ordering errors - class GenericModel(pydantic.BaseModel): - ... + class GenericModel(pydantic.BaseModel): ... else: import pydantic.generics - class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): - ... + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... # cached properties @@ -193,30 +198,22 @@ class typed_cached_property(Generic[_T]): func: Callable[[Any], _T] attrname: str | None - def __init__(self, func: Callable[[Any], _T]) -> None: - ... + def __init__(self, func: Callable[[Any], _T]) -> None: ... @overload - def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: - ... + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... @overload - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: - ... + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: raise NotImplementedError() - def __set_name__(self, owner: type[Any], name: str) -> None: - ... + def __set_name__(self, owner: type[Any], name: str) -> None: ... # __set__ is not defined at runtime, but @cached_property is designed to be settable - def __set__(self, instance: object, value: _T) -> None: - ... + def __set__(self, instance: object, value: _T) -> None: ... else: - try: - from functools import cached_property as cached_property - except ImportError: - from cached_property import cached_property as cached_property + from functools import cached_property as cached_property typed_cached_property = cached_property diff --git a/src/lithic/_constants.py b/src/lithic/_constants.py index bf15141a..6ddf2c71 100644 --- a/src/lithic/_constants.py +++ b/src/lithic/_constants.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import httpx @@ -6,9 +6,9 @@ OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" # default timeout is 1 minute -DEFAULT_TIMEOUT = httpx.Timeout(timeout=60.0, connect=5.0) +DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0) DEFAULT_MAX_RETRIES = 2 -DEFAULT_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) INITIAL_RETRY_DELAY = 0.5 MAX_RETRY_DELAY = 8.0 diff --git a/src/lithic/_exceptions.py b/src/lithic/_exceptions.py index a9133f78..0d7465a0 100644 --- a/src/lithic/_exceptions.py +++ b/src/lithic/_exceptions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/_files.py b/src/lithic/_files.py index b6e8af8b..715cc207 100644 --- a/src/lithic/_files.py +++ b/src/lithic/_files.py @@ -13,12 +13,17 @@ FileContent, RequestFiles, HttpxFileTypes, + Base64FileInput, HttpxFileContent, HttpxRequestFiles, ) from ._utils import is_tuple_t, is_mapping_t, is_sequence_t +def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: + return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + def is_file_content(obj: object) -> TypeGuard[FileContent]: return ( isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) @@ -34,13 +39,11 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None: @overload -def to_httpx_files(files: None) -> None: - ... +def to_httpx_files(files: None) -> None: ... @overload -def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: - ... +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: @@ -78,13 +81,11 @@ def _read_file_content(file: FileContent) -> HttpxFileContent: @overload -async def async_to_httpx_files(files: None) -> None: - ... +async def async_to_httpx_files(files: None) -> None: ... @overload -async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: - ... +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: diff --git a/src/lithic/_legacy_response.py b/src/lithic/_legacy_response.py index a13df7ab..93436bcf 100644 --- a/src/lithic/_legacy_response.py +++ b/src/lithic/_legacy_response.py @@ -5,7 +5,18 @@ import logging import datetime import functools -from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, Iterator, AsyncIterator, cast, overload +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, + overload, +) from typing_extensions import Awaitable, ParamSpec, override, deprecated, get_origin import anyio @@ -13,7 +24,7 @@ import pydantic from ._types import NoneType -from ._utils import is_given +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type @@ -53,6 +64,9 @@ class LegacyAPIResponse(Generic[R]): http_response: httpx.Response + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + def __init__( self, *, @@ -62,6 +76,7 @@ def __init__( stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, options: FinalRequestOptions, + retries_taken: int = 0, ) -> None: self._cast_to = cast_to self._client = client @@ -70,14 +85,13 @@ def __init__( self._stream_cls = stream_cls self._options = options self.http_response = raw + self.retries_taken = retries_taken @overload - def parse(self, *, to: type[_T]) -> _T: - ... + def parse(self, *, to: type[_T]) -> _T: ... @overload - def parse(self) -> R: - ... + def parse(self) -> R: ... def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. @@ -107,6 +121,8 @@ class MyModel(BaseModel): - `list` - `Union` - `str` + - `int` + - `float` - `httpx.Response` """ cache_key = to if to is not None else self._cast_to @@ -172,6 +188,18 @@ def elapsed(self) -> datetime.timedelta: return self.http_response.elapsed def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + + origin = get_origin(cast_to) or cast_to + if self._stream: if to: if not is_stream_class_type(to): @@ -206,13 +234,12 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: return cast( R, stream_cls( - cast_to=self._cast_to, + cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), ), ) - cast_to = to if to is not None else self._cast_to if cast_to is NoneType: return cast(R, None) @@ -220,7 +247,14 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to == str: return cast(R, response.text) - origin = get_origin(cast_to) or cast_to + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + + if cast_to == bool: + return cast(R, response.text.lower() == "true") if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent): return cast(R, cast_to(response)) # type: ignore @@ -228,7 +262,9 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if origin == LegacyAPIResponse: raise RuntimeError("Unexpected state - cast_to is `APIResponse`") - if inspect.isclass(origin) and issubclass(origin, httpx.Response): + if inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) and issubclass(origin, httpx.Response): # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response # and pass that class to our request functions. We cannot change the variance to be either # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct @@ -238,7 +274,13 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) - if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel): + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): raise TypeError("Pydantic models must subclass our base model type, e.g. `from lithic import BaseModel`") if ( @@ -307,7 +349,7 @@ def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIRespon @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "true" kwargs["extra_headers"] = extra_headers @@ -324,7 +366,7 @@ def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P @functools.wraps(func) async def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "true" kwargs["extra_headers"] = extra_headers diff --git a/src/lithic/_models.py b/src/lithic/_models.py index 81089149..34935716 100644 --- a/src/lithic/_models.py +++ b/src/lithic/_models.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import inspect from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast from datetime import date, datetime @@ -9,7 +10,9 @@ ClassVar, Protocol, Required, + ParamSpec, TypedDict, + TypeGuard, final, override, runtime_checkable, @@ -30,7 +33,22 @@ AnyMapping, HttpxRequestFiles, ) -from ._utils import is_list, is_given, is_mapping, parse_date, parse_datetime, strip_not_given +from ._utils import ( + PropertyInfo, + is_list, + is_given, + json_safe, + lru_cache, + is_mapping, + parse_date, + coerce_boolean, + parse_datetime, + strip_not_given, + extract_type_arg, + is_annotated_type, + is_type_alias_type, + strip_annotated_type, +) from ._compat import ( PYDANTIC_V2, ConfigDict, @@ -46,9 +64,15 @@ ) from ._constants import RAW_RESPONSE_HEADER +if TYPE_CHECKING: + from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema + __all__ = ["BaseModel", "GenericModel"] _T = TypeVar("_T") +_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") + +P = ParamSpec("P") @runtime_checkable @@ -58,7 +82,9 @@ class _ConfigProtocol(Protocol): class BaseModel(pydantic.BaseModel): if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(extra="allow") + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) else: @property @@ -70,24 +96,97 @@ def model_fields_set(self) -> set[str]: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] extra: Any = pydantic.Extra.allow # type: ignore + def to_dict( + self, + *, + mode: Literal["json", "python"] = "python", + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> dict[str, object]: + """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + mode: + If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. + If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` + + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. + """ + return self.model_dump( + mode=mode, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + def to_json( + self, + *, + indent: int | None = 2, + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> str: + """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. + """ + return self.model_dump_json( + indent=indent, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + @override def __str__(self) -> str: # mypy complains about an invalid self arg - return f'{self.__repr_name__()}({self.__repr_str__(", ")})' # type: ignore[misc] + return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] # Override the 'construct' method in a way that supports recursive parsing without validation. # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. @classmethod @override - def construct( - cls: Type[ModelT], + def construct( # pyright: ignore[reportIncompatibleMethodOverride] + __cls: Type[ModelT], _fields_set: set[str] | None = None, **values: object, ) -> ModelT: - m = cls.__new__(cls) + m = __cls.__new__(__cls) fields_values: dict[str, object] = {} - config = get_model_config(cls) + config = get_model_config(__cls) populate_by_name = ( config.allow_population_by_field_name if isinstance(config, _ConfigProtocol) @@ -97,7 +196,7 @@ def construct( if _fields_set is None: _fields_set = set() - model_fields = get_model_fields(cls) + model_fields = get_model_fields(__cls) for name, field in model_fields.items(): key = field.alias if key is None or (key not in values and populate_by_name): @@ -151,14 +250,16 @@ def model_dump( self, *, mode: Literal["json", "python"] | str = "python", - include: IncEx = None, - exclude: IncEx = None, + include: IncEx | None = None, + exclude: IncEx | None = None, by_alias: bool = False, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, - warnings: bool = True, + warnings: bool | Literal["none", "warn", "error"] = True, + context: dict[str, Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -180,13 +281,17 @@ def model_dump( Returns: A dictionary representation of the model. """ - if mode != "python": - raise ValueError("mode is only supported in Pydantic v2") + if mode not in {"json", "python"}: + raise ValueError("mode must be either 'json' or 'python'") if round_trip != False: raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") - return super().dict( # pyright: ignore[reportDeprecated] + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, by_alias=by_alias, @@ -195,19 +300,23 @@ def model_dump( exclude_none=exclude_none, ) + return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped + @override def model_dump_json( self, *, indent: int | None = None, - include: IncEx = None, - exclude: IncEx = None, + include: IncEx | None = None, + exclude: IncEx | None = None, by_alias: bool = False, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, - warnings: bool = True, + warnings: bool | Literal["none", "warn", "error"] = True, + context: dict[str, Any] | None = None, + serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -231,6 +340,10 @@ def model_dump_json( raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, @@ -259,7 +372,6 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: def is_basemodel(type_: type) -> bool: """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" - origin = get_origin(type_) or type_ if is_union(type_): for variant in get_args(type_): if is_basemodel(variant): @@ -267,15 +379,72 @@ def is_basemodel(type_: type) -> bool: return False + return is_basemodel_type(type_) + + +def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: + origin = get_origin(type_) or type_ + if not inspect.isclass(origin): + return False return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) -def construct_type(*, value: object, type_: type) -> object: +def build( + base_model_cls: Callable[P, _BaseModelT], + *args: P.args, + **kwargs: P.kwargs, +) -> _BaseModelT: + """Construct a BaseModel class without validation. + + This is useful for cases where you need to instantiate a `BaseModel` + from an API response as this provides type-safe params which isn't supported + by helpers like `construct_type()`. + + ```py + build(MyModel, my_field_a="foo", my_field_b=123) + ``` + """ + if args: + raise TypeError( + "Received positional arguments which are not supported; Keyword arguments must be used instead", + ) + + return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) + + +def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: + """Loose coercion to the expected type with construction of nested values. + + Note: the returned value from this function is not guaranteed to match the + given type. + """ + return cast(_T, construct_type(value=value, type_=type_)) + + +def construct_type(*, value: object, type_: object) -> object: """Loose coercion to the expected type with construction of nested values. If the given value does not match the expected type then it is returned as-is. """ + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + + # we allow `object` as the input type because otherwise, passing things like + # `Literal['value']` will be reported as a type error by type checkers + type_ = cast("type[object]", type_) + if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] + type_ = type_.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(type_): + meta: tuple[Any, ...] = get_args(type_)[1:] + type_ = extract_type_arg(type_, 0) + else: + meta = tuple() + # we need to use the origin class for any types that are subscripted generics # e.g. Dict[str, object] origin = get_origin(type_) or type_ @@ -283,10 +452,32 @@ def construct_type(*, value: object, type_: type) -> object: if is_union(origin): try: - return validate_type(type_=cast("type[object]", type_), value=value) + return validate_type(type_=cast("type[object]", original_type or type_), value=value) except Exception: pass + # if the type is a discriminated union then we want to construct the right variant + # in the union, even if the data doesn't match exactly, otherwise we'd break code + # that relies on the constructed class types, e.g. + # + # class FooType: + # kind: Literal['foo'] + # value: str + # + # class BarType: + # kind: Literal['bar'] + # value: int + # + # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then + # we'd end up constructing `FooType` when it should be `BarType`. + discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) + if discriminator and is_mapping(value): + variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) + if variant_value and isinstance(variant_value, str): + variant_type = discriminator.mapping.get(variant_value) + if variant_type: + return construct_type(type_=variant_type, value=value) + # if the data is not valid, use the first variant that doesn't fail while deserializing for variant in args: try: @@ -303,7 +494,11 @@ def construct_type(*, value: object, type_: type) -> object: _, items_type = get_args(type_) # Dict[_, items_type] return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} - if not is_literal_type(type_) and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)): + if ( + not is_literal_type(type_) + and inspect.isclass(origin) + and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) + ): if is_list(value): return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] @@ -344,6 +539,132 @@ def construct_type(*, value: object, type_: type) -> object: return value +@runtime_checkable +class CachedDiscriminatorType(Protocol): + __discriminator__: DiscriminatorDetails + + +class DiscriminatorDetails: + field_name: str + """The name of the discriminator field in the variant class, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] + ``` + + Will result in field_name='type' + """ + + field_alias_from: str | None + """The name of the discriminator field in the API response, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] = Field(alias='type_from_api') + ``` + + Will result in field_alias_from='type_from_api' + """ + + mapping: dict[str, type] + """Mapping of discriminator value to variant type, e.g. + + {'foo': FooVariant, 'bar': BarVariant} + """ + + def __init__( + self, + *, + mapping: dict[str, type], + discriminator_field: str, + discriminator_alias: str | None, + ) -> None: + self.mapping = mapping + self.field_name = discriminator_field + self.field_alias_from = discriminator_alias + + +def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: + if isinstance(union, CachedDiscriminatorType): + return union.__discriminator__ + + discriminator_field_name: str | None = None + + for annotation in meta_annotations: + if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: + discriminator_field_name = annotation.discriminator + break + + if not discriminator_field_name: + return None + + mapping: dict[str, type] = {} + discriminator_alias: str | None = None + + for variant in get_args(union): + variant = strip_annotated_type(variant) + if is_basemodel_type(variant): + if PYDANTIC_V2: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field.get("serialization_alias") + + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: + if isinstance(entry, str): + mapping[entry] = variant + else: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field_info.alias + + if field_info.annotation and is_literal_type(field_info.annotation): + for entry in get_args(field_info.annotation): + if isinstance(entry, str): + mapping[entry] = variant + + if not mapping: + return None + + details = DiscriminatorDetails( + mapping=mapping, + discriminator_field=discriminator_field_name, + discriminator_alias=discriminator_alias, + ) + cast(CachedDiscriminatorType, union).__discriminator__ = details + return details + + +def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: + schema = model.__pydantic_core_schema__ + if schema["type"] == "definitions": + schema = schema["schema"] + + if schema["type"] != "model": + return None + + schema = cast("ModelSchema", schema) + fields_schema = schema["schema"] + if fields_schema["type"] != "model-fields": + return None + + fields_schema = cast("ModelFieldsSchema", fields_schema) + field = fields_schema["fields"].get(field_name) + if not field: + return None + + return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] + + def validate_type(*, type_: type[_T], value: object) -> _T: """Strict validation that the given value matches the expected type""" if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): @@ -352,7 +673,15 @@ def validate_type(*, type_: type[_T], value: object) -> _T: return cast(_T, _validate_non_model_type(type_=type_, value=value)) -# our use of subclasssing here causes weirdness for type checkers, +def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: + """Add a pydantic config for the given type. + + Note: this is a no-op on Pydantic v1. + """ + setattr(typ, "__pydantic_config__", config) # noqa: B010 + + +# our use of subclassing here causes weirdness for type checkers, # so we just pretend that we don't subclass if TYPE_CHECKING: GenericModel = BaseModel @@ -363,7 +692,14 @@ class GenericModel(BaseGenericModel, BaseModel): if PYDANTIC_V2: - from pydantic import TypeAdapter + from pydantic import TypeAdapter as _TypeAdapter + + _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) + + if TYPE_CHECKING: + from pydantic import TypeAdapter + else: + TypeAdapter = _CachedTypeAdapter def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: return TypeAdapter(type_).validate_python(value) diff --git a/src/lithic/_resource.py b/src/lithic/_resource.py index f38e1085..62fc8293 100644 --- a/src/lithic/_resource.py +++ b/src/lithic/_resource.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/_response.py b/src/lithic/_response.py index 5e4db166..44110a9d 100644 --- a/src/lithic/_response.py +++ b/src/lithic/_response.py @@ -25,7 +25,7 @@ import pydantic from ._types import NoneType -from ._utils import is_given, extract_type_var_from_base +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base from ._models import BaseModel, is_basemodel from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type @@ -55,6 +55,9 @@ class BaseAPIResponse(Generic[R]): http_response: httpx.Response + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + def __init__( self, *, @@ -64,6 +67,7 @@ def __init__( stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, options: FinalRequestOptions, + retries_taken: int = 0, ) -> None: self._cast_to = cast_to self._client = client @@ -72,6 +76,7 @@ def __init__( self._stream_cls = stream_cls self._options = options self.http_response = raw + self.retries_taken = retries_taken @property def headers(self) -> httpx.Headers: @@ -121,6 +126,18 @@ def __repr__(self) -> str: ) def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + + origin = get_origin(cast_to) or cast_to + if self._is_sse_stream: if to: if not is_stream_class_type(to): @@ -155,13 +172,12 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: return cast( R, stream_cls( - cast_to=self._cast_to, + cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), ), ) - cast_to = to if to is not None else self._cast_to if cast_to is NoneType: return cast(R, None) @@ -172,7 +188,14 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if cast_to == bytes: return cast(R, response.content) - origin = get_origin(cast_to) or cast_to + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + + if cast_to == bool: + return cast(R, response.text.lower() == "true") # handle the legacy binary response case if inspect.isclass(cast_to) and cast_to.__name__ == "HttpxBinaryResponseContent": @@ -191,7 +214,13 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) - if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel): + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): raise TypeError("Pydantic models must subclass our base model type, e.g. `from lithic import BaseModel`") if ( @@ -244,12 +273,10 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: class APIResponse(BaseAPIResponse[R]): @overload - def parse(self, *, to: type[_T]) -> _T: - ... + def parse(self, *, to: type[_T]) -> _T: ... @overload - def parse(self) -> R: - ... + def parse(self) -> R: ... def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. @@ -277,6 +304,8 @@ class MyModel(BaseModel): - `list` - `Union` - `str` + - `int` + - `float` - `httpx.Response` """ cache_key = to if to is not None else self._cast_to @@ -346,12 +375,10 @@ def iter_lines(self) -> Iterator[str]: class AsyncAPIResponse(BaseAPIResponse[R]): @overload - async def parse(self, *, to: type[_T]) -> _T: - ... + async def parse(self, *, to: type[_T]) -> _T: ... @overload - async def parse(self) -> R: - ... + async def parse(self) -> R: ... async def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. @@ -626,7 +653,7 @@ def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseCo @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" kwargs["extra_headers"] = extra_headers @@ -647,7 +674,7 @@ def async_to_streamed_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" kwargs["extra_headers"] = extra_headers @@ -671,7 +698,7 @@ def to_custom_streamed_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -696,7 +723,7 @@ def async_to_custom_streamed_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -716,7 +743,7 @@ def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]] @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers @@ -733,7 +760,7 @@ def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P @functools.wraps(func) async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers @@ -755,7 +782,7 @@ def to_custom_raw_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls @@ -778,7 +805,7 @@ def async_to_custom_raw_response_wrapper( @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: - extra_headers = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls diff --git a/src/lithic/_streaming.py b/src/lithic/_streaming.py index 2689d4d3..4363f365 100644 --- a/src/lithic/_streaming.py +++ b/src/lithic/_streaming.py @@ -5,7 +5,7 @@ import inspect from types import TracebackType from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast -from typing_extensions import Self, TypeGuard, override, get_origin +from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable import httpx @@ -23,6 +23,8 @@ class Stream(Generic[_T]): response: httpx.Response + _decoder: SSEBytesDecoder + def __init__( self, *, @@ -33,7 +35,7 @@ def __init__( self.response = response self._cast_to = cast_to self._client = client - self._decoder = SSEDecoder() + self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() def __next__(self) -> _T: @@ -44,7 +46,7 @@ def __iter__(self) -> Iterator[_T]: yield item def _iter_events(self) -> Iterator[ServerSentEvent]: - yield from self._decoder.iter(self.response.iter_lines()) + yield from self._decoder.iter_bytes(self.response.iter_bytes()) def __stream__(self) -> Iterator[_T]: cast_to = cast(Any, self._cast_to) @@ -84,6 +86,8 @@ class AsyncStream(Generic[_T]): response: httpx.Response + _decoder: SSEDecoder | SSEBytesDecoder + def __init__( self, *, @@ -94,7 +98,7 @@ def __init__( self.response = response self._cast_to = cast_to self._client = client - self._decoder = SSEDecoder() + self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() async def __anext__(self) -> _T: @@ -105,7 +109,7 @@ async def __aiter__(self) -> AsyncIterator[_T]: yield item async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: - async for sse in self._decoder.aiter(self.response.aiter_lines()): + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): yield sse async def __stream__(self) -> AsyncIterator[_T]: @@ -194,21 +198,49 @@ def __init__(self) -> None: self._last_event_id = None self._retry = None - def iter(self, iterator: Iterator[str]) -> Iterator[ServerSentEvent]: - """Given an iterator that yields lines, iterate over it & yield every event encountered""" - for line in iterator: - line = line.rstrip("\n") - sse = self.decode(line) - if sse is not None: - yield sse - - async def aiter(self, iterator: AsyncIterator[str]) -> AsyncIterator[ServerSentEvent]: - """Given an async iterator that yields lines, iterate over it & yield every event encountered""" - async for line in iterator: - line = line.rstrip("\n") - sse = self.decode(line) - if sse is not None: - yield sse + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + for chunk in self._iter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + async for chunk in self._aiter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + async for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data def decode(self, line: str) -> ServerSentEvent | None: # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 @@ -259,6 +291,17 @@ def decode(self, line: str) -> ServerSentEvent | None: return None +@runtime_checkable +class SSEBytesDecoder(Protocol): + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" origin = get_origin(typ) or typ diff --git a/src/lithic/_types.py b/src/lithic/_types.py index e6b86b65..3cb9cf06 100644 --- a/src/lithic/_types.py +++ b/src/lithic/_types.py @@ -16,7 +16,7 @@ Optional, Sequence, ) -from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable +from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable import httpx import pydantic @@ -41,8 +41,10 @@ ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] ProxiesTypes = Union[str, Proxy, ProxiesDict] if TYPE_CHECKING: + Base64FileInput = Union[IO[bytes], PathLike[str]] FileContent = Union[IO[bytes], bytes, PathLike[str]] else: + Base64FileInput = Union[IO[bytes], PathLike] FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. FileTypes = Union[ # file (or bytes) @@ -110,8 +112,7 @@ class NotGiven: For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: - ... + def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... get(timeout=1) # 1s timeout @@ -161,16 +162,14 @@ def build( *, response: Response, data: object, - ) -> _T: - ... + ) -> _T: ... Headers = Mapping[str, Union[str, Omit]] class HeadersLikeProtocol(Protocol): - def get(self, __key: str) -> str | None: - ... + def get(self, __key: str) -> str | None: ... HeadersLike = Union[Headers, HeadersLikeProtocol] @@ -195,8 +194,8 @@ def get(self, __key: str) -> str | None: StrBytesIntFloat = Union[str, bytes, int, float] # Note: copied from Pydantic -# https://github.com/pydantic/pydantic/blob/32ea570bf96e84234d2992e1ddf40ab8a565925a/pydantic/main.py#L49 -IncEx: TypeAlias = "set[int] | set[str] | dict[int, Any] | dict[str, Any] | None" +# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 +IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] PostParser = Callable[[Any], Any] diff --git a/src/lithic/_utils/__init__.py b/src/lithic/_utils/__init__.py index b5790a87..d4fda26f 100644 --- a/src/lithic/_utils/__init__.py +++ b/src/lithic/_utils/__init__.py @@ -6,6 +6,8 @@ is_list as is_list, is_given as is_given, is_tuple as is_tuple, + json_safe as json_safe, + lru_cache as lru_cache, is_mapping as is_mapping, is_tuple_t as is_tuple_t, parse_date as parse_date, @@ -37,6 +39,7 @@ is_iterable_type as is_iterable_type, is_required_type as is_required_type, is_annotated_type as is_annotated_type, + is_type_alias_type as is_type_alias_type, strip_annotated_type as strip_annotated_type, extract_type_var_from_base as extract_type_var_from_base, ) @@ -44,5 +47,11 @@ from ._transform import ( PropertyInfo as PropertyInfo, transform as transform, + async_transform as async_transform, maybe_transform as maybe_transform, + async_maybe_transform as async_maybe_transform, +) +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, ) diff --git a/src/lithic/_utils/_proxy.py b/src/lithic/_utils/_proxy.py index b9c12dc3..ffd883e9 100644 --- a/src/lithic/_utils/_proxy.py +++ b/src/lithic/_utils/_proxy.py @@ -10,7 +10,7 @@ class LazyProxy(Generic[T], ABC): """Implements data methods to pretend that an instance is another instance. - This includes forwarding attribute access and othe methods. + This includes forwarding attribute access and other methods. """ # Note: we have to special case proxies that themselves return proxies @@ -59,5 +59,4 @@ def __as_proxied__(self) -> T: return cast(T, self) @abstractmethod - def __load__(self) -> T: - ... + def __load__(self) -> T: ... diff --git a/src/lithic/_utils/_reflection.py b/src/lithic/_utils/_reflection.py new file mode 100644 index 00000000..89aa712a --- /dev/null +++ b/src/lithic/_utils/_reflection.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import inspect +from typing import Any, Callable + + +def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: + """Returns whether or not the given function has a specific parameter""" + sig = inspect.signature(func) + return arg_name in sig.parameters + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), +) -> None: + """Ensure that the signature of the second function matches the first.""" + + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, source_param in source_sig.parameters.items(): + if name in exclude_params: + continue + + custom_param = check_sig.parameters.get(name) + if not custom_param: + errors.append(f"the `{name}` param is missing") + continue + + if custom_param.annotation != source_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" + ) + continue + + if errors: + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) diff --git a/src/lithic/_utils/_sync.py b/src/lithic/_utils/_sync.py index 595924e5..ad7ec71b 100644 --- a/src/lithic/_utils/_sync.py +++ b/src/lithic/_utils/_sync.py @@ -1,54 +1,77 @@ from __future__ import annotations +import sys +import asyncio import functools -from typing import TypeVar, Callable, Awaitable +import contextvars +from typing import Any, TypeVar, Callable, Awaitable from typing_extensions import ParamSpec import anyio +import sniffio import anyio.to_thread T_Retval = TypeVar("T_Retval") T_ParamSpec = ParamSpec("T_ParamSpec") -# copied from `asyncer`, https://github.com/tiangolo/asyncer -def asyncify( - function: Callable[T_ParamSpec, T_Retval], - *, - cancellable: bool = False, - limiter: anyio.CapacityLimiter | None = None, -) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: +if sys.version_info >= (3, 9): + _asyncio_to_thread = asyncio.to_thread +else: + # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread + # for Python 3.8 support + async def _asyncio_to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs + ) -> Any: + """Asynchronously run function *func* in a separate thread. + + Any *args and **kwargs supplied for this function are directly passed + to *func*. Also, the current :class:`contextvars.Context` is propagated, + allowing context variables from the main thread to be accessed in the + separate thread. + + Returns a coroutine that can be awaited to get the eventual result of *func*. + """ + loop = asyncio.events.get_running_loop() + ctx = contextvars.copy_context() + func_call = functools.partial(ctx.run, func, *args, **kwargs) + return await loop.run_in_executor(None, func_call) + + +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await _asyncio_to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + +# inspired by `asyncer`, https://github.com/tiangolo/asyncer +def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same - positional and keyword arguments, and that when called, calls the original function - in a worker thread using `anyio.to_thread.run_sync()`. Internally, - `asyncer.asyncify()` uses the same `anyio.to_thread.run_sync()`, but it supports - keyword arguments additional to positional arguments and it adds better support for - autocompletion and inline errors for the arguments of the function called and the - return value. - - If the `cancellable` option is enabled and the task waiting for its completion is - cancelled, the thread will still run its course but its return value (or any raised - exception) will be ignored. + positional and keyword arguments. For python version 3.9 and above, it uses + asyncio.to_thread to run the function in a separate thread. For python version + 3.8, it uses locally defined copy of the asyncio.to_thread function which was + introduced in python 3.9. - Use it like this: + Usage: - ```Python - def do_work(arg1, arg2, kwarg1="", kwarg2="") -> str: - # Do work - return "Some result" + ```python + def blocking_func(arg1, arg2, kwarg1=None): + # blocking code + return result - result = await to_thread.asyncify(do_work)("spam", "ham", kwarg1="a", kwarg2="b") - print(result) + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) ``` ## Arguments `function`: a blocking regular callable (e.g. a function) - `cancellable`: `True` to allow cancellation of the operation - `limiter`: capacity limiter to use to limit the total amount of threads running - (if omitted, the default limiter is used) ## Return @@ -58,7 +81,6 @@ def do_work(arg1, arg2, kwarg1="", kwarg2="") -> str: """ async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: - partial_f = functools.partial(function, *args, **kwargs) - return await anyio.to_thread.run_sync(partial_f, cancellable=cancellable, limiter=limiter) + return await to_thread(function, *args, **kwargs) return wrapper diff --git a/src/lithic/_utils/_transform.py b/src/lithic/_utils/_transform.py index 2cb7726c..3ec62081 100644 --- a/src/lithic/_utils/_transform.py +++ b/src/lithic/_utils/_transform.py @@ -1,9 +1,13 @@ from __future__ import annotations +import io +import base64 +import pathlib from typing import Any, Mapping, TypeVar, cast from datetime import date, datetime from typing_extensions import Literal, get_args, override, get_type_hints +import anyio import pydantic from ._utils import ( @@ -11,6 +15,7 @@ is_mapping, is_iterable, ) +from .._files import is_base64_file_input from ._typing import ( is_list_type, is_union_type, @@ -20,7 +25,7 @@ is_annotated_type, strip_annotated_type, ) -from .._compat import model_dump, is_typeddict +from .._compat import get_origin, model_dump, is_typeddict _T = TypeVar("_T") @@ -29,7 +34,7 @@ # TODO: ensure works correctly with forward references in all cases -PropertyFormat = Literal["iso8601", "custom"] +PropertyFormat = Literal["iso8601", "base64", "custom"] class PropertyInfo: @@ -46,6 +51,7 @@ class MyParams(TypedDict): alias: str | None format: PropertyFormat | None format_template: str | None + discriminator: str | None def __init__( self, @@ -53,14 +59,16 @@ def __init__( alias: str | None = None, format: PropertyFormat | None = None, format_template: str | None = None, + discriminator: str | None = None, ) -> None: self.alias = alias self.format = format self.format_template = format_template + self.discriminator = discriminator @override def __repr__(self) -> str: - return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}')" + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" def maybe_transform( @@ -118,7 +126,7 @@ def _get_annotated_type(type_: type) -> type | None: def _maybe_transform_key(key: str, type_: type) -> str: """Transform the given `data` based on the annotations provided in `type_`. - Note: this function only looks at `Annotated` types that contain `PropertInfo` metadata. + Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. """ annotated_type = _get_annotated_type(type_) if annotated_type is None: @@ -134,6 +142,10 @@ def _maybe_transform_key(key: str, type_: type) -> str: return key +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + def _transform_recursive( data: object, *, @@ -156,16 +168,35 @@ def _transform_recursive( inner_type = annotation stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return _transform_typeddict(data, stripped_type) + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + if ( # List[T] (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): @@ -178,13 +209,9 @@ def _transform_recursive( return data if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True) - - return _transform_value(data, annotation) + return model_dump(data, exclude_unset=True, mode="json") - -def _transform_value(data: object, type_: type) -> object: - annotated_type = _get_annotated_type(type_) + annotated_type = _get_annotated_type(annotation) if annotated_type is None: return data @@ -205,6 +232,22 @@ def _format_data(data: object, format_: PropertyFormat, format_template: str | N if format_ == "custom" and format_template is not None: return data.strftime(format_template) + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = data.read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + return data @@ -222,3 +265,160 @@ def _transform_typeddict( else: result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) return result + + +async def async_maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `async_transform()` that allows `None` to be passed. + + See `async_transform()` for more details. + """ + if data is None: + return None + return await async_transform(data, expected_type) + + +async def async_transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +async def _async_transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return await _async_transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return await _async_format_data(data, annotation.format, annotation.format_template) + + return data + + +async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = await anyio.Path(data).read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +async def _async_transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) + return result diff --git a/src/lithic/_utils/_typing.py b/src/lithic/_utils/_typing.py index c036991f..278749b1 100644 --- a/src/lithic/_utils/_typing.py +++ b/src/lithic/_utils/_typing.py @@ -1,8 +1,17 @@ from __future__ import annotations +import sys +import typing +import typing_extensions from typing import Any, TypeVar, Iterable, cast from collections import abc as _c_abc -from typing_extensions import Required, Annotated, get_args, get_origin +from typing_extensions import ( + TypeIs, + Required, + Annotated, + get_args, + get_origin, +) from .._types import InheritsGeneric from .._compat import is_union as _is_union @@ -36,6 +45,26 @@ def is_typevar(typ: type) -> bool: return type(typ) == TypeVar # type: ignore +_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) +if sys.version_info >= (3, 12): + _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) + + +def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: + """Return whether the provided argument is an instance of `TypeAliasType`. + + ```python + type Int = int + is_type_alias_type(Int) + # > True + Str = TypeAliasType("Str", str) + is_type_alias_type(Str) + # > True + ``` + """ + return isinstance(tp, _TYPE_ALIAS_TYPES) + + # Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] def strip_annotated_type(typ: type) -> type: if is_required_type(typ) or is_annotated_type(typ): diff --git a/src/lithic/_utils/_utils.py b/src/lithic/_utils/_utils.py index 93c95517..e5811bba 100644 --- a/src/lithic/_utils/_utils.py +++ b/src/lithic/_utils/_utils.py @@ -16,11 +16,12 @@ overload, ) from pathlib import Path +from datetime import date, datetime from typing_extensions import TypeGuard import sniffio -from .._types import Headers, NotGiven, FileTypes, NotGivenOr, HeadersLike +from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike from .._compat import parse_date as parse_date, parse_datetime as parse_datetime _T = TypeVar("_T") @@ -211,20 +212,17 @@ def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: Example usage: ```py @overload - def foo(*, a: str) -> str: - ... + def foo(*, a: str) -> str: ... @overload - def foo(*, b: bool) -> str: - ... + def foo(*, b: bool) -> str: ... # This enforces the same constraints that a static type checker would # i.e. that either a or b must be passed to the function @required_args(["a"], ["b"]) - def foo(*, a: str | None = None, b: bool | None = None) -> str: - ... + def foo(*, a: str | None = None, b: bool | None = None) -> str: ... ``` """ @@ -265,6 +263,8 @@ def wrapper(*args: object, **kwargs: object) -> object: ) msg = f"Missing required arguments; Expected either {variations} arguments to be given" else: + assert len(variants) > 0 + # TODO: this error message is not deterministic missing = list(set(variants[0]) - given_params) if len(missing) > 1: @@ -284,18 +284,15 @@ def wrapper(*args: object, **kwargs: object) -> object: @overload -def strip_not_given(obj: None) -> None: - ... +def strip_not_given(obj: None) -> None: ... @overload -def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: - ... +def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... @overload -def strip_not_given(obj: object) -> object: - ... +def strip_not_given(obj: object) -> object: ... def strip_not_given(obj: object | None) -> object: @@ -367,13 +364,13 @@ def file_from_path(path: str) -> FileTypes: def get_required_header(headers: HeadersLike, header: str) -> str: lower_header = header.lower() - if isinstance(headers, Mapping): - headers = cast(Headers, headers) - for k, v in headers.items(): + if is_mapping_t(headers): + # mypy doesn't understand the type narrowing here + for k, v in headers.items(): # type: ignore if k.lower() == lower_header and isinstance(v, str): return v - """ to deal with the case where the header looks like Stainless-Event-Id """ + # to deal with the case where the header looks like Stainless-Event-Id intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) for normalized_header in [header, lower_header, header.upper(), intercaps_header]: @@ -389,3 +386,29 @@ def get_async_library() -> str: return sniffio.current_async_library() except Exception: return "false" + + +def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: + """A version of functools.lru_cache that retains the type signature + for the wrapped function arguments. + """ + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) + return cast(Any, wrapper) # type: ignore[no-any-return] + + +def json_safe(data: object) -> object: + """Translates a mapping / sequence recursively in the same fashion + as `pydantic` v2's `model_dump(mode="json")`. + """ + if is_mapping(data): + return {json_safe(key): json_safe(value) for key, value in data.items()} + + if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): + return [json_safe(item) for item in data] + + if isinstance(data, (datetime, date)): + return data.isoformat() + + return data diff --git a/src/lithic/_version.py b/src/lithic/_version.py index 4381d2f0..2cf6d98e 100644 --- a/src/lithic/_version.py +++ b/src/lithic/_version.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "lithic" -__version__ = "0.39.0" # x-release-please-version +__version__ = "0.88.0" # x-release-please-version diff --git a/src/lithic/pagination.py b/src/lithic/pagination.py index 31ab30d4..73624536 100644 --- a/src/lithic/pagination.py +++ b/src/lithic/pagination.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Any, List, Generic, TypeVar, Optional, cast from typing_extensions import Protocol, override, runtime_checkable @@ -26,6 +26,11 @@ def _get_page_items(self) -> List[_T]: return [] return data + @override + def has_next_page(self) -> bool: + has_more = self.has_more + return has_more and super().has_next_page() + @override def next_page_info(self) -> Optional[PageInfo]: is_forwards = not self._options.params.get("ending_before", False) @@ -61,6 +66,11 @@ def _get_page_items(self) -> List[_T]: return [] return data + @override + def has_next_page(self) -> bool: + has_more = self.has_more + return has_more and super().has_next_page() + @override def next_page_info(self) -> Optional[PageInfo]: is_forwards = not self._options.params.get("ending_before", False) @@ -96,6 +106,11 @@ def _get_page_items(self) -> List[_T]: return [] return data + @override + def has_next_page(self) -> bool: + has_more = self.has_more + return has_more and super().has_next_page() + @override def next_page_info(self) -> None: """ @@ -116,6 +131,11 @@ def _get_page_items(self) -> List[_T]: return [] return data + @override + def has_next_page(self) -> bool: + has_more = self.has_more + return has_more and super().has_next_page() + @override def next_page_info(self) -> None: """ diff --git a/src/lithic/resources/__init__.py b/src/lithic/resources/__init__.py index aabd710d..f4c1ae5a 100644 --- a/src/lithic/resources/__init__.py +++ b/src/lithic/resources/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .cards import ( Cards, @@ -64,7 +64,6 @@ ThreeDSWithStreamingResponse, AsyncThreeDSWithStreamingResponse, ) -from .webhooks import Webhooks, AsyncWebhooks from .auth_rules import ( AuthRules, AsyncAuthRules, @@ -73,14 +72,6 @@ AuthRulesWithStreamingResponse, AsyncAuthRulesWithStreamingResponse, ) -from .card_product import ( - CardProduct, - AsyncCardProduct, - CardProductWithRawResponse, - AsyncCardProductWithRawResponse, - CardProductWithStreamingResponse, - AsyncCardProductWithStreamingResponse, -) from .transactions import ( Transactions, AsyncTransactions, @@ -105,6 +96,14 @@ TokenizationsWithStreamingResponse, AsyncTokenizationsWithStreamingResponse, ) +from .book_transfers import ( + BookTransfers, + AsyncBookTransfers, + BookTransfersWithRawResponse, + AsyncBookTransfersWithRawResponse, + BookTransfersWithStreamingResponse, + AsyncBookTransfersWithStreamingResponse, +) from .account_holders import ( AccountHolders, AsyncAccountHolders, @@ -113,6 +112,14 @@ AccountHoldersWithStreamingResponse, AsyncAccountHoldersWithStreamingResponse, ) +from .credit_products import ( + CreditProducts, + AsyncCreditProducts, + CreditProductsWithRawResponse, + AsyncCreditProductsWithRawResponse, + CreditProductsWithStreamingResponse, + AsyncCreditProductsWithStreamingResponse, +) from .digital_card_art import ( DigitalCardArtResource, AsyncDigitalCardArtResource, @@ -121,6 +128,14 @@ DigitalCardArtResourceWithStreamingResponse, AsyncDigitalCardArtResourceWithStreamingResponse, ) +from .external_payments import ( + ExternalPayments, + AsyncExternalPayments, + ExternalPaymentsWithRawResponse, + AsyncExternalPaymentsWithRawResponse, + ExternalPaymentsWithStreamingResponse, + AsyncExternalPaymentsWithStreamingResponse, +) from .aggregate_balances import ( AggregateBalances, AsyncAggregateBalances, @@ -145,6 +160,14 @@ ResponderEndpointsWithStreamingResponse, AsyncResponderEndpointsWithStreamingResponse, ) +from .management_operations import ( + ManagementOperations, + AsyncManagementOperations, + ManagementOperationsWithRawResponse, + AsyncManagementOperationsWithRawResponse, + ManagementOperationsWithStreamingResponse, + AsyncManagementOperationsWithStreamingResponse, +) from .auth_stream_enrollment import ( AuthStreamEnrollment, AsyncAuthStreamEnrollment, @@ -255,8 +278,6 @@ "AsyncResponderEndpointsWithRawResponse", "ResponderEndpointsWithStreamingResponse", "AsyncResponderEndpointsWithStreamingResponse", - "Webhooks", - "AsyncWebhooks", "ExternalBankAccounts", "AsyncExternalBankAccounts", "ExternalBankAccountsWithRawResponse", @@ -281,12 +302,6 @@ "AsyncReportsWithRawResponse", "ReportsWithStreamingResponse", "AsyncReportsWithStreamingResponse", - "CardProduct", - "AsyncCardProduct", - "CardProductWithRawResponse", - "AsyncCardProductWithRawResponse", - "CardProductWithStreamingResponse", - "AsyncCardProductWithStreamingResponse", "CardPrograms", "AsyncCardPrograms", "CardProgramsWithRawResponse", @@ -299,4 +314,28 @@ "AsyncDigitalCardArtResourceWithRawResponse", "DigitalCardArtResourceWithStreamingResponse", "AsyncDigitalCardArtResourceWithStreamingResponse", + "BookTransfers", + "AsyncBookTransfers", + "BookTransfersWithRawResponse", + "AsyncBookTransfersWithRawResponse", + "BookTransfersWithStreamingResponse", + "AsyncBookTransfersWithStreamingResponse", + "CreditProducts", + "AsyncCreditProducts", + "CreditProductsWithRawResponse", + "AsyncCreditProductsWithRawResponse", + "CreditProductsWithStreamingResponse", + "AsyncCreditProductsWithStreamingResponse", + "ExternalPayments", + "AsyncExternalPayments", + "ExternalPaymentsWithRawResponse", + "AsyncExternalPaymentsWithRawResponse", + "ExternalPaymentsWithStreamingResponse", + "AsyncExternalPaymentsWithStreamingResponse", + "ManagementOperations", + "AsyncManagementOperations", + "ManagementOperationsWithRawResponse", + "AsyncManagementOperationsWithRawResponse", + "ManagementOperationsWithStreamingResponse", + "AsyncManagementOperationsWithStreamingResponse", ] diff --git a/src/lithic/resources/account_holders.py b/src/lithic/resources/account_holders.py index 39981086..bee201ca 100644 --- a/src/lithic/resources/account_holders.py +++ b/src/lithic/resources/account_holders.py @@ -1,36 +1,43 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import Iterable, overload -from typing_extensions import Literal +from typing import Any, List, Union, Iterable, cast +from datetime import datetime +from typing_extensions import Literal, overload import httpx from .. import _legacy_response from ..types import ( - AccountHolder, - AccountHolderDocument, - AccountHolderCreateResponse, - AccountHolderUpdateResponse, - AccountHolderListDocumentsResponse, - shared_params, account_holder_list_params, account_holder_create_params, account_holder_update_params, - account_holder_resubmit_params, account_holder_upload_document_params, + account_holder_simulate_enrollment_review_params, + account_holder_simulate_enrollment_document_review_params, ) from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import required_args, maybe_transform +from .._utils import ( + is_given, + required_args, + maybe_transform, + async_maybe_transform, +) from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._constants import DEFAULT_TIMEOUT from ..pagination import SyncSinglePage, AsyncSinglePage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.account_holder import AccountHolder +from ..types.shared.document import Document +from ..types.address_update_param import AddressUpdateParam +from ..types.shared_params.address import Address +from ..types.account_holder_create_response import AccountHolderCreateResponse +from ..types.account_holder_update_response import AccountHolderUpdateResponse +from ..types.account_holder_list_documents_response import AccountHolderListDocumentsResponse +from ..types.account_holder_simulate_enrollment_review_response import AccountHolderSimulateEnrollmentReviewResponse __all__ = ["AccountHolders", "AsyncAccountHolders"] @@ -38,23 +45,35 @@ class AccountHolders(SyncAPIResource): @cached_property def with_raw_response(self) -> AccountHoldersWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AccountHoldersWithRawResponse(self) @cached_property def with_streaming_response(self) -> AccountHoldersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AccountHoldersWithStreamingResponse(self) @overload def create( self, *, - beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity], beneficial_owner_individuals: Iterable[account_holder_create_params.KYBBeneficialOwnerIndividual], business_entity: account_holder_create_params.KYBBusinessEntity, control_person: account_holder_create_params.KYBControlPerson, nature_of_business: str, tos_timestamp: str, workflow: Literal["KYB_BASIC", "KYB_BYO"], + beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] + | NotGiven = NOT_GIVEN, external_id: str | NotGiven = NOT_GIVEN, kyb_passed_timestamp: str | NotGiven = NOT_GIVEN, website_url: str | NotGiven = NOT_GIVEN, @@ -63,33 +82,23 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 300, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderCreateResponse: """ - Run an individual or business's information through the Customer Identification - Program (CIP) and return an `account_token` if the status is accepted or pending - (i.e., further action required). All calls to this endpoint will return an - immediate response - though in some cases, the response may indicate the - workflow is under review or further action will be needed to complete the - account creation process. This endpoint can only be used on accounts that are - part of the program that the calling API key manages. + Create an account holder and initiate the appropriate onboarding workflow. + Account holders and accounts have a 1:1 relationship. When an account holder is + successfully created an associated account is also created. All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the enrollment is under review or further action will be needed to + complete the account enrollment process. This endpoint can only be used on + accounts that are part of the program that the calling API key manages. Args: - beneficial_owner_entities: List of all entities with >25% ownership in the company. If no entity or - individual owns >25% of the company, and the largest shareholder is an entity, - please identify them in this field. See - [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) - (Section I) for more background. If no business owner is an entity, pass in an - empty list. However, either this parameter or `beneficial_owner_individuals` - must be populated. on entities that should be included. - - beneficial_owner_individuals: List of all individuals with >25% ownership in the company. If no entity or - individual owns >25% of the company, and the largest shareholder is an - individual, please identify them in this field. See + beneficial_owner_individuals: List of all direct and indirect individuals with >25% ownership in the company. + If no individual owns >25% of the company, please identify the largest + shareholder in this field. See [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) - (Section I) for more background on individuals that should be included. If no - individual is an entity, pass in an empty list. However, either this parameter - or `beneficial_owner_entities` must be populated. + (Section I) for more background on individuals that should be included. business_entity: Information for business for which the account is being opened and KYB is being run. @@ -112,6 +121,8 @@ def create( workflow: Specifies the type of KYB workflow to run. + beneficial_owner_entities: Deprecated. + external_id: A user provided id that can be used to link an account holder with an external system @@ -138,7 +149,7 @@ def create( *, individual: account_holder_create_params.KYCIndividual, tos_timestamp: str, - workflow: Literal["KYC_ADVANCED", "KYC_BASIC", "KYC_BYO"], + workflow: Literal["KYC_BASIC", "KYC_BYO"], external_id: str | NotGiven = NOT_GIVEN, kyc_passed_timestamp: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -146,16 +157,16 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 300, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderCreateResponse: """ - Run an individual or business's information through the Customer Identification - Program (CIP) and return an `account_token` if the status is accepted or pending - (i.e., further action required). All calls to this endpoint will return an - immediate response - though in some cases, the response may indicate the - workflow is under review or further action will be needed to complete the - account creation process. This endpoint can only be used on accounts that are - part of the program that the calling API key manages. + Create an account holder and initiate the appropriate onboarding workflow. + Account holders and accounts have a 1:1 relationship. When an account holder is + successfully created an associated account is also created. All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the enrollment is under review or further action will be needed to + complete the account enrollment process. This endpoint can only be used on + accounts that are part of the program that the calling API key manages. Args: individual: Information on individual for whom the account is being opened and KYC is being @@ -189,13 +200,13 @@ def create( def create( self, *, + address: Address, email: str, first_name: str, kyc_exemption_type: Literal["AUTHORIZED_USER", "PREPAID_CARD_USER"], last_name: str, phone_number: str, workflow: Literal["KYC_EXEMPT"], - address: shared_params.Address | NotGiven = NOT_GIVEN, business_account_token: str | NotGiven = NOT_GIVEN, external_id: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -203,18 +214,21 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 300, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderCreateResponse: """ - Run an individual or business's information through the Customer Identification - Program (CIP) and return an `account_token` if the status is accepted or pending - (i.e., further action required). All calls to this endpoint will return an - immediate response - though in some cases, the response may indicate the - workflow is under review or further action will be needed to complete the - account creation process. This endpoint can only be used on accounts that are - part of the program that the calling API key manages. + Create an account holder and initiate the appropriate onboarding workflow. + Account holders and accounts have a 1:1 relationship. When an account holder is + successfully created an associated account is also created. All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the enrollment is under review or further action will be needed to + complete the account enrollment process. This endpoint can only be used on + accounts that are part of the program that the calling API key manages. Args: + address: KYC Exempt user's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + email: The KYC Exempt user's email first_name: The KYC Exempt user's first name @@ -223,13 +237,10 @@ def create( last_name: The KYC Exempt user's last name - phone_number: The KYC Exempt user's phone number + phone_number: The KYC Exempt user's phone number, entered in E.164 format. workflow: Specifies the workflow type. This must be 'KYC_EXEMPT' - address: KYC Exempt user's current address - PO boxes, UPS drops, and FedEx drops are not - acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. - business_account_token: Only applicable for customers using the KYC-Exempt workflow to enroll authorized users of businesses. Pass the account_token of the enrolled business associated with the AUTHORIZED_USER in this field. @@ -249,7 +260,6 @@ def create( @required_args( [ - "beneficial_owner_entities", "beneficial_owner_individuals", "business_entity", "control_person", @@ -258,63 +268,63 @@ def create( "workflow", ], ["individual", "tos_timestamp", "workflow"], - ["email", "first_name", "kyc_exemption_type", "last_name", "phone_number", "workflow"], + ["address", "email", "first_name", "kyc_exemption_type", "last_name", "phone_number", "workflow"], ) def create( self, *, - beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] - | NotGiven = NOT_GIVEN, beneficial_owner_individuals: Iterable[account_holder_create_params.KYBBeneficialOwnerIndividual] | NotGiven = NOT_GIVEN, business_entity: account_holder_create_params.KYBBusinessEntity | NotGiven = NOT_GIVEN, control_person: account_holder_create_params.KYBControlPerson | NotGiven = NOT_GIVEN, nature_of_business: str | NotGiven = NOT_GIVEN, tos_timestamp: str | NotGiven = NOT_GIVEN, - workflow: Literal["KYB_BASIC", "KYB_BYO"] - | Literal["KYC_ADVANCED", "KYC_BASIC", "KYC_BYO"] - | Literal["KYC_EXEMPT"], + workflow: Literal["KYB_BASIC", "KYB_BYO"] | Literal["KYC_BASIC", "KYC_BYO"] | Literal["KYC_EXEMPT"], + beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] + | NotGiven = NOT_GIVEN, external_id: str | NotGiven = NOT_GIVEN, kyb_passed_timestamp: str | NotGiven = NOT_GIVEN, website_url: str | NotGiven = NOT_GIVEN, individual: account_holder_create_params.KYCIndividual | NotGiven = NOT_GIVEN, kyc_passed_timestamp: str | NotGiven = NOT_GIVEN, + address: Address | NotGiven = NOT_GIVEN, email: str | NotGiven = NOT_GIVEN, first_name: str | NotGiven = NOT_GIVEN, kyc_exemption_type: Literal["AUTHORIZED_USER", "PREPAID_CARD_USER"] | NotGiven = NOT_GIVEN, last_name: str | NotGiven = NOT_GIVEN, phone_number: str | NotGiven = NOT_GIVEN, - address: shared_params.Address | NotGiven = NOT_GIVEN, business_account_token: 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 = 300, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderCreateResponse: + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 300 return self._post( - "/account_holders", + "/v1/account_holders", body=maybe_transform( { - "beneficial_owner_entities": beneficial_owner_entities, "beneficial_owner_individuals": beneficial_owner_individuals, "business_entity": business_entity, "control_person": control_person, "nature_of_business": nature_of_business, "tos_timestamp": tos_timestamp, "workflow": workflow, + "beneficial_owner_entities": beneficial_owner_entities, "external_id": external_id, "kyb_passed_timestamp": kyb_passed_timestamp, "website_url": website_url, "individual": individual, "kyc_passed_timestamp": kyc_passed_timestamp, + "address": address, "email": email, "first_name": first_name, "kyc_exemption_type": kyc_exemption_type, "last_name": last_name, "phone_number": phone_number, - "address": address, "business_account_token": business_account_token, }, account_holder_create_params.AccountHolderCreateParams, @@ -354,19 +364,145 @@ def retrieve( f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" ) return self._get( - f"/account_holders/{account_holder_token}", + f"/v1/account_holders/{account_holder_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=AccountHolder, ) + @overload + def update( + self, + account_holder_token: str, + *, + beneficial_owner_entities: Iterable[account_holder_update_params.KYBPatchRequestBeneficialOwnerEntity] + | NotGiven = NOT_GIVEN, + beneficial_owner_individuals: Iterable[account_holder_update_params.KYBPatchRequestBeneficialOwnerIndividual] + | NotGiven = NOT_GIVEN, + business_entity: account_holder_update_params.KYBPatchRequestBusinessEntity | NotGiven = NOT_GIVEN, + control_person: account_holder_update_params.KYBPatchRequestControlPerson | NotGiven = NOT_GIVEN, + external_id: str | NotGiven = NOT_GIVEN, + nature_of_business: str | NotGiven = NOT_GIVEN, + website_url: 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, + ) -> AccountHolderUpdateResponse: + """ + Update the information associated with a particular account holder (including + business owners and control persons associated to a business account). If Lithic + is performing KYB or KYC and additional verification is required we will run the + individual's or business's updated information again and return whether the + status is accepted or pending (i.e., further action required). All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the workflow is under review or further action will be needed to + complete the evaluation process. This endpoint can only be used on existing + accounts that are part of the program that the calling API key manages. + + Args: + beneficial_owner_entities: List of all entities with >25% ownership in the company. If no entity or + individual owns >25% of the company, and the largest shareholder is an entity, + please identify them in this field. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf)(Section + I) for more background. If no business owner is an entity, pass in an empty + list. However, either this parameter or `beneficial_owner_individuals` must be + populated. on entities that should be included. + + beneficial_owner_individuals: List of all individuals with >25% ownership in the company. If no entity or + individual owns >25% of the company, and the largest shareholder is an + individual, please identify them in this field. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf)(Section + I) for more background on individuals that should be included. If no individual + is an entity, pass in an empty list. However, either this parameter or + `beneficial_owner_entities` must be populated. + + business_entity: Information for business for which the account is being opened and KYB is being + run. + + control_person: An individual with significant responsibility for managing the legal entity + (e.g., a Chief Executive Officer, Chief Financial Officer, Chief Operating + Officer, Managing Member, General Partner, President, Vice President, or + Treasurer). This can be an executive, or someone who will have program-wide + access to the cards that Lithic will provide. In some cases, this individual + could also be a beneficial owner listed above. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) + (Section II) for more background. + + external_id: A user provided id that can be used to link an account holder with an external + system + + nature_of_business: Short description of the company's line of business (i.e., what does the company + do?). + + website_url: Company website URL. + + 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( + self, + account_holder_token: str, + *, + external_id: str | NotGiven = NOT_GIVEN, + individual: account_holder_update_params.KYCPatchRequestIndividual | 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, + ) -> AccountHolderUpdateResponse: + """ + Update the information associated with a particular account holder (including + business owners and control persons associated to a business account). If Lithic + is performing KYB or KYC and additional verification is required we will run the + individual's or business's updated information again and return whether the + status is accepted or pending (i.e., further action required). All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the workflow is under review or further action will be needed to + complete the evaluation process. This endpoint can only be used on existing + accounts that are part of the program that the calling API key manages. + + Args: + external_id: A user provided id that can be used to link an account holder with an external + system + + individual: Information on the individual for whom the account is being opened and KYC is + being run. + + 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( self, account_holder_token: str, *, + address: AddressUpdateParam | NotGiven = NOT_GIVEN, business_account_token: str | NotGiven = NOT_GIVEN, email: str | NotGiven = NOT_GIVEN, + first_name: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + legal_business_name: str | NotGiven = NOT_GIVEN, phone_number: 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. @@ -376,20 +512,35 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderUpdateResponse: """ - Update the information associated with a particular account holder. + Update the information associated with a particular account holder (including + business owners and control persons associated to a business account). If Lithic + is performing KYB or KYC and additional verification is required we will run the + individual's or business's updated information again and return whether the + status is accepted or pending (i.e., further action required). All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the workflow is under review or further action will be needed to + complete the evaluation process. This endpoint can only be used on existing + accounts that are part of the program that the calling API key manages. Args: - business_account_token: Only applicable for customers using the KYC-Exempt workflow to enroll authorized - users of businesses. Pass the account_token of the enrolled business associated - with the AUTHORIZED_USER in this field. + address: Allowed for: KYC-Exempt, BYO-KYC, BYO-KYB. + + business_account_token: Allowed for: KYC-Exempt, BYO-KYC. The token of the business account to which the + account holder is associated. + + email: Allowed for all Account Holders. Account holder's email address. The primary + purpose of this field is for cardholder identification and verification during + the digital wallet tokenization process. + + first_name: Allowed for KYC-Exempt, BYO-KYC. Account holder's first name. - email: Account holder's email address. The primary purpose of this field is for - cardholder identification and verification during the digital wallet - tokenization process. + last_name: Allowed for KYC-Exempt, BYO-KYC. Account holder's last name. - phone_number: Account holder's phone number, entered in E.164 format. The primary purpose of - this field is for cardholder identification and verification during the digital - wallet tokenization process. + legal_business_name: Allowed for BYO-KYB. Legal business name of the account holder. + + phone_number: Allowed for all Account Holders. Account holder's phone number, entered in E.164 + format. The primary purpose of this field is for cardholder identification and + verification during the digital wallet tokenization process. extra_headers: Send extra headers @@ -399,32 +550,86 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + def update( + self, + account_holder_token: str, + *, + beneficial_owner_entities: Iterable[account_holder_update_params.KYBPatchRequestBeneficialOwnerEntity] + | NotGiven = NOT_GIVEN, + beneficial_owner_individuals: Iterable[account_holder_update_params.KYBPatchRequestBeneficialOwnerIndividual] + | NotGiven = NOT_GIVEN, + business_entity: account_holder_update_params.KYBPatchRequestBusinessEntity | NotGiven = NOT_GIVEN, + control_person: account_holder_update_params.KYBPatchRequestControlPerson | NotGiven = NOT_GIVEN, + external_id: str | NotGiven = NOT_GIVEN, + nature_of_business: str | NotGiven = NOT_GIVEN, + website_url: str | NotGiven = NOT_GIVEN, + individual: account_holder_update_params.KYCPatchRequestIndividual | NotGiven = NOT_GIVEN, + address: AddressUpdateParam | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + first_name: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + legal_business_name: str | NotGiven = NOT_GIVEN, + phone_number: 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, + ) -> AccountHolderUpdateResponse: if not account_holder_token: raise ValueError( f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" ) - return self._patch( - f"/account_holders/{account_holder_token}", - body=maybe_transform( - { - "business_account_token": business_account_token, - "email": email, - "phone_number": phone_number, - }, - account_holder_update_params.AccountHolderUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + return cast( + AccountHolderUpdateResponse, + self._patch( + f"/v1/account_holders/{account_holder_token}", + body=maybe_transform( + { + "beneficial_owner_entities": beneficial_owner_entities, + "beneficial_owner_individuals": beneficial_owner_individuals, + "business_entity": business_entity, + "control_person": control_person, + "external_id": external_id, + "nature_of_business": nature_of_business, + "website_url": website_url, + "individual": individual, + "address": address, + "business_account_token": business_account_token, + "email": email, + "first_name": first_name, + "last_name": last_name, + "legal_business_name": legal_business_name, + "phone_number": phone_number, + }, + account_holder_update_params.AccountHolderUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, AccountHolderUpdateResponse + ), # Union types cannot be passed in as arguments in the type system ), - cast_to=AccountHolderUpdateResponse, ) def list( self, *, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, ending_before: str | NotGiven = NOT_GIVEN, external_id: str | NotGiven = NOT_GIVEN, + first_name: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + legal_business_name: str | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, + phone_number: str | NotGiven = NOT_GIVEN, starting_after: 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. @@ -438,13 +643,33 @@ def list( evaluation status. Args: + begin: Date string in RFC 3339 format. Only entries created after the specified time + will be included. UTC time zone. + + email: Email address of the account holder. The query must be an exact match, case + insensitive. + + end: Date string in RFC 3339 format. Only entries created before the specified time + will be included. UTC time zone. + ending_before: A cursor representing an item's token before which a page of results should end. Used to retrieve the previous page of results before this item. external_id: If applicable, represents the external_id associated with the account_holder. + first_name: (Individual Account Holders only) The first name of the account holder. The + query is case insensitive and supports partial matches. + + last_name: (Individual Account Holders only) The last name of the account holder. The query + is case insensitive and supports partial matches. + + legal_business_name: (Business Account Holders only) The legal business name of the account holder. + The query is case insensitive and supports partial matches. + limit: The number of account_holders to limit the response to. + phone_number: Phone number of the account holder. The query must be an exact match. + starting_after: A cursor representing an item's token after which a page of results should begin. Used to retrieve the next page of results after this item. @@ -457,7 +682,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/account_holders", + "/v1/account_holders", page=SyncSinglePage[AccountHolder], options=make_request_options( extra_headers=extra_headers, @@ -466,9 +691,16 @@ def list( timeout=timeout, query=maybe_transform( { + "begin": begin, + "email": email, + "end": end, "ending_before": ending_before, "external_id": external_id, + "first_name": first_name, + "last_name": last_name, + "legal_business_name": legal_business_name, "limit": limit, + "phone_number": phone_number, "starting_after": starting_after, }, account_holder_list_params.AccountHolderListParams, @@ -519,74 +751,13 @@ def list_documents( f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" ) return self._get( - f"/account_holders/{account_holder_token}/documents", + f"/v1/account_holders/{account_holder_token}/documents", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=AccountHolderListDocumentsResponse, ) - def resubmit( - self, - account_holder_token: str, - *, - individual: account_holder_resubmit_params.Individual, - tos_timestamp: str, - workflow: Literal["KYC_ADVANCED"], - # 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, - ) -> AccountHolder: - """Resubmit a KYC submission. - - This endpoint should be used in cases where a KYC - submission returned a `PENDING_RESUBMIT` result, meaning one or more critical - KYC fields may have been mis-entered and the individual's identity has not yet - been successfully verified. This step must be completed in order to proceed with - the KYC evaluation. - - Two resubmission attempts are permitted via this endpoint before a `REJECTED` - status is returned and the account creation process is ended. - - Args: - individual: Information on individual for whom the account is being opened and KYC is being - re-run. - - tos_timestamp: An RFC 3339 timestamp indicating when the account holder accepted the applicable - legal agreements (e.g., cardholder terms) as agreed upon during API customer's - implementation with Lithic. - - 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 account_holder_token: - raise ValueError( - f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" - ) - return self._post( - f"/account_holders/{account_holder_token}/resubmit", - body=maybe_transform( - { - "individual": individual, - "tos_timestamp": tos_timestamp, - "workflow": workflow, - }, - account_holder_resubmit_params.AccountHolderResubmitParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AccountHolder, - ) - def retrieve_document( self, document_token: str, @@ -598,7 +769,7 @@ def retrieve_document( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AccountHolderDocument: + ) -> Document: """ Check the status of an account holder document upload, or retrieve the upload URLs to process your image uploads. @@ -631,47 +802,52 @@ def retrieve_document( if not document_token: raise ValueError(f"Expected a non-empty value for `document_token` but received {document_token!r}") return self._get( - f"/account_holders/{account_holder_token}/documents/{document_token}", + f"/v1/account_holders/{account_holder_token}/documents/{document_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AccountHolderDocument, + cast_to=Document, ) - def upload_document( + def simulate_enrollment_document_review( self, - account_holder_token: str, *, - document_type: Literal["commercial_license", "drivers_license", "passport", "passport_card", "visa"], + document_upload_token: str, + status: Literal["UPLOADED", "ACCEPTED", "REJECTED", "PARTIAL_APPROVAL"], + accepted_entity_status_reasons: List[str] | NotGiven = NOT_GIVEN, + status_reason: Literal[ + "DOCUMENT_MISSING_REQUIRED_DATA", + "DOCUMENT_UPLOAD_TOO_BLURRY", + "FILE_SIZE_TOO_LARGE", + "INVALID_DOCUMENT_TYPE", + "INVALID_DOCUMENT_UPLOAD", + "INVALID_ENTITY", + "DOCUMENT_EXPIRED", + "DOCUMENT_ISSUED_GREATER_THAN_30_DAYS", + "DOCUMENT_TYPE_NOT_SUPPORTED", + "UNKNOWN_FAILURE_REASON", + "UNKNOWN_ERROR", + ] + | 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, - ) -> AccountHolderDocument: + ) -> Document: """ - Use this endpoint to identify which type of supported government-issued - documentation you will upload for further verification. It will return two URLs - to upload your document images to - one for the front image and one for the back - image. - - This endpoint is only valid for evaluations in a `PENDING_DOCUMENT` state. + Simulates a review for an account holder document upload. - Uploaded images must either be a `jpg` or `png` file, and each must be less than - 15 MiB. Once both required uploads have been successfully completed, your - document will be run through KYC verification. + Args: + document_upload_token: The account holder document upload which to perform the simulation upon. - If you have registered a webhook, you will receive evaluation updates for any - document submission evaluations, as well as for any failed document uploads. + status: An account holder document's upload status for use within the simulation. - Two document submission attempts are permitted via this endpoint before a - `REJECTED` status is returned and the account creation process is ended. - Currently only one type of account holder document is supported per KYC - verification. + accepted_entity_status_reasons: A list of status reasons associated with a KYB account holder in PENDING_REVIEW - Args: - document_type: Type of the document to upload. + status_reason: Status reason that will be associated with the simulated account holder status. + Only required for a `REJECTED` status or `PARTIAL_APPROVAL` status. extra_headers: Send extra headers @@ -681,43 +857,212 @@ def upload_document( timeout: Override the client-level default timeout for this request, in seconds """ - if not account_holder_token: - raise ValueError( - f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" - ) return self._post( - f"/account_holders/{account_holder_token}/documents", + "/v1/simulate/account_holders/enrollment_document_review", body=maybe_transform( - {"document_type": document_type}, - account_holder_upload_document_params.AccountHolderUploadDocumentParams, + { + "document_upload_token": document_upload_token, + "status": status, + "accepted_entity_status_reasons": accepted_entity_status_reasons, + "status_reason": status_reason, + }, + account_holder_simulate_enrollment_document_review_params.AccountHolderSimulateEnrollmentDocumentReviewParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AccountHolderDocument, + cast_to=Document, ) - + def simulate_enrollment_review( + self, + *, + account_holder_token: str | NotGiven = NOT_GIVEN, + status: Literal["ACCEPTED", "REJECTED"] | NotGiven = NOT_GIVEN, + status_reasons: List[ + Literal[ + "PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ADDRESS_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_NAME_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_BUSINESS_OFFICERS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_SOS_FILING_INACTIVE", + "PRIMARY_BUSINESS_ENTITY_SOS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_CMRA_FAILURE", + "PRIMARY_BUSINESS_ENTITY_WATCHLIST_FAILURE", + "PRIMARY_BUSINESS_ENTITY_REGISTERED_AGENT_FAILURE", + "CONTROL_PERSON_BLOCKLIST_ALERT_FAILURE", + "CONTROL_PERSON_ID_VERIFICATION_FAILURE", + "CONTROL_PERSON_DOB_VERIFICATION_FAILURE", + "CONTROL_PERSON_NAME_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_DOB_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_BLOCKLIST_ALERT_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_ID_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_NAME_VERIFICATION_FAILURE", + ] + ] + | 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, + ) -> AccountHolderSimulateEnrollmentReviewResponse: + """Simulates an enrollment review for an account holder. + + This endpoint is only + applicable for workflows that may required intervention such as `KYB_BASIC`. + + Args: + account_holder_token: The account holder which to perform the simulation upon. + + status: An account holder's status for use within the simulation. + + status_reasons: Status reason that will be associated with the simulated account holder status. + Only required for a `REJECTED` status. + + 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( + "/v1/simulate/account_holders/enrollment_review", + body=maybe_transform( + { + "account_holder_token": account_holder_token, + "status": status, + "status_reasons": status_reasons, + }, + account_holder_simulate_enrollment_review_params.AccountHolderSimulateEnrollmentReviewParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccountHolderSimulateEnrollmentReviewResponse, + ) + + def upload_document( + self, + account_holder_token: str, + *, + document_type: Literal[ + "EIN_LETTER", + "TAX_RETURN", + "OPERATING_AGREEMENT", + "CERTIFICATE_OF_FORMATION", + "DRIVERS_LICENSE", + "PASSPORT", + "PASSPORT_CARD", + "CERTIFICATE_OF_GOOD_STANDING", + "ARTICLES_OF_INCORPORATION", + "ARTICLES_OF_ORGANIZATION", + "BYLAWS", + "GOVERNMENT_BUSINESS_LICENSE", + "PARTNERSHIP_AGREEMENT", + "SS4_FORM", + "BANK_STATEMENT", + "UTILITY_BILL_STATEMENT", + "SSN_CARD", + "ITIN_LETTER", + "FINCEN_BOI_REPORT", + ], + entity_token: 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, + ) -> Document: + """ + Use this endpoint to identify which type of supported government-issued + documentation you will upload for further verification. It will return two URLs + to upload your document images to - one for the front image and one for the back + image. + + This endpoint is only valid for evaluations in a `PENDING_DOCUMENT` state. + + Uploaded images must either be a `jpg` or `png` file, and each must be less than + 15 MiB. Once both required uploads have been successfully completed, your + document will be run through KYC verification. + + If you have registered a webhook, you will receive evaluation updates for any + document submission evaluations, as well as for any failed document uploads. + + Two document submission attempts are permitted via this endpoint before a + `REJECTED` status is returned and the account creation process is ended. + Currently only one type of account holder document is supported per KYC + verification. + + Args: + document_type: The type of document to upload + + entity_token: Globally unique identifier for the entity. + + 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 account_holder_token: + raise ValueError( + f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" + ) + return self._post( + f"/v1/account_holders/{account_holder_token}/documents", + body=maybe_transform( + { + "document_type": document_type, + "entity_token": entity_token, + }, + account_holder_upload_document_params.AccountHolderUploadDocumentParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Document, + ) + + class AsyncAccountHolders(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncAccountHoldersWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncAccountHoldersWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAccountHoldersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncAccountHoldersWithStreamingResponse(self) @overload async def create( self, *, - beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity], beneficial_owner_individuals: Iterable[account_holder_create_params.KYBBeneficialOwnerIndividual], business_entity: account_holder_create_params.KYBBusinessEntity, control_person: account_holder_create_params.KYBControlPerson, nature_of_business: str, tos_timestamp: str, workflow: Literal["KYB_BASIC", "KYB_BYO"], + beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] + | NotGiven = NOT_GIVEN, external_id: str | NotGiven = NOT_GIVEN, kyb_passed_timestamp: str | NotGiven = NOT_GIVEN, website_url: str | NotGiven = NOT_GIVEN, @@ -726,33 +1071,23 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 300, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderCreateResponse: """ - Run an individual or business's information through the Customer Identification - Program (CIP) and return an `account_token` if the status is accepted or pending - (i.e., further action required). All calls to this endpoint will return an - immediate response - though in some cases, the response may indicate the - workflow is under review or further action will be needed to complete the - account creation process. This endpoint can only be used on accounts that are - part of the program that the calling API key manages. + Create an account holder and initiate the appropriate onboarding workflow. + Account holders and accounts have a 1:1 relationship. When an account holder is + successfully created an associated account is also created. All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the enrollment is under review or further action will be needed to + complete the account enrollment process. This endpoint can only be used on + accounts that are part of the program that the calling API key manages. Args: - beneficial_owner_entities: List of all entities with >25% ownership in the company. If no entity or - individual owns >25% of the company, and the largest shareholder is an entity, - please identify them in this field. See - [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) - (Section I) for more background. If no business owner is an entity, pass in an - empty list. However, either this parameter or `beneficial_owner_individuals` - must be populated. on entities that should be included. - - beneficial_owner_individuals: List of all individuals with >25% ownership in the company. If no entity or - individual owns >25% of the company, and the largest shareholder is an - individual, please identify them in this field. See + beneficial_owner_individuals: List of all direct and indirect individuals with >25% ownership in the company. + If no individual owns >25% of the company, please identify the largest + shareholder in this field. See [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) - (Section I) for more background on individuals that should be included. If no - individual is an entity, pass in an empty list. However, either this parameter - or `beneficial_owner_entities` must be populated. + (Section I) for more background on individuals that should be included. business_entity: Information for business for which the account is being opened and KYB is being run. @@ -775,6 +1110,8 @@ async def create( workflow: Specifies the type of KYB workflow to run. + beneficial_owner_entities: Deprecated. + external_id: A user provided id that can be used to link an account holder with an external system @@ -801,7 +1138,7 @@ async def create( *, individual: account_holder_create_params.KYCIndividual, tos_timestamp: str, - workflow: Literal["KYC_ADVANCED", "KYC_BASIC", "KYC_BYO"], + workflow: Literal["KYC_BASIC", "KYC_BYO"], external_id: str | NotGiven = NOT_GIVEN, kyc_passed_timestamp: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -809,16 +1146,16 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 300, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderCreateResponse: """ - Run an individual or business's information through the Customer Identification - Program (CIP) and return an `account_token` if the status is accepted or pending - (i.e., further action required). All calls to this endpoint will return an - immediate response - though in some cases, the response may indicate the - workflow is under review or further action will be needed to complete the - account creation process. This endpoint can only be used on accounts that are - part of the program that the calling API key manages. + Create an account holder and initiate the appropriate onboarding workflow. + Account holders and accounts have a 1:1 relationship. When an account holder is + successfully created an associated account is also created. All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the enrollment is under review or further action will be needed to + complete the account enrollment process. This endpoint can only be used on + accounts that are part of the program that the calling API key manages. Args: individual: Information on individual for whom the account is being opened and KYC is being @@ -852,13 +1189,13 @@ async def create( async def create( self, *, + address: Address, email: str, first_name: str, kyc_exemption_type: Literal["AUTHORIZED_USER", "PREPAID_CARD_USER"], last_name: str, phone_number: str, workflow: Literal["KYC_EXEMPT"], - address: shared_params.Address | NotGiven = NOT_GIVEN, business_account_token: str | NotGiven = NOT_GIVEN, external_id: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -866,18 +1203,21 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = 300, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderCreateResponse: """ - Run an individual or business's information through the Customer Identification - Program (CIP) and return an `account_token` if the status is accepted or pending - (i.e., further action required). All calls to this endpoint will return an - immediate response - though in some cases, the response may indicate the - workflow is under review or further action will be needed to complete the - account creation process. This endpoint can only be used on accounts that are - part of the program that the calling API key manages. + Create an account holder and initiate the appropriate onboarding workflow. + Account holders and accounts have a 1:1 relationship. When an account holder is + successfully created an associated account is also created. All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the enrollment is under review or further action will be needed to + complete the account enrollment process. This endpoint can only be used on + accounts that are part of the program that the calling API key manages. Args: + address: KYC Exempt user's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + email: The KYC Exempt user's email first_name: The KYC Exempt user's first name @@ -886,13 +1226,10 @@ async def create( last_name: The KYC Exempt user's last name - phone_number: The KYC Exempt user's phone number + phone_number: The KYC Exempt user's phone number, entered in E.164 format. workflow: Specifies the workflow type. This must be 'KYC_EXEMPT' - address: KYC Exempt user's current address - PO boxes, UPS drops, and FedEx drops are not - acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. - business_account_token: Only applicable for customers using the KYC-Exempt workflow to enroll authorized users of businesses. Pass the account_token of the enrolled business associated with the AUTHORIZED_USER in this field. @@ -912,7 +1249,6 @@ async def create( @required_args( [ - "beneficial_owner_entities", "beneficial_owner_individuals", "business_entity", "control_person", @@ -921,63 +1257,63 @@ async def create( "workflow", ], ["individual", "tos_timestamp", "workflow"], - ["email", "first_name", "kyc_exemption_type", "last_name", "phone_number", "workflow"], + ["address", "email", "first_name", "kyc_exemption_type", "last_name", "phone_number", "workflow"], ) async def create( self, *, - beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] - | NotGiven = NOT_GIVEN, beneficial_owner_individuals: Iterable[account_holder_create_params.KYBBeneficialOwnerIndividual] | NotGiven = NOT_GIVEN, business_entity: account_holder_create_params.KYBBusinessEntity | NotGiven = NOT_GIVEN, control_person: account_holder_create_params.KYBControlPerson | NotGiven = NOT_GIVEN, nature_of_business: str | NotGiven = NOT_GIVEN, tos_timestamp: str | NotGiven = NOT_GIVEN, - workflow: Literal["KYB_BASIC", "KYB_BYO"] - | Literal["KYC_ADVANCED", "KYC_BASIC", "KYC_BYO"] - | Literal["KYC_EXEMPT"], + workflow: Literal["KYB_BASIC", "KYB_BYO"] | Literal["KYC_BASIC", "KYC_BYO"] | Literal["KYC_EXEMPT"], + beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] + | NotGiven = NOT_GIVEN, external_id: str | NotGiven = NOT_GIVEN, kyb_passed_timestamp: str | NotGiven = NOT_GIVEN, website_url: str | NotGiven = NOT_GIVEN, individual: account_holder_create_params.KYCIndividual | NotGiven = NOT_GIVEN, kyc_passed_timestamp: str | NotGiven = NOT_GIVEN, + address: Address | NotGiven = NOT_GIVEN, email: str | NotGiven = NOT_GIVEN, first_name: str | NotGiven = NOT_GIVEN, kyc_exemption_type: Literal["AUTHORIZED_USER", "PREPAID_CARD_USER"] | NotGiven = NOT_GIVEN, last_name: str | NotGiven = NOT_GIVEN, phone_number: str | NotGiven = NOT_GIVEN, - address: shared_params.Address | NotGiven = NOT_GIVEN, business_account_token: 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 = 300, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderCreateResponse: + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 300 return await self._post( - "/account_holders", - body=maybe_transform( + "/v1/account_holders", + body=await async_maybe_transform( { - "beneficial_owner_entities": beneficial_owner_entities, "beneficial_owner_individuals": beneficial_owner_individuals, "business_entity": business_entity, "control_person": control_person, "nature_of_business": nature_of_business, "tos_timestamp": tos_timestamp, "workflow": workflow, + "beneficial_owner_entities": beneficial_owner_entities, "external_id": external_id, "kyb_passed_timestamp": kyb_passed_timestamp, "website_url": website_url, "individual": individual, "kyc_passed_timestamp": kyc_passed_timestamp, + "address": address, "email": email, "first_name": first_name, "kyc_exemption_type": kyc_exemption_type, "last_name": last_name, "phone_number": phone_number, - "address": address, "business_account_token": business_account_token, }, account_holder_create_params.AccountHolderCreateParams, @@ -1017,19 +1353,145 @@ async def retrieve( f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" ) return await self._get( - f"/account_holders/{account_holder_token}", + f"/v1/account_holders/{account_holder_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=AccountHolder, ) + @overload + async def update( + self, + account_holder_token: str, + *, + beneficial_owner_entities: Iterable[account_holder_update_params.KYBPatchRequestBeneficialOwnerEntity] + | NotGiven = NOT_GIVEN, + beneficial_owner_individuals: Iterable[account_holder_update_params.KYBPatchRequestBeneficialOwnerIndividual] + | NotGiven = NOT_GIVEN, + business_entity: account_holder_update_params.KYBPatchRequestBusinessEntity | NotGiven = NOT_GIVEN, + control_person: account_holder_update_params.KYBPatchRequestControlPerson | NotGiven = NOT_GIVEN, + external_id: str | NotGiven = NOT_GIVEN, + nature_of_business: str | NotGiven = NOT_GIVEN, + website_url: 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, + ) -> AccountHolderUpdateResponse: + """ + Update the information associated with a particular account holder (including + business owners and control persons associated to a business account). If Lithic + is performing KYB or KYC and additional verification is required we will run the + individual's or business's updated information again and return whether the + status is accepted or pending (i.e., further action required). All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the workflow is under review or further action will be needed to + complete the evaluation process. This endpoint can only be used on existing + accounts that are part of the program that the calling API key manages. + + Args: + beneficial_owner_entities: List of all entities with >25% ownership in the company. If no entity or + individual owns >25% of the company, and the largest shareholder is an entity, + please identify them in this field. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf)(Section + I) for more background. If no business owner is an entity, pass in an empty + list. However, either this parameter or `beneficial_owner_individuals` must be + populated. on entities that should be included. + + beneficial_owner_individuals: List of all individuals with >25% ownership in the company. If no entity or + individual owns >25% of the company, and the largest shareholder is an + individual, please identify them in this field. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf)(Section + I) for more background on individuals that should be included. If no individual + is an entity, pass in an empty list. However, either this parameter or + `beneficial_owner_entities` must be populated. + + business_entity: Information for business for which the account is being opened and KYB is being + run. + + control_person: An individual with significant responsibility for managing the legal entity + (e.g., a Chief Executive Officer, Chief Financial Officer, Chief Operating + Officer, Managing Member, General Partner, President, Vice President, or + Treasurer). This can be an executive, or someone who will have program-wide + access to the cards that Lithic will provide. In some cases, this individual + could also be a beneficial owner listed above. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) + (Section II) for more background. + + external_id: A user provided id that can be used to link an account holder with an external + system + + nature_of_business: Short description of the company's line of business (i.e., what does the company + do?). + + website_url: Company website URL. + + 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( + self, + account_holder_token: str, + *, + external_id: str | NotGiven = NOT_GIVEN, + individual: account_holder_update_params.KYCPatchRequestIndividual | 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, + ) -> AccountHolderUpdateResponse: + """ + Update the information associated with a particular account holder (including + business owners and control persons associated to a business account). If Lithic + is performing KYB or KYC and additional verification is required we will run the + individual's or business's updated information again and return whether the + status is accepted or pending (i.e., further action required). All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the workflow is under review or further action will be needed to + complete the evaluation process. This endpoint can only be used on existing + accounts that are part of the program that the calling API key manages. + + Args: + external_id: A user provided id that can be used to link an account holder with an external + system + + individual: Information on the individual for whom the account is being opened and KYC is + being run. + + 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( self, account_holder_token: str, *, + address: AddressUpdateParam | NotGiven = NOT_GIVEN, business_account_token: str | NotGiven = NOT_GIVEN, email: str | NotGiven = NOT_GIVEN, + first_name: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + legal_business_name: str | NotGiven = NOT_GIVEN, phone_number: 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. @@ -1039,20 +1501,35 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AccountHolderUpdateResponse: """ - Update the information associated with a particular account holder. + Update the information associated with a particular account holder (including + business owners and control persons associated to a business account). If Lithic + is performing KYB or KYC and additional verification is required we will run the + individual's or business's updated information again and return whether the + status is accepted or pending (i.e., further action required). All calls to this + endpoint will return an immediate response - though in some cases, the response + may indicate the workflow is under review or further action will be needed to + complete the evaluation process. This endpoint can only be used on existing + accounts that are part of the program that the calling API key manages. Args: - business_account_token: Only applicable for customers using the KYC-Exempt workflow to enroll authorized - users of businesses. Pass the account_token of the enrolled business associated - with the AUTHORIZED_USER in this field. + address: Allowed for: KYC-Exempt, BYO-KYC, BYO-KYB. + + business_account_token: Allowed for: KYC-Exempt, BYO-KYC. The token of the business account to which the + account holder is associated. - email: Account holder's email address. The primary purpose of this field is for - cardholder identification and verification during the digital wallet - tokenization process. + email: Allowed for all Account Holders. Account holder's email address. The primary + purpose of this field is for cardholder identification and verification during + the digital wallet tokenization process. - phone_number: Account holder's phone number, entered in E.164 format. The primary purpose of - this field is for cardholder identification and verification during the digital - wallet tokenization process. + first_name: Allowed for KYC-Exempt, BYO-KYC. Account holder's first name. + + last_name: Allowed for KYC-Exempt, BYO-KYC. Account holder's last name. + + legal_business_name: Allowed for BYO-KYB. Legal business name of the account holder. + + phone_number: Allowed for all Account Holders. Account holder's phone number, entered in E.164 + format. The primary purpose of this field is for cardholder identification and + verification during the digital wallet tokenization process. extra_headers: Send extra headers @@ -1062,32 +1539,86 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + async def update( + self, + account_holder_token: str, + *, + beneficial_owner_entities: Iterable[account_holder_update_params.KYBPatchRequestBeneficialOwnerEntity] + | NotGiven = NOT_GIVEN, + beneficial_owner_individuals: Iterable[account_holder_update_params.KYBPatchRequestBeneficialOwnerIndividual] + | NotGiven = NOT_GIVEN, + business_entity: account_holder_update_params.KYBPatchRequestBusinessEntity | NotGiven = NOT_GIVEN, + control_person: account_holder_update_params.KYBPatchRequestControlPerson | NotGiven = NOT_GIVEN, + external_id: str | NotGiven = NOT_GIVEN, + nature_of_business: str | NotGiven = NOT_GIVEN, + website_url: str | NotGiven = NOT_GIVEN, + individual: account_holder_update_params.KYCPatchRequestIndividual | NotGiven = NOT_GIVEN, + address: AddressUpdateParam | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + first_name: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + legal_business_name: str | NotGiven = NOT_GIVEN, + phone_number: 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, + ) -> AccountHolderUpdateResponse: if not account_holder_token: raise ValueError( f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" ) - return await self._patch( - f"/account_holders/{account_holder_token}", - body=maybe_transform( - { - "business_account_token": business_account_token, - "email": email, - "phone_number": phone_number, - }, - account_holder_update_params.AccountHolderUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + return cast( + AccountHolderUpdateResponse, + await self._patch( + f"/v1/account_holders/{account_holder_token}", + body=await async_maybe_transform( + { + "beneficial_owner_entities": beneficial_owner_entities, + "beneficial_owner_individuals": beneficial_owner_individuals, + "business_entity": business_entity, + "control_person": control_person, + "external_id": external_id, + "nature_of_business": nature_of_business, + "website_url": website_url, + "individual": individual, + "address": address, + "business_account_token": business_account_token, + "email": email, + "first_name": first_name, + "last_name": last_name, + "legal_business_name": legal_business_name, + "phone_number": phone_number, + }, + account_holder_update_params.AccountHolderUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, AccountHolderUpdateResponse + ), # Union types cannot be passed in as arguments in the type system ), - cast_to=AccountHolderUpdateResponse, ) def list( self, *, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, ending_before: str | NotGiven = NOT_GIVEN, external_id: str | NotGiven = NOT_GIVEN, + first_name: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + legal_business_name: str | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, + phone_number: str | NotGiven = NOT_GIVEN, starting_after: 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. @@ -1101,13 +1632,33 @@ def list( evaluation status. Args: + begin: Date string in RFC 3339 format. Only entries created after the specified time + will be included. UTC time zone. + + email: Email address of the account holder. The query must be an exact match, case + insensitive. + + end: Date string in RFC 3339 format. Only entries created before the specified time + will be included. UTC time zone. + ending_before: A cursor representing an item's token before which a page of results should end. Used to retrieve the previous page of results before this item. external_id: If applicable, represents the external_id associated with the account_holder. + first_name: (Individual Account Holders only) The first name of the account holder. The + query is case insensitive and supports partial matches. + + last_name: (Individual Account Holders only) The last name of the account holder. The query + is case insensitive and supports partial matches. + + legal_business_name: (Business Account Holders only) The legal business name of the account holder. + The query is case insensitive and supports partial matches. + limit: The number of account_holders to limit the response to. + phone_number: Phone number of the account holder. The query must be an exact match. + starting_after: A cursor representing an item's token after which a page of results should begin. Used to retrieve the next page of results after this item. @@ -1120,7 +1671,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/account_holders", + "/v1/account_holders", page=AsyncSinglePage[AccountHolder], options=make_request_options( extra_headers=extra_headers, @@ -1129,9 +1680,16 @@ def list( timeout=timeout, query=maybe_transform( { + "begin": begin, + "email": email, + "end": end, "ending_before": ending_before, "external_id": external_id, + "first_name": first_name, + "last_name": last_name, + "legal_business_name": legal_business_name, "limit": limit, + "phone_number": phone_number, "starting_after": starting_after, }, account_holder_list_params.AccountHolderListParams, @@ -1182,46 +1740,42 @@ async def list_documents( f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" ) return await self._get( - f"/account_holders/{account_holder_token}/documents", + f"/v1/account_holders/{account_holder_token}/documents", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=AccountHolderListDocumentsResponse, ) - async def resubmit( + async def retrieve_document( self, - account_holder_token: str, + document_token: str, *, - individual: account_holder_resubmit_params.Individual, - tos_timestamp: str, - workflow: Literal["KYC_ADVANCED"], + account_holder_token: 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, - ) -> AccountHolder: - """Resubmit a KYC submission. - - This endpoint should be used in cases where a KYC - submission returned a `PENDING_RESUBMIT` result, meaning one or more critical - KYC fields may have been mis-entered and the individual's identity has not yet - been successfully verified. This step must be completed in order to proceed with - the KYC evaluation. + ) -> Document: + """ + Check the status of an account holder document upload, or retrieve the upload + URLs to process your image uploads. - Two resubmission attempts are permitted via this endpoint before a `REJECTED` - status is returned and the account creation process is ended. + Note that this is not equivalent to checking the status of the KYC evaluation + overall (a document may be successfully uploaded but not be sufficient for KYC + to pass). - Args: - individual: Information on individual for whom the account is being opened and KYC is being - re-run. + In the event your upload URLs have expired, calling this endpoint will refresh + them. Similarly, in the event a document upload has failed, you can use this + endpoint to get a new upload URL for the failed image upload. - tos_timestamp: An RFC 3339 timestamp indicating when the account holder accepted the applicable - legal agreements (e.g., cardholder terms) as agreed upon during API customer's - implementation with Lithic. + When a new account holder document upload is generated for a failed attempt, the + response will show an additional entry in the `required_document_uploads` array + in a `PENDING` state for the corresponding `image_type`. + Args: extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1234,51 +1788,128 @@ async def resubmit( raise ValueError( f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" ) + if not document_token: + raise ValueError(f"Expected a non-empty value for `document_token` but received {document_token!r}") + return await self._get( + f"/v1/account_holders/{account_holder_token}/documents/{document_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Document, + ) + + async def simulate_enrollment_document_review( + self, + *, + document_upload_token: str, + status: Literal["UPLOADED", "ACCEPTED", "REJECTED", "PARTIAL_APPROVAL"], + accepted_entity_status_reasons: List[str] | NotGiven = NOT_GIVEN, + status_reason: Literal[ + "DOCUMENT_MISSING_REQUIRED_DATA", + "DOCUMENT_UPLOAD_TOO_BLURRY", + "FILE_SIZE_TOO_LARGE", + "INVALID_DOCUMENT_TYPE", + "INVALID_DOCUMENT_UPLOAD", + "INVALID_ENTITY", + "DOCUMENT_EXPIRED", + "DOCUMENT_ISSUED_GREATER_THAN_30_DAYS", + "DOCUMENT_TYPE_NOT_SUPPORTED", + "UNKNOWN_FAILURE_REASON", + "UNKNOWN_ERROR", + ] + | 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, + ) -> Document: + """ + Simulates a review for an account holder document upload. + + Args: + document_upload_token: The account holder document upload which to perform the simulation upon. + + status: An account holder document's upload status for use within the simulation. + + accepted_entity_status_reasons: A list of status reasons associated with a KYB account holder in PENDING_REVIEW + + status_reason: Status reason that will be associated with the simulated account holder status. + Only required for a `REJECTED` status or `PARTIAL_APPROVAL` status. + + 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( - f"/account_holders/{account_holder_token}/resubmit", - body=maybe_transform( + "/v1/simulate/account_holders/enrollment_document_review", + body=await async_maybe_transform( { - "individual": individual, - "tos_timestamp": tos_timestamp, - "workflow": workflow, + "document_upload_token": document_upload_token, + "status": status, + "accepted_entity_status_reasons": accepted_entity_status_reasons, + "status_reason": status_reason, }, - account_holder_resubmit_params.AccountHolderResubmitParams, + account_holder_simulate_enrollment_document_review_params.AccountHolderSimulateEnrollmentDocumentReviewParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AccountHolder, + cast_to=Document, ) - async def retrieve_document( + async def simulate_enrollment_review( self, - document_token: str, *, - account_holder_token: str, + account_holder_token: str | NotGiven = NOT_GIVEN, + status: Literal["ACCEPTED", "REJECTED"] | NotGiven = NOT_GIVEN, + status_reasons: List[ + Literal[ + "PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ADDRESS_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_NAME_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_BUSINESS_OFFICERS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_SOS_FILING_INACTIVE", + "PRIMARY_BUSINESS_ENTITY_SOS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_CMRA_FAILURE", + "PRIMARY_BUSINESS_ENTITY_WATCHLIST_FAILURE", + "PRIMARY_BUSINESS_ENTITY_REGISTERED_AGENT_FAILURE", + "CONTROL_PERSON_BLOCKLIST_ALERT_FAILURE", + "CONTROL_PERSON_ID_VERIFICATION_FAILURE", + "CONTROL_PERSON_DOB_VERIFICATION_FAILURE", + "CONTROL_PERSON_NAME_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_DOB_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_BLOCKLIST_ALERT_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_ID_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_NAME_VERIFICATION_FAILURE", + ] + ] + | 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, - ) -> AccountHolderDocument: - """ - Check the status of an account holder document upload, or retrieve the upload - URLs to process your image uploads. + ) -> AccountHolderSimulateEnrollmentReviewResponse: + """Simulates an enrollment review for an account holder. - Note that this is not equivalent to checking the status of the KYC evaluation - overall (a document may be successfully uploaded but not be sufficient for KYC - to pass). + This endpoint is only + applicable for workflows that may required intervention such as `KYB_BASIC`. - In the event your upload URLs have expired, calling this endpoint will refresh - them. Similarly, in the event a document upload has failed, you can use this - endpoint to get a new upload URL for the failed image upload. + Args: + account_holder_token: The account holder which to perform the simulation upon. - When a new account holder document upload is generated for a failed attempt, the - response will show an additional entry in the `required_document_uploads` array - in a `PENDING` state for the corresponding `image_type`. + status: An account holder's status for use within the simulation. + + status_reasons: Status reason that will be associated with the simulated account holder status. + Only required for a `REJECTED` status. - Args: extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1287,32 +1918,55 @@ async def retrieve_document( timeout: Override the client-level default timeout for this request, in seconds """ - if not account_holder_token: - raise ValueError( - f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" - ) - if not document_token: - raise ValueError(f"Expected a non-empty value for `document_token` but received {document_token!r}") - return await self._get( - f"/account_holders/{account_holder_token}/documents/{document_token}", + return await self._post( + "/v1/simulate/account_holders/enrollment_review", + body=await async_maybe_transform( + { + "account_holder_token": account_holder_token, + "status": status, + "status_reasons": status_reasons, + }, + account_holder_simulate_enrollment_review_params.AccountHolderSimulateEnrollmentReviewParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AccountHolderDocument, + cast_to=AccountHolderSimulateEnrollmentReviewResponse, ) async def upload_document( self, account_holder_token: str, *, - document_type: Literal["commercial_license", "drivers_license", "passport", "passport_card", "visa"], + document_type: Literal[ + "EIN_LETTER", + "TAX_RETURN", + "OPERATING_AGREEMENT", + "CERTIFICATE_OF_FORMATION", + "DRIVERS_LICENSE", + "PASSPORT", + "PASSPORT_CARD", + "CERTIFICATE_OF_GOOD_STANDING", + "ARTICLES_OF_INCORPORATION", + "ARTICLES_OF_ORGANIZATION", + "BYLAWS", + "GOVERNMENT_BUSINESS_LICENSE", + "PARTNERSHIP_AGREEMENT", + "SS4_FORM", + "BANK_STATEMENT", + "UTILITY_BILL_STATEMENT", + "SSN_CARD", + "ITIN_LETTER", + "FINCEN_BOI_REPORT", + ], + entity_token: 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, - ) -> AccountHolderDocument: + ) -> Document: """ Use this endpoint to identify which type of supported government-issued documentation you will upload for further verification. It will return two URLs @@ -1334,7 +1988,9 @@ async def upload_document( verification. Args: - document_type: Type of the document to upload. + document_type: The type of document to upload + + entity_token: Globally unique identifier for the entity. extra_headers: Send extra headers @@ -1349,15 +2005,18 @@ async def upload_document( f"Expected a non-empty value for `account_holder_token` but received {account_holder_token!r}" ) return await self._post( - f"/account_holders/{account_holder_token}/documents", - body=maybe_transform( - {"document_type": document_type}, + f"/v1/account_holders/{account_holder_token}/documents", + body=await async_maybe_transform( + { + "document_type": document_type, + "entity_token": entity_token, + }, account_holder_upload_document_params.AccountHolderUploadDocumentParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AccountHolderDocument, + cast_to=Document, ) @@ -1380,12 +2039,15 @@ def __init__(self, account_holders: AccountHolders) -> None: self.list_documents = _legacy_response.to_raw_response_wrapper( account_holders.list_documents, ) - self.resubmit = _legacy_response.to_raw_response_wrapper( - account_holders.resubmit, - ) self.retrieve_document = _legacy_response.to_raw_response_wrapper( account_holders.retrieve_document, ) + self.simulate_enrollment_document_review = _legacy_response.to_raw_response_wrapper( + account_holders.simulate_enrollment_document_review, + ) + self.simulate_enrollment_review = _legacy_response.to_raw_response_wrapper( + account_holders.simulate_enrollment_review, + ) self.upload_document = _legacy_response.to_raw_response_wrapper( account_holders.upload_document, ) @@ -1410,12 +2072,15 @@ def __init__(self, account_holders: AsyncAccountHolders) -> None: self.list_documents = _legacy_response.async_to_raw_response_wrapper( account_holders.list_documents, ) - self.resubmit = _legacy_response.async_to_raw_response_wrapper( - account_holders.resubmit, - ) self.retrieve_document = _legacy_response.async_to_raw_response_wrapper( account_holders.retrieve_document, ) + self.simulate_enrollment_document_review = _legacy_response.async_to_raw_response_wrapper( + account_holders.simulate_enrollment_document_review, + ) + self.simulate_enrollment_review = _legacy_response.async_to_raw_response_wrapper( + account_holders.simulate_enrollment_review, + ) self.upload_document = _legacy_response.async_to_raw_response_wrapper( account_holders.upload_document, ) @@ -1440,12 +2105,15 @@ def __init__(self, account_holders: AccountHolders) -> None: self.list_documents = to_streamed_response_wrapper( account_holders.list_documents, ) - self.resubmit = to_streamed_response_wrapper( - account_holders.resubmit, - ) self.retrieve_document = to_streamed_response_wrapper( account_holders.retrieve_document, ) + self.simulate_enrollment_document_review = to_streamed_response_wrapper( + account_holders.simulate_enrollment_document_review, + ) + self.simulate_enrollment_review = to_streamed_response_wrapper( + account_holders.simulate_enrollment_review, + ) self.upload_document = to_streamed_response_wrapper( account_holders.upload_document, ) @@ -1470,12 +2138,15 @@ def __init__(self, account_holders: AsyncAccountHolders) -> None: self.list_documents = async_to_streamed_response_wrapper( account_holders.list_documents, ) - self.resubmit = async_to_streamed_response_wrapper( - account_holders.resubmit, - ) self.retrieve_document = async_to_streamed_response_wrapper( account_holders.retrieve_document, ) + self.simulate_enrollment_document_review = async_to_streamed_response_wrapper( + account_holders.simulate_enrollment_document_review, + ) + self.simulate_enrollment_review = async_to_streamed_response_wrapper( + account_holders.simulate_enrollment_review, + ) self.upload_document = async_to_streamed_response_wrapper( account_holders.upload_document, ) diff --git a/src/lithic/resources/accounts/accounts.py b/src/lithic/resources/accounts.py similarity index 79% rename from src/lithic/resources/accounts/accounts.py rename to src/lithic/resources/accounts.py index 74547a0d..076386f7 100644 --- a/src/lithic/resources/accounts/accounts.py +++ b/src/lithic/resources/accounts.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -8,41 +8,42 @@ import httpx -from ... import _legacy_response -from ...types import Account, AccountSpendLimits, account_list_params, account_update_params -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) -from .credit_configurations import ( - CreditConfigurations, - AsyncCreditConfigurations, - CreditConfigurationsWithRawResponse, - AsyncCreditConfigurationsWithRawResponse, - CreditConfigurationsWithStreamingResponse, - AsyncCreditConfigurationsWithStreamingResponse, +from .. import _legacy_response +from ..types import account_list_params, account_update_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + maybe_transform, + async_maybe_transform, ) +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.account import Account +from ..types.account_spend_limits import AccountSpendLimits __all__ = ["Accounts", "AsyncAccounts"] class Accounts(SyncAPIResource): - @cached_property - def credit_configurations(self) -> CreditConfigurations: - return CreditConfigurations(self._client) - @cached_property def with_raw_response(self) -> AccountsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AccountsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AccountsWithStreamingResponse(self) def retrieve( @@ -71,7 +72,7 @@ def retrieve( if not account_token: raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") return self._get( - f"/accounts/{account_token}", + f"/v1/accounts/{account_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -94,33 +95,33 @@ def update( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Account: - """Update account configuration such as spend limits and verification address. + """Update account configuration such as state or spend limits. - Can - only be run on accounts that are part of the program managed by this API key. - - Accounts that are in the `PAUSED` state will not be able to transact or create - new cards. + Can only be run on + accounts that are part of the program managed by this API key. Accounts that are + in the `PAUSED` state will not be able to transact or create new cards. Args: - daily_spend_limit: Amount (in cents) for the account's daily spend limit. By default the daily - spend limit is set to $1,250. + daily_spend_limit: Amount (in cents) for the account's daily spend limit (e.g. 100000 would be a + $1,000 limit). By default the daily spend limit is set to $1,250. - lifetime_spend_limit: Amount (in cents) for the account's lifetime spend limit. Once this limit is - reached, no transactions will be accepted on any card created for this account - until the limit is updated. Note that a spend limit of 0 is effectively no - limit, and should only be used to reset or remove a prior limit. Only a limit of - 1 or above will result in declined transactions due to checks against the - account limit. This behavior differs from the daily spend limit and the monthly - spend limit. + lifetime_spend_limit: Amount (in cents) for the account's lifetime spend limit (e.g. 100000 would be a + $1,000 limit). Once this limit is reached, no transactions will be accepted on + any card created for this account until the limit is updated. Note that a spend + limit of 0 is effectively no limit, and should only be used to reset or remove a + prior limit. Only a limit of 1 or above will result in declined transactions due + to checks against the account limit. This behavior differs from the daily spend + limit and the monthly spend limit. - monthly_spend_limit: Amount (in cents) for the account's monthly spend limit. By default the monthly - spend limit is set to $5,000. + monthly_spend_limit: Amount (in cents) for the account's monthly spend limit (e.g. 100000 would be a + $1,000 limit). By default the monthly spend limit is set to $5,000. state: Account states. verification_address: Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. + transactions if enabled via Auth Rules. This field is deprecated as AVS checks + are no longer supported by Authorization Rules. The field will be removed from + the schema in a future release. extra_headers: Send extra headers @@ -133,7 +134,7 @@ def update( if not account_token: raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") return self._patch( - f"/accounts/{account_token}", + f"/v1/accounts/{account_token}", body=maybe_transform( { "daily_spend_limit": daily_spend_limit, @@ -193,7 +194,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/accounts", + "/v1/accounts", page=SyncCursorPage[Account], options=make_request_options( extra_headers=extra_headers, @@ -244,7 +245,7 @@ def retrieve_spend_limits( if not account_token: raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") return self._get( - f"/accounts/{account_token}/spend_limits", + f"/v1/accounts/{account_token}/spend_limits", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -253,16 +254,23 @@ def retrieve_spend_limits( class AsyncAccounts(AsyncAPIResource): - @cached_property - def credit_configurations(self) -> AsyncCreditConfigurations: - return AsyncCreditConfigurations(self._client) - @cached_property def with_raw_response(self) -> AsyncAccountsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncAccountsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncAccountsWithStreamingResponse(self) async def retrieve( @@ -291,7 +299,7 @@ async def retrieve( if not account_token: raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") return await self._get( - f"/accounts/{account_token}", + f"/v1/accounts/{account_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -314,33 +322,33 @@ async def update( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Account: - """Update account configuration such as spend limits and verification address. + """Update account configuration such as state or spend limits. - Can - only be run on accounts that are part of the program managed by this API key. - - Accounts that are in the `PAUSED` state will not be able to transact or create - new cards. + Can only be run on + accounts that are part of the program managed by this API key. Accounts that are + in the `PAUSED` state will not be able to transact or create new cards. Args: - daily_spend_limit: Amount (in cents) for the account's daily spend limit. By default the daily - spend limit is set to $1,250. + daily_spend_limit: Amount (in cents) for the account's daily spend limit (e.g. 100000 would be a + $1,000 limit). By default the daily spend limit is set to $1,250. - lifetime_spend_limit: Amount (in cents) for the account's lifetime spend limit. Once this limit is - reached, no transactions will be accepted on any card created for this account - until the limit is updated. Note that a spend limit of 0 is effectively no - limit, and should only be used to reset or remove a prior limit. Only a limit of - 1 or above will result in declined transactions due to checks against the - account limit. This behavior differs from the daily spend limit and the monthly - spend limit. + lifetime_spend_limit: Amount (in cents) for the account's lifetime spend limit (e.g. 100000 would be a + $1,000 limit). Once this limit is reached, no transactions will be accepted on + any card created for this account until the limit is updated. Note that a spend + limit of 0 is effectively no limit, and should only be used to reset or remove a + prior limit. Only a limit of 1 or above will result in declined transactions due + to checks against the account limit. This behavior differs from the daily spend + limit and the monthly spend limit. - monthly_spend_limit: Amount (in cents) for the account's monthly spend limit. By default the monthly - spend limit is set to $5,000. + monthly_spend_limit: Amount (in cents) for the account's monthly spend limit (e.g. 100000 would be a + $1,000 limit). By default the monthly spend limit is set to $5,000. state: Account states. verification_address: Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. + transactions if enabled via Auth Rules. This field is deprecated as AVS checks + are no longer supported by Authorization Rules. The field will be removed from + the schema in a future release. extra_headers: Send extra headers @@ -353,8 +361,8 @@ async def update( if not account_token: raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") return await self._patch( - f"/accounts/{account_token}", - body=maybe_transform( + f"/v1/accounts/{account_token}", + body=await async_maybe_transform( { "daily_spend_limit": daily_spend_limit, "lifetime_spend_limit": lifetime_spend_limit, @@ -413,7 +421,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/accounts", + "/v1/accounts", page=AsyncCursorPage[Account], options=make_request_options( extra_headers=extra_headers, @@ -464,7 +472,7 @@ async def retrieve_spend_limits( if not account_token: raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") return await self._get( - f"/accounts/{account_token}/spend_limits", + f"/v1/accounts/{account_token}/spend_limits", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -489,10 +497,6 @@ def __init__(self, accounts: Accounts) -> None: accounts.retrieve_spend_limits, ) - @cached_property - def credit_configurations(self) -> CreditConfigurationsWithRawResponse: - return CreditConfigurationsWithRawResponse(self._accounts.credit_configurations) - class AsyncAccountsWithRawResponse: def __init__(self, accounts: AsyncAccounts) -> None: @@ -511,10 +515,6 @@ def __init__(self, accounts: AsyncAccounts) -> None: accounts.retrieve_spend_limits, ) - @cached_property - def credit_configurations(self) -> AsyncCreditConfigurationsWithRawResponse: - return AsyncCreditConfigurationsWithRawResponse(self._accounts.credit_configurations) - class AccountsWithStreamingResponse: def __init__(self, accounts: Accounts) -> None: @@ -533,10 +533,6 @@ def __init__(self, accounts: Accounts) -> None: accounts.retrieve_spend_limits, ) - @cached_property - def credit_configurations(self) -> CreditConfigurationsWithStreamingResponse: - return CreditConfigurationsWithStreamingResponse(self._accounts.credit_configurations) - class AsyncAccountsWithStreamingResponse: def __init__(self, accounts: AsyncAccounts) -> None: @@ -554,7 +550,3 @@ def __init__(self, accounts: AsyncAccounts) -> None: self.retrieve_spend_limits = async_to_streamed_response_wrapper( accounts.retrieve_spend_limits, ) - - @cached_property - def credit_configurations(self) -> AsyncCreditConfigurationsWithStreamingResponse: - return AsyncCreditConfigurationsWithStreamingResponse(self._accounts.credit_configurations) diff --git a/src/lithic/resources/accounts/__init__.py b/src/lithic/resources/accounts/__init__.py deleted file mode 100644 index ab103384..00000000 --- a/src/lithic/resources/accounts/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from .accounts import ( - Accounts, - AsyncAccounts, - AccountsWithRawResponse, - AsyncAccountsWithRawResponse, - AccountsWithStreamingResponse, - AsyncAccountsWithStreamingResponse, -) -from .credit_configurations import ( - CreditConfigurations, - AsyncCreditConfigurations, - CreditConfigurationsWithRawResponse, - AsyncCreditConfigurationsWithRawResponse, - CreditConfigurationsWithStreamingResponse, - AsyncCreditConfigurationsWithStreamingResponse, -) - -__all__ = [ - "CreditConfigurations", - "AsyncCreditConfigurations", - "CreditConfigurationsWithRawResponse", - "AsyncCreditConfigurationsWithRawResponse", - "CreditConfigurationsWithStreamingResponse", - "AsyncCreditConfigurationsWithStreamingResponse", - "Accounts", - "AsyncAccounts", - "AccountsWithRawResponse", - "AsyncAccountsWithRawResponse", - "AccountsWithStreamingResponse", - "AsyncAccountsWithStreamingResponse", -] diff --git a/src/lithic/resources/aggregate_balances.py b/src/lithic/resources/aggregate_balances.py index 502542ca..8dce819b 100644 --- a/src/lithic/resources/aggregate_balances.py +++ b/src/lithic/resources/aggregate_balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -7,17 +7,15 @@ import httpx from .. import _legacy_response -from ..types import AggregateBalance, aggregate_balance_list_params +from ..types import aggregate_balance_list_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncSinglePage, AsyncSinglePage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.aggregate_balance import AggregateBalance __all__ = ["AggregateBalances", "AsyncAggregateBalances"] @@ -25,10 +23,21 @@ class AggregateBalances(SyncAPIResource): @cached_property def with_raw_response(self) -> AggregateBalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AggregateBalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AggregateBalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AggregateBalancesWithStreamingResponse(self) def list( @@ -58,7 +67,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/aggregate_balances", + "/v1/aggregate_balances", page=SyncSinglePage[AggregateBalance], options=make_request_options( extra_headers=extra_headers, @@ -77,10 +86,21 @@ def list( class AsyncAggregateBalances(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncAggregateBalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncAggregateBalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAggregateBalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncAggregateBalancesWithStreamingResponse(self) def list( @@ -110,7 +130,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/aggregate_balances", + "/v1/aggregate_balances", page=AsyncSinglePage[AggregateBalance], options=make_request_options( extra_headers=extra_headers, diff --git a/src/lithic/resources/auth_rules.py b/src/lithic/resources/auth_rules.py deleted file mode 100644 index 159f0a03..00000000 --- a/src/lithic/resources/auth_rules.py +++ /dev/null @@ -1,795 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import List - -import httpx - -from .. import _legacy_response -from ..types import ( - AuthRule, - AuthRuleRemoveResponse, - AuthRuleRetrieveResponse, - auth_rule_list_params, - auth_rule_apply_params, - auth_rule_create_params, - auth_rule_remove_params, - auth_rule_update_params, -) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..pagination import SyncCursorPage, AsyncCursorPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) - -__all__ = ["AuthRules", "AsyncAuthRules"] - - -class AuthRules(SyncAPIResource): - @cached_property - def with_raw_response(self) -> AuthRulesWithRawResponse: - return AuthRulesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AuthRulesWithStreamingResponse: - return AuthRulesWithStreamingResponse(self) - - def create( - self, - *, - account_tokens: List[str] | NotGiven = NOT_GIVEN, - allowed_countries: List[str] | NotGiven = NOT_GIVEN, - allowed_mcc: List[str] | NotGiven = NOT_GIVEN, - blocked_countries: List[str] | NotGiven = NOT_GIVEN, - blocked_mcc: List[str] | NotGiven = NOT_GIVEN, - card_tokens: List[str] | NotGiven = NOT_GIVEN, - program_level: bool | 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, - ) -> AuthRule: - """ - Creates an authorization rule (Auth Rule) and applies it at the program, - account, or card level. - - Args: - account_tokens: Array of account_token(s) identifying the accounts that the Auth Rule applies - to. Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - - allowed_countries: Countries in which the Auth Rule permits transactions. Note that Lithic - maintains a list of countries in which all transactions are blocked; "allowing" - those countries in an Auth Rule does not override the Lithic-wide restrictions. - - allowed_mcc: Merchant category codes for which the Auth Rule permits transactions. - - blocked_countries: Countries in which the Auth Rule automatically declines transactions. - - blocked_mcc: Merchant category codes for which the Auth Rule automatically declines - transactions. - - card_tokens: Array of card_token(s) identifying the cards that the Auth Rule applies to. Note - that only this field or `account_tokens` can be provided for a given Auth Rule. - - program_level: Boolean indicating whether the Auth Rule is applied at the program level. - - 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( - "/auth_rules", - body=maybe_transform( - { - "account_tokens": account_tokens, - "allowed_countries": allowed_countries, - "allowed_mcc": allowed_mcc, - "blocked_countries": blocked_countries, - "blocked_mcc": blocked_mcc, - "card_tokens": card_tokens, - "program_level": program_level, - }, - auth_rule_create_params.AuthRuleCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRule, - ) - - def retrieve( - self, - auth_rule_token: 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, - ) -> AuthRuleRetrieveResponse: - """ - Detail the properties and entities (program, accounts, and cards) associated - with an existing authorization rule (Auth Rule). - - 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 auth_rule_token: - raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") - return self._get( - f"/auth_rules/{auth_rule_token}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRuleRetrieveResponse, - ) - - def update( - self, - auth_rule_token: str, - *, - allowed_countries: List[str] | NotGiven = NOT_GIVEN, - allowed_mcc: List[str] | NotGiven = NOT_GIVEN, - blocked_countries: List[str] | NotGiven = NOT_GIVEN, - blocked_mcc: List[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, - ) -> AuthRule: - """ - Update the properties associated with an existing authorization rule (Auth - Rule). - - Args: - allowed_countries: Array of country codes for which the Auth Rule will permit transactions. Note - that only this field or `blocked_countries` can be used for a given Auth Rule. - - allowed_mcc: Array of merchant category codes for which the Auth Rule will permit - transactions. Note that only this field or `blocked_mcc` can be used for a given - Auth Rule. - - blocked_countries: Array of country codes for which the Auth Rule will automatically decline - transactions. Note that only this field or `allowed_countries` can be used for a - given Auth Rule. - - blocked_mcc: Array of merchant category codes for which the Auth Rule will automatically - decline transactions. Note that only this field or `allowed_mcc` can be used for - a given Auth Rule. - - 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 auth_rule_token: - raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") - return self._put( - f"/auth_rules/{auth_rule_token}", - body=maybe_transform( - { - "allowed_countries": allowed_countries, - "allowed_mcc": allowed_mcc, - "blocked_countries": blocked_countries, - "blocked_mcc": blocked_mcc, - }, - auth_rule_update_params.AuthRuleUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRule, - ) - - def list( - self, - *, - ending_before: str | NotGiven = NOT_GIVEN, - page_size: int | NotGiven = NOT_GIVEN, - starting_after: 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, - ) -> SyncCursorPage[AuthRule]: - """ - Return all of the Auth Rules under the program. - - Args: - ending_before: A cursor representing an item's token before which a page of results should end. - Used to retrieve the previous page of results before this item. - - page_size: Page size (for pagination). - - starting_after: A cursor representing an item's token after which a page of results should - begin. Used to retrieve the next page of results after this item. - - 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( - "/auth_rules", - page=SyncCursorPage[AuthRule], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "ending_before": ending_before, - "page_size": page_size, - "starting_after": starting_after, - }, - auth_rule_list_params.AuthRuleListParams, - ), - ), - model=AuthRule, - ) - - def apply( - self, - auth_rule_token: str, - *, - account_tokens: List[str] | NotGiven = NOT_GIVEN, - card_tokens: List[str] | NotGiven = NOT_GIVEN, - program_level: bool | 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, - ) -> AuthRule: - """ - Applies an existing authorization rule (Auth Rule) to an program, account, or - card level. - - Args: - account_tokens: Array of account_token(s) identifying the accounts that the Auth Rule applies - to. Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - - card_tokens: Array of card_token(s) identifying the cards that the Auth Rule applies to. Note - that only this field or `account_tokens` can be provided for a given Auth Rule. - - program_level: Boolean indicating whether the Auth Rule is applied at the program level. - - 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 auth_rule_token: - raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") - return self._post( - f"/auth_rules/{auth_rule_token}/apply", - body=maybe_transform( - { - "account_tokens": account_tokens, - "card_tokens": card_tokens, - "program_level": program_level, - }, - auth_rule_apply_params.AuthRuleApplyParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRule, - ) - - def remove( - self, - *, - account_tokens: List[str] | NotGiven = NOT_GIVEN, - card_tokens: List[str] | NotGiven = NOT_GIVEN, - program_level: bool | 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, - ) -> AuthRuleRemoveResponse: - """ - Remove an existing authorization rule (Auth Rule) from an program, account, or - card-level. - - Args: - account_tokens: Array of account_token(s) identifying the accounts that the Auth Rule applies - to. Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - - card_tokens: Array of card_token(s) identifying the cards that the Auth Rule applies to. Note - that only this field or `account_tokens` can be provided for a given Auth Rule. - - program_level: Boolean indicating whether the Auth Rule is applied at the program level. - - 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._delete( - "/auth_rules/remove", - body=maybe_transform( - { - "account_tokens": account_tokens, - "card_tokens": card_tokens, - "program_level": program_level, - }, - auth_rule_remove_params.AuthRuleRemoveParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRuleRemoveResponse, - ) - - -class AsyncAuthRules(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncAuthRulesWithRawResponse: - return AsyncAuthRulesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAuthRulesWithStreamingResponse: - return AsyncAuthRulesWithStreamingResponse(self) - - async def create( - self, - *, - account_tokens: List[str] | NotGiven = NOT_GIVEN, - allowed_countries: List[str] | NotGiven = NOT_GIVEN, - allowed_mcc: List[str] | NotGiven = NOT_GIVEN, - blocked_countries: List[str] | NotGiven = NOT_GIVEN, - blocked_mcc: List[str] | NotGiven = NOT_GIVEN, - card_tokens: List[str] | NotGiven = NOT_GIVEN, - program_level: bool | 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, - ) -> AuthRule: - """ - Creates an authorization rule (Auth Rule) and applies it at the program, - account, or card level. - - Args: - account_tokens: Array of account_token(s) identifying the accounts that the Auth Rule applies - to. Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - - allowed_countries: Countries in which the Auth Rule permits transactions. Note that Lithic - maintains a list of countries in which all transactions are blocked; "allowing" - those countries in an Auth Rule does not override the Lithic-wide restrictions. - - allowed_mcc: Merchant category codes for which the Auth Rule permits transactions. - - blocked_countries: Countries in which the Auth Rule automatically declines transactions. - - blocked_mcc: Merchant category codes for which the Auth Rule automatically declines - transactions. - - card_tokens: Array of card_token(s) identifying the cards that the Auth Rule applies to. Note - that only this field or `account_tokens` can be provided for a given Auth Rule. - - program_level: Boolean indicating whether the Auth Rule is applied at the program level. - - 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( - "/auth_rules", - body=maybe_transform( - { - "account_tokens": account_tokens, - "allowed_countries": allowed_countries, - "allowed_mcc": allowed_mcc, - "blocked_countries": blocked_countries, - "blocked_mcc": blocked_mcc, - "card_tokens": card_tokens, - "program_level": program_level, - }, - auth_rule_create_params.AuthRuleCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRule, - ) - - async def retrieve( - self, - auth_rule_token: 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, - ) -> AuthRuleRetrieveResponse: - """ - Detail the properties and entities (program, accounts, and cards) associated - with an existing authorization rule (Auth Rule). - - 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 auth_rule_token: - raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") - return await self._get( - f"/auth_rules/{auth_rule_token}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRuleRetrieveResponse, - ) - - async def update( - self, - auth_rule_token: str, - *, - allowed_countries: List[str] | NotGiven = NOT_GIVEN, - allowed_mcc: List[str] | NotGiven = NOT_GIVEN, - blocked_countries: List[str] | NotGiven = NOT_GIVEN, - blocked_mcc: List[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, - ) -> AuthRule: - """ - Update the properties associated with an existing authorization rule (Auth - Rule). - - Args: - allowed_countries: Array of country codes for which the Auth Rule will permit transactions. Note - that only this field or `blocked_countries` can be used for a given Auth Rule. - - allowed_mcc: Array of merchant category codes for which the Auth Rule will permit - transactions. Note that only this field or `blocked_mcc` can be used for a given - Auth Rule. - - blocked_countries: Array of country codes for which the Auth Rule will automatically decline - transactions. Note that only this field or `allowed_countries` can be used for a - given Auth Rule. - - blocked_mcc: Array of merchant category codes for which the Auth Rule will automatically - decline transactions. Note that only this field or `allowed_mcc` can be used for - a given Auth Rule. - - 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 auth_rule_token: - raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") - return await self._put( - f"/auth_rules/{auth_rule_token}", - body=maybe_transform( - { - "allowed_countries": allowed_countries, - "allowed_mcc": allowed_mcc, - "blocked_countries": blocked_countries, - "blocked_mcc": blocked_mcc, - }, - auth_rule_update_params.AuthRuleUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRule, - ) - - def list( - self, - *, - ending_before: str | NotGiven = NOT_GIVEN, - page_size: int | NotGiven = NOT_GIVEN, - starting_after: 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, - ) -> AsyncPaginator[AuthRule, AsyncCursorPage[AuthRule]]: - """ - Return all of the Auth Rules under the program. - - Args: - ending_before: A cursor representing an item's token before which a page of results should end. - Used to retrieve the previous page of results before this item. - - page_size: Page size (for pagination). - - starting_after: A cursor representing an item's token after which a page of results should - begin. Used to retrieve the next page of results after this item. - - 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( - "/auth_rules", - page=AsyncCursorPage[AuthRule], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "ending_before": ending_before, - "page_size": page_size, - "starting_after": starting_after, - }, - auth_rule_list_params.AuthRuleListParams, - ), - ), - model=AuthRule, - ) - - async def apply( - self, - auth_rule_token: str, - *, - account_tokens: List[str] | NotGiven = NOT_GIVEN, - card_tokens: List[str] | NotGiven = NOT_GIVEN, - program_level: bool | 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, - ) -> AuthRule: - """ - Applies an existing authorization rule (Auth Rule) to an program, account, or - card level. - - Args: - account_tokens: Array of account_token(s) identifying the accounts that the Auth Rule applies - to. Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - - card_tokens: Array of card_token(s) identifying the cards that the Auth Rule applies to. Note - that only this field or `account_tokens` can be provided for a given Auth Rule. - - program_level: Boolean indicating whether the Auth Rule is applied at the program level. - - 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 auth_rule_token: - raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") - return await self._post( - f"/auth_rules/{auth_rule_token}/apply", - body=maybe_transform( - { - "account_tokens": account_tokens, - "card_tokens": card_tokens, - "program_level": program_level, - }, - auth_rule_apply_params.AuthRuleApplyParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRule, - ) - - async def remove( - self, - *, - account_tokens: List[str] | NotGiven = NOT_GIVEN, - card_tokens: List[str] | NotGiven = NOT_GIVEN, - program_level: bool | 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, - ) -> AuthRuleRemoveResponse: - """ - Remove an existing authorization rule (Auth Rule) from an program, account, or - card-level. - - Args: - account_tokens: Array of account_token(s) identifying the accounts that the Auth Rule applies - to. Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - - card_tokens: Array of card_token(s) identifying the cards that the Auth Rule applies to. Note - that only this field or `account_tokens` can be provided for a given Auth Rule. - - program_level: Boolean indicating whether the Auth Rule is applied at the program level. - - 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._delete( - "/auth_rules/remove", - body=maybe_transform( - { - "account_tokens": account_tokens, - "card_tokens": card_tokens, - "program_level": program_level, - }, - auth_rule_remove_params.AuthRuleRemoveParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AuthRuleRemoveResponse, - ) - - -class AuthRulesWithRawResponse: - def __init__(self, auth_rules: AuthRules) -> None: - self._auth_rules = auth_rules - - self.create = _legacy_response.to_raw_response_wrapper( - auth_rules.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - auth_rules.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - auth_rules.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - auth_rules.list, - ) - self.apply = _legacy_response.to_raw_response_wrapper( - auth_rules.apply, - ) - self.remove = _legacy_response.to_raw_response_wrapper( - auth_rules.remove, - ) - - -class AsyncAuthRulesWithRawResponse: - def __init__(self, auth_rules: AsyncAuthRules) -> None: - self._auth_rules = auth_rules - - self.create = _legacy_response.async_to_raw_response_wrapper( - auth_rules.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - auth_rules.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - auth_rules.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - auth_rules.list, - ) - self.apply = _legacy_response.async_to_raw_response_wrapper( - auth_rules.apply, - ) - self.remove = _legacy_response.async_to_raw_response_wrapper( - auth_rules.remove, - ) - - -class AuthRulesWithStreamingResponse: - def __init__(self, auth_rules: AuthRules) -> None: - self._auth_rules = auth_rules - - self.create = to_streamed_response_wrapper( - auth_rules.create, - ) - self.retrieve = to_streamed_response_wrapper( - auth_rules.retrieve, - ) - self.update = to_streamed_response_wrapper( - auth_rules.update, - ) - self.list = to_streamed_response_wrapper( - auth_rules.list, - ) - self.apply = to_streamed_response_wrapper( - auth_rules.apply, - ) - self.remove = to_streamed_response_wrapper( - auth_rules.remove, - ) - - -class AsyncAuthRulesWithStreamingResponse: - def __init__(self, auth_rules: AsyncAuthRules) -> None: - self._auth_rules = auth_rules - - self.create = async_to_streamed_response_wrapper( - auth_rules.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - auth_rules.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - auth_rules.update, - ) - self.list = async_to_streamed_response_wrapper( - auth_rules.list, - ) - self.apply = async_to_streamed_response_wrapper( - auth_rules.apply, - ) - self.remove = async_to_streamed_response_wrapper( - auth_rules.remove, - ) diff --git a/src/lithic/resources/auth_rules/__init__.py b/src/lithic/resources/auth_rules/__init__.py new file mode 100644 index 00000000..21d5015f --- /dev/null +++ b/src/lithic/resources/auth_rules/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .v2 import ( + V2, + AsyncV2, + V2WithRawResponse, + AsyncV2WithRawResponse, + V2WithStreamingResponse, + AsyncV2WithStreamingResponse, +) +from .auth_rules import ( + AuthRules, + AsyncAuthRules, + AuthRulesWithRawResponse, + AsyncAuthRulesWithRawResponse, + AuthRulesWithStreamingResponse, + AsyncAuthRulesWithStreamingResponse, +) + +__all__ = [ + "V2", + "AsyncV2", + "V2WithRawResponse", + "AsyncV2WithRawResponse", + "V2WithStreamingResponse", + "AsyncV2WithStreamingResponse", + "AuthRules", + "AsyncAuthRules", + "AuthRulesWithRawResponse", + "AsyncAuthRulesWithRawResponse", + "AuthRulesWithStreamingResponse", + "AsyncAuthRulesWithStreamingResponse", +] diff --git a/src/lithic/resources/auth_rules/auth_rules.py b/src/lithic/resources/auth_rules/auth_rules.py new file mode 100644 index 00000000..e7293a53 --- /dev/null +++ b/src/lithic/resources/auth_rules/auth_rules.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .v2.v2 import ( + V2, + AsyncV2, + V2WithRawResponse, + AsyncV2WithRawResponse, + V2WithStreamingResponse, + AsyncV2WithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["AuthRules", "AsyncAuthRules"] + + +class AuthRules(SyncAPIResource): + @cached_property + def v2(self) -> V2: + return V2(self._client) + + @cached_property + def with_raw_response(self) -> AuthRulesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AuthRulesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AuthRulesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AuthRulesWithStreamingResponse(self) + + +class AsyncAuthRules(AsyncAPIResource): + @cached_property + def v2(self) -> AsyncV2: + return AsyncV2(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAuthRulesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncAuthRulesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAuthRulesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncAuthRulesWithStreamingResponse(self) + + +class AuthRulesWithRawResponse: + def __init__(self, auth_rules: AuthRules) -> None: + self._auth_rules = auth_rules + + @cached_property + def v2(self) -> V2WithRawResponse: + return V2WithRawResponse(self._auth_rules.v2) + + +class AsyncAuthRulesWithRawResponse: + def __init__(self, auth_rules: AsyncAuthRules) -> None: + self._auth_rules = auth_rules + + @cached_property + def v2(self) -> AsyncV2WithRawResponse: + return AsyncV2WithRawResponse(self._auth_rules.v2) + + +class AuthRulesWithStreamingResponse: + def __init__(self, auth_rules: AuthRules) -> None: + self._auth_rules = auth_rules + + @cached_property + def v2(self) -> V2WithStreamingResponse: + return V2WithStreamingResponse(self._auth_rules.v2) + + +class AsyncAuthRulesWithStreamingResponse: + def __init__(self, auth_rules: AsyncAuthRules) -> None: + self._auth_rules = auth_rules + + @cached_property + def v2(self) -> AsyncV2WithStreamingResponse: + return AsyncV2WithStreamingResponse(self._auth_rules.v2) diff --git a/src/lithic/resources/auth_rules/v2/__init__.py b/src/lithic/resources/auth_rules/v2/__init__.py new file mode 100644 index 00000000..aa9d53c0 --- /dev/null +++ b/src/lithic/resources/auth_rules/v2/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .v2 import ( + V2, + AsyncV2, + V2WithRawResponse, + AsyncV2WithRawResponse, + V2WithStreamingResponse, + AsyncV2WithStreamingResponse, +) +from .backtests import ( + Backtests, + AsyncBacktests, + BacktestsWithRawResponse, + AsyncBacktestsWithRawResponse, + BacktestsWithStreamingResponse, + AsyncBacktestsWithStreamingResponse, +) + +__all__ = [ + "Backtests", + "AsyncBacktests", + "BacktestsWithRawResponse", + "AsyncBacktestsWithRawResponse", + "BacktestsWithStreamingResponse", + "AsyncBacktestsWithStreamingResponse", + "V2", + "AsyncV2", + "V2WithRawResponse", + "AsyncV2WithRawResponse", + "V2WithStreamingResponse", + "AsyncV2WithStreamingResponse", +] diff --git a/src/lithic/resources/auth_rules/v2/backtests.py b/src/lithic/resources/auth_rules/v2/backtests.py new file mode 100644 index 00000000..8dd61a5f --- /dev/null +++ b/src/lithic/resources/auth_rules/v2/backtests.py @@ -0,0 +1,364 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.auth_rules.v2 import backtest_create_params +from ....types.auth_rules.v2.backtest_results import BacktestResults +from ....types.auth_rules.v2.backtest_create_response import BacktestCreateResponse + +__all__ = ["Backtests", "AsyncBacktests"] + + +class Backtests(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BacktestsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return BacktestsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BacktestsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return BacktestsWithStreamingResponse(self) + + def create( + self, + auth_rule_token: str, + *, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + start: Union[str, datetime] | 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, + ) -> BacktestCreateResponse: + """ + Initiates a request to asynchronously generate a backtest for an authorization + rule. During backtesting, both the active version (if one exists) and the draft + version of the Authorization Rule are evaluated by replaying historical + transaction data against the rule's conditions. This process allows customers to + simulate and understand the effects of proposed rule changes before deployment. + The generated backtest report provides detailed results showing whether the + draft version of the Auth Rule would have approved or declined historical + transactions which were processed during the backtest period. These reports help + evaluate how changes to rule configurations might affect overall transaction + approval rates. + + The generated backtest report will be delivered asynchronously through a webhook + with `event_type` = `auth_rules.backtest_report.created`. See the docs on + setting up [webhook subscriptions](https://docs.lithic.com/docs/events-api). It + is also possible to request backtest reports on-demand through the + `/v2/auth_rules/{auth_rule_token}/backtests/{auth_rule_backtest_token}` + endpoint. + + Lithic currently supports backtesting for `CONDITIONAL_BLOCK` rules. Backtesting + for `VELOCITY_LIMIT` rules is generally not supported. In specific cases (i.e. + where Lithic has pre-calculated the requested velocity metrics for historical + transactions), a backtest may be feasible. However, such cases are uncommon and + customers should not anticipate support for velocity backtests under most + configurations. If a historical transaction does not feature the required inputs + to evaluate the rule, then it will not be included in the final backtest report. + + Args: + end: The end time of the backtest. + + start: The start time of the backtest. + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._post( + f"/v2/auth_rules/{auth_rule_token}/backtests", + body=maybe_transform( + { + "end": end, + "start": start, + }, + backtest_create_params.BacktestCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BacktestCreateResponse, + ) + + def retrieve( + self, + auth_rule_backtest_token: str, + *, + auth_rule_token: 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, + ) -> BacktestResults: + """ + Returns the backtest results of an authorization rule (if available). + + Backtesting is an asynchronous process that requires time to complete. If a + customer retrieves the backtest results using this endpoint before the report is + fully generated, the response will return null for `results.current_version` and + `results.draft_version`. Customers are advised to wait for the backtest creation + process to complete (as indicated by the webhook event + auth_rules.backtest_report.created) before retrieving results from this + endpoint. + + Backtesting is an asynchronous process, while the backtest is being processed, + results will not be available which will cause `results.current_version` and + `results.draft_version` objects to contain `null`. The entries in `results` will + also always represent the configuration of the rule at the time requests are + made to this endpoint. For example, the results for `current_version` in the + served backtest report will be consistent with which version of the rule is + currently activated in the Auth Stream, regardless of which version of the rule + was active in the Auth Stream at the time a backtest is requested. + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + if not auth_rule_backtest_token: + raise ValueError( + f"Expected a non-empty value for `auth_rule_backtest_token` but received {auth_rule_backtest_token!r}" + ) + return self._get( + f"/v2/auth_rules/{auth_rule_token}/backtests/{auth_rule_backtest_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BacktestResults, + ) + + +class AsyncBacktests(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBacktestsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncBacktestsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBacktestsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncBacktestsWithStreamingResponse(self) + + async def create( + self, + auth_rule_token: str, + *, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + start: Union[str, datetime] | 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, + ) -> BacktestCreateResponse: + """ + Initiates a request to asynchronously generate a backtest for an authorization + rule. During backtesting, both the active version (if one exists) and the draft + version of the Authorization Rule are evaluated by replaying historical + transaction data against the rule's conditions. This process allows customers to + simulate and understand the effects of proposed rule changes before deployment. + The generated backtest report provides detailed results showing whether the + draft version of the Auth Rule would have approved or declined historical + transactions which were processed during the backtest period. These reports help + evaluate how changes to rule configurations might affect overall transaction + approval rates. + + The generated backtest report will be delivered asynchronously through a webhook + with `event_type` = `auth_rules.backtest_report.created`. See the docs on + setting up [webhook subscriptions](https://docs.lithic.com/docs/events-api). It + is also possible to request backtest reports on-demand through the + `/v2/auth_rules/{auth_rule_token}/backtests/{auth_rule_backtest_token}` + endpoint. + + Lithic currently supports backtesting for `CONDITIONAL_BLOCK` rules. Backtesting + for `VELOCITY_LIMIT` rules is generally not supported. In specific cases (i.e. + where Lithic has pre-calculated the requested velocity metrics for historical + transactions), a backtest may be feasible. However, such cases are uncommon and + customers should not anticipate support for velocity backtests under most + configurations. If a historical transaction does not feature the required inputs + to evaluate the rule, then it will not be included in the final backtest report. + + Args: + end: The end time of the backtest. + + start: The start time of the backtest. + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._post( + f"/v2/auth_rules/{auth_rule_token}/backtests", + body=await async_maybe_transform( + { + "end": end, + "start": start, + }, + backtest_create_params.BacktestCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BacktestCreateResponse, + ) + + async def retrieve( + self, + auth_rule_backtest_token: str, + *, + auth_rule_token: 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, + ) -> BacktestResults: + """ + Returns the backtest results of an authorization rule (if available). + + Backtesting is an asynchronous process that requires time to complete. If a + customer retrieves the backtest results using this endpoint before the report is + fully generated, the response will return null for `results.current_version` and + `results.draft_version`. Customers are advised to wait for the backtest creation + process to complete (as indicated by the webhook event + auth_rules.backtest_report.created) before retrieving results from this + endpoint. + + Backtesting is an asynchronous process, while the backtest is being processed, + results will not be available which will cause `results.current_version` and + `results.draft_version` objects to contain `null`. The entries in `results` will + also always represent the configuration of the rule at the time requests are + made to this endpoint. For example, the results for `current_version` in the + served backtest report will be consistent with which version of the rule is + currently activated in the Auth Stream, regardless of which version of the rule + was active in the Auth Stream at the time a backtest is requested. + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + if not auth_rule_backtest_token: + raise ValueError( + f"Expected a non-empty value for `auth_rule_backtest_token` but received {auth_rule_backtest_token!r}" + ) + return await self._get( + f"/v2/auth_rules/{auth_rule_token}/backtests/{auth_rule_backtest_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BacktestResults, + ) + + +class BacktestsWithRawResponse: + def __init__(self, backtests: Backtests) -> None: + self._backtests = backtests + + self.create = _legacy_response.to_raw_response_wrapper( + backtests.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + backtests.retrieve, + ) + + +class AsyncBacktestsWithRawResponse: + def __init__(self, backtests: AsyncBacktests) -> None: + self._backtests = backtests + + self.create = _legacy_response.async_to_raw_response_wrapper( + backtests.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + backtests.retrieve, + ) + + +class BacktestsWithStreamingResponse: + def __init__(self, backtests: Backtests) -> None: + self._backtests = backtests + + self.create = to_streamed_response_wrapper( + backtests.create, + ) + self.retrieve = to_streamed_response_wrapper( + backtests.retrieve, + ) + + +class AsyncBacktestsWithStreamingResponse: + def __init__(self, backtests: AsyncBacktests) -> None: + self._backtests = backtests + + self.create = async_to_streamed_response_wrapper( + backtests.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + backtests.retrieve, + ) diff --git a/src/lithic/resources/auth_rules/v2/v2.py b/src/lithic/resources/auth_rules/v2/v2.py new file mode 100644 index 00000000..f2539db9 --- /dev/null +++ b/src/lithic/resources/auth_rules/v2/v2.py @@ -0,0 +1,1718 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, overload + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ...._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) +from .backtests import ( + Backtests, + AsyncBacktests, + BacktestsWithRawResponse, + AsyncBacktestsWithRawResponse, + BacktestsWithStreamingResponse, + AsyncBacktestsWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.auth_rules import v2_list_params, v2_apply_params, v2_draft_params, v2_create_params, v2_update_params +from ....types.auth_rules.v2_list_response import V2ListResponse +from ....types.auth_rules.v2_apply_response import V2ApplyResponse +from ....types.auth_rules.v2_draft_response import V2DraftResponse +from ....types.auth_rules.v2_create_response import V2CreateResponse +from ....types.auth_rules.v2_report_response import V2ReportResponse +from ....types.auth_rules.v2_update_response import V2UpdateResponse +from ....types.auth_rules.v2_promote_response import V2PromoteResponse +from ....types.auth_rules.v2_retrieve_response import V2RetrieveResponse + +__all__ = ["V2", "AsyncV2"] + + +class V2(SyncAPIResource): + @cached_property + def backtests(self) -> Backtests: + return Backtests(self._client) + + @cached_property + def with_raw_response(self) -> V2WithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return V2WithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> V2WithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return V2WithStreamingResponse(self) + + @overload + def create( + self, + *, + account_tokens: List[str], + name: Optional[str] | NotGiven = NOT_GIVEN, + parameters: v2_create_params.CreateAuthRuleRequestAccountTokensParameters | NotGiven = NOT_GIVEN, + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] | 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, + ) -> V2CreateResponse: + """ + Creates a new V2 authorization rule in draft mode + + Args: + account_tokens: Account tokens to which the Auth Rule applies. + + name: Auth Rule Name + + parameters: Parameters for the Auth Rule + + type: The type of Auth Rule + + 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 create( + self, + *, + card_tokens: List[str], + name: Optional[str] | NotGiven = NOT_GIVEN, + parameters: v2_create_params.CreateAuthRuleRequestCardTokensParameters | NotGiven = NOT_GIVEN, + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] | 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, + ) -> V2CreateResponse: + """ + Creates a new V2 authorization rule in draft mode + + Args: + card_tokens: Card tokens to which the Auth Rule applies. + + name: Auth Rule Name + + parameters: Parameters for the Auth Rule + + type: The type of Auth Rule + + 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 create( + self, + *, + program_level: bool, + excluded_card_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + parameters: v2_create_params.CreateAuthRuleRequestProgramLevelParameters | NotGiven = NOT_GIVEN, + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] | 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, + ) -> V2CreateResponse: + """ + Creates a new V2 authorization rule in draft mode + + Args: + program_level: Whether the Auth Rule applies to all authorizations on the card program. + + excluded_card_tokens: Card tokens to which the Auth Rule does not apply. + + name: Auth Rule Name + + parameters: Parameters for the Auth Rule + + type: The type of Auth Rule + + 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 + """ + ... + + @required_args(["account_tokens"], ["card_tokens"], ["program_level"]) + def create( + self, + *, + account_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + parameters: v2_create_params.CreateAuthRuleRequestAccountTokensParameters | NotGiven = NOT_GIVEN, + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] | NotGiven = NOT_GIVEN, + card_tokens: List[str] | NotGiven = NOT_GIVEN, + program_level: bool | NotGiven = NOT_GIVEN, + excluded_card_tokens: List[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, + ) -> V2CreateResponse: + return self._post( + "/v2/auth_rules", + body=maybe_transform( + { + "account_tokens": account_tokens, + "name": name, + "parameters": parameters, + "type": type, + "card_tokens": card_tokens, + "program_level": program_level, + "excluded_card_tokens": excluded_card_tokens, + }, + v2_create_params.V2CreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2CreateResponse, + ) + + def retrieve( + self, + auth_rule_token: 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, + ) -> V2RetrieveResponse: + """ + Fetches a V2 authorization rule by its token + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._get( + f"/v2/auth_rules/{auth_rule_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2RetrieveResponse, + ) + + @overload + def update( + self, + auth_rule_token: str, + *, + account_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + state: Literal["INACTIVE"] | 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, + ) -> V2UpdateResponse: + """ + Updates a V2 authorization rule's properties + + If `account_tokens`, `card_tokens`, `program_level`, or `excluded_card_tokens` + is provided, this will replace existing associations with the provided list of + entities. + + Args: + account_tokens: Account tokens to which the Auth Rule applies. + + name: Auth Rule Name + + state: The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + + 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( + self, + auth_rule_token: str, + *, + card_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + state: Literal["INACTIVE"] | 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, + ) -> V2UpdateResponse: + """ + Updates a V2 authorization rule's properties + + If `account_tokens`, `card_tokens`, `program_level`, or `excluded_card_tokens` + is provided, this will replace existing associations with the provided list of + entities. + + Args: + card_tokens: Card tokens to which the Auth Rule applies. + + name: Auth Rule Name + + state: The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + + 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( + self, + auth_rule_token: str, + *, + excluded_card_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + program_level: bool | NotGiven = NOT_GIVEN, + state: Literal["INACTIVE"] | 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, + ) -> V2UpdateResponse: + """ + Updates a V2 authorization rule's properties + + If `account_tokens`, `card_tokens`, `program_level`, or `excluded_card_tokens` + is provided, this will replace existing associations with the provided list of + entities. + + Args: + excluded_card_tokens: Card tokens to which the Auth Rule does not apply. + + name: Auth Rule Name + + program_level: Whether the Auth Rule applies to all authorizations on the card program. + + state: The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + + 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 + """ + ... + + def update( + self, + auth_rule_token: str, + *, + account_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + state: Literal["INACTIVE"] | NotGiven = NOT_GIVEN, + card_tokens: List[str] | NotGiven = NOT_GIVEN, + excluded_card_tokens: List[str] | NotGiven = NOT_GIVEN, + program_level: bool | 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, + ) -> V2UpdateResponse: + if not auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._patch( + f"/v2/auth_rules/{auth_rule_token}", + body=maybe_transform( + { + "account_tokens": account_tokens, + "name": name, + "state": state, + "card_tokens": card_tokens, + "excluded_card_tokens": excluded_card_tokens, + "program_level": program_level, + }, + v2_update_params.V2UpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2UpdateResponse, + ) + + def list( + self, + *, + account_token: str | NotGiven = NOT_GIVEN, + card_token: str | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: 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, + ) -> SyncCursorPage[V2ListResponse]: + """ + Lists V2 authorization rules + + Args: + account_token: Only return Authorization Rules that are bound to the provided account token. + + card_token: Only return Authorization Rules that are bound to the provided card token. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + page_size: Page size (for pagination). + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + 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( + "/v2/auth_rules", + page=SyncCursorPage[V2ListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "account_token": account_token, + "card_token": card_token, + "ending_before": ending_before, + "page_size": page_size, + "starting_after": starting_after, + }, + v2_list_params.V2ListParams, + ), + ), + model=V2ListResponse, + ) + + def delete( + self, + auth_rule_token: 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: + """ + Deletes a V2 authorization rule + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._delete( + f"/v2/auth_rules/{auth_rule_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + @overload + def apply( + self, + auth_rule_token: str, + *, + account_tokens: List[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, + ) -> V2ApplyResponse: + """ + Associates a V2 authorization rule with a card program, the provided account(s) + or card(s). + + Prefer using the `PATCH` method for this operation. + + Args: + account_tokens: Account tokens to which the Auth Rule applies. + + 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 apply( + self, + auth_rule_token: str, + *, + card_tokens: List[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, + ) -> V2ApplyResponse: + """ + Associates a V2 authorization rule with a card program, the provided account(s) + or card(s). + + Prefer using the `PATCH` method for this operation. + + Args: + card_tokens: Card tokens to which the Auth Rule applies. + + 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 apply( + self, + auth_rule_token: str, + *, + program_level: bool, + excluded_card_tokens: List[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, + ) -> V2ApplyResponse: + """ + Associates a V2 authorization rule with a card program, the provided account(s) + or card(s). + + Prefer using the `PATCH` method for this operation. + + Args: + program_level: Whether the Auth Rule applies to all authorizations on the card program. + + excluded_card_tokens: Card tokens to which the Auth Rule does not apply. + + 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 + """ + ... + + @required_args(["account_tokens"], ["card_tokens"], ["program_level"]) + def apply( + self, + auth_rule_token: str, + *, + account_tokens: List[str] | NotGiven = NOT_GIVEN, + card_tokens: List[str] | NotGiven = NOT_GIVEN, + program_level: bool | NotGiven = NOT_GIVEN, + excluded_card_tokens: List[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, + ) -> V2ApplyResponse: + if not auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._post( + f"/v2/auth_rules/{auth_rule_token}/apply", + body=maybe_transform( + { + "account_tokens": account_tokens, + "card_tokens": card_tokens, + "program_level": program_level, + "excluded_card_tokens": excluded_card_tokens, + }, + v2_apply_params.V2ApplyParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2ApplyResponse, + ) + + def draft( + self, + auth_rule_token: str, + *, + parameters: Optional[v2_draft_params.Parameters] | 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, + ) -> V2DraftResponse: + """ + Creates a new draft version of a rule that will be ran in shadow mode. + + This can also be utilized to reset the draft parameters, causing a draft version + to no longer be ran in shadow mode. + + Args: + parameters: Parameters for the Auth Rule + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._post( + f"/v2/auth_rules/{auth_rule_token}/draft", + body=maybe_transform({"parameters": parameters}, v2_draft_params.V2DraftParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2DraftResponse, + ) + + def promote( + self, + auth_rule_token: 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, + ) -> V2PromoteResponse: + """ + Promotes the draft version of an authorization rule to the currently active + version such that it is enforced in the authorization stream. + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._post( + f"/v2/auth_rules/{auth_rule_token}/promote", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2PromoteResponse, + ) + + def report( + self, + auth_rule_token: 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, + ) -> V2ReportResponse: + """ + Requests a performance report of an authorization rule to be asynchronously + generated. Reports can only be run on rules in draft or active mode and will + included approved and declined statistics as well as examples. The generated + report will be delivered asynchronously through a webhook with `event_type` = + `auth_rules.performance_report.created`. See the docs on setting up + [webhook subscriptions](https://docs.lithic.com/docs/events-api). + + Reports are generated based on data collected by Lithic's authorization + processing system in the trailing week. The performance of the auth rule will be + assessed on the configuration of the auth rule at the time the report is + requested. This implies that if a performance report is requested, right after + updating an auth rule, depending on the number of authorizations processed for a + card program, it may be the case that no data is available for the report. + Therefore Lithic recommends to decouple making updates to an Auth Rule, and + requesting performance reports. + + To make this concrete, consider the following example: + + 1. At time `t`, a new Auth Rule is created, and applies to all authorizations on + a card program. The Auth Rule has not yet been promoted, causing the draft + version of the rule to be applied in shadow mode. + 2. At time `t + 1 hour` a performance report is requested for the Auth Rule. + This performance report will _only_ contain data for the Auth Rule being + executed in the window between `t` and `t + 1 hour`. This is because Lithic's + transaction processing system will only start capturing data for the Auth + Rule at the time it is created. + 3. At time `t + 2 hours` the draft version of the Auth Rule is promoted to the + active version of the Auth Rule by calling the + `/v2/auth_rules/{auth_rule_token}/promote` endpoint. If a performance report + is requested at this moment it will still only contain data for this version + of the rule, but the window of available data will now span from `t` to + `t + 2 hours`. + 4. At time `t + 3 hours` a new version of the rule is drafted by calling the + `/v2/auth_rules/{auth_rule_token}/draft` endpoint. If a performance report is + requested right at this moment, it will only contain data for authorizations + to which both the active version and the draft version is applied. Lithic + does this to ensure that performance reports represent a fair comparison + between rules. Because there may be no authorizations in this window, and + because there may be some lag before data is available in a performance + report, the requested performance report could contain no to little data. + 5. At time `t + 4 hours` another performance report is requested: this time the + performance report will contain data from the window between `t + 3 hours` + and `t + 4 hours`, for any authorizations to which both the current version + of the authorization rule (in enforcing mode) and the draft version of the + authorization rule (in shadow mode) applied. + + Note that generating a report may take up to 15 minutes and that delivery is not + guaranteed. Customers are required to have created an event subscription to + receive the webhook. Additionally, there is a delay of approximately 15 minutes + between when Lithic's transaction processing systems have processed the + transaction, and when a transaction will be included in the report. + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return self._post( + f"/v2/auth_rules/{auth_rule_token}/report", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2ReportResponse, + ) + + +class AsyncV2(AsyncAPIResource): + @cached_property + def backtests(self) -> AsyncBacktests: + return AsyncBacktests(self._client) + + @cached_property + def with_raw_response(self) -> AsyncV2WithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncV2WithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncV2WithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncV2WithStreamingResponse(self) + + @overload + async def create( + self, + *, + account_tokens: List[str], + name: Optional[str] | NotGiven = NOT_GIVEN, + parameters: v2_create_params.CreateAuthRuleRequestAccountTokensParameters | NotGiven = NOT_GIVEN, + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] | 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, + ) -> V2CreateResponse: + """ + Creates a new V2 authorization rule in draft mode + + Args: + account_tokens: Account tokens to which the Auth Rule applies. + + name: Auth Rule Name + + parameters: Parameters for the Auth Rule + + type: The type of Auth Rule + + 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 create( + self, + *, + card_tokens: List[str], + name: Optional[str] | NotGiven = NOT_GIVEN, + parameters: v2_create_params.CreateAuthRuleRequestCardTokensParameters | NotGiven = NOT_GIVEN, + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] | 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, + ) -> V2CreateResponse: + """ + Creates a new V2 authorization rule in draft mode + + Args: + card_tokens: Card tokens to which the Auth Rule applies. + + name: Auth Rule Name + + parameters: Parameters for the Auth Rule + + type: The type of Auth Rule + + 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 create( + self, + *, + program_level: bool, + excluded_card_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + parameters: v2_create_params.CreateAuthRuleRequestProgramLevelParameters | NotGiven = NOT_GIVEN, + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] | 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, + ) -> V2CreateResponse: + """ + Creates a new V2 authorization rule in draft mode + + Args: + program_level: Whether the Auth Rule applies to all authorizations on the card program. + + excluded_card_tokens: Card tokens to which the Auth Rule does not apply. + + name: Auth Rule Name + + parameters: Parameters for the Auth Rule + + type: The type of Auth Rule + + 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 + """ + ... + + @required_args(["account_tokens"], ["card_tokens"], ["program_level"]) + async def create( + self, + *, + account_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + parameters: v2_create_params.CreateAuthRuleRequestAccountTokensParameters | NotGiven = NOT_GIVEN, + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] | NotGiven = NOT_GIVEN, + card_tokens: List[str] | NotGiven = NOT_GIVEN, + program_level: bool | NotGiven = NOT_GIVEN, + excluded_card_tokens: List[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, + ) -> V2CreateResponse: + return await self._post( + "/v2/auth_rules", + body=await async_maybe_transform( + { + "account_tokens": account_tokens, + "name": name, + "parameters": parameters, + "type": type, + "card_tokens": card_tokens, + "program_level": program_level, + "excluded_card_tokens": excluded_card_tokens, + }, + v2_create_params.V2CreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2CreateResponse, + ) + + async def retrieve( + self, + auth_rule_token: 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, + ) -> V2RetrieveResponse: + """ + Fetches a V2 authorization rule by its token + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._get( + f"/v2/auth_rules/{auth_rule_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2RetrieveResponse, + ) + + @overload + async def update( + self, + auth_rule_token: str, + *, + account_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + state: Literal["INACTIVE"] | 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, + ) -> V2UpdateResponse: + """ + Updates a V2 authorization rule's properties + + If `account_tokens`, `card_tokens`, `program_level`, or `excluded_card_tokens` + is provided, this will replace existing associations with the provided list of + entities. + + Args: + account_tokens: Account tokens to which the Auth Rule applies. + + name: Auth Rule Name + + state: The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + + 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( + self, + auth_rule_token: str, + *, + card_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + state: Literal["INACTIVE"] | 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, + ) -> V2UpdateResponse: + """ + Updates a V2 authorization rule's properties + + If `account_tokens`, `card_tokens`, `program_level`, or `excluded_card_tokens` + is provided, this will replace existing associations with the provided list of + entities. + + Args: + card_tokens: Card tokens to which the Auth Rule applies. + + name: Auth Rule Name + + state: The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + + 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( + self, + auth_rule_token: str, + *, + excluded_card_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + program_level: bool | NotGiven = NOT_GIVEN, + state: Literal["INACTIVE"] | 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, + ) -> V2UpdateResponse: + """ + Updates a V2 authorization rule's properties + + If `account_tokens`, `card_tokens`, `program_level`, or `excluded_card_tokens` + is provided, this will replace existing associations with the provided list of + entities. + + Args: + excluded_card_tokens: Card tokens to which the Auth Rule does not apply. + + name: Auth Rule Name + + program_level: Whether the Auth Rule applies to all authorizations on the card program. + + state: The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + + 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 + """ + ... + + async def update( + self, + auth_rule_token: str, + *, + account_tokens: List[str] | NotGiven = NOT_GIVEN, + name: Optional[str] | NotGiven = NOT_GIVEN, + state: Literal["INACTIVE"] | NotGiven = NOT_GIVEN, + card_tokens: List[str] | NotGiven = NOT_GIVEN, + excluded_card_tokens: List[str] | NotGiven = NOT_GIVEN, + program_level: bool | 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, + ) -> V2UpdateResponse: + if not auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._patch( + f"/v2/auth_rules/{auth_rule_token}", + body=await async_maybe_transform( + { + "account_tokens": account_tokens, + "name": name, + "state": state, + "card_tokens": card_tokens, + "excluded_card_tokens": excluded_card_tokens, + "program_level": program_level, + }, + v2_update_params.V2UpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2UpdateResponse, + ) + + def list( + self, + *, + account_token: str | NotGiven = NOT_GIVEN, + card_token: str | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: 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, + ) -> AsyncPaginator[V2ListResponse, AsyncCursorPage[V2ListResponse]]: + """ + Lists V2 authorization rules + + Args: + account_token: Only return Authorization Rules that are bound to the provided account token. + + card_token: Only return Authorization Rules that are bound to the provided card token. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + page_size: Page size (for pagination). + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + 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( + "/v2/auth_rules", + page=AsyncCursorPage[V2ListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "account_token": account_token, + "card_token": card_token, + "ending_before": ending_before, + "page_size": page_size, + "starting_after": starting_after, + }, + v2_list_params.V2ListParams, + ), + ), + model=V2ListResponse, + ) + + async def delete( + self, + auth_rule_token: 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: + """ + Deletes a V2 authorization rule + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._delete( + f"/v2/auth_rules/{auth_rule_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + @overload + async def apply( + self, + auth_rule_token: str, + *, + account_tokens: List[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, + ) -> V2ApplyResponse: + """ + Associates a V2 authorization rule with a card program, the provided account(s) + or card(s). + + Prefer using the `PATCH` method for this operation. + + Args: + account_tokens: Account tokens to which the Auth Rule applies. + + 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 apply( + self, + auth_rule_token: str, + *, + card_tokens: List[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, + ) -> V2ApplyResponse: + """ + Associates a V2 authorization rule with a card program, the provided account(s) + or card(s). + + Prefer using the `PATCH` method for this operation. + + Args: + card_tokens: Card tokens to which the Auth Rule applies. + + 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 apply( + self, + auth_rule_token: str, + *, + program_level: bool, + excluded_card_tokens: List[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, + ) -> V2ApplyResponse: + """ + Associates a V2 authorization rule with a card program, the provided account(s) + or card(s). + + Prefer using the `PATCH` method for this operation. + + Args: + program_level: Whether the Auth Rule applies to all authorizations on the card program. + + excluded_card_tokens: Card tokens to which the Auth Rule does not apply. + + 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 + """ + ... + + @required_args(["account_tokens"], ["card_tokens"], ["program_level"]) + async def apply( + self, + auth_rule_token: str, + *, + account_tokens: List[str] | NotGiven = NOT_GIVEN, + card_tokens: List[str] | NotGiven = NOT_GIVEN, + program_level: bool | NotGiven = NOT_GIVEN, + excluded_card_tokens: List[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, + ) -> V2ApplyResponse: + if not auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._post( + f"/v2/auth_rules/{auth_rule_token}/apply", + body=await async_maybe_transform( + { + "account_tokens": account_tokens, + "card_tokens": card_tokens, + "program_level": program_level, + "excluded_card_tokens": excluded_card_tokens, + }, + v2_apply_params.V2ApplyParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2ApplyResponse, + ) + + async def draft( + self, + auth_rule_token: str, + *, + parameters: Optional[v2_draft_params.Parameters] | 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, + ) -> V2DraftResponse: + """ + Creates a new draft version of a rule that will be ran in shadow mode. + + This can also be utilized to reset the draft parameters, causing a draft version + to no longer be ran in shadow mode. + + Args: + parameters: Parameters for the Auth Rule + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._post( + f"/v2/auth_rules/{auth_rule_token}/draft", + body=await async_maybe_transform({"parameters": parameters}, v2_draft_params.V2DraftParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2DraftResponse, + ) + + async def promote( + self, + auth_rule_token: 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, + ) -> V2PromoteResponse: + """ + Promotes the draft version of an authorization rule to the currently active + version such that it is enforced in the authorization stream. + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._post( + f"/v2/auth_rules/{auth_rule_token}/promote", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2PromoteResponse, + ) + + async def report( + self, + auth_rule_token: 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, + ) -> V2ReportResponse: + """ + Requests a performance report of an authorization rule to be asynchronously + generated. Reports can only be run on rules in draft or active mode and will + included approved and declined statistics as well as examples. The generated + report will be delivered asynchronously through a webhook with `event_type` = + `auth_rules.performance_report.created`. See the docs on setting up + [webhook subscriptions](https://docs.lithic.com/docs/events-api). + + Reports are generated based on data collected by Lithic's authorization + processing system in the trailing week. The performance of the auth rule will be + assessed on the configuration of the auth rule at the time the report is + requested. This implies that if a performance report is requested, right after + updating an auth rule, depending on the number of authorizations processed for a + card program, it may be the case that no data is available for the report. + Therefore Lithic recommends to decouple making updates to an Auth Rule, and + requesting performance reports. + + To make this concrete, consider the following example: + + 1. At time `t`, a new Auth Rule is created, and applies to all authorizations on + a card program. The Auth Rule has not yet been promoted, causing the draft + version of the rule to be applied in shadow mode. + 2. At time `t + 1 hour` a performance report is requested for the Auth Rule. + This performance report will _only_ contain data for the Auth Rule being + executed in the window between `t` and `t + 1 hour`. This is because Lithic's + transaction processing system will only start capturing data for the Auth + Rule at the time it is created. + 3. At time `t + 2 hours` the draft version of the Auth Rule is promoted to the + active version of the Auth Rule by calling the + `/v2/auth_rules/{auth_rule_token}/promote` endpoint. If a performance report + is requested at this moment it will still only contain data for this version + of the rule, but the window of available data will now span from `t` to + `t + 2 hours`. + 4. At time `t + 3 hours` a new version of the rule is drafted by calling the + `/v2/auth_rules/{auth_rule_token}/draft` endpoint. If a performance report is + requested right at this moment, it will only contain data for authorizations + to which both the active version and the draft version is applied. Lithic + does this to ensure that performance reports represent a fair comparison + between rules. Because there may be no authorizations in this window, and + because there may be some lag before data is available in a performance + report, the requested performance report could contain no to little data. + 5. At time `t + 4 hours` another performance report is requested: this time the + performance report will contain data from the window between `t + 3 hours` + and `t + 4 hours`, for any authorizations to which both the current version + of the authorization rule (in enforcing mode) and the draft version of the + authorization rule (in shadow mode) applied. + + Note that generating a report may take up to 15 minutes and that delivery is not + guaranteed. Customers are required to have created an event subscription to + receive the webhook. Additionally, there is a delay of approximately 15 minutes + between when Lithic's transaction processing systems have processed the + transaction, and when a transaction will be included in the report. + + 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 auth_rule_token: + raise ValueError(f"Expected a non-empty value for `auth_rule_token` but received {auth_rule_token!r}") + return await self._post( + f"/v2/auth_rules/{auth_rule_token}/report", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=V2ReportResponse, + ) + + +class V2WithRawResponse: + def __init__(self, v2: V2) -> None: + self._v2 = v2 + + self.create = _legacy_response.to_raw_response_wrapper( + v2.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + v2.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + v2.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + v2.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + v2.delete, + ) + self.apply = _legacy_response.to_raw_response_wrapper( + v2.apply, + ) + self.draft = _legacy_response.to_raw_response_wrapper( + v2.draft, + ) + self.promote = _legacy_response.to_raw_response_wrapper( + v2.promote, + ) + self.report = _legacy_response.to_raw_response_wrapper( + v2.report, + ) + + @cached_property + def backtests(self) -> BacktestsWithRawResponse: + return BacktestsWithRawResponse(self._v2.backtests) + + +class AsyncV2WithRawResponse: + def __init__(self, v2: AsyncV2) -> None: + self._v2 = v2 + + self.create = _legacy_response.async_to_raw_response_wrapper( + v2.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + v2.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + v2.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + v2.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + v2.delete, + ) + self.apply = _legacy_response.async_to_raw_response_wrapper( + v2.apply, + ) + self.draft = _legacy_response.async_to_raw_response_wrapper( + v2.draft, + ) + self.promote = _legacy_response.async_to_raw_response_wrapper( + v2.promote, + ) + self.report = _legacy_response.async_to_raw_response_wrapper( + v2.report, + ) + + @cached_property + def backtests(self) -> AsyncBacktestsWithRawResponse: + return AsyncBacktestsWithRawResponse(self._v2.backtests) + + +class V2WithStreamingResponse: + def __init__(self, v2: V2) -> None: + self._v2 = v2 + + self.create = to_streamed_response_wrapper( + v2.create, + ) + self.retrieve = to_streamed_response_wrapper( + v2.retrieve, + ) + self.update = to_streamed_response_wrapper( + v2.update, + ) + self.list = to_streamed_response_wrapper( + v2.list, + ) + self.delete = to_streamed_response_wrapper( + v2.delete, + ) + self.apply = to_streamed_response_wrapper( + v2.apply, + ) + self.draft = to_streamed_response_wrapper( + v2.draft, + ) + self.promote = to_streamed_response_wrapper( + v2.promote, + ) + self.report = to_streamed_response_wrapper( + v2.report, + ) + + @cached_property + def backtests(self) -> BacktestsWithStreamingResponse: + return BacktestsWithStreamingResponse(self._v2.backtests) + + +class AsyncV2WithStreamingResponse: + def __init__(self, v2: AsyncV2) -> None: + self._v2 = v2 + + self.create = async_to_streamed_response_wrapper( + v2.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + v2.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + v2.update, + ) + self.list = async_to_streamed_response_wrapper( + v2.list, + ) + self.delete = async_to_streamed_response_wrapper( + v2.delete, + ) + self.apply = async_to_streamed_response_wrapper( + v2.apply, + ) + self.draft = async_to_streamed_response_wrapper( + v2.draft, + ) + self.promote = async_to_streamed_response_wrapper( + v2.promote, + ) + self.report = async_to_streamed_response_wrapper( + v2.report, + ) + + @cached_property + def backtests(self) -> AsyncBacktestsWithStreamingResponse: + return AsyncBacktestsWithStreamingResponse(self._v2.backtests) diff --git a/src/lithic/resources/auth_stream_enrollment.py b/src/lithic/resources/auth_stream_enrollment.py index 231dcbee..330f8640 100644 --- a/src/lithic/resources/auth_stream_enrollment.py +++ b/src/lithic/resources/auth_stream_enrollment.py @@ -1,18 +1,16 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx from .. import _legacy_response -from ..types import AuthStreamSecret from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options +from ..types.auth_stream_secret import AuthStreamSecret __all__ = ["AuthStreamEnrollment", "AsyncAuthStreamEnrollment"] @@ -20,10 +18,21 @@ class AuthStreamEnrollment(SyncAPIResource): @cached_property def with_raw_response(self) -> AuthStreamEnrollmentWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AuthStreamEnrollmentWithRawResponse(self) @cached_property def with_streaming_response(self) -> AuthStreamEnrollmentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AuthStreamEnrollmentWithStreamingResponse(self) def retrieve_secret( @@ -46,7 +55,7 @@ def retrieve_secret( for more detail about verifying ASA webhooks. """ return self._get( - "/auth_stream/secret", + "/v1/auth_stream/secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -71,7 +80,7 @@ def rotate_secret( request to retrieve the new secret key. """ return self._post( - "/auth_stream/secret/rotate", + "/v1/auth_stream/secret/rotate", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -82,10 +91,21 @@ def rotate_secret( class AsyncAuthStreamEnrollment(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncAuthStreamEnrollmentWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncAuthStreamEnrollmentWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAuthStreamEnrollmentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncAuthStreamEnrollmentWithStreamingResponse(self) async def retrieve_secret( @@ -108,7 +128,7 @@ async def retrieve_secret( for more detail about verifying ASA webhooks. """ return await self._get( - "/auth_stream/secret", + "/v1/auth_stream/secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -133,7 +153,7 @@ async def rotate_secret( request to retrieve the new secret key. """ return await self._post( - "/auth_stream/secret/rotate", + "/v1/auth_stream/secret/rotate", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/lithic/resources/balances.py b/src/lithic/resources/balances.py index ff18ab1c..e9bbc5c0 100644 --- a/src/lithic/resources/balances.py +++ b/src/lithic/resources/balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,17 +9,15 @@ import httpx from .. import _legacy_response -from ..types import Balance, balance_list_params +from ..types import balance_list_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncSinglePage, AsyncSinglePage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.balance import Balance __all__ = ["Balances", "AsyncBalances"] @@ -27,10 +25,21 @@ class Balances(SyncAPIResource): @cached_property def with_raw_response(self) -> BalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return BalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> BalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return BalancesWithStreamingResponse(self) def list( @@ -38,6 +47,7 @@ def list( *, account_token: str | NotGiven = NOT_GIVEN, balance_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, financial_account_type: Literal["ISSUING", "OPERATING", "RESERVE"] | 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. @@ -47,7 +57,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncSinglePage[Balance]: """ - Get the balances for a program or a given end-user account + Get the balances for a program, business, or a given end-user account Args: account_token: List balances for all financial accounts of a given account_token. @@ -55,6 +65,8 @@ def list( balance_date: UTC date and time of the balances to retrieve. Defaults to latest available balances + business_account_token: List balances for all financial accounts of a given business_account_token. + financial_account_type: List balances for a given Financial Account type. extra_headers: Send extra headers @@ -66,7 +78,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/balances", + "/v1/balances", page=SyncSinglePage[Balance], options=make_request_options( extra_headers=extra_headers, @@ -77,6 +89,7 @@ def list( { "account_token": account_token, "balance_date": balance_date, + "business_account_token": business_account_token, "financial_account_type": financial_account_type, }, balance_list_params.BalanceListParams, @@ -89,10 +102,21 @@ def list( class AsyncBalances(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncBalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncBalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncBalancesWithStreamingResponse(self) def list( @@ -100,6 +124,7 @@ def list( *, account_token: str | NotGiven = NOT_GIVEN, balance_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, financial_account_type: Literal["ISSUING", "OPERATING", "RESERVE"] | 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. @@ -109,7 +134,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Balance, AsyncSinglePage[Balance]]: """ - Get the balances for a program or a given end-user account + Get the balances for a program, business, or a given end-user account Args: account_token: List balances for all financial accounts of a given account_token. @@ -117,6 +142,8 @@ def list( balance_date: UTC date and time of the balances to retrieve. Defaults to latest available balances + business_account_token: List balances for all financial accounts of a given business_account_token. + financial_account_type: List balances for a given Financial Account type. extra_headers: Send extra headers @@ -128,7 +155,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/balances", + "/v1/balances", page=AsyncSinglePage[Balance], options=make_request_options( extra_headers=extra_headers, @@ -139,6 +166,7 @@ def list( { "account_token": account_token, "balance_date": balance_date, + "business_account_token": business_account_token, "financial_account_type": financial_account_type, }, balance_list_params.BalanceListParams, diff --git a/src/lithic/resources/book_transfers.py b/src/lithic/resources/book_transfers.py new file mode 100644 index 00000000..6a47e684 --- /dev/null +++ b/src/lithic/resources/book_transfers.py @@ -0,0 +1,669 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from .. import _legacy_response +from ..types import book_transfer_list_params, book_transfer_create_params, book_transfer_reverse_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + maybe_transform, + async_maybe_transform, +) +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.book_transfer_response import BookTransferResponse + +__all__ = ["BookTransfers", "AsyncBookTransfers"] + + +class BookTransfers(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BookTransfersWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return BookTransfersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BookTransfersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return BookTransfersWithStreamingResponse(self) + + def create( + self, + *, + amount: int, + category: Literal["ADJUSTMENT", "BALANCE_OR_FUNDING", "DERECOGNITION", "DISPUTE", "FEE", "REWARD", "TRANSFER"], + from_financial_account_token: str, + subtype: str, + to_financial_account_token: str, + type: Literal[ + "ATM_WITHDRAWAL", + "ATM_DECLINE", + "INTERNATIONAL_ATM_WITHDRAWAL", + "INACTIVITY", + "STATEMENT", + "MONTHLY", + "QUARTERLY", + "ANNUAL", + "CUSTOMER_SERVICE", + "ACCOUNT_MAINTENANCE", + "ACCOUNT_ACTIVATION", + "ACCOUNT_CLOSURE", + "CARD_REPLACEMENT", + "CARD_DELIVERY", + "CARD_CREATE", + "CURRENCY_CONVERSION", + "INTEREST", + "LATE_PAYMENT", + "BILL_PAYMENT", + "CASH_BACK", + "ACCOUNT_TO_ACCOUNT", + "CARD_TO_CARD", + "DISBURSE", + "BILLING_ERROR", + "LOSS_WRITE_OFF", + "EXPIRED_CARD", + "EARLY_DERECOGNITION", + "ESCHEATMENT", + "INACTIVITY_FEE_DOWN", + "PROVISIONAL_CREDIT", + "DISPUTE_WON", + "TRANSFER", + ], + token: str | NotGiven = NOT_GIVEN, + memo: 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, + ) -> BookTransferResponse: + """ + Book transfer funds between two financial accounts or between a financial + account and card + + Args: + amount: Amount to be transferred in the currency’s smallest unit (e.g., cents for USD). + This should always be a positive value. + + category: Category of the book transfer + + from_financial_account_token: Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + + subtype: The program specific subtype code for the specified category/type. + + to_financial_account_token: Globally unique identifier for the financial account or card that will receive + the funds. Accepted type dependent on the program's use case. + + type: Type of book_transfer + + token: Customer-provided token that will serve as an idempotency token. This token will + become the transaction token. + + memo: Optional descriptor for the transfer. + + 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( + "/v1/book_transfers", + body=maybe_transform( + { + "amount": amount, + "category": category, + "from_financial_account_token": from_financial_account_token, + "subtype": subtype, + "to_financial_account_token": to_financial_account_token, + "type": type, + "token": token, + "memo": memo, + }, + book_transfer_create_params.BookTransferCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BookTransferResponse, + ) + + def retrieve( + self, + book_transfer_token: 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, + ) -> BookTransferResponse: + """ + Get book transfer by token + + 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 book_transfer_token: + raise ValueError( + f"Expected a non-empty value for `book_transfer_token` but received {book_transfer_token!r}" + ) + return self._get( + f"/v1/book_transfers/{book_transfer_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BookTransferResponse, + ) + + def list( + self, + *, + account_token: str | NotGiven = NOT_GIVEN, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + category: Literal["BALANCE_OR_FUNDING", "FEE", "REWARD", "ADJUSTMENT", "DERECOGNITION", "DISPUTE", "INTERNAL"] + | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + financial_account_token: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + result: Literal["APPROVED", "DECLINED"] | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + status: Literal["DECLINED", "SETTLED"] | 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, + ) -> SyncCursorPage[BookTransferResponse]: + """List book transfers + + Args: + begin: Date string in RFC 3339 format. + + Only entries created after the specified time + will be included. UTC time zone. + + category: Book Transfer category to be returned. + + end: Date string in RFC 3339 format. Only entries created before the specified time + will be included. UTC time zone. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + financial_account_token: Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + + page_size: Page size (for pagination). + + result: Book transfer result to be returned. + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + status: Book transfer status to be returned. + + 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( + "/v1/book_transfers", + page=SyncCursorPage[BookTransferResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "account_token": account_token, + "begin": begin, + "business_account_token": business_account_token, + "category": category, + "end": end, + "ending_before": ending_before, + "financial_account_token": financial_account_token, + "page_size": page_size, + "result": result, + "starting_after": starting_after, + "status": status, + }, + book_transfer_list_params.BookTransferListParams, + ), + ), + model=BookTransferResponse, + ) + + def reverse( + self, + book_transfer_token: str, + *, + memo: 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, + ) -> BookTransferResponse: + """ + Reverse a book transfer + + Args: + memo: Optional descriptor for the reversal. + + 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 book_transfer_token: + raise ValueError( + f"Expected a non-empty value for `book_transfer_token` but received {book_transfer_token!r}" + ) + return self._post( + f"/v1/book_transfers/{book_transfer_token}/reverse", + body=maybe_transform({"memo": memo}, book_transfer_reverse_params.BookTransferReverseParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BookTransferResponse, + ) + + +class AsyncBookTransfers(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBookTransfersWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncBookTransfersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBookTransfersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncBookTransfersWithStreamingResponse(self) + + async def create( + self, + *, + amount: int, + category: Literal["ADJUSTMENT", "BALANCE_OR_FUNDING", "DERECOGNITION", "DISPUTE", "FEE", "REWARD", "TRANSFER"], + from_financial_account_token: str, + subtype: str, + to_financial_account_token: str, + type: Literal[ + "ATM_WITHDRAWAL", + "ATM_DECLINE", + "INTERNATIONAL_ATM_WITHDRAWAL", + "INACTIVITY", + "STATEMENT", + "MONTHLY", + "QUARTERLY", + "ANNUAL", + "CUSTOMER_SERVICE", + "ACCOUNT_MAINTENANCE", + "ACCOUNT_ACTIVATION", + "ACCOUNT_CLOSURE", + "CARD_REPLACEMENT", + "CARD_DELIVERY", + "CARD_CREATE", + "CURRENCY_CONVERSION", + "INTEREST", + "LATE_PAYMENT", + "BILL_PAYMENT", + "CASH_BACK", + "ACCOUNT_TO_ACCOUNT", + "CARD_TO_CARD", + "DISBURSE", + "BILLING_ERROR", + "LOSS_WRITE_OFF", + "EXPIRED_CARD", + "EARLY_DERECOGNITION", + "ESCHEATMENT", + "INACTIVITY_FEE_DOWN", + "PROVISIONAL_CREDIT", + "DISPUTE_WON", + "TRANSFER", + ], + token: str | NotGiven = NOT_GIVEN, + memo: 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, + ) -> BookTransferResponse: + """ + Book transfer funds between two financial accounts or between a financial + account and card + + Args: + amount: Amount to be transferred in the currency’s smallest unit (e.g., cents for USD). + This should always be a positive value. + + category: Category of the book transfer + + from_financial_account_token: Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + + subtype: The program specific subtype code for the specified category/type. + + to_financial_account_token: Globally unique identifier for the financial account or card that will receive + the funds. Accepted type dependent on the program's use case. + + type: Type of book_transfer + + token: Customer-provided token that will serve as an idempotency token. This token will + become the transaction token. + + memo: Optional descriptor for the transfer. + + 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( + "/v1/book_transfers", + body=await async_maybe_transform( + { + "amount": amount, + "category": category, + "from_financial_account_token": from_financial_account_token, + "subtype": subtype, + "to_financial_account_token": to_financial_account_token, + "type": type, + "token": token, + "memo": memo, + }, + book_transfer_create_params.BookTransferCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BookTransferResponse, + ) + + async def retrieve( + self, + book_transfer_token: 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, + ) -> BookTransferResponse: + """ + Get book transfer by token + + 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 book_transfer_token: + raise ValueError( + f"Expected a non-empty value for `book_transfer_token` but received {book_transfer_token!r}" + ) + return await self._get( + f"/v1/book_transfers/{book_transfer_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BookTransferResponse, + ) + + def list( + self, + *, + account_token: str | NotGiven = NOT_GIVEN, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + category: Literal["BALANCE_OR_FUNDING", "FEE", "REWARD", "ADJUSTMENT", "DERECOGNITION", "DISPUTE", "INTERNAL"] + | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + financial_account_token: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + result: Literal["APPROVED", "DECLINED"] | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + status: Literal["DECLINED", "SETTLED"] | 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, + ) -> AsyncPaginator[BookTransferResponse, AsyncCursorPage[BookTransferResponse]]: + """List book transfers + + Args: + begin: Date string in RFC 3339 format. + + Only entries created after the specified time + will be included. UTC time zone. + + category: Book Transfer category to be returned. + + end: Date string in RFC 3339 format. Only entries created before the specified time + will be included. UTC time zone. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + financial_account_token: Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + + page_size: Page size (for pagination). + + result: Book transfer result to be returned. + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + status: Book transfer status to be returned. + + 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( + "/v1/book_transfers", + page=AsyncCursorPage[BookTransferResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "account_token": account_token, + "begin": begin, + "business_account_token": business_account_token, + "category": category, + "end": end, + "ending_before": ending_before, + "financial_account_token": financial_account_token, + "page_size": page_size, + "result": result, + "starting_after": starting_after, + "status": status, + }, + book_transfer_list_params.BookTransferListParams, + ), + ), + model=BookTransferResponse, + ) + + async def reverse( + self, + book_transfer_token: str, + *, + memo: 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, + ) -> BookTransferResponse: + """ + Reverse a book transfer + + Args: + memo: Optional descriptor for the reversal. + + 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 book_transfer_token: + raise ValueError( + f"Expected a non-empty value for `book_transfer_token` but received {book_transfer_token!r}" + ) + return await self._post( + f"/v1/book_transfers/{book_transfer_token}/reverse", + body=await async_maybe_transform({"memo": memo}, book_transfer_reverse_params.BookTransferReverseParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BookTransferResponse, + ) + + +class BookTransfersWithRawResponse: + def __init__(self, book_transfers: BookTransfers) -> None: + self._book_transfers = book_transfers + + self.create = _legacy_response.to_raw_response_wrapper( + book_transfers.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + book_transfers.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + book_transfers.list, + ) + self.reverse = _legacy_response.to_raw_response_wrapper( + book_transfers.reverse, + ) + + +class AsyncBookTransfersWithRawResponse: + def __init__(self, book_transfers: AsyncBookTransfers) -> None: + self._book_transfers = book_transfers + + self.create = _legacy_response.async_to_raw_response_wrapper( + book_transfers.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + book_transfers.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + book_transfers.list, + ) + self.reverse = _legacy_response.async_to_raw_response_wrapper( + book_transfers.reverse, + ) + + +class BookTransfersWithStreamingResponse: + def __init__(self, book_transfers: BookTransfers) -> None: + self._book_transfers = book_transfers + + self.create = to_streamed_response_wrapper( + book_transfers.create, + ) + self.retrieve = to_streamed_response_wrapper( + book_transfers.retrieve, + ) + self.list = to_streamed_response_wrapper( + book_transfers.list, + ) + self.reverse = to_streamed_response_wrapper( + book_transfers.reverse, + ) + + +class AsyncBookTransfersWithStreamingResponse: + def __init__(self, book_transfers: AsyncBookTransfers) -> None: + self._book_transfers = book_transfers + + self.create = async_to_streamed_response_wrapper( + book_transfers.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + book_transfers.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + book_transfers.list, + ) + self.reverse = async_to_streamed_response_wrapper( + book_transfers.reverse, + ) diff --git a/src/lithic/resources/card_product.py b/src/lithic/resources/card_product.py deleted file mode 100644 index 857a808d..00000000 --- a/src/lithic/resources/card_product.py +++ /dev/null @@ -1,111 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import httpx - -from .. import _legacy_response -from ..types import CardProductCreditDetailResponse -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .._base_client import ( - make_request_options, -) - -__all__ = ["CardProduct", "AsyncCardProduct"] - - -class CardProduct(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CardProductWithRawResponse: - return CardProductWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CardProductWithStreamingResponse: - return CardProductWithStreamingResponse(self) - - def credit_detail( - self, - *, - # 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, - ) -> CardProductCreditDetailResponse: - """Get the Credit Detail for the card product""" - return self._get( - "/card_product/credit_detail", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=CardProductCreditDetailResponse, - ) - - -class AsyncCardProduct(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCardProductWithRawResponse: - return AsyncCardProductWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCardProductWithStreamingResponse: - return AsyncCardProductWithStreamingResponse(self) - - async def credit_detail( - self, - *, - # 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, - ) -> CardProductCreditDetailResponse: - """Get the Credit Detail for the card product""" - return await self._get( - "/card_product/credit_detail", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=CardProductCreditDetailResponse, - ) - - -class CardProductWithRawResponse: - def __init__(self, card_product: CardProduct) -> None: - self._card_product = card_product - - self.credit_detail = _legacy_response.to_raw_response_wrapper( - card_product.credit_detail, - ) - - -class AsyncCardProductWithRawResponse: - def __init__(self, card_product: AsyncCardProduct) -> None: - self._card_product = card_product - - self.credit_detail = _legacy_response.async_to_raw_response_wrapper( - card_product.credit_detail, - ) - - -class CardProductWithStreamingResponse: - def __init__(self, card_product: CardProduct) -> None: - self._card_product = card_product - - self.credit_detail = to_streamed_response_wrapper( - card_product.credit_detail, - ) - - -class AsyncCardProductWithStreamingResponse: - def __init__(self, card_product: AsyncCardProduct) -> None: - self._card_product = card_product - - self.credit_detail = async_to_streamed_response_wrapper( - card_product.credit_detail, - ) diff --git a/src/lithic/resources/card_programs.py b/src/lithic/resources/card_programs.py index 8338431c..46a28ad7 100644 --- a/src/lithic/resources/card_programs.py +++ b/src/lithic/resources/card_programs.py @@ -1,21 +1,19 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx from .. import _legacy_response -from ..types import CardProgram, card_program_list_params +from ..types import card_program_list_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncCursorPage, AsyncCursorPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.card_program import CardProgram __all__ = ["CardPrograms", "AsyncCardPrograms"] @@ -23,10 +21,21 @@ class CardPrograms(SyncAPIResource): @cached_property def with_raw_response(self) -> CardProgramsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return CardProgramsWithRawResponse(self) @cached_property def with_streaming_response(self) -> CardProgramsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return CardProgramsWithStreamingResponse(self) def retrieve( @@ -55,7 +64,7 @@ def retrieve( if not card_program_token: raise ValueError(f"Expected a non-empty value for `card_program_token` but received {card_program_token!r}") return self._get( - f"/card_programs/{card_program_token}", + f"/v1/card_programs/{card_program_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -96,7 +105,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/card_programs", + "/v1/card_programs", page=SyncCursorPage[CardProgram], options=make_request_options( extra_headers=extra_headers, @@ -119,10 +128,21 @@ def list( class AsyncCardPrograms(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCardProgramsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncCardProgramsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncCardProgramsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncCardProgramsWithStreamingResponse(self) async def retrieve( @@ -151,7 +171,7 @@ async def retrieve( if not card_program_token: raise ValueError(f"Expected a non-empty value for `card_program_token` but received {card_program_token!r}") return await self._get( - f"/card_programs/{card_program_token}", + f"/v1/card_programs/{card_program_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -192,7 +212,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/card_programs", + "/v1/card_programs", page=AsyncCursorPage[CardProgram], options=make_request_options( extra_headers=extra_headers, diff --git a/src/lithic/resources/cards/__init__.py b/src/lithic/resources/cards/__init__.py index 9159556e..790baf59 100644 --- a/src/lithic/resources/cards/__init__.py +++ b/src/lithic/resources/cards/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .cards import ( Cards, diff --git a/src/lithic/resources/cards/aggregate_balances.py b/src/lithic/resources/cards/aggregate_balances.py index d89c7f60..07db0ecc 100644 --- a/src/lithic/resources/cards/aggregate_balances.py +++ b/src/lithic/resources/cards/aggregate_balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -11,11 +11,9 @@ from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncSinglePage, AsyncSinglePage -from ...types.cards import AggregateBalanceListResponse, aggregate_balance_list_params -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ...types.cards import aggregate_balance_list_params +from ..._base_client import AsyncPaginator, make_request_options +from ...types.cards.aggregate_balance_list_response import AggregateBalanceListResponse __all__ = ["AggregateBalances", "AsyncAggregateBalances"] @@ -23,10 +21,21 @@ class AggregateBalances(SyncAPIResource): @cached_property def with_raw_response(self) -> AggregateBalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AggregateBalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AggregateBalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AggregateBalancesWithStreamingResponse(self) def list( @@ -58,7 +67,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/cards/aggregate_balances", + "/v1/cards/aggregate_balances", page=SyncSinglePage[AggregateBalanceListResponse], options=make_request_options( extra_headers=extra_headers, @@ -80,10 +89,21 @@ def list( class AsyncAggregateBalances(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncAggregateBalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncAggregateBalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAggregateBalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncAggregateBalancesWithStreamingResponse(self) def list( @@ -115,7 +135,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/cards/aggregate_balances", + "/v1/cards/aggregate_balances", page=AsyncSinglePage[AggregateBalanceListResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/lithic/resources/cards/balances.py b/src/lithic/resources/cards/balances.py index dbafa920..35fa6ff7 100644 --- a/src/lithic/resources/cards/balances.py +++ b/src/lithic/resources/cards/balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -8,7 +8,6 @@ import httpx from ... import _legacy_response -from ...types import Balance from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property @@ -16,10 +15,8 @@ from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncSinglePage, AsyncSinglePage from ...types.cards import balance_list_params -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options +from ...types.cards.balance_list_response import BalanceListResponse __all__ = ["Balances", "AsyncBalances"] @@ -27,10 +24,21 @@ class Balances(SyncAPIResource): @cached_property def with_raw_response(self) -> BalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return BalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> BalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return BalancesWithStreamingResponse(self) def list( @@ -45,7 +53,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncSinglePage[Balance]: + ) -> SyncSinglePage[BalanceListResponse]: """ Get the balances for a given card. @@ -67,8 +75,8 @@ def list( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._get_api_list( - f"/cards/{card_token}/balances", - page=SyncSinglePage[Balance], + f"/v1/cards/{card_token}/balances", + page=SyncSinglePage[BalanceListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -82,17 +90,28 @@ def list( balance_list_params.BalanceListParams, ), ), - model=Balance, + model=BalanceListResponse, ) class AsyncBalances(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncBalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncBalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncBalancesWithStreamingResponse(self) def list( @@ -107,7 +126,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[Balance, AsyncSinglePage[Balance]]: + ) -> AsyncPaginator[BalanceListResponse, AsyncSinglePage[BalanceListResponse]]: """ Get the balances for a given card. @@ -129,8 +148,8 @@ def list( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._get_api_list( - f"/cards/{card_token}/balances", - page=AsyncSinglePage[Balance], + f"/v1/cards/{card_token}/balances", + page=AsyncSinglePage[BalanceListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -144,7 +163,7 @@ def list( balance_list_params.BalanceListParams, ), ), - model=Balance, + model=BalanceListResponse, ) diff --git a/src/lithic/resources/cards/cards.py b/src/lithic/resources/cards/cards.py index d2c1234b..695a36e8 100644 --- a/src/lithic/resources/cards/cards.py +++ b/src/lithic/resources/cards/cards.py @@ -1,25 +1,16 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -import hmac -import json -import base64 -import hashlib from typing import Union -from datetime import datetime, timezone, timedelta +from datetime import datetime from typing_extensions import Literal import httpx -from httpx import URL from ... import _legacy_response from ...types import ( - Card, - CardSpendLimits, SpendLimitDuration, - CardProvisionResponse, - shared_params, card_list_params, card_embed_params, card_renew_params, @@ -27,11 +18,21 @@ card_update_params, card_reissue_params, card_provision_params, - card_get_embed_url_params, card_search_by_pan_params, + card_convert_physical_params, +) +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + Base64FileInput, +) +from ..._utils import ( + maybe_transform, + async_maybe_transform, ) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform, strip_not_given from .balances import ( Balances, AsyncBalances, @@ -44,11 +45,8 @@ from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import ( - AsyncPaginator, - _merge_mappings, - make_request_options, -) +from ...types.card import Card +from ..._base_client import AsyncPaginator, make_request_options from .aggregate_balances import ( AggregateBalances, AsyncAggregateBalances, @@ -65,6 +63,11 @@ FinancialTransactionsWithStreamingResponse, AsyncFinancialTransactionsWithStreamingResponse, ) +from ...types.card_spend_limits import CardSpendLimits +from ...types.spend_limit_duration import SpendLimitDuration +from ...types.shared_params.carrier import Carrier +from ...types.card_provision_response import CardProvisionResponse +from ...types.shared_params.shipping_address import ShippingAddress __all__ = ["Cards", "AsyncCards"] @@ -84,27 +87,39 @@ def financial_transactions(self) -> FinancialTransactions: @cached_property def with_raw_response(self) -> CardsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return CardsWithRawResponse(self) @cached_property def with_streaming_response(self) -> CardsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return CardsWithStreamingResponse(self) def create( self, *, - type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL"], + type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"], account_token: str | NotGiven = NOT_GIVEN, card_program_token: str | NotGiven = NOT_GIVEN, - carrier: shared_params.Carrier | NotGiven = NOT_GIVEN, + carrier: Carrier | NotGiven = NOT_GIVEN, digital_card_art_token: str | NotGiven = NOT_GIVEN, exp_month: str | NotGiven = NOT_GIVEN, exp_year: str | NotGiven = NOT_GIVEN, memo: str | NotGiven = NOT_GIVEN, pin: str | NotGiven = NOT_GIVEN, product_id: str | NotGiven = NOT_GIVEN, + replacement_account_token: str | NotGiven = NOT_GIVEN, replacement_for: str | NotGiven = NOT_GIVEN, - shipping_address: shared_params.ShippingAddress | NotGiven = NOT_GIVEN, + shipping_address: ShippingAddress | NotGiven = NOT_GIVEN, shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] | NotGiven = NOT_GIVEN, spend_limit: int | NotGiven = NOT_GIVEN, @@ -119,7 +134,7 @@ def create( ) -> Card: """Create a new virtual or physical card. - Parameters `pin`, `shipping_address`, and + Parameters `shipping_address` and `product_id` only apply to physical cards. Args: @@ -136,6 +151,10 @@ def create( - `SINGLE_USE` - Card is closed upon first successful authorization. - `MERCHANT_LOCKED` - _[Deprecated]_ Card is locked to the first merchant that successfully authorizes the card. + - `UNLOCKED` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please use + VIRTUAL instead. + - `DIGITAL_WALLET` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please + use VIRTUAL instead. account_token: Globally unique identifier for the account that the card will be associated with. Required for programs enrolling users using the @@ -160,19 +179,25 @@ def create( exp_year: Four digit (yyyy) expiry year. If neither `exp_month` nor `exp_year` is provided, an expiration date will be generated. - memo: Friendly name to identify the card. We recommend against using this field to - store JSON data as it can cause unexpected behavior. + memo: Friendly name to identify the card. - pin: Encrypted PIN block (in base64). Only applies to cards of type `PHYSICAL` and + pin: Encrypted PIN block (in base64). Applies to cards of type `PHYSICAL` and `VIRTUAL`. See - [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). product_id: Only applicable to cards of type `PHYSICAL`. This must be configured with Lithic before use. Specifies the configuration (i.e., physical card art) that the card should be manufactured with. - replacement_for: Only applicable to cards of type `PHYSICAL`. Globally unique identifier for the - card that this physical card will replace. + replacement_account_token: Restricted field limited to select use cases. Lithic will reach out directly if + this field should be used. Globally unique identifier for the replacement card's + account. If this field is specified, `replacement_for` must also be specified. + If `replacement_for` is specified and this field is omitted, the replacement + card's account will be inferred from the card being replaced. + + replacement_for: Globally unique identifier for the card that this card will replace. If the card + type is `PHYSICAL` it will be replaced by a `PHYSICAL` card. If the card type is + `VIRTUAL` it will be replaced by a `VIRTUAL` card. shipping_method: Shipping method for the card. Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` require additional permissions. @@ -187,11 +212,11 @@ def create( - `EXPEDITED` - FedEx Standard Overnight or similar international option, with tracking - spend_limit: Amount (in cents) to limit approved authorizations. Transaction requests above - the spend limit will be declined. Note that a spend limit of 0 is effectively no - limit, and should only be used to reset or remove a prior limit. Only a limit of - 1 or above will result in declined transactions due to checks against the card - limit. + spend_limit: Amount (in cents) to limit approved authorizations (e.g. 100000 would be a + $1,000 limit). Transaction requests above the spend limit will be declined. Note + that a spend limit of 0 is effectively no limit, and should only be used to + reset or remove a prior limit. Only a limit of 1 or above will result in + declined transactions due to checks against the card limit. spend_limit_duration: Spend limit duration values: @@ -224,7 +249,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/cards", + "/v1/cards", body=maybe_transform( { "type": type, @@ -237,6 +262,7 @@ def create( "memo": memo, "pin": pin, "product_id": product_id, + "replacement_account_token": replacement_account_token, "replacement_for": replacement_for, "shipping_address": shipping_address, "shipping_method": shipping_method, @@ -278,7 +304,7 @@ def retrieve( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._get( - f"/cards/{card_token}", + f"/v1/cards/{card_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -289,10 +315,10 @@ def update( self, card_token: str, *, - auth_rule_token: str | NotGiven = NOT_GIVEN, digital_card_art_token: str | NotGiven = NOT_GIVEN, memo: str | NotGiven = NOT_GIVEN, pin: str | NotGiven = NOT_GIVEN, + pin_status: Literal["OK"] | NotGiven = NOT_GIVEN, spend_limit: int | NotGiven = NOT_GIVEN, spend_limit_duration: SpendLimitDuration | NotGiven = NOT_GIVEN, state: Literal["CLOSED", "OPEN", "PAUSED"] | NotGiven = NOT_GIVEN, @@ -306,32 +332,31 @@ def update( """Update the specified properties of the card. Unsupplied properties will remain - unchanged. `pin` parameter only applies to physical cards. + unchanged. _Note: setting a card to a `CLOSED` state is a final action that cannot be undone._ Args: - auth_rule_token: Identifier for any Auth Rules that will be applied to transactions taking place - with the card. - digital_card_art_token: Specifies the digital card art to be displayed in the user’s digital wallet after tokenization. This artwork must be approved by Mastercard and configured by Lithic to use. See [Flexible Card Art Guide](https://docs.lithic.com/docs/about-digital-wallets#flexible-card-art). - memo: Friendly name to identify the card. We recommend against using this field to - store JSON data as it can cause unexpected behavior. + memo: Friendly name to identify the card. pin: Encrypted PIN block (in base64). Only applies to cards of type `PHYSICAL` and - `VIRTUAL`. See - [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + `VIRTUAL`. Changing PIN also resets PIN status to `OK`. See + [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). + + pin_status: Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect + attempts). Can only be set to `OK` to unblock a card. - spend_limit: Amount (in cents) to limit approved authorizations. Transaction requests above - the spend limit will be declined. Note that a spend limit of 0 is effectively no - limit, and should only be used to reset or remove a prior limit. Only a limit of - 1 or above will result in declined transactions due to checks against the card - limit. + spend_limit: Amount (in cents) to limit approved authorizations (e.g. 100000 would be a + $1,000 limit). Transaction requests above the spend limit will be declined. Note + that a spend limit of 0 is effectively no limit, and should only be used to + reset or remove a prior limit. Only a limit of 1 or above will result in + declined transactions due to checks against the card limit. spend_limit_duration: Spend limit duration values: @@ -368,13 +393,13 @@ def update( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._patch( - f"/cards/{card_token}", + f"/v1/cards/{card_token}", body=maybe_transform( { - "auth_rule_token": auth_rule_token, "digital_card_art_token": digital_card_art_token, "memo": memo, "pin": pin, + "pin_status": pin_status, "spend_limit": spend_limit, "spend_limit_duration": spend_limit_duration, "state": state, @@ -435,7 +460,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/cards", + "/v1/cards", page=SyncCursorPage[Card], options=make_request_options( extra_headers=extra_headers, @@ -458,6 +483,84 @@ def list( model=Card, ) + def convert_physical( + self, + card_token: str, + *, + shipping_address: ShippingAddress, + carrier: Carrier | NotGiven = NOT_GIVEN, + product_id: str | NotGiven = NOT_GIVEN, + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + | 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, + ) -> Card: + """Convert a virtual card into a physical card and manufacture it. + + Customer must + supply relevant fields for physical card creation including `product_id`, + `carrier`, `shipping_method`, and `shipping_address`. The card token will be + unchanged. The card's type will be altered to `PHYSICAL`. The card will be set + to state `PENDING_FULFILLMENT` and fulfilled at next fulfillment cycle. Virtual + cards created on card programs which do not support physical cards cannot be + converted. The card program cannot be changed as part of the conversion. Cards + must be in an `OPEN` state to be converted. Only applies to cards of type + `VIRTUAL` (or existing cards with deprecated types of `DIGITAL_WALLET` and + `UNLOCKED`). + + Args: + shipping_address: The shipping address this card will be sent to. + + carrier: If omitted, the previous carrier will be used. + + product_id: Specifies the configuration (e.g. physical card art) that the card should be + manufactured with, and only applies to cards of type `PHYSICAL`. This must be + configured with Lithic before use. + + shipping_method: Shipping method for the card. Only applies to cards of type PHYSICAL. Use of + options besides `STANDARD` require additional permissions. + + - `STANDARD` - USPS regular mail or similar international option, with no + tracking + - `STANDARD_WITH_TRACKING` - USPS regular mail or similar international option, + with tracking + - `PRIORITY` - USPS Priority, 1-3 day shipping, with tracking + - `EXPRESS` - FedEx Express, 3-day shipping, with tracking + - `2_DAY` - FedEx 2-day shipping, with tracking + - `EXPEDITED` - FedEx Standard Overnight or similar international option, with + tracking + + 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 card_token: + raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") + return self._post( + f"/v1/cards/{card_token}/convert_physical", + body=maybe_transform( + { + "shipping_address": shipping_address, + "carrier": carrier, + "product_id": product_id, + "shipping_method": shipping_method, + }, + card_convert_physical_params.CardConvertPhysicalParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Card, + ) + def embed( self, *, @@ -480,9 +583,10 @@ def embed( that we provide, optionally styled in the customer's branding using a specified css stylesheet. A user's browser makes the request directly to api.lithic.com, so card PANs and CVVs never touch the API customer's servers while full card - data is displayed to their end-users. The response contains an HTML document. - This means that the url for the request can be inserted straight into the `src` - attribute of an iframe. + data is displayed to their end-users. The response contains an HTML document + (see Embedded Card UI or Changelog for upcoming changes in January). This means + that the url for the request can be inserted straight into the `src` attribute + of an iframe. ```html - ``` - - You should compute the request payload on the server side. You can render it (or - the whole iframe) on the server or make an ajax call from your front end code, - but **do not ever embed your API key into front end code, as doing so introduces - a serious security vulnerability**. - """ - # Default expiration of 1 minute from now. - if isinstance(expiration, NotGiven): - expiration = datetime.now(timezone.utc) + timedelta(minutes=1) - - query = maybe_transform( - strip_not_given( - { - "css": css, - "token": token, - "expiration": expiration, - "target_origin": target_origin, - } - ), - card_get_embed_url_params.CardGetEmbedURLParams, - ) - serialized = json.dumps(query, sort_keys=True, separators=(",", ":")) - params = { - "embed_request": base64.b64encode(bytes(serialized, "utf-8")).decode("utf-8"), - "hmac": base64.b64encode( - hmac.new( - key=bytes(self._client.api_key, "utf-8"), - msg=bytes(serialized, "utf-8"), - digestmod=hashlib.sha256, - ).digest() - ).decode("utf-8"), - } - - # Copied nearly directly from httpx.BaseClient._merge_url - base_url = self._client.base_url - raw_path = base_url.raw_path + URL("embed/card").raw_path - return base_url.copy_with(raw_path=raw_path).copy_merge_params(params) - def provision( self, card_token: str, *, - certificate: str | NotGiven = NOT_GIVEN, + certificate: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, + client_device_id: str | NotGiven = NOT_GIVEN, + client_wallet_account_id: str | NotGiven = NOT_GIVEN, digital_wallet: Literal["APPLE_PAY", "GOOGLE_PAY", "SAMSUNG_PAY"] | NotGiven = NOT_GIVEN, - nonce: str | NotGiven = NOT_GIVEN, - nonce_signature: str | NotGiven = NOT_GIVEN, + nonce: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, + nonce_signature: Union[str, Base64FileInput] | 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, @@ -670,6 +665,14 @@ def provision( encoded in PEM format with headers `(-----BEGIN CERTIFICATE-----)` and trailers omitted. Provided by the device's wallet. + client_device_id: Only applicable if `digital_wallet` is `GOOGLE_PAY` or `SAMSUNG_PAY` and the + card is on the Visa network. Stable device identification set by the wallet + provider. + + client_wallet_account_id: Only applicable if `digital_wallet` is `GOOGLE_PAY` or `SAMSUNG_PAY` and the + card is on the Visa network. Consumer ID that identifies the wallet account + holder entity. + digital_wallet: Name of digital wallet provider. nonce: Only applicable if `digital_wallet` is `APPLE_PAY`. Omit to receive only @@ -691,10 +694,12 @@ def provision( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._post( - f"/cards/{card_token}/provision", + f"/v1/cards/{card_token}/provision", body=maybe_transform( { "certificate": certificate, + "client_device_id": client_device_id, + "client_wallet_account_id": client_wallet_account_id, "digital_wallet": digital_wallet, "nonce": nonce, "nonce_signature": nonce_signature, @@ -711,10 +716,10 @@ def reissue( self, card_token: str, *, - carrier: shared_params.Carrier | NotGiven = NOT_GIVEN, + carrier: Carrier | NotGiven = NOT_GIVEN, product_id: str | NotGiven = NOT_GIVEN, - shipping_address: shared_params.ShippingAddress | NotGiven = NOT_GIVEN, - shipping_method: Literal["2-DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_address: ShippingAddress | NotGiven = NOT_GIVEN, + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] | 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. @@ -723,10 +728,13 @@ def reissue( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Card: - """ - Initiate print and shipment of a duplicate physical card. + """Initiate print and shipment of a duplicate physical card (e.g. - Only applies to cards of type `PHYSICAL`. + card is + physically damaged). The PAN, expiry, and CVC2 will remain the same and the + original card can continue to be used until the new card is activated. Only + applies to cards of type `PHYSICAL`. A card can be replaced or renewed a total + of 8 times. Args: carrier: If omitted, the previous carrier will be used. @@ -737,8 +745,8 @@ def reissue( shipping_address: If omitted, the previous shipping address will be used. - shipping_method: Shipping method for the card. Use of options besides `STANDARD` require - additional permissions. + shipping_method: Shipping method for the card. Only applies to cards of type PHYSICAL. Use of + options besides `STANDARD` require additional permissions. - `STANDARD` - USPS regular mail or similar international option, with no tracking @@ -761,7 +769,7 @@ def reissue( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._post( - f"/cards/{card_token}/reissue", + f"/v1/cards/{card_token}/reissue", body=maybe_transform( { "carrier": carrier, @@ -781,12 +789,12 @@ def renew( self, card_token: str, *, - shipping_address: shared_params.ShippingAddress, - carrier: shared_params.Carrier | NotGiven = NOT_GIVEN, + shipping_address: ShippingAddress, + carrier: Carrier | NotGiven = NOT_GIVEN, exp_month: str | NotGiven = NOT_GIVEN, exp_year: str | NotGiven = NOT_GIVEN, product_id: str | NotGiven = NOT_GIVEN, - shipping_method: Literal["2-DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] | 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. @@ -795,10 +803,17 @@ def renew( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Card: - """ - Initiate print and shipment of a renewed physical card. - - Only applies to cards of type `PHYSICAL`. + """Applies to card types `PHYSICAL` and `VIRTUAL`. + + For `PHYSICAL`, creates a new + card with the same card token and PAN, but updated expiry and CVC2 code. The + original card will keep working for card-present transactions until the new card + is activated. For card-not-present transactions, the original card details + (expiry, CVC2) will also keep working until the new card is activated. A + `PHYSICAL` card can be replaced or renewed a total of 8 times. For `VIRTUAL`, + the card will retain the same card token and PAN and receive an updated expiry + and CVC2 code. `product_id`, `shipping_method`, `shipping_address`, `carrier` + are only relevant for renewing `PHYSICAL` cards. Args: shipping_address: The shipping address this card will be sent to. @@ -815,8 +830,8 @@ def renew( manufactured with, and only applies to cards of type `PHYSICAL`. This must be configured with Lithic before use. - shipping_method: Shipping method for the card. Use of options besides `STANDARD` require - additional permissions. + shipping_method: Shipping method for the card. Only applies to cards of type PHYSICAL. Use of + options besides `STANDARD` require additional permissions. - `STANDARD` - USPS regular mail or similar international option, with no tracking @@ -839,7 +854,7 @@ def renew( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._post( - f"/cards/{card_token}/renew", + f"/v1/cards/{card_token}/renew", body=maybe_transform( { "shipping_address": shipping_address, @@ -886,7 +901,7 @@ def retrieve_spend_limits( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._get( - f"/cards/{card_token}/spend_limits", + f"/v1/cards/{card_token}/spend_limits", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -924,7 +939,7 @@ def search_by_pan( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/cards/search_by_pan", + "/v1/cards/search_by_pan", body=maybe_transform({"pan": pan}, card_search_by_pan_params.CardSearchByPanParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -948,27 +963,39 @@ def financial_transactions(self) -> AsyncFinancialTransactions: @cached_property def with_raw_response(self) -> AsyncCardsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncCardsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncCardsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncCardsWithStreamingResponse(self) async def create( self, *, - type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL"], + type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"], account_token: str | NotGiven = NOT_GIVEN, card_program_token: str | NotGiven = NOT_GIVEN, - carrier: shared_params.Carrier | NotGiven = NOT_GIVEN, + carrier: Carrier | NotGiven = NOT_GIVEN, digital_card_art_token: str | NotGiven = NOT_GIVEN, exp_month: str | NotGiven = NOT_GIVEN, exp_year: str | NotGiven = NOT_GIVEN, memo: str | NotGiven = NOT_GIVEN, pin: str | NotGiven = NOT_GIVEN, product_id: str | NotGiven = NOT_GIVEN, + replacement_account_token: str | NotGiven = NOT_GIVEN, replacement_for: str | NotGiven = NOT_GIVEN, - shipping_address: shared_params.ShippingAddress | NotGiven = NOT_GIVEN, + shipping_address: ShippingAddress | NotGiven = NOT_GIVEN, shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] | NotGiven = NOT_GIVEN, spend_limit: int | NotGiven = NOT_GIVEN, @@ -983,7 +1010,7 @@ async def create( ) -> Card: """Create a new virtual or physical card. - Parameters `pin`, `shipping_address`, and + Parameters `shipping_address` and `product_id` only apply to physical cards. Args: @@ -1000,6 +1027,10 @@ async def create( - `SINGLE_USE` - Card is closed upon first successful authorization. - `MERCHANT_LOCKED` - _[Deprecated]_ Card is locked to the first merchant that successfully authorizes the card. + - `UNLOCKED` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please use + VIRTUAL instead. + - `DIGITAL_WALLET` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please + use VIRTUAL instead. account_token: Globally unique identifier for the account that the card will be associated with. Required for programs enrolling users using the @@ -1024,19 +1055,25 @@ async def create( exp_year: Four digit (yyyy) expiry year. If neither `exp_month` nor `exp_year` is provided, an expiration date will be generated. - memo: Friendly name to identify the card. We recommend against using this field to - store JSON data as it can cause unexpected behavior. + memo: Friendly name to identify the card. - pin: Encrypted PIN block (in base64). Only applies to cards of type `PHYSICAL` and + pin: Encrypted PIN block (in base64). Applies to cards of type `PHYSICAL` and `VIRTUAL`. See - [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). product_id: Only applicable to cards of type `PHYSICAL`. This must be configured with Lithic before use. Specifies the configuration (i.e., physical card art) that the card should be manufactured with. - replacement_for: Only applicable to cards of type `PHYSICAL`. Globally unique identifier for the - card that this physical card will replace. + replacement_account_token: Restricted field limited to select use cases. Lithic will reach out directly if + this field should be used. Globally unique identifier for the replacement card's + account. If this field is specified, `replacement_for` must also be specified. + If `replacement_for` is specified and this field is omitted, the replacement + card's account will be inferred from the card being replaced. + + replacement_for: Globally unique identifier for the card that this card will replace. If the card + type is `PHYSICAL` it will be replaced by a `PHYSICAL` card. If the card type is + `VIRTUAL` it will be replaced by a `VIRTUAL` card. shipping_method: Shipping method for the card. Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` require additional permissions. @@ -1051,11 +1088,11 @@ async def create( - `EXPEDITED` - FedEx Standard Overnight or similar international option, with tracking - spend_limit: Amount (in cents) to limit approved authorizations. Transaction requests above - the spend limit will be declined. Note that a spend limit of 0 is effectively no - limit, and should only be used to reset or remove a prior limit. Only a limit of - 1 or above will result in declined transactions due to checks against the card - limit. + spend_limit: Amount (in cents) to limit approved authorizations (e.g. 100000 would be a + $1,000 limit). Transaction requests above the spend limit will be declined. Note + that a spend limit of 0 is effectively no limit, and should only be used to + reset or remove a prior limit. Only a limit of 1 or above will result in + declined transactions due to checks against the card limit. spend_limit_duration: Spend limit duration values: @@ -1088,8 +1125,8 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/cards", - body=maybe_transform( + "/v1/cards", + body=await async_maybe_transform( { "type": type, "account_token": account_token, @@ -1101,6 +1138,7 @@ async def create( "memo": memo, "pin": pin, "product_id": product_id, + "replacement_account_token": replacement_account_token, "replacement_for": replacement_for, "shipping_address": shipping_address, "shipping_method": shipping_method, @@ -1142,7 +1180,7 @@ async def retrieve( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return await self._get( - f"/cards/{card_token}", + f"/v1/cards/{card_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1153,10 +1191,10 @@ async def update( self, card_token: str, *, - auth_rule_token: str | NotGiven = NOT_GIVEN, digital_card_art_token: str | NotGiven = NOT_GIVEN, memo: str | NotGiven = NOT_GIVEN, pin: str | NotGiven = NOT_GIVEN, + pin_status: Literal["OK"] | NotGiven = NOT_GIVEN, spend_limit: int | NotGiven = NOT_GIVEN, spend_limit_duration: SpendLimitDuration | NotGiven = NOT_GIVEN, state: Literal["CLOSED", "OPEN", "PAUSED"] | NotGiven = NOT_GIVEN, @@ -1170,32 +1208,31 @@ async def update( """Update the specified properties of the card. Unsupplied properties will remain - unchanged. `pin` parameter only applies to physical cards. + unchanged. _Note: setting a card to a `CLOSED` state is a final action that cannot be undone._ Args: - auth_rule_token: Identifier for any Auth Rules that will be applied to transactions taking place - with the card. - digital_card_art_token: Specifies the digital card art to be displayed in the user’s digital wallet after tokenization. This artwork must be approved by Mastercard and configured by Lithic to use. See [Flexible Card Art Guide](https://docs.lithic.com/docs/about-digital-wallets#flexible-card-art). - memo: Friendly name to identify the card. We recommend against using this field to - store JSON data as it can cause unexpected behavior. + memo: Friendly name to identify the card. pin: Encrypted PIN block (in base64). Only applies to cards of type `PHYSICAL` and - `VIRTUAL`. See - [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + `VIRTUAL`. Changing PIN also resets PIN status to `OK`. See + [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). - spend_limit: Amount (in cents) to limit approved authorizations. Transaction requests above - the spend limit will be declined. Note that a spend limit of 0 is effectively no - limit, and should only be used to reset or remove a prior limit. Only a limit of - 1 or above will result in declined transactions due to checks against the card - limit. + pin_status: Indicates if a card is blocked due a PIN status issue (e.g. excessive incorrect + attempts). Can only be set to `OK` to unblock a card. + + spend_limit: Amount (in cents) to limit approved authorizations (e.g. 100000 would be a + $1,000 limit). Transaction requests above the spend limit will be declined. Note + that a spend limit of 0 is effectively no limit, and should only be used to + reset or remove a prior limit. Only a limit of 1 or above will result in + declined transactions due to checks against the card limit. spend_limit_duration: Spend limit duration values: @@ -1232,13 +1269,13 @@ async def update( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return await self._patch( - f"/cards/{card_token}", - body=maybe_transform( + f"/v1/cards/{card_token}", + body=await async_maybe_transform( { - "auth_rule_token": auth_rule_token, "digital_card_art_token": digital_card_art_token, "memo": memo, "pin": pin, + "pin_status": pin_status, "spend_limit": spend_limit, "spend_limit_duration": spend_limit_duration, "state": state, @@ -1299,7 +1336,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/cards", + "/v1/cards", page=AsyncCursorPage[Card], options=make_request_options( extra_headers=extra_headers, @@ -1322,6 +1359,84 @@ def list( model=Card, ) + async def convert_physical( + self, + card_token: str, + *, + shipping_address: ShippingAddress, + carrier: Carrier | NotGiven = NOT_GIVEN, + product_id: str | NotGiven = NOT_GIVEN, + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + | 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, + ) -> Card: + """Convert a virtual card into a physical card and manufacture it. + + Customer must + supply relevant fields for physical card creation including `product_id`, + `carrier`, `shipping_method`, and `shipping_address`. The card token will be + unchanged. The card's type will be altered to `PHYSICAL`. The card will be set + to state `PENDING_FULFILLMENT` and fulfilled at next fulfillment cycle. Virtual + cards created on card programs which do not support physical cards cannot be + converted. The card program cannot be changed as part of the conversion. Cards + must be in an `OPEN` state to be converted. Only applies to cards of type + `VIRTUAL` (or existing cards with deprecated types of `DIGITAL_WALLET` and + `UNLOCKED`). + + Args: + shipping_address: The shipping address this card will be sent to. + + carrier: If omitted, the previous carrier will be used. + + product_id: Specifies the configuration (e.g. physical card art) that the card should be + manufactured with, and only applies to cards of type `PHYSICAL`. This must be + configured with Lithic before use. + + shipping_method: Shipping method for the card. Only applies to cards of type PHYSICAL. Use of + options besides `STANDARD` require additional permissions. + + - `STANDARD` - USPS regular mail or similar international option, with no + tracking + - `STANDARD_WITH_TRACKING` - USPS regular mail or similar international option, + with tracking + - `PRIORITY` - USPS Priority, 1-3 day shipping, with tracking + - `EXPRESS` - FedEx Express, 3-day shipping, with tracking + - `2_DAY` - FedEx 2-day shipping, with tracking + - `EXPEDITED` - FedEx Standard Overnight or similar international option, with + tracking + + 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 card_token: + raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") + return await self._post( + f"/v1/cards/{card_token}/convert_physical", + body=await async_maybe_transform( + { + "shipping_address": shipping_address, + "carrier": carrier, + "product_id": product_id, + "shipping_method": shipping_method, + }, + card_convert_physical_params.CardConvertPhysicalParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Card, + ) + async def embed( self, *, @@ -1344,9 +1459,10 @@ async def embed( that we provide, optionally styled in the customer's branding using a specified css stylesheet. A user's browser makes the request directly to api.lithic.com, so card PANs and CVVs never touch the API customer's servers while full card - data is displayed to their end-users. The response contains an HTML document. - This means that the url for the request can be inserted straight into the `src` - attribute of an iframe. + data is displayed to their end-users. The response contains an HTML document + (see Embedded Card UI or Changelog for upcoming changes in January). This means + that the url for the request can be inserted straight into the `src` attribute + of an iframe. ```html - ``` - - You should compute the request payload on the server side. You can render it (or - the whole iframe) on the server or make an ajax call from your front end code, - but **do not ever embed your API key into front end code, as doing so introduces - a serious security vulnerability**. - """ - # Default expiration of 1 minute from now. - if isinstance(expiration, NotGiven): - expiration = datetime.now(timezone.utc) + timedelta(minutes=1) - - query = maybe_transform( - strip_not_given( - { - "css": css, - "token": token, - "expiration": expiration, - "target_origin": target_origin, - } - ), - card_get_embed_url_params.CardGetEmbedURLParams, - ) - serialized = json.dumps(query, sort_keys=True, separators=(",", ":")) - params = { - "embed_request": base64.b64encode(bytes(serialized, "utf-8")).decode("utf-8"), - "hmac": base64.b64encode( - hmac.new( - key=bytes(self._client.api_key, "utf-8"), - msg=bytes(serialized, "utf-8"), - digestmod=hashlib.sha256, - ).digest() - ).decode("utf-8"), - } - - # Copied nearly directly from httpx.BaseClient._merge_url - base_url = self._client.base_url - raw_path = base_url.raw_path + URL("embed/card").raw_path - return base_url.copy_with(raw_path=raw_path).copy_merge_params(params) - async def provision( self, card_token: str, *, - certificate: str | NotGiven = NOT_GIVEN, + certificate: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, + client_device_id: str | NotGiven = NOT_GIVEN, + client_wallet_account_id: str | NotGiven = NOT_GIVEN, digital_wallet: Literal["APPLE_PAY", "GOOGLE_PAY", "SAMSUNG_PAY"] | NotGiven = NOT_GIVEN, - nonce: str | NotGiven = NOT_GIVEN, - nonce_signature: str | NotGiven = NOT_GIVEN, + nonce: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, + nonce_signature: Union[str, Base64FileInput] | 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, @@ -1534,6 +1541,14 @@ async def provision( encoded in PEM format with headers `(-----BEGIN CERTIFICATE-----)` and trailers omitted. Provided by the device's wallet. + client_device_id: Only applicable if `digital_wallet` is `GOOGLE_PAY` or `SAMSUNG_PAY` and the + card is on the Visa network. Stable device identification set by the wallet + provider. + + client_wallet_account_id: Only applicable if `digital_wallet` is `GOOGLE_PAY` or `SAMSUNG_PAY` and the + card is on the Visa network. Consumer ID that identifies the wallet account + holder entity. + digital_wallet: Name of digital wallet provider. nonce: Only applicable if `digital_wallet` is `APPLE_PAY`. Omit to receive only @@ -1555,10 +1570,12 @@ async def provision( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return await self._post( - f"/cards/{card_token}/provision", - body=maybe_transform( + f"/v1/cards/{card_token}/provision", + body=await async_maybe_transform( { "certificate": certificate, + "client_device_id": client_device_id, + "client_wallet_account_id": client_wallet_account_id, "digital_wallet": digital_wallet, "nonce": nonce, "nonce_signature": nonce_signature, @@ -1575,10 +1592,10 @@ async def reissue( self, card_token: str, *, - carrier: shared_params.Carrier | NotGiven = NOT_GIVEN, + carrier: Carrier | NotGiven = NOT_GIVEN, product_id: str | NotGiven = NOT_GIVEN, - shipping_address: shared_params.ShippingAddress | NotGiven = NOT_GIVEN, - shipping_method: Literal["2-DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_address: ShippingAddress | NotGiven = NOT_GIVEN, + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] | 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. @@ -1587,10 +1604,13 @@ async def reissue( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Card: - """ - Initiate print and shipment of a duplicate physical card. + """Initiate print and shipment of a duplicate physical card (e.g. - Only applies to cards of type `PHYSICAL`. + card is + physically damaged). The PAN, expiry, and CVC2 will remain the same and the + original card can continue to be used until the new card is activated. Only + applies to cards of type `PHYSICAL`. A card can be replaced or renewed a total + of 8 times. Args: carrier: If omitted, the previous carrier will be used. @@ -1601,8 +1621,8 @@ async def reissue( shipping_address: If omitted, the previous shipping address will be used. - shipping_method: Shipping method for the card. Use of options besides `STANDARD` require - additional permissions. + shipping_method: Shipping method for the card. Only applies to cards of type PHYSICAL. Use of + options besides `STANDARD` require additional permissions. - `STANDARD` - USPS regular mail or similar international option, with no tracking @@ -1625,8 +1645,8 @@ async def reissue( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return await self._post( - f"/cards/{card_token}/reissue", - body=maybe_transform( + f"/v1/cards/{card_token}/reissue", + body=await async_maybe_transform( { "carrier": carrier, "product_id": product_id, @@ -1645,12 +1665,12 @@ async def renew( self, card_token: str, *, - shipping_address: shared_params.ShippingAddress, - carrier: shared_params.Carrier | NotGiven = NOT_GIVEN, + shipping_address: ShippingAddress, + carrier: Carrier | NotGiven = NOT_GIVEN, exp_month: str | NotGiven = NOT_GIVEN, exp_year: str | NotGiven = NOT_GIVEN, product_id: str | NotGiven = NOT_GIVEN, - shipping_method: Literal["2-DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] | 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. @@ -1659,10 +1679,17 @@ async def renew( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Card: - """ - Initiate print and shipment of a renewed physical card. - - Only applies to cards of type `PHYSICAL`. + """Applies to card types `PHYSICAL` and `VIRTUAL`. + + For `PHYSICAL`, creates a new + card with the same card token and PAN, but updated expiry and CVC2 code. The + original card will keep working for card-present transactions until the new card + is activated. For card-not-present transactions, the original card details + (expiry, CVC2) will also keep working until the new card is activated. A + `PHYSICAL` card can be replaced or renewed a total of 8 times. For `VIRTUAL`, + the card will retain the same card token and PAN and receive an updated expiry + and CVC2 code. `product_id`, `shipping_method`, `shipping_address`, `carrier` + are only relevant for renewing `PHYSICAL` cards. Args: shipping_address: The shipping address this card will be sent to. @@ -1679,8 +1706,8 @@ async def renew( manufactured with, and only applies to cards of type `PHYSICAL`. This must be configured with Lithic before use. - shipping_method: Shipping method for the card. Use of options besides `STANDARD` require - additional permissions. + shipping_method: Shipping method for the card. Only applies to cards of type PHYSICAL. Use of + options besides `STANDARD` require additional permissions. - `STANDARD` - USPS regular mail or similar international option, with no tracking @@ -1703,8 +1730,8 @@ async def renew( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return await self._post( - f"/cards/{card_token}/renew", - body=maybe_transform( + f"/v1/cards/{card_token}/renew", + body=await async_maybe_transform( { "shipping_address": shipping_address, "carrier": carrier, @@ -1750,7 +1777,7 @@ async def retrieve_spend_limits( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return await self._get( - f"/cards/{card_token}/spend_limits", + f"/v1/cards/{card_token}/spend_limits", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1788,8 +1815,8 @@ async def search_by_pan( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/cards/search_by_pan", - body=maybe_transform({"pan": pan}, card_search_by_pan_params.CardSearchByPanParams), + "/v1/cards/search_by_pan", + body=await async_maybe_transform({"pan": pan}, card_search_by_pan_params.CardSearchByPanParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1813,6 +1840,9 @@ def __init__(self, cards: Cards) -> None: self.list = _legacy_response.to_raw_response_wrapper( cards.list, ) + self.convert_physical = _legacy_response.to_raw_response_wrapper( + cards.convert_physical, + ) self.embed = _legacy_response.to_raw_response_wrapper( cards.embed, ) @@ -1861,6 +1891,9 @@ def __init__(self, cards: AsyncCards) -> None: self.list = _legacy_response.async_to_raw_response_wrapper( cards.list, ) + self.convert_physical = _legacy_response.async_to_raw_response_wrapper( + cards.convert_physical, + ) self.embed = _legacy_response.async_to_raw_response_wrapper( cards.embed, ) @@ -1909,6 +1942,9 @@ def __init__(self, cards: Cards) -> None: self.list = to_streamed_response_wrapper( cards.list, ) + self.convert_physical = to_streamed_response_wrapper( + cards.convert_physical, + ) self.embed = to_streamed_response_wrapper( cards.embed, ) @@ -1957,6 +1993,9 @@ def __init__(self, cards: AsyncCards) -> None: self.list = async_to_streamed_response_wrapper( cards.list, ) + self.convert_physical = async_to_streamed_response_wrapper( + cards.convert_physical, + ) self.embed = async_to_streamed_response_wrapper( cards.embed, ) diff --git a/src/lithic/resources/cards/financial_transactions.py b/src/lithic/resources/cards/financial_transactions.py index 57864af9..be6d106b 100644 --- a/src/lithic/resources/cards/financial_transactions.py +++ b/src/lithic/resources/cards/financial_transactions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,7 +9,6 @@ import httpx from ... import _legacy_response -from ...types import FinancialTransaction from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property @@ -17,10 +16,8 @@ from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncSinglePage, AsyncSinglePage from ...types.cards import financial_transaction_list_params -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options +from ...types.financial_transaction import FinancialTransaction __all__ = ["FinancialTransactions", "AsyncFinancialTransactions"] @@ -28,10 +25,21 @@ class FinancialTransactions(SyncAPIResource): @cached_property def with_raw_response(self) -> FinancialTransactionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return FinancialTransactionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> FinancialTransactionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return FinancialTransactionsWithStreamingResponse(self) def retrieve( @@ -65,7 +73,7 @@ def retrieve( f"Expected a non-empty value for `financial_transaction_token` but received {financial_transaction_token!r}" ) return self._get( - f"/cards/{card_token}/financial_transactions/{financial_transaction_token}", + f"/v1/cards/{card_token}/financial_transactions/{financial_transaction_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -123,7 +131,7 @@ def list( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._get_api_list( - f"/cards/{card_token}/financial_transactions", + f"/v1/cards/{card_token}/financial_transactions", page=SyncSinglePage[FinancialTransaction], options=make_request_options( extra_headers=extra_headers, @@ -150,10 +158,21 @@ def list( class AsyncFinancialTransactions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFinancialTransactionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncFinancialTransactionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncFinancialTransactionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncFinancialTransactionsWithStreamingResponse(self) async def retrieve( @@ -187,7 +206,7 @@ async def retrieve( f"Expected a non-empty value for `financial_transaction_token` but received {financial_transaction_token!r}" ) return await self._get( - f"/cards/{card_token}/financial_transactions/{financial_transaction_token}", + f"/v1/cards/{card_token}/financial_transactions/{financial_transaction_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -245,7 +264,7 @@ def list( if not card_token: raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._get_api_list( - f"/cards/{card_token}/financial_transactions", + f"/v1/cards/{card_token}/financial_transactions", page=AsyncSinglePage[FinancialTransaction], options=make_request_options( extra_headers=extra_headers, diff --git a/src/lithic/resources/credit_products/__init__.py b/src/lithic/resources/credit_products/__init__.py new file mode 100644 index 00000000..f7b13736 --- /dev/null +++ b/src/lithic/resources/credit_products/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .prime_rates import ( + PrimeRates, + AsyncPrimeRates, + PrimeRatesWithRawResponse, + AsyncPrimeRatesWithRawResponse, + PrimeRatesWithStreamingResponse, + AsyncPrimeRatesWithStreamingResponse, +) +from .credit_products import ( + CreditProducts, + AsyncCreditProducts, + CreditProductsWithRawResponse, + AsyncCreditProductsWithRawResponse, + CreditProductsWithStreamingResponse, + AsyncCreditProductsWithStreamingResponse, +) +from .extended_credit import ( + ExtendedCreditResource, + AsyncExtendedCreditResource, + ExtendedCreditResourceWithRawResponse, + AsyncExtendedCreditResourceWithRawResponse, + ExtendedCreditResourceWithStreamingResponse, + AsyncExtendedCreditResourceWithStreamingResponse, +) + +__all__ = [ + "ExtendedCreditResource", + "AsyncExtendedCreditResource", + "ExtendedCreditResourceWithRawResponse", + "AsyncExtendedCreditResourceWithRawResponse", + "ExtendedCreditResourceWithStreamingResponse", + "AsyncExtendedCreditResourceWithStreamingResponse", + "PrimeRates", + "AsyncPrimeRates", + "PrimeRatesWithRawResponse", + "AsyncPrimeRatesWithRawResponse", + "PrimeRatesWithStreamingResponse", + "AsyncPrimeRatesWithStreamingResponse", + "CreditProducts", + "AsyncCreditProducts", + "CreditProductsWithRawResponse", + "AsyncCreditProductsWithRawResponse", + "CreditProductsWithStreamingResponse", + "AsyncCreditProductsWithStreamingResponse", +] diff --git a/src/lithic/resources/credit_products/credit_products.py b/src/lithic/resources/credit_products/credit_products.py new file mode 100644 index 00000000..5f6effe3 --- /dev/null +++ b/src/lithic/resources/credit_products/credit_products.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .prime_rates import ( + PrimeRates, + AsyncPrimeRates, + PrimeRatesWithRawResponse, + AsyncPrimeRatesWithRawResponse, + PrimeRatesWithStreamingResponse, + AsyncPrimeRatesWithStreamingResponse, +) +from .extended_credit import ( + ExtendedCreditResource, + AsyncExtendedCreditResource, + ExtendedCreditResourceWithRawResponse, + AsyncExtendedCreditResourceWithRawResponse, + ExtendedCreditResourceWithStreamingResponse, + AsyncExtendedCreditResourceWithStreamingResponse, +) + +__all__ = ["CreditProducts", "AsyncCreditProducts"] + + +class CreditProducts(SyncAPIResource): + @cached_property + def extended_credit(self) -> ExtendedCreditResource: + return ExtendedCreditResource(self._client) + + @cached_property + def prime_rates(self) -> PrimeRates: + return PrimeRates(self._client) + + @cached_property + def with_raw_response(self) -> CreditProductsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return CreditProductsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CreditProductsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return CreditProductsWithStreamingResponse(self) + + +class AsyncCreditProducts(AsyncAPIResource): + @cached_property + def extended_credit(self) -> AsyncExtendedCreditResource: + return AsyncExtendedCreditResource(self._client) + + @cached_property + def prime_rates(self) -> AsyncPrimeRates: + return AsyncPrimeRates(self._client) + + @cached_property + def with_raw_response(self) -> AsyncCreditProductsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncCreditProductsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCreditProductsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncCreditProductsWithStreamingResponse(self) + + +class CreditProductsWithRawResponse: + def __init__(self, credit_products: CreditProducts) -> None: + self._credit_products = credit_products + + @cached_property + def extended_credit(self) -> ExtendedCreditResourceWithRawResponse: + return ExtendedCreditResourceWithRawResponse(self._credit_products.extended_credit) + + @cached_property + def prime_rates(self) -> PrimeRatesWithRawResponse: + return PrimeRatesWithRawResponse(self._credit_products.prime_rates) + + +class AsyncCreditProductsWithRawResponse: + def __init__(self, credit_products: AsyncCreditProducts) -> None: + self._credit_products = credit_products + + @cached_property + def extended_credit(self) -> AsyncExtendedCreditResourceWithRawResponse: + return AsyncExtendedCreditResourceWithRawResponse(self._credit_products.extended_credit) + + @cached_property + def prime_rates(self) -> AsyncPrimeRatesWithRawResponse: + return AsyncPrimeRatesWithRawResponse(self._credit_products.prime_rates) + + +class CreditProductsWithStreamingResponse: + def __init__(self, credit_products: CreditProducts) -> None: + self._credit_products = credit_products + + @cached_property + def extended_credit(self) -> ExtendedCreditResourceWithStreamingResponse: + return ExtendedCreditResourceWithStreamingResponse(self._credit_products.extended_credit) + + @cached_property + def prime_rates(self) -> PrimeRatesWithStreamingResponse: + return PrimeRatesWithStreamingResponse(self._credit_products.prime_rates) + + +class AsyncCreditProductsWithStreamingResponse: + def __init__(self, credit_products: AsyncCreditProducts) -> None: + self._credit_products = credit_products + + @cached_property + def extended_credit(self) -> AsyncExtendedCreditResourceWithStreamingResponse: + return AsyncExtendedCreditResourceWithStreamingResponse(self._credit_products.extended_credit) + + @cached_property + def prime_rates(self) -> AsyncPrimeRatesWithStreamingResponse: + return AsyncPrimeRatesWithStreamingResponse(self._credit_products.prime_rates) diff --git a/src/lithic/resources/credit_products/extended_credit.py b/src/lithic/resources/credit_products/extended_credit.py new file mode 100644 index 00000000..82f709d2 --- /dev/null +++ b/src/lithic/resources/credit_products/extended_credit.py @@ -0,0 +1,163 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.credit_products.extended_credit import ExtendedCredit + +__all__ = ["ExtendedCreditResource", "AsyncExtendedCreditResource"] + + +class ExtendedCreditResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExtendedCreditResourceWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return ExtendedCreditResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExtendedCreditResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return ExtendedCreditResourceWithStreamingResponse(self) + + def retrieve( + self, + credit_product_token: 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, + ) -> ExtendedCredit: + """ + Get the extended credit for a given credit product under a program + + 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 credit_product_token: + raise ValueError( + f"Expected a non-empty value for `credit_product_token` but received {credit_product_token!r}" + ) + return self._get( + f"/v1/credit_products/{credit_product_token}/extended_credit", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtendedCredit, + ) + + +class AsyncExtendedCreditResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExtendedCreditResourceWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncExtendedCreditResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExtendedCreditResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncExtendedCreditResourceWithStreamingResponse(self) + + async def retrieve( + self, + credit_product_token: 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, + ) -> ExtendedCredit: + """ + Get the extended credit for a given credit product under a program + + 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 credit_product_token: + raise ValueError( + f"Expected a non-empty value for `credit_product_token` but received {credit_product_token!r}" + ) + return await self._get( + f"/v1/credit_products/{credit_product_token}/extended_credit", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExtendedCredit, + ) + + +class ExtendedCreditResourceWithRawResponse: + def __init__(self, extended_credit: ExtendedCreditResource) -> None: + self._extended_credit = extended_credit + + self.retrieve = _legacy_response.to_raw_response_wrapper( + extended_credit.retrieve, + ) + + +class AsyncExtendedCreditResourceWithRawResponse: + def __init__(self, extended_credit: AsyncExtendedCreditResource) -> None: + self._extended_credit = extended_credit + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + extended_credit.retrieve, + ) + + +class ExtendedCreditResourceWithStreamingResponse: + def __init__(self, extended_credit: ExtendedCreditResource) -> None: + self._extended_credit = extended_credit + + self.retrieve = to_streamed_response_wrapper( + extended_credit.retrieve, + ) + + +class AsyncExtendedCreditResourceWithStreamingResponse: + def __init__(self, extended_credit: AsyncExtendedCreditResource) -> None: + self._extended_credit = extended_credit + + self.retrieve = async_to_streamed_response_wrapper( + extended_credit.retrieve, + ) diff --git a/src/lithic/resources/credit_products/prime_rates.py b/src/lithic/resources/credit_products/prime_rates.py new file mode 100644 index 00000000..9c1b4aa4 --- /dev/null +++ b/src/lithic/resources/credit_products/prime_rates.py @@ -0,0 +1,319 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.credit_products import prime_rate_create_params, prime_rate_retrieve_params +from ...types.credit_products.prime_rate_retrieve_response import PrimeRateRetrieveResponse + +__all__ = ["PrimeRates", "AsyncPrimeRates"] + + +class PrimeRates(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PrimeRatesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return PrimeRatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PrimeRatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return PrimeRatesWithStreamingResponse(self) + + def create( + self, + credit_product_token: str, + *, + effective_date: Union[str, date], + rate: 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: + """ + Post Credit Product Prime Rate + + Args: + credit_product_token: Globally unique identifier for credit products. + + effective_date: Date the rate goes into effect + + rate: The rate in decimal format + + 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 credit_product_token: + raise ValueError( + f"Expected a non-empty value for `credit_product_token` but received {credit_product_token!r}" + ) + return self._post( + f"/v1/credit_products/{credit_product_token}/prime_rates", + body=maybe_transform( + { + "effective_date": effective_date, + "rate": rate, + }, + prime_rate_create_params.PrimeRateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def retrieve( + self, + credit_product_token: str, + *, + ending_before: Union[str, date] | NotGiven = NOT_GIVEN, + starting_after: Union[str, date] | 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, + ) -> PrimeRateRetrieveResponse: + """ + Get Credit Product Prime Rates + + Args: + credit_product_token: Globally unique identifier for credit products. + + ending_before: The effective date that the prime rates ends before + + starting_after: The effective date that the prime rate starts after + + 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 credit_product_token: + raise ValueError( + f"Expected a non-empty value for `credit_product_token` but received {credit_product_token!r}" + ) + return self._get( + f"/v1/credit_products/{credit_product_token}/prime_rates", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "ending_before": ending_before, + "starting_after": starting_after, + }, + prime_rate_retrieve_params.PrimeRateRetrieveParams, + ), + ), + cast_to=PrimeRateRetrieveResponse, + ) + + +class AsyncPrimeRates(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPrimeRatesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncPrimeRatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPrimeRatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncPrimeRatesWithStreamingResponse(self) + + async def create( + self, + credit_product_token: str, + *, + effective_date: Union[str, date], + rate: 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: + """ + Post Credit Product Prime Rate + + Args: + credit_product_token: Globally unique identifier for credit products. + + effective_date: Date the rate goes into effect + + rate: The rate in decimal format + + 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 credit_product_token: + raise ValueError( + f"Expected a non-empty value for `credit_product_token` but received {credit_product_token!r}" + ) + return await self._post( + f"/v1/credit_products/{credit_product_token}/prime_rates", + body=await async_maybe_transform( + { + "effective_date": effective_date, + "rate": rate, + }, + prime_rate_create_params.PrimeRateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def retrieve( + self, + credit_product_token: str, + *, + ending_before: Union[str, date] | NotGiven = NOT_GIVEN, + starting_after: Union[str, date] | 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, + ) -> PrimeRateRetrieveResponse: + """ + Get Credit Product Prime Rates + + Args: + credit_product_token: Globally unique identifier for credit products. + + ending_before: The effective date that the prime rates ends before + + starting_after: The effective date that the prime rate starts after + + 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 credit_product_token: + raise ValueError( + f"Expected a non-empty value for `credit_product_token` but received {credit_product_token!r}" + ) + return await self._get( + f"/v1/credit_products/{credit_product_token}/prime_rates", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "ending_before": ending_before, + "starting_after": starting_after, + }, + prime_rate_retrieve_params.PrimeRateRetrieveParams, + ), + ), + cast_to=PrimeRateRetrieveResponse, + ) + + +class PrimeRatesWithRawResponse: + def __init__(self, prime_rates: PrimeRates) -> None: + self._prime_rates = prime_rates + + self.create = _legacy_response.to_raw_response_wrapper( + prime_rates.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + prime_rates.retrieve, + ) + + +class AsyncPrimeRatesWithRawResponse: + def __init__(self, prime_rates: AsyncPrimeRates) -> None: + self._prime_rates = prime_rates + + self.create = _legacy_response.async_to_raw_response_wrapper( + prime_rates.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + prime_rates.retrieve, + ) + + +class PrimeRatesWithStreamingResponse: + def __init__(self, prime_rates: PrimeRates) -> None: + self._prime_rates = prime_rates + + self.create = to_streamed_response_wrapper( + prime_rates.create, + ) + self.retrieve = to_streamed_response_wrapper( + prime_rates.retrieve, + ) + + +class AsyncPrimeRatesWithStreamingResponse: + def __init__(self, prime_rates: AsyncPrimeRates) -> None: + self._prime_rates = prime_rates + + self.create = async_to_streamed_response_wrapper( + prime_rates.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + prime_rates.retrieve, + ) diff --git a/src/lithic/resources/digital_card_art.py b/src/lithic/resources/digital_card_art.py index 714026c9..2b6217ac 100644 --- a/src/lithic/resources/digital_card_art.py +++ b/src/lithic/resources/digital_card_art.py @@ -1,21 +1,19 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx from .. import _legacy_response -from ..types import DigitalCardArt, digital_card_art_list_params +from ..types import digital_card_art_list_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncCursorPage, AsyncCursorPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.digital_card_art import DigitalCardArt __all__ = ["DigitalCardArtResource", "AsyncDigitalCardArtResource"] @@ -23,10 +21,21 @@ class DigitalCardArtResource(SyncAPIResource): @cached_property def with_raw_response(self) -> DigitalCardArtResourceWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return DigitalCardArtResourceWithRawResponse(self) @cached_property def with_streaming_response(self) -> DigitalCardArtResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return DigitalCardArtResourceWithStreamingResponse(self) def retrieve( @@ -57,7 +66,7 @@ def retrieve( f"Expected a non-empty value for `digital_card_art_token` but received {digital_card_art_token!r}" ) return self._get( - f"/digital_card_art/{digital_card_art_token}", + f"/v1/digital_card_art/{digital_card_art_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -98,7 +107,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/digital_card_art", + "/v1/digital_card_art", page=SyncCursorPage[DigitalCardArt], options=make_request_options( extra_headers=extra_headers, @@ -121,10 +130,21 @@ def list( class AsyncDigitalCardArtResource(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncDigitalCardArtResourceWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncDigitalCardArtResourceWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncDigitalCardArtResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncDigitalCardArtResourceWithStreamingResponse(self) async def retrieve( @@ -155,7 +175,7 @@ async def retrieve( f"Expected a non-empty value for `digital_card_art_token` but received {digital_card_art_token!r}" ) return await self._get( - f"/digital_card_art/{digital_card_art_token}", + f"/v1/digital_card_art/{digital_card_art_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -196,7 +216,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/digital_card_art", + "/v1/digital_card_art", page=AsyncCursorPage[DigitalCardArt], options=make_request_options( extra_headers=extra_headers, diff --git a/src/lithic/resources/disputes.py b/src/lithic/resources/disputes.py index 627d2bea..fe1eb4bc 100644 --- a/src/lithic/resources/disputes.py +++ b/src/lithic/resources/disputes.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -10,33 +10,24 @@ from .. import _legacy_response from ..types import ( - Dispute, - DisputeEvidence, dispute_list_params, dispute_create_params, dispute_update_params, dispute_list_evidences_params, dispute_initiate_evidence_upload_params, ) -from .._types import ( - NOT_GIVEN, - Body, - Omit, - Query, - Headers, - NoneType, - NotGiven, - FileTypes, +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + maybe_transform, + async_maybe_transform, ) -from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncCursorPage, AsyncCursorPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.dispute import Dispute +from ..types.dispute_evidence import DisputeEvidence __all__ = ["Disputes", "AsyncDisputes"] @@ -44,10 +35,21 @@ class Disputes(SyncAPIResource): @cached_property def with_raw_response(self) -> DisputesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return DisputesWithRawResponse(self) @cached_property def with_streaming_response(self) -> DisputesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return DisputesWithStreamingResponse(self) def create( @@ -103,7 +105,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/disputes", + "/v1/disputes", body=maybe_transform( { "amount": amount, @@ -146,7 +148,7 @@ def retrieve( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return self._get( - f"/disputes/{dispute_token}", + f"/v1/disputes/{dispute_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -208,7 +210,7 @@ def update( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return self._patch( - f"/disputes/{dispute_token}", + f"/v1/disputes/{dispute_token}", body=maybe_transform( { "amount": amount, @@ -283,7 +285,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/disputes", + "/v1/disputes", page=SyncCursorPage[Dispute], options=make_request_options( extra_headers=extra_headers, @@ -332,7 +334,7 @@ def delete( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return self._delete( - f"/disputes/{dispute_token}", + f"/v1/disputes/{dispute_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -370,7 +372,7 @@ def delete_evidence( if not evidence_token: raise ValueError(f"Expected a non-empty value for `evidence_token` but received {evidence_token!r}") return self._delete( - f"/disputes/{dispute_token}/evidences/{evidence_token}", + f"/v1/disputes/{dispute_token}/evidences/{evidence_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -411,7 +413,7 @@ def initiate_evidence_upload( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return self._post( - f"/disputes/{dispute_token}/evidences", + f"/v1/disputes/{dispute_token}/evidences", body=maybe_transform( {"filename": filename}, dispute_initiate_evidence_upload_params.DisputeInitiateEvidenceUploadParams ), @@ -466,7 +468,7 @@ def list_evidences( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return self._get_api_list( - f"/disputes/{dispute_token}/evidences", + f"/v1/disputes/{dispute_token}/evidences", page=SyncCursorPage[DisputeEvidence], options=make_request_options( extra_headers=extra_headers, @@ -516,43 +518,32 @@ def retrieve_evidence( if not evidence_token: raise ValueError(f"Expected a non-empty value for `evidence_token` but received {evidence_token!r}") return self._get( - f"/disputes/{dispute_token}/evidences/{evidence_token}", + f"/v1/disputes/{dispute_token}/evidences/{evidence_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=DisputeEvidence, ) - def upload_evidence( - self, - dispute_token: str, - file: FileTypes, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - ) -> None: - """ - Initiates the Dispute Evidence Upload, then uploads the file to the returned - `upload_url`. - """ - payload = self._client.disputes.initiate_evidence_upload( - dispute_token, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body - ) - if not payload.upload_url: - raise ValueError("Missing 'upload_url' from response payload") - files = {"file": file} - options = make_request_options(extra_headers={"Authorization": Omit()}) - self._put(payload.upload_url, cast_to=NoneType, body=None, files=files, options=options) - class AsyncDisputes(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncDisputesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncDisputesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncDisputesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncDisputesWithStreamingResponse(self) async def create( @@ -608,8 +599,8 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/disputes", - body=maybe_transform( + "/v1/disputes", + body=await async_maybe_transform( { "amount": amount, "reason": reason, @@ -651,7 +642,7 @@ async def retrieve( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return await self._get( - f"/disputes/{dispute_token}", + f"/v1/disputes/{dispute_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -713,8 +704,8 @@ async def update( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return await self._patch( - f"/disputes/{dispute_token}", - body=maybe_transform( + f"/v1/disputes/{dispute_token}", + body=await async_maybe_transform( { "amount": amount, "customer_filed_date": customer_filed_date, @@ -788,7 +779,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/disputes", + "/v1/disputes", page=AsyncCursorPage[Dispute], options=make_request_options( extra_headers=extra_headers, @@ -837,7 +828,7 @@ async def delete( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return await self._delete( - f"/disputes/{dispute_token}", + f"/v1/disputes/{dispute_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -875,7 +866,7 @@ async def delete_evidence( if not evidence_token: raise ValueError(f"Expected a non-empty value for `evidence_token` but received {evidence_token!r}") return await self._delete( - f"/disputes/{dispute_token}/evidences/{evidence_token}", + f"/v1/disputes/{dispute_token}/evidences/{evidence_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -916,8 +907,8 @@ async def initiate_evidence_upload( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return await self._post( - f"/disputes/{dispute_token}/evidences", - body=maybe_transform( + f"/v1/disputes/{dispute_token}/evidences", + body=await async_maybe_transform( {"filename": filename}, dispute_initiate_evidence_upload_params.DisputeInitiateEvidenceUploadParams ), options=make_request_options( @@ -971,7 +962,7 @@ def list_evidences( if not dispute_token: raise ValueError(f"Expected a non-empty value for `dispute_token` but received {dispute_token!r}") return self._get_api_list( - f"/disputes/{dispute_token}/evidences", + f"/v1/disputes/{dispute_token}/evidences", page=AsyncCursorPage[DisputeEvidence], options=make_request_options( extra_headers=extra_headers, @@ -1021,35 +1012,13 @@ async def retrieve_evidence( if not evidence_token: raise ValueError(f"Expected a non-empty value for `evidence_token` but received {evidence_token!r}") return await self._get( - f"/disputes/{dispute_token}/evidences/{evidence_token}", + f"/v1/disputes/{dispute_token}/evidences/{evidence_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=DisputeEvidence, ) - async def upload_evidence( - self, - dispute_token: str, - file: FileTypes, - *, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - ) -> None: - """ - Initiates the Dispute Evidence Upload, then uploads the file to the returned - `upload_url`. - """ - payload = await self._client.disputes.initiate_evidence_upload( - dispute_token, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body - ) - if not payload.upload_url: - raise ValueError("Missing 'upload_url' from response payload") - files = {"file": file} - options = make_request_options(extra_headers={"Authorization": Omit()}) - await self._put(payload.upload_url, cast_to=NoneType, body=None, files=files, options=options) - class DisputesWithRawResponse: def __init__(self, disputes: Disputes) -> None: diff --git a/src/lithic/resources/events/__init__.py b/src/lithic/resources/events/__init__.py index d788f91f..bb505c94 100644 --- a/src/lithic/resources/events/__init__.py +++ b/src/lithic/resources/events/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .events import ( Events, @@ -16,6 +16,14 @@ SubscriptionsWithStreamingResponse, AsyncSubscriptionsWithStreamingResponse, ) +from .event_subscriptions import ( + EventSubscriptions, + AsyncEventSubscriptions, + EventSubscriptionsWithRawResponse, + AsyncEventSubscriptionsWithRawResponse, + EventSubscriptionsWithStreamingResponse, + AsyncEventSubscriptionsWithStreamingResponse, +) __all__ = [ "Subscriptions", @@ -24,6 +32,12 @@ "AsyncSubscriptionsWithRawResponse", "SubscriptionsWithStreamingResponse", "AsyncSubscriptionsWithStreamingResponse", + "EventSubscriptions", + "AsyncEventSubscriptions", + "EventSubscriptionsWithRawResponse", + "AsyncEventSubscriptionsWithRawResponse", + "EventSubscriptionsWithStreamingResponse", + "AsyncEventSubscriptionsWithStreamingResponse", "Events", "AsyncEvents", "EventsWithRawResponse", diff --git a/src/lithic/resources/events/event_subscriptions.py b/src/lithic/resources/events/event_subscriptions.py new file mode 100644 index 00000000..2644dd3f --- /dev/null +++ b/src/lithic/resources/events/event_subscriptions.py @@ -0,0 +1,168 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options + +__all__ = ["EventSubscriptions", "AsyncEventSubscriptions"] + + +class EventSubscriptions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EventSubscriptionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return EventSubscriptionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EventSubscriptionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return EventSubscriptionsWithStreamingResponse(self) + + def resend( + self, + event_subscription_token: str, + *, + event_token: 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: + """ + Resend an event to an event subscription. + + 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 event_token: + raise ValueError(f"Expected a non-empty value for `event_token` but received {event_token!r}") + if not event_subscription_token: + raise ValueError( + f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" + ) + return self._post( + f"/v1/events/{event_token}/event_subscriptions/{event_subscription_token}/resend", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncEventSubscriptions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEventSubscriptionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncEventSubscriptionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEventSubscriptionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncEventSubscriptionsWithStreamingResponse(self) + + async def resend( + self, + event_subscription_token: str, + *, + event_token: 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: + """ + Resend an event to an event subscription. + + 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 event_token: + raise ValueError(f"Expected a non-empty value for `event_token` but received {event_token!r}") + if not event_subscription_token: + raise ValueError( + f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" + ) + return await self._post( + f"/v1/events/{event_token}/event_subscriptions/{event_subscription_token}/resend", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class EventSubscriptionsWithRawResponse: + def __init__(self, event_subscriptions: EventSubscriptions) -> None: + self._event_subscriptions = event_subscriptions + + self.resend = _legacy_response.to_raw_response_wrapper( + event_subscriptions.resend, + ) + + +class AsyncEventSubscriptionsWithRawResponse: + def __init__(self, event_subscriptions: AsyncEventSubscriptions) -> None: + self._event_subscriptions = event_subscriptions + + self.resend = _legacy_response.async_to_raw_response_wrapper( + event_subscriptions.resend, + ) + + +class EventSubscriptionsWithStreamingResponse: + def __init__(self, event_subscriptions: EventSubscriptions) -> None: + self._event_subscriptions = event_subscriptions + + self.resend = to_streamed_response_wrapper( + event_subscriptions.resend, + ) + + +class AsyncEventSubscriptionsWithStreamingResponse: + def __init__(self, event_subscriptions: AsyncEventSubscriptions) -> None: + self._event_subscriptions = event_subscriptions + + self.resend = async_to_streamed_response_wrapper( + event_subscriptions.resend, + ) diff --git a/src/lithic/resources/events/events.py b/src/lithic/resources/events/events.py index 76bb29e4..4ed3aff6 100644 --- a/src/lithic/resources/events/events.py +++ b/src/lithic/resources/events/events.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,18 +9,14 @@ import httpx from ... import _legacy_response -from ...types import ( - Event, - MessageAttempt, - event_list_params, - event_list_attempts_params, -) -from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ...types import event_list_params, event_list_attempts_params +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage +from ...types.event import Event from .subscriptions import ( Subscriptions, AsyncSubscriptions, @@ -29,10 +25,16 @@ SubscriptionsWithStreamingResponse, AsyncSubscriptionsWithStreamingResponse, ) -from ..._base_client import ( - AsyncPaginator, - make_request_options, +from ..._base_client import AsyncPaginator, make_request_options +from .event_subscriptions import ( + EventSubscriptions, + AsyncEventSubscriptions, + EventSubscriptionsWithRawResponse, + AsyncEventSubscriptionsWithRawResponse, + EventSubscriptionsWithStreamingResponse, + AsyncEventSubscriptionsWithStreamingResponse, ) +from ...types.message_attempt import MessageAttempt __all__ = ["Events", "AsyncEvents"] @@ -42,12 +44,27 @@ class Events(SyncAPIResource): def subscriptions(self) -> Subscriptions: return Subscriptions(self._client) + @cached_property + def event_subscriptions(self) -> EventSubscriptions: + return EventSubscriptions(self._client) + @cached_property def with_raw_response(self) -> EventsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return EventsWithRawResponse(self) @cached_property def with_streaming_response(self) -> EventsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return EventsWithStreamingResponse(self) def retrieve( @@ -76,7 +93,7 @@ def retrieve( if not event_token: raise ValueError(f"Expected a non-empty value for `event_token` but received {event_token!r}") return self._get( - f"/events/{event_token}", + f"/v1/events/{event_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -94,20 +111,45 @@ def list( "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] | NotGiven = NOT_GIVEN, @@ -153,7 +195,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/events", + "/v1/events", page=SyncCursorPage[Event], options=make_request_options( extra_headers=extra_headers, @@ -222,7 +264,7 @@ def list_attempts( if not event_token: raise ValueError(f"Expected a non-empty value for `event_token` but received {event_token!r}") return self._get_api_list( - f"/events/{event_token}/attempts", + f"/v1/events/{event_token}/attempts", page=SyncCursorPage[MessageAttempt], options=make_request_options( extra_headers=extra_headers, @@ -244,34 +286,33 @@ def list_attempts( model=MessageAttempt, ) - def resend( - self, - event_token: str, - *, - event_subscription_token: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - ) -> None: - """Resend an event to an event subscription.""" - self._post( - f"/events/{event_token}/event_subscriptions/{event_subscription_token}/resend", - options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), - cast_to=NoneType, - ) - class AsyncEvents(AsyncAPIResource): @cached_property def subscriptions(self) -> AsyncSubscriptions: return AsyncSubscriptions(self._client) + @cached_property + def event_subscriptions(self) -> AsyncEventSubscriptions: + return AsyncEventSubscriptions(self._client) + @cached_property def with_raw_response(self) -> AsyncEventsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncEventsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncEventsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncEventsWithStreamingResponse(self) async def retrieve( @@ -300,7 +341,7 @@ async def retrieve( if not event_token: raise ValueError(f"Expected a non-empty value for `event_token` but received {event_token!r}") return await self._get( - f"/events/{event_token}", + f"/v1/events/{event_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -318,20 +359,45 @@ def list( "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] | NotGiven = NOT_GIVEN, @@ -377,7 +443,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/events", + "/v1/events", page=AsyncCursorPage[Event], options=make_request_options( extra_headers=extra_headers, @@ -446,7 +512,7 @@ def list_attempts( if not event_token: raise ValueError(f"Expected a non-empty value for `event_token` but received {event_token!r}") return self._get_api_list( - f"/events/{event_token}/attempts", + f"/v1/events/{event_token}/attempts", page=AsyncCursorPage[MessageAttempt], options=make_request_options( extra_headers=extra_headers, @@ -468,22 +534,6 @@ def list_attempts( model=MessageAttempt, ) - async def resend( - self, - event_token: str, - *, - event_subscription_token: str, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - ) -> None: - """Resend an event to an event subscription.""" - await self._post( - f"/events/{event_token}/event_subscriptions/{event_subscription_token}/resend", - options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body), - cast_to=NoneType, - ) - class EventsWithRawResponse: def __init__(self, events: Events) -> None: @@ -503,6 +553,10 @@ def __init__(self, events: Events) -> None: def subscriptions(self) -> SubscriptionsWithRawResponse: return SubscriptionsWithRawResponse(self._events.subscriptions) + @cached_property + def event_subscriptions(self) -> EventSubscriptionsWithRawResponse: + return EventSubscriptionsWithRawResponse(self._events.event_subscriptions) + class AsyncEventsWithRawResponse: def __init__(self, events: AsyncEvents) -> None: @@ -522,6 +576,10 @@ def __init__(self, events: AsyncEvents) -> None: def subscriptions(self) -> AsyncSubscriptionsWithRawResponse: return AsyncSubscriptionsWithRawResponse(self._events.subscriptions) + @cached_property + def event_subscriptions(self) -> AsyncEventSubscriptionsWithRawResponse: + return AsyncEventSubscriptionsWithRawResponse(self._events.event_subscriptions) + class EventsWithStreamingResponse: def __init__(self, events: Events) -> None: @@ -541,6 +599,10 @@ def __init__(self, events: Events) -> None: def subscriptions(self) -> SubscriptionsWithStreamingResponse: return SubscriptionsWithStreamingResponse(self._events.subscriptions) + @cached_property + def event_subscriptions(self) -> EventSubscriptionsWithStreamingResponse: + return EventSubscriptionsWithStreamingResponse(self._events.event_subscriptions) + class AsyncEventsWithStreamingResponse: def __init__(self, events: AsyncEvents) -> None: @@ -559,3 +621,7 @@ def __init__(self, events: AsyncEvents) -> None: @cached_property def subscriptions(self) -> AsyncSubscriptionsWithStreamingResponse: return AsyncSubscriptionsWithStreamingResponse(self._events.subscriptions) + + @cached_property + def event_subscriptions(self) -> AsyncEventSubscriptionsWithStreamingResponse: + return AsyncEventSubscriptionsWithStreamingResponse(self._events.event_subscriptions) diff --git a/src/lithic/resources/events/subscriptions.py b/src/lithic/resources/events/subscriptions.py index 331a9779..f1752978 100644 --- a/src/lithic/resources/events/subscriptions.py +++ b/src/lithic/resources/events/subscriptions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,19 +9,17 @@ import httpx from ... import _legacy_response -from ...types import MessageAttempt, EventSubscription from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, 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 to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options from ...types.events import ( - SubscriptionRetrieveSecretResponse, subscription_list_params, subscription_create_params, subscription_update_params, @@ -30,6 +28,9 @@ subscription_replay_missing_params, subscription_send_simulated_example_params, ) +from ...types.message_attempt import MessageAttempt +from ...types.event_subscription import EventSubscription +from ...types.events.subscription_retrieve_secret_response import SubscriptionRetrieveSecretResponse __all__ = ["Subscriptions", "AsyncSubscriptions"] @@ -37,10 +38,21 @@ class Subscriptions(SyncAPIResource): @cached_property def with_raw_response(self) -> SubscriptionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return SubscriptionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> SubscriptionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return SubscriptionsWithStreamingResponse(self) def create( @@ -54,20 +66,45 @@ def create( "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] | NotGiven = NOT_GIVEN, @@ -100,7 +137,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/event_subscriptions", + "/v1/event_subscriptions", body=maybe_transform( { "url": url, @@ -144,7 +181,7 @@ def retrieve( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._get( - f"/event_subscriptions/{event_subscription_token}", + f"/v1/event_subscriptions/{event_subscription_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -163,20 +200,45 @@ def update( "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] | NotGiven = NOT_GIVEN, @@ -213,7 +275,7 @@ def update( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._patch( - f"/event_subscriptions/{event_subscription_token}", + f"/v1/event_subscriptions/{event_subscription_token}", body=maybe_transform( { "url": url, @@ -263,7 +325,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/event_subscriptions", + "/v1/event_subscriptions", page=SyncCursorPage[EventSubscription], options=make_request_options( extra_headers=extra_headers, @@ -310,7 +372,7 @@ def delete( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._delete( - f"/event_subscriptions/{event_subscription_token}", + f"/v1/event_subscriptions/{event_subscription_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -365,7 +427,7 @@ def list_attempts( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._get_api_list( - f"/event_subscriptions/{event_subscription_token}/attempts", + f"/v1/event_subscriptions/{event_subscription_token}/attempts", page=SyncCursorPage[MessageAttempt], options=make_request_options( extra_headers=extra_headers, @@ -423,7 +485,7 @@ def recover( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._post( - f"/event_subscriptions/{event_subscription_token}/recover", + f"/v1/event_subscriptions/{event_subscription_token}/recover", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -481,7 +543,7 @@ def replay_missing( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._post( - f"/event_subscriptions/{event_subscription_token}/replay_missing", + f"/v1/event_subscriptions/{event_subscription_token}/replay_missing", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -526,7 +588,7 @@ def retrieve_secret( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._get( - f"/event_subscriptions/{event_subscription_token}/secret", + f"/v1/event_subscriptions/{event_subscription_token}/secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -563,7 +625,7 @@ def rotate_secret( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._post( - f"/event_subscriptions/{event_subscription_token}/secret/rotate", + f"/v1/event_subscriptions/{event_subscription_token}/secret/rotate", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -578,20 +640,45 @@ def send_simulated_example( "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -620,7 +707,7 @@ def send_simulated_example( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._post( - f"/simulate/event_subscriptions/{event_subscription_token}/send_example", + f"/v1/simulate/event_subscriptions/{event_subscription_token}/send_example", body=maybe_transform( {"event_type": event_type}, subscription_send_simulated_example_params.SubscriptionSendSimulatedExampleParams, @@ -635,10 +722,21 @@ def send_simulated_example( class AsyncSubscriptions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncSubscriptionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncSubscriptionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncSubscriptionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncSubscriptionsWithStreamingResponse(self) async def create( @@ -652,20 +750,45 @@ async def create( "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] | NotGiven = NOT_GIVEN, @@ -698,8 +821,8 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/event_subscriptions", - body=maybe_transform( + "/v1/event_subscriptions", + body=await async_maybe_transform( { "url": url, "description": description, @@ -742,7 +865,7 @@ async def retrieve( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return await self._get( - f"/event_subscriptions/{event_subscription_token}", + f"/v1/event_subscriptions/{event_subscription_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -761,20 +884,45 @@ async def update( "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] | NotGiven = NOT_GIVEN, @@ -811,8 +959,8 @@ async def update( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return await self._patch( - f"/event_subscriptions/{event_subscription_token}", - body=maybe_transform( + f"/v1/event_subscriptions/{event_subscription_token}", + body=await async_maybe_transform( { "url": url, "description": description, @@ -861,7 +1009,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/event_subscriptions", + "/v1/event_subscriptions", page=AsyncCursorPage[EventSubscription], options=make_request_options( extra_headers=extra_headers, @@ -908,7 +1056,7 @@ async def delete( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return await self._delete( - f"/event_subscriptions/{event_subscription_token}", + f"/v1/event_subscriptions/{event_subscription_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -963,7 +1111,7 @@ def list_attempts( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return self._get_api_list( - f"/event_subscriptions/{event_subscription_token}/attempts", + f"/v1/event_subscriptions/{event_subscription_token}/attempts", page=AsyncCursorPage[MessageAttempt], options=make_request_options( extra_headers=extra_headers, @@ -1021,13 +1169,13 @@ async def recover( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return await self._post( - f"/event_subscriptions/{event_subscription_token}/recover", + f"/v1/event_subscriptions/{event_subscription_token}/recover", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform( + query=await async_maybe_transform( { "begin": begin, "end": end, @@ -1079,13 +1227,13 @@ async def replay_missing( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return await self._post( - f"/event_subscriptions/{event_subscription_token}/replay_missing", + f"/v1/event_subscriptions/{event_subscription_token}/replay_missing", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform( + query=await async_maybe_transform( { "begin": begin, "end": end, @@ -1124,7 +1272,7 @@ async def retrieve_secret( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return await self._get( - f"/event_subscriptions/{event_subscription_token}/secret", + f"/v1/event_subscriptions/{event_subscription_token}/secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1161,7 +1309,7 @@ async def rotate_secret( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return await self._post( - f"/event_subscriptions/{event_subscription_token}/secret/rotate", + f"/v1/event_subscriptions/{event_subscription_token}/secret/rotate", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1176,20 +1324,45 @@ async def send_simulated_example( "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1218,8 +1391,8 @@ async def send_simulated_example( f"Expected a non-empty value for `event_subscription_token` but received {event_subscription_token!r}" ) return await self._post( - f"/simulate/event_subscriptions/{event_subscription_token}/send_example", - body=maybe_transform( + f"/v1/simulate/event_subscriptions/{event_subscription_token}/send_example", + body=await async_maybe_transform( {"event_type": event_type}, subscription_send_simulated_example_params.SubscriptionSendSimulatedExampleParams, ), diff --git a/src/lithic/resources/external_bank_accounts/__init__.py b/src/lithic/resources/external_bank_accounts/__init__.py index 135f4be6..7e7201a8 100644 --- a/src/lithic/resources/external_bank_accounts/__init__.py +++ b/src/lithic/resources/external_bank_accounts/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .micro_deposits import ( MicroDeposits, diff --git a/src/lithic/resources/external_bank_accounts/external_bank_accounts.py b/src/lithic/resources/external_bank_accounts/external_bank_accounts.py index aa2580a3..4b2a8a0d 100644 --- a/src/lithic/resources/external_bank_accounts/external_bank_accounts.py +++ b/src/lithic/resources/external_bank_accounts/external_bank_accounts.py @@ -1,10 +1,10 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing import List, Union, overload +from typing import List, Union from datetime import date -from typing_extensions import Literal +from typing_extensions import Literal, overload import httpx @@ -12,26 +12,23 @@ from ...types import ( OwnerType, VerificationMethod, - ExternalBankAccountAddressParam, - ExternalBankAccountListResponse, - ExternalBankAccountCreateResponse, - ExternalBankAccountUpdateResponse, - ExternalBankAccountRetrieveResponse, - ExternalBankAccountRetryMicroDepositsResponse, external_bank_account_list_params, external_bank_account_create_params, external_bank_account_update_params, + external_bank_account_retry_prenote_params, + external_bank_account_retry_micro_deposits_params, ) from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import required_args, maybe_transform +from ..._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options from .micro_deposits import ( MicroDeposits, AsyncMicroDeposits, @@ -40,6 +37,15 @@ MicroDepositsWithStreamingResponse, AsyncMicroDepositsWithStreamingResponse, ) +from ...types.owner_type import OwnerType +from ...types.verification_method import VerificationMethod +from ...types.external_bank_account_address_param import ExternalBankAccountAddressParam +from ...types.external_bank_account_list_response import ExternalBankAccountListResponse +from ...types.external_bank_account_create_response import ExternalBankAccountCreateResponse +from ...types.external_bank_account_update_response import ExternalBankAccountUpdateResponse +from ...types.external_bank_account_retrieve_response import ExternalBankAccountRetrieveResponse +from ...types.external_bank_account_retry_prenote_response import ExternalBankAccountRetryPrenoteResponse +from ...types.external_bank_account_retry_micro_deposits_response import ExternalBankAccountRetryMicroDepositsResponse __all__ = ["ExternalBankAccounts", "AsyncExternalBankAccounts"] @@ -51,10 +57,21 @@ def micro_deposits(self) -> MicroDeposits: @cached_property def with_raw_response(self) -> ExternalBankAccountsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return ExternalBankAccountsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ExternalBankAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return ExternalBankAccountsWithStreamingResponse(self) @overload @@ -64,6 +81,7 @@ def create( account_number: str, country: str, currency: str, + financial_account_token: str, owner: str, owner_type: OwnerType, routing_number: str, @@ -88,15 +106,41 @@ def create( Creates an external bank account within a program or Lithic account. Args: - address: Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. + account_number: Account Number + + country: The country that the bank account is located in using ISO 3166-1. We will only + accept USA bank accounts e.g., USA + + currency: currency of the external account 3-character alphabetic ISO 4217 code + + financial_account_token: The financial account token of the operating account to fund the micro deposits + + owner: Legal Name of the business or individual who owns the external account. This + will appear in statements + + owner_type: Owner Type + + routing_number: Routing Number + + type: Account Type + + verification_method: Verification Method + + account_token: Indicates which Lithic account the external account is associated with. For + external accounts that are associated with the program, account_token field + returned will be null + + address: Address + + company_id: Optional field that helps identify bank accounts in receipts dob: Date of Birth of the Individual that owns the external bank account - verification_enforcement: Indicates whether verification was enforced for a given association record. For - MICRO_DEPOSIT, option to disable verification if the external bank account has - already been verified before. By default, verification will be required unless - users pass in a value of false + doing_business_as: Doing Business As + + name: The nickname for this External Bank Account + + user_defined_id: User Defined ID extra_headers: Send extra headers @@ -132,8 +176,99 @@ def create( Creates an external bank account within a program or Lithic account. Args: + owner: Legal Name of the business or individual who owns the external account. This + will appear in statements + + owner_type: Owner Type + + verification_method: Verification Method + + account_token: Indicates which Lithic account the external account is associated with. For + external accounts that are associated with the program, account_token field + returned will be null + + company_id: Optional field that helps identify bank accounts in receipts + dob: Date of Birth of the Individual that owns the external bank account + doing_business_as: Doing Business As + + user_defined_id: User Defined ID + + 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 create( + self, + *, + account_number: str, + country: str, + currency: str, + owner: str, + owner_type: OwnerType, + routing_number: str, + type: Literal["CHECKING", "SAVINGS"], + verification_method: Literal["EXTERNALLY_VERIFIED"], + account_token: str | NotGiven = NOT_GIVEN, + address: ExternalBankAccountAddressParam | NotGiven = NOT_GIVEN, + company_id: str | NotGiven = NOT_GIVEN, + dob: Union[str, date] | NotGiven = NOT_GIVEN, + doing_business_as: str | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + user_defined_id: 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, + ) -> ExternalBankAccountCreateResponse: + """ + Creates an external bank account within a program or Lithic account. + + Args: + account_number: Account Number + + country: The country that the bank account is located in using ISO 3166-1. We will only + accept USA bank accounts e.g., USA + + currency: currency of the external account 3-character alphabetic ISO 4217 code + + owner: Legal Name of the business or individual who owns the external account. This + will appear in statements + + owner_type: Owner Type + + routing_number: Routing Number + + type: Account Type + + verification_method: Verification Method + + account_token: Indicates which Lithic account the external account is associated with. For + external accounts that are associated with the program, account_token field + returned will be null + + address: Address + + company_id: Optional field that helps identify bank accounts in receipts + + dob: Date of Birth of the Individual that owns the external bank account + + doing_business_as: Doing Business As + + name: The nickname for this External Bank Account + + user_defined_id: User Defined ID + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -149,6 +284,7 @@ def create( "account_number", "country", "currency", + "financial_account_token", "owner", "owner_type", "routing_number", @@ -156,6 +292,16 @@ def create( "verification_method", ], ["owner", "owner_type", "processor_token", "verification_method"], + [ + "account_number", + "country", + "currency", + "owner", + "owner_type", + "routing_number", + "type", + "verification_method", + ], ) def create( self, @@ -163,11 +309,12 @@ def create( account_number: str | NotGiven = NOT_GIVEN, country: str | NotGiven = NOT_GIVEN, currency: str | NotGiven = NOT_GIVEN, + financial_account_token: str | NotGiven = NOT_GIVEN, owner: str, owner_type: OwnerType, routing_number: str | NotGiven = NOT_GIVEN, type: Literal["CHECKING", "SAVINGS"] | NotGiven = NOT_GIVEN, - verification_method: VerificationMethod, + verification_method: VerificationMethod | Literal["EXTERNALLY_VERIFIED"], account_token: str | NotGiven = NOT_GIVEN, address: ExternalBankAccountAddressParam | NotGiven = NOT_GIVEN, company_id: str | NotGiven = NOT_GIVEN, @@ -185,12 +332,13 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ExternalBankAccountCreateResponse: return self._post( - "/external_bank_accounts", + "/v1/external_bank_accounts", body=maybe_transform( { "account_number": account_number, "country": country, "currency": currency, + "financial_account_token": financial_account_token, "owner": owner, "owner_type": owner_type, "routing_number": routing_number, @@ -242,7 +390,7 @@ def retrieve( f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" ) return self._get( - f"/external_bank_accounts/{external_bank_account_token}", + f"/v1/external_bank_accounts/{external_bank_account_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -260,6 +408,7 @@ def update( name: str | NotGiven = NOT_GIVEN, owner: str | NotGiven = NOT_GIVEN, owner_type: OwnerType | NotGiven = NOT_GIVEN, + type: Literal["CHECKING", "SAVINGS"] | NotGiven = NOT_GIVEN, user_defined_id: 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. @@ -272,11 +421,23 @@ def update( Update the external bank account by token. Args: - address: Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. + address: Address + + company_id: Optional field that helps identify bank accounts in receipts dob: Date of Birth of the Individual that owns the external bank account + doing_business_as: Doing Business As + + name: The nickname for this External Bank Account + + owner: Legal Name of the business or individual who owns the external account. This + will appear in statements + + owner_type: Owner Type + + user_defined_id: User Defined ID + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -290,7 +451,7 @@ def update( f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" ) return self._patch( - f"/external_bank_accounts/{external_bank_account_token}", + f"/v1/external_bank_accounts/{external_bank_account_token}", body=maybe_transform( { "address": address, @@ -300,6 +461,7 @@ def update( "name": name, "owner": owner, "owner_type": owner_type, + "type": type, "user_defined_id": user_defined_id, }, external_bank_account_update_params.ExternalBankAccountUpdateParams, @@ -320,8 +482,8 @@ def list( owner_types: List[OwnerType] | NotGiven = NOT_GIVEN, page_size: int | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, - states: List[Literal["CLOSED", "ENABLED", "PAUSED"]] | NotGiven = NOT_GIVEN, - verification_states: List[Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"]] + states: List[Literal["ENABLED", "CLOSED", "PAUSED"]] | NotGiven = NOT_GIVEN, + verification_states: List[Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"]] | 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. @@ -351,7 +513,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/external_bank_accounts", + "/v1/external_bank_accounts", page=SyncCursorPage[ExternalBankAccountListResponse], options=make_request_options( extra_headers=extra_headers, @@ -380,6 +542,7 @@ def retry_micro_deposits( self, external_bank_account_token: str, *, + financial_account_token: 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, @@ -404,13 +567,57 @@ def retry_micro_deposits( f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" ) return self._post( - f"/external_bank_accounts/{external_bank_account_token}/retry_micro_deposits", + f"/v1/external_bank_accounts/{external_bank_account_token}/retry_micro_deposits", + body=maybe_transform( + {"financial_account_token": financial_account_token}, + external_bank_account_retry_micro_deposits_params.ExternalBankAccountRetryMicroDepositsParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ExternalBankAccountRetryMicroDepositsResponse, ) + def retry_prenote( + self, + external_bank_account_token: str, + *, + financial_account_token: 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, + ) -> ExternalBankAccountRetryPrenoteResponse: + """ + Retry external bank account prenote verification. + + 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 external_bank_account_token: + raise ValueError( + f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" + ) + return self._post( + f"/v1/external_bank_accounts/{external_bank_account_token}/retry_prenote", + body=maybe_transform( + {"financial_account_token": financial_account_token}, + external_bank_account_retry_prenote_params.ExternalBankAccountRetryPrenoteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalBankAccountRetryPrenoteResponse, + ) + class AsyncExternalBankAccounts(AsyncAPIResource): @cached_property @@ -419,10 +626,21 @@ def micro_deposits(self) -> AsyncMicroDeposits: @cached_property def with_raw_response(self) -> AsyncExternalBankAccountsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncExternalBankAccountsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncExternalBankAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncExternalBankAccountsWithStreamingResponse(self) @overload @@ -432,6 +650,7 @@ async def create( account_number: str, country: str, currency: str, + financial_account_token: str, owner: str, owner_type: OwnerType, routing_number: str, @@ -456,15 +675,41 @@ async def create( Creates an external bank account within a program or Lithic account. Args: - address: Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. + account_number: Account Number + + country: The country that the bank account is located in using ISO 3166-1. We will only + accept USA bank accounts e.g., USA + + currency: currency of the external account 3-character alphabetic ISO 4217 code + + financial_account_token: The financial account token of the operating account to fund the micro deposits + + owner: Legal Name of the business or individual who owns the external account. This + will appear in statements + + owner_type: Owner Type + + routing_number: Routing Number + + type: Account Type + + verification_method: Verification Method + + account_token: Indicates which Lithic account the external account is associated with. For + external accounts that are associated with the program, account_token field + returned will be null + + address: Address + + company_id: Optional field that helps identify bank accounts in receipts dob: Date of Birth of the Individual that owns the external bank account - verification_enforcement: Indicates whether verification was enforced for a given association record. For - MICRO_DEPOSIT, option to disable verification if the external bank account has - already been verified before. By default, verification will be required unless - users pass in a value of false + doing_business_as: Doing Business As + + name: The nickname for this External Bank Account + + user_defined_id: User Defined ID extra_headers: Send extra headers @@ -500,8 +745,99 @@ async def create( Creates an external bank account within a program or Lithic account. Args: + owner: Legal Name of the business or individual who owns the external account. This + will appear in statements + + owner_type: Owner Type + + verification_method: Verification Method + + account_token: Indicates which Lithic account the external account is associated with. For + external accounts that are associated with the program, account_token field + returned will be null + + company_id: Optional field that helps identify bank accounts in receipts + dob: Date of Birth of the Individual that owns the external bank account + doing_business_as: Doing Business As + + user_defined_id: User Defined ID + + 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 create( + self, + *, + account_number: str, + country: str, + currency: str, + owner: str, + owner_type: OwnerType, + routing_number: str, + type: Literal["CHECKING", "SAVINGS"], + verification_method: Literal["EXTERNALLY_VERIFIED"], + account_token: str | NotGiven = NOT_GIVEN, + address: ExternalBankAccountAddressParam | NotGiven = NOT_GIVEN, + company_id: str | NotGiven = NOT_GIVEN, + dob: Union[str, date] | NotGiven = NOT_GIVEN, + doing_business_as: str | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, + user_defined_id: 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, + ) -> ExternalBankAccountCreateResponse: + """ + Creates an external bank account within a program or Lithic account. + + Args: + account_number: Account Number + + country: The country that the bank account is located in using ISO 3166-1. We will only + accept USA bank accounts e.g., USA + + currency: currency of the external account 3-character alphabetic ISO 4217 code + + owner: Legal Name of the business or individual who owns the external account. This + will appear in statements + + owner_type: Owner Type + + routing_number: Routing Number + + type: Account Type + + verification_method: Verification Method + + account_token: Indicates which Lithic account the external account is associated with. For + external accounts that are associated with the program, account_token field + returned will be null + + address: Address + + company_id: Optional field that helps identify bank accounts in receipts + + dob: Date of Birth of the Individual that owns the external bank account + + doing_business_as: Doing Business As + + name: The nickname for this External Bank Account + + user_defined_id: User Defined ID + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -517,6 +853,7 @@ async def create( "account_number", "country", "currency", + "financial_account_token", "owner", "owner_type", "routing_number", @@ -524,6 +861,16 @@ async def create( "verification_method", ], ["owner", "owner_type", "processor_token", "verification_method"], + [ + "account_number", + "country", + "currency", + "owner", + "owner_type", + "routing_number", + "type", + "verification_method", + ], ) async def create( self, @@ -531,11 +878,12 @@ async def create( account_number: str | NotGiven = NOT_GIVEN, country: str | NotGiven = NOT_GIVEN, currency: str | NotGiven = NOT_GIVEN, + financial_account_token: str | NotGiven = NOT_GIVEN, owner: str, owner_type: OwnerType, routing_number: str | NotGiven = NOT_GIVEN, type: Literal["CHECKING", "SAVINGS"] | NotGiven = NOT_GIVEN, - verification_method: VerificationMethod, + verification_method: VerificationMethod | Literal["EXTERNALLY_VERIFIED"], account_token: str | NotGiven = NOT_GIVEN, address: ExternalBankAccountAddressParam | NotGiven = NOT_GIVEN, company_id: str | NotGiven = NOT_GIVEN, @@ -553,12 +901,13 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ExternalBankAccountCreateResponse: return await self._post( - "/external_bank_accounts", - body=maybe_transform( + "/v1/external_bank_accounts", + body=await async_maybe_transform( { "account_number": account_number, "country": country, "currency": currency, + "financial_account_token": financial_account_token, "owner": owner, "owner_type": owner_type, "routing_number": routing_number, @@ -610,7 +959,7 @@ async def retrieve( f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" ) return await self._get( - f"/external_bank_accounts/{external_bank_account_token}", + f"/v1/external_bank_accounts/{external_bank_account_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -628,6 +977,7 @@ async def update( name: str | NotGiven = NOT_GIVEN, owner: str | NotGiven = NOT_GIVEN, owner_type: OwnerType | NotGiven = NOT_GIVEN, + type: Literal["CHECKING", "SAVINGS"] | NotGiven = NOT_GIVEN, user_defined_id: 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. @@ -640,11 +990,23 @@ async def update( Update the external bank account by token. Args: - address: Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. + address: Address + + company_id: Optional field that helps identify bank accounts in receipts dob: Date of Birth of the Individual that owns the external bank account + doing_business_as: Doing Business As + + name: The nickname for this External Bank Account + + owner: Legal Name of the business or individual who owns the external account. This + will appear in statements + + owner_type: Owner Type + + user_defined_id: User Defined ID + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -658,8 +1020,8 @@ async def update( f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" ) return await self._patch( - f"/external_bank_accounts/{external_bank_account_token}", - body=maybe_transform( + f"/v1/external_bank_accounts/{external_bank_account_token}", + body=await async_maybe_transform( { "address": address, "company_id": company_id, @@ -668,6 +1030,7 @@ async def update( "name": name, "owner": owner, "owner_type": owner_type, + "type": type, "user_defined_id": user_defined_id, }, external_bank_account_update_params.ExternalBankAccountUpdateParams, @@ -688,8 +1051,8 @@ def list( owner_types: List[OwnerType] | NotGiven = NOT_GIVEN, page_size: int | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, - states: List[Literal["CLOSED", "ENABLED", "PAUSED"]] | NotGiven = NOT_GIVEN, - verification_states: List[Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"]] + states: List[Literal["ENABLED", "CLOSED", "PAUSED"]] | NotGiven = NOT_GIVEN, + verification_states: List[Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"]] | 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. @@ -719,7 +1082,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/external_bank_accounts", + "/v1/external_bank_accounts", page=AsyncCursorPage[ExternalBankAccountListResponse], options=make_request_options( extra_headers=extra_headers, @@ -748,6 +1111,7 @@ async def retry_micro_deposits( self, external_bank_account_token: str, *, + financial_account_token: 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, @@ -772,13 +1136,57 @@ async def retry_micro_deposits( f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" ) return await self._post( - f"/external_bank_accounts/{external_bank_account_token}/retry_micro_deposits", + f"/v1/external_bank_accounts/{external_bank_account_token}/retry_micro_deposits", + body=await async_maybe_transform( + {"financial_account_token": financial_account_token}, + external_bank_account_retry_micro_deposits_params.ExternalBankAccountRetryMicroDepositsParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ExternalBankAccountRetryMicroDepositsResponse, ) + async def retry_prenote( + self, + external_bank_account_token: str, + *, + financial_account_token: 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, + ) -> ExternalBankAccountRetryPrenoteResponse: + """ + Retry external bank account prenote verification. + + 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 external_bank_account_token: + raise ValueError( + f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" + ) + return await self._post( + f"/v1/external_bank_accounts/{external_bank_account_token}/retry_prenote", + body=await async_maybe_transform( + {"financial_account_token": financial_account_token}, + external_bank_account_retry_prenote_params.ExternalBankAccountRetryPrenoteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalBankAccountRetryPrenoteResponse, + ) + class ExternalBankAccountsWithRawResponse: def __init__(self, external_bank_accounts: ExternalBankAccounts) -> None: @@ -799,6 +1207,9 @@ def __init__(self, external_bank_accounts: ExternalBankAccounts) -> None: self.retry_micro_deposits = _legacy_response.to_raw_response_wrapper( external_bank_accounts.retry_micro_deposits, ) + self.retry_prenote = _legacy_response.to_raw_response_wrapper( + external_bank_accounts.retry_prenote, + ) @cached_property def micro_deposits(self) -> MicroDepositsWithRawResponse: @@ -824,6 +1235,9 @@ def __init__(self, external_bank_accounts: AsyncExternalBankAccounts) -> None: self.retry_micro_deposits = _legacy_response.async_to_raw_response_wrapper( external_bank_accounts.retry_micro_deposits, ) + self.retry_prenote = _legacy_response.async_to_raw_response_wrapper( + external_bank_accounts.retry_prenote, + ) @cached_property def micro_deposits(self) -> AsyncMicroDepositsWithRawResponse: @@ -849,6 +1263,9 @@ def __init__(self, external_bank_accounts: ExternalBankAccounts) -> None: self.retry_micro_deposits = to_streamed_response_wrapper( external_bank_accounts.retry_micro_deposits, ) + self.retry_prenote = to_streamed_response_wrapper( + external_bank_accounts.retry_prenote, + ) @cached_property def micro_deposits(self) -> MicroDepositsWithStreamingResponse: @@ -874,6 +1291,9 @@ def __init__(self, external_bank_accounts: AsyncExternalBankAccounts) -> None: self.retry_micro_deposits = async_to_streamed_response_wrapper( external_bank_accounts.retry_micro_deposits, ) + self.retry_prenote = async_to_streamed_response_wrapper( + external_bank_accounts.retry_prenote, + ) @cached_property def micro_deposits(self) -> AsyncMicroDepositsWithStreamingResponse: diff --git a/src/lithic/resources/external_bank_accounts/micro_deposits.py b/src/lithic/resources/external_bank_accounts/micro_deposits.py index 82066717..91080eeb 100644 --- a/src/lithic/resources/external_bank_accounts/micro_deposits.py +++ b/src/lithic/resources/external_bank_accounts/micro_deposits.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -8,14 +8,16 @@ from ... import _legacy_response 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 to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..._base_client import ( - make_request_options, -) -from ...types.external_bank_accounts import MicroDepositCreateResponse, micro_deposit_create_params +from ..._base_client import make_request_options +from ...types.external_bank_accounts import micro_deposit_create_params +from ...types.external_bank_accounts.micro_deposit_create_response import MicroDepositCreateResponse __all__ = ["MicroDeposits", "AsyncMicroDeposits"] @@ -23,10 +25,21 @@ class MicroDeposits(SyncAPIResource): @cached_property def with_raw_response(self) -> MicroDepositsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return MicroDepositsWithRawResponse(self) @cached_property def with_streaming_response(self) -> MicroDepositsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return MicroDepositsWithStreamingResponse(self) def create( @@ -58,7 +71,7 @@ def create( f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" ) return self._post( - f"/external_bank_accounts/{external_bank_account_token}/micro_deposits", + f"/v1/external_bank_accounts/{external_bank_account_token}/micro_deposits", body=maybe_transform( {"micro_deposits": micro_deposits}, micro_deposit_create_params.MicroDepositCreateParams ), @@ -72,10 +85,21 @@ def create( class AsyncMicroDeposits(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncMicroDepositsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncMicroDepositsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMicroDepositsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncMicroDepositsWithStreamingResponse(self) async def create( @@ -107,8 +131,8 @@ async def create( f"Expected a non-empty value for `external_bank_account_token` but received {external_bank_account_token!r}" ) return await self._post( - f"/external_bank_accounts/{external_bank_account_token}/micro_deposits", - body=maybe_transform( + f"/v1/external_bank_accounts/{external_bank_account_token}/micro_deposits", + body=await async_maybe_transform( {"micro_deposits": micro_deposits}, micro_deposit_create_params.MicroDepositCreateParams ), options=make_request_options( diff --git a/src/lithic/resources/external_payments.py b/src/lithic/resources/external_payments.py new file mode 100644 index 00000000..c03fdaef --- /dev/null +++ b/src/lithic/resources/external_payments.py @@ -0,0 +1,882 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date, datetime +from typing_extensions import Literal + +import httpx + +from .. import _legacy_response +from ..types import ( + external_payment_list_params, + external_payment_cancel_params, + external_payment_create_params, + external_payment_settle_params, + external_payment_release_params, + external_payment_reverse_params, +) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + maybe_transform, + async_maybe_transform, +) +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.external_payment import ExternalPayment + +__all__ = ["ExternalPayments", "AsyncExternalPayments"] + + +class ExternalPayments(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExternalPaymentsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return ExternalPaymentsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExternalPaymentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return ExternalPaymentsWithStreamingResponse(self) + + def create( + self, + *, + amount: int, + category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"], + effective_date: Union[str, date], + financial_account_token: str, + payment_type: Literal["DEPOSIT", "WITHDRAWAL"], + token: str | NotGiven = NOT_GIVEN, + memo: str | NotGiven = NOT_GIVEN, + progress_to: Literal["SETTLED", "RELEASED"] | NotGiven = NOT_GIVEN, + user_defined_id: 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, + ) -> ExternalPayment: + """ + Create external payment + + 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 + """ + return self._post( + "/v1/external_payments", + body=maybe_transform( + { + "amount": amount, + "category": category, + "effective_date": effective_date, + "financial_account_token": financial_account_token, + "payment_type": payment_type, + "token": token, + "memo": memo, + "progress_to": progress_to, + "user_defined_id": user_defined_id, + }, + external_payment_create_params.ExternalPaymentCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + def retrieve( + self, + external_payment_token: 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, + ) -> ExternalPayment: + """ + Get external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return self._get( + f"/v1/external_payments/{external_payment_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + def list( + self, + *, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"] + | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + financial_account_token: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + result: Literal["APPROVED", "DECLINED"] | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED"] | 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, + ) -> SyncCursorPage[ExternalPayment]: + """List external payments + + Args: + begin: Date string in RFC 3339 format. + + Only entries created after the specified time + will be included. UTC time zone. + + category: External Payment category to be returned. + + end: Date string in RFC 3339 format. Only entries created before the specified time + will be included. UTC time zone. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + financial_account_token: Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + + page_size: Page size (for pagination). + + result: External Payment result to be returned. + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + status: Book transfer status to be returned. + + 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( + "/v1/external_payments", + page=SyncCursorPage[ExternalPayment], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "begin": begin, + "business_account_token": business_account_token, + "category": category, + "end": end, + "ending_before": ending_before, + "financial_account_token": financial_account_token, + "page_size": page_size, + "result": result, + "starting_after": starting_after, + "status": status, + }, + external_payment_list_params.ExternalPaymentListParams, + ), + ), + model=ExternalPayment, + ) + + def cancel( + self, + external_payment_token: str, + *, + effective_date: Union[str, date], + memo: 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, + ) -> ExternalPayment: + """ + Cancel external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return self._post( + f"/v1/external_payments/{external_payment_token}/cancel", + body=maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + }, + external_payment_cancel_params.ExternalPaymentCancelParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + def release( + self, + external_payment_token: str, + *, + effective_date: Union[str, date], + memo: 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, + ) -> ExternalPayment: + """ + Release external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return self._post( + f"/v1/external_payments/{external_payment_token}/release", + body=maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + }, + external_payment_release_params.ExternalPaymentReleaseParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + def reverse( + self, + external_payment_token: str, + *, + effective_date: Union[str, date], + memo: 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, + ) -> ExternalPayment: + """ + Reverse external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return self._post( + f"/v1/external_payments/{external_payment_token}/reverse", + body=maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + }, + external_payment_reverse_params.ExternalPaymentReverseParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + def settle( + self, + external_payment_token: str, + *, + effective_date: Union[str, date], + memo: str | NotGiven = NOT_GIVEN, + progress_to: Literal["SETTLED", "RELEASED"] | 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, + ) -> ExternalPayment: + """ + Settle external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return self._post( + f"/v1/external_payments/{external_payment_token}/settle", + body=maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + "progress_to": progress_to, + }, + external_payment_settle_params.ExternalPaymentSettleParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + +class AsyncExternalPayments(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExternalPaymentsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncExternalPaymentsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExternalPaymentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncExternalPaymentsWithStreamingResponse(self) + + async def create( + self, + *, + amount: int, + category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"], + effective_date: Union[str, date], + financial_account_token: str, + payment_type: Literal["DEPOSIT", "WITHDRAWAL"], + token: str | NotGiven = NOT_GIVEN, + memo: str | NotGiven = NOT_GIVEN, + progress_to: Literal["SETTLED", "RELEASED"] | NotGiven = NOT_GIVEN, + user_defined_id: 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, + ) -> ExternalPayment: + """ + Create external payment + + 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 + """ + return await self._post( + "/v1/external_payments", + body=await async_maybe_transform( + { + "amount": amount, + "category": category, + "effective_date": effective_date, + "financial_account_token": financial_account_token, + "payment_type": payment_type, + "token": token, + "memo": memo, + "progress_to": progress_to, + "user_defined_id": user_defined_id, + }, + external_payment_create_params.ExternalPaymentCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + async def retrieve( + self, + external_payment_token: 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, + ) -> ExternalPayment: + """ + Get external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return await self._get( + f"/v1/external_payments/{external_payment_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + def list( + self, + *, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"] + | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + financial_account_token: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + result: Literal["APPROVED", "DECLINED"] | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED"] | 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, + ) -> AsyncPaginator[ExternalPayment, AsyncCursorPage[ExternalPayment]]: + """List external payments + + Args: + begin: Date string in RFC 3339 format. + + Only entries created after the specified time + will be included. UTC time zone. + + category: External Payment category to be returned. + + end: Date string in RFC 3339 format. Only entries created before the specified time + will be included. UTC time zone. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + financial_account_token: Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + + page_size: Page size (for pagination). + + result: External Payment result to be returned. + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + status: Book transfer status to be returned. + + 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( + "/v1/external_payments", + page=AsyncCursorPage[ExternalPayment], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "begin": begin, + "business_account_token": business_account_token, + "category": category, + "end": end, + "ending_before": ending_before, + "financial_account_token": financial_account_token, + "page_size": page_size, + "result": result, + "starting_after": starting_after, + "status": status, + }, + external_payment_list_params.ExternalPaymentListParams, + ), + ), + model=ExternalPayment, + ) + + async def cancel( + self, + external_payment_token: str, + *, + effective_date: Union[str, date], + memo: 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, + ) -> ExternalPayment: + """ + Cancel external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return await self._post( + f"/v1/external_payments/{external_payment_token}/cancel", + body=await async_maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + }, + external_payment_cancel_params.ExternalPaymentCancelParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + async def release( + self, + external_payment_token: str, + *, + effective_date: Union[str, date], + memo: 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, + ) -> ExternalPayment: + """ + Release external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return await self._post( + f"/v1/external_payments/{external_payment_token}/release", + body=await async_maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + }, + external_payment_release_params.ExternalPaymentReleaseParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + async def reverse( + self, + external_payment_token: str, + *, + effective_date: Union[str, date], + memo: 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, + ) -> ExternalPayment: + """ + Reverse external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return await self._post( + f"/v1/external_payments/{external_payment_token}/reverse", + body=await async_maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + }, + external_payment_reverse_params.ExternalPaymentReverseParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + async def settle( + self, + external_payment_token: str, + *, + effective_date: Union[str, date], + memo: str | NotGiven = NOT_GIVEN, + progress_to: Literal["SETTLED", "RELEASED"] | 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, + ) -> ExternalPayment: + """ + Settle external payment + + 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 external_payment_token: + raise ValueError( + f"Expected a non-empty value for `external_payment_token` but received {external_payment_token!r}" + ) + return await self._post( + f"/v1/external_payments/{external_payment_token}/settle", + body=await async_maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + "progress_to": progress_to, + }, + external_payment_settle_params.ExternalPaymentSettleParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExternalPayment, + ) + + +class ExternalPaymentsWithRawResponse: + def __init__(self, external_payments: ExternalPayments) -> None: + self._external_payments = external_payments + + self.create = _legacy_response.to_raw_response_wrapper( + external_payments.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + external_payments.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + external_payments.list, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + external_payments.cancel, + ) + self.release = _legacy_response.to_raw_response_wrapper( + external_payments.release, + ) + self.reverse = _legacy_response.to_raw_response_wrapper( + external_payments.reverse, + ) + self.settle = _legacy_response.to_raw_response_wrapper( + external_payments.settle, + ) + + +class AsyncExternalPaymentsWithRawResponse: + def __init__(self, external_payments: AsyncExternalPayments) -> None: + self._external_payments = external_payments + + self.create = _legacy_response.async_to_raw_response_wrapper( + external_payments.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + external_payments.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + external_payments.list, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + external_payments.cancel, + ) + self.release = _legacy_response.async_to_raw_response_wrapper( + external_payments.release, + ) + self.reverse = _legacy_response.async_to_raw_response_wrapper( + external_payments.reverse, + ) + self.settle = _legacy_response.async_to_raw_response_wrapper( + external_payments.settle, + ) + + +class ExternalPaymentsWithStreamingResponse: + def __init__(self, external_payments: ExternalPayments) -> None: + self._external_payments = external_payments + + self.create = to_streamed_response_wrapper( + external_payments.create, + ) + self.retrieve = to_streamed_response_wrapper( + external_payments.retrieve, + ) + self.list = to_streamed_response_wrapper( + external_payments.list, + ) + self.cancel = to_streamed_response_wrapper( + external_payments.cancel, + ) + self.release = to_streamed_response_wrapper( + external_payments.release, + ) + self.reverse = to_streamed_response_wrapper( + external_payments.reverse, + ) + self.settle = to_streamed_response_wrapper( + external_payments.settle, + ) + + +class AsyncExternalPaymentsWithStreamingResponse: + def __init__(self, external_payments: AsyncExternalPayments) -> None: + self._external_payments = external_payments + + self.create = async_to_streamed_response_wrapper( + external_payments.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + external_payments.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + external_payments.list, + ) + self.cancel = async_to_streamed_response_wrapper( + external_payments.cancel, + ) + self.release = async_to_streamed_response_wrapper( + external_payments.release, + ) + self.reverse = async_to_streamed_response_wrapper( + external_payments.reverse, + ) + self.settle = async_to_streamed_response_wrapper( + external_payments.settle, + ) diff --git a/src/lithic/resources/financial_accounts/__init__.py b/src/lithic/resources/financial_accounts/__init__.py index e9c62a23..ad35c907 100644 --- a/src/lithic/resources/financial_accounts/__init__.py +++ b/src/lithic/resources/financial_accounts/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .balances import ( Balances, @@ -8,6 +8,14 @@ BalancesWithStreamingResponse, AsyncBalancesWithStreamingResponse, ) +from .loan_tapes import ( + LoanTapes, + AsyncLoanTapes, + LoanTapesWithRawResponse, + AsyncLoanTapesWithRawResponse, + LoanTapesWithStreamingResponse, + AsyncLoanTapesWithStreamingResponse, +) from .statements import ( Statements, AsyncStatements, @@ -24,6 +32,14 @@ FinancialAccountsWithStreamingResponse, AsyncFinancialAccountsWithStreamingResponse, ) +from .credit_configuration import ( + CreditConfiguration, + AsyncCreditConfiguration, + CreditConfigurationWithRawResponse, + AsyncCreditConfigurationWithRawResponse, + CreditConfigurationWithStreamingResponse, + AsyncCreditConfigurationWithStreamingResponse, +) from .financial_transactions import ( FinancialTransactions, AsyncFinancialTransactions, @@ -46,12 +62,24 @@ "AsyncFinancialTransactionsWithRawResponse", "FinancialTransactionsWithStreamingResponse", "AsyncFinancialTransactionsWithStreamingResponse", + "CreditConfiguration", + "AsyncCreditConfiguration", + "CreditConfigurationWithRawResponse", + "AsyncCreditConfigurationWithRawResponse", + "CreditConfigurationWithStreamingResponse", + "AsyncCreditConfigurationWithStreamingResponse", "Statements", "AsyncStatements", "StatementsWithRawResponse", "AsyncStatementsWithRawResponse", "StatementsWithStreamingResponse", "AsyncStatementsWithStreamingResponse", + "LoanTapes", + "AsyncLoanTapes", + "LoanTapesWithRawResponse", + "AsyncLoanTapesWithRawResponse", + "LoanTapesWithStreamingResponse", + "AsyncLoanTapesWithStreamingResponse", "FinancialAccounts", "AsyncFinancialAccounts", "FinancialAccountsWithRawResponse", diff --git a/src/lithic/resources/financial_accounts/balances.py b/src/lithic/resources/financial_accounts/balances.py index 8742575f..4c118fae 100644 --- a/src/lithic/resources/financial_accounts/balances.py +++ b/src/lithic/resources/financial_accounts/balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -8,18 +8,15 @@ import httpx from ... import _legacy_response -from ...types import Balance from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncSinglePage, AsyncSinglePage -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options from ...types.financial_accounts import balance_list_params +from ...types.financial_accounts.balance_list_response import BalanceListResponse __all__ = ["Balances", "AsyncBalances"] @@ -27,10 +24,21 @@ class Balances(SyncAPIResource): @cached_property def with_raw_response(self) -> BalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return BalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> BalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return BalancesWithStreamingResponse(self) def list( @@ -45,7 +53,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncSinglePage[Balance]: + ) -> SyncSinglePage[BalanceListResponse]: """ Get the balances for a given financial account. @@ -69,8 +77,8 @@ def list( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return self._get_api_list( - f"/financial_accounts/{financial_account_token}/balances", - page=SyncSinglePage[Balance], + f"/v1/financial_accounts/{financial_account_token}/balances", + page=SyncSinglePage[BalanceListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -84,17 +92,28 @@ def list( balance_list_params.BalanceListParams, ), ), - model=Balance, + model=BalanceListResponse, ) class AsyncBalances(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncBalancesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncBalancesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBalancesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncBalancesWithStreamingResponse(self) def list( @@ -109,7 +128,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[Balance, AsyncSinglePage[Balance]]: + ) -> AsyncPaginator[BalanceListResponse, AsyncSinglePage[BalanceListResponse]]: """ Get the balances for a given financial account. @@ -133,8 +152,8 @@ def list( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return self._get_api_list( - f"/financial_accounts/{financial_account_token}/balances", - page=AsyncSinglePage[Balance], + f"/v1/financial_accounts/{financial_account_token}/balances", + page=AsyncSinglePage[BalanceListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -148,7 +167,7 @@ def list( balance_list_params.BalanceListParams, ), ), - model=Balance, + model=BalanceListResponse, ) diff --git a/src/lithic/resources/accounts/credit_configurations.py b/src/lithic/resources/financial_accounts/credit_configuration.py similarity index 51% rename from src/lithic/resources/accounts/credit_configurations.py rename to src/lithic/resources/financial_accounts/credit_configuration.py index 39e55d2a..2849da85 100644 --- a/src/lithic/resources/accounts/credit_configurations.py +++ b/src/lithic/resources/financial_accounts/credit_configuration.py @@ -1,36 +1,48 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx from ... import _legacy_response -from ...types import BusinessAccount 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 to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..._base_client import ( - make_request_options, -) -from ...types.accounts import credit_configuration_update_params +from ..._base_client import make_request_options +from ...types.financial_accounts import credit_configuration_update_params +from ...types.financial_accounts.financial_account_credit_config import FinancialAccountCreditConfig -__all__ = ["CreditConfigurations", "AsyncCreditConfigurations"] +__all__ = ["CreditConfiguration", "AsyncCreditConfiguration"] -class CreditConfigurations(SyncAPIResource): +class CreditConfiguration(SyncAPIResource): @cached_property - def with_raw_response(self) -> CreditConfigurationsWithRawResponse: - return CreditConfigurationsWithRawResponse(self) + def with_raw_response(self) -> CreditConfigurationWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return CreditConfigurationWithRawResponse(self) @cached_property - def with_streaming_response(self) -> CreditConfigurationsWithStreamingResponse: - return CreditConfigurationsWithStreamingResponse(self) + def with_streaming_response(self) -> CreditConfigurationWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return CreditConfigurationWithStreamingResponse(self) def retrieve( self, - account_token: str, + financial_account_token: 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. @@ -38,7 +50,7 @@ def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BusinessAccount: + ) -> FinancialAccountCreditConfig: """ Get an Account's credit configuration @@ -51,42 +63,40 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ - if not account_token: - raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) return self._get( - f"/accounts/{account_token}/credit_configuration", + f"/v1/financial_accounts/{financial_account_token}/credit_configuration", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=BusinessAccount, + cast_to=FinancialAccountCreditConfig, ) def update( self, - account_token: str, + financial_account_token: str, *, - billing_period: int | NotGiven = NOT_GIVEN, credit_limit: int | NotGiven = NOT_GIVEN, + credit_product_token: str | NotGiven = NOT_GIVEN, external_bank_account_token: str | NotGiven = NOT_GIVEN, - payment_period: int | NotGiven = NOT_GIVEN, + tier: 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, - ) -> BusinessAccount: + ) -> FinancialAccountCreditConfig: """ - Update a Business Accounts credit configuration + Update an account's credit configuration Args: - billing_period: Number of days within the billing period - - credit_limit: Credit limit extended to the Business Account - - external_bank_account_token: The external bank account token to use for auto-collections + credit_product_token: Globally unique identifier for the credit product - payment_period: Number of days after the billing period ends that a payment is required + tier: Tier to assign to a financial account extra_headers: Send extra headers @@ -96,38 +106,51 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ - if not account_token: - raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) return self._patch( - f"/accounts/{account_token}/credit_configuration", + f"/v1/financial_accounts/{financial_account_token}/credit_configuration", body=maybe_transform( { - "billing_period": billing_period, "credit_limit": credit_limit, + "credit_product_token": credit_product_token, "external_bank_account_token": external_bank_account_token, - "payment_period": payment_period, + "tier": tier, }, credit_configuration_update_params.CreditConfigurationUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=BusinessAccount, + cast_to=FinancialAccountCreditConfig, ) -class AsyncCreditConfigurations(AsyncAPIResource): +class AsyncCreditConfiguration(AsyncAPIResource): @cached_property - def with_raw_response(self) -> AsyncCreditConfigurationsWithRawResponse: - return AsyncCreditConfigurationsWithRawResponse(self) + def with_raw_response(self) -> AsyncCreditConfigurationWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncCreditConfigurationWithRawResponse(self) @cached_property - def with_streaming_response(self) -> AsyncCreditConfigurationsWithStreamingResponse: - return AsyncCreditConfigurationsWithStreamingResponse(self) + def with_streaming_response(self) -> AsyncCreditConfigurationWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncCreditConfigurationWithStreamingResponse(self) async def retrieve( self, - account_token: str, + financial_account_token: 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. @@ -135,7 +158,7 @@ async def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BusinessAccount: + ) -> FinancialAccountCreditConfig: """ Get an Account's credit configuration @@ -148,42 +171,40 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ - if not account_token: - raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) return await self._get( - f"/accounts/{account_token}/credit_configuration", + f"/v1/financial_accounts/{financial_account_token}/credit_configuration", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=BusinessAccount, + cast_to=FinancialAccountCreditConfig, ) async def update( self, - account_token: str, + financial_account_token: str, *, - billing_period: int | NotGiven = NOT_GIVEN, credit_limit: int | NotGiven = NOT_GIVEN, + credit_product_token: str | NotGiven = NOT_GIVEN, external_bank_account_token: str | NotGiven = NOT_GIVEN, - payment_period: int | NotGiven = NOT_GIVEN, + tier: 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, - ) -> BusinessAccount: + ) -> FinancialAccountCreditConfig: """ - Update a Business Accounts credit configuration + Update an account's credit configuration Args: - billing_period: Number of days within the billing period - - credit_limit: Credit limit extended to the Business Account + credit_product_token: Globally unique identifier for the credit product - external_bank_account_token: The external bank account token to use for auto-collections - - payment_period: Number of days after the billing period ends that a payment is required + tier: Tier to assign to a financial account extra_headers: Send extra headers @@ -193,69 +214,71 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ - if not account_token: - raise ValueError(f"Expected a non-empty value for `account_token` but received {account_token!r}") + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) return await self._patch( - f"/accounts/{account_token}/credit_configuration", - body=maybe_transform( + f"/v1/financial_accounts/{financial_account_token}/credit_configuration", + body=await async_maybe_transform( { - "billing_period": billing_period, "credit_limit": credit_limit, + "credit_product_token": credit_product_token, "external_bank_account_token": external_bank_account_token, - "payment_period": payment_period, + "tier": tier, }, credit_configuration_update_params.CreditConfigurationUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=BusinessAccount, + cast_to=FinancialAccountCreditConfig, ) -class CreditConfigurationsWithRawResponse: - def __init__(self, credit_configurations: CreditConfigurations) -> None: - self._credit_configurations = credit_configurations +class CreditConfigurationWithRawResponse: + def __init__(self, credit_configuration: CreditConfiguration) -> None: + self._credit_configuration = credit_configuration self.retrieve = _legacy_response.to_raw_response_wrapper( - credit_configurations.retrieve, + credit_configuration.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( - credit_configurations.update, + credit_configuration.update, ) -class AsyncCreditConfigurationsWithRawResponse: - def __init__(self, credit_configurations: AsyncCreditConfigurations) -> None: - self._credit_configurations = credit_configurations +class AsyncCreditConfigurationWithRawResponse: + def __init__(self, credit_configuration: AsyncCreditConfiguration) -> None: + self._credit_configuration = credit_configuration self.retrieve = _legacy_response.async_to_raw_response_wrapper( - credit_configurations.retrieve, + credit_configuration.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( - credit_configurations.update, + credit_configuration.update, ) -class CreditConfigurationsWithStreamingResponse: - def __init__(self, credit_configurations: CreditConfigurations) -> None: - self._credit_configurations = credit_configurations +class CreditConfigurationWithStreamingResponse: + def __init__(self, credit_configuration: CreditConfiguration) -> None: + self._credit_configuration = credit_configuration self.retrieve = to_streamed_response_wrapper( - credit_configurations.retrieve, + credit_configuration.retrieve, ) self.update = to_streamed_response_wrapper( - credit_configurations.update, + credit_configuration.update, ) -class AsyncCreditConfigurationsWithStreamingResponse: - def __init__(self, credit_configurations: AsyncCreditConfigurations) -> None: - self._credit_configurations = credit_configurations +class AsyncCreditConfigurationWithStreamingResponse: + def __init__(self, credit_configuration: AsyncCreditConfiguration) -> None: + self._credit_configuration = credit_configuration self.retrieve = async_to_streamed_response_wrapper( - credit_configurations.retrieve, + credit_configuration.retrieve, ) self.update = async_to_streamed_response_wrapper( - credit_configurations.update, + credit_configuration.update, ) diff --git a/src/lithic/resources/financial_accounts/financial_accounts.py b/src/lithic/resources/financial_accounts/financial_accounts.py index 6d29a6f9..5876eeb4 100644 --- a/src/lithic/resources/financial_accounts/financial_accounts.py +++ b/src/lithic/resources/financial_accounts/financial_accounts.py @@ -1,20 +1,24 @@ -# File generated from our OpenAPI spec by Stainless. +# 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 Literal import httpx from ... import _legacy_response from ...types import ( - FinancialAccount, financial_account_list_params, financial_account_create_params, financial_account_update_params, + financial_account_update_status_params, ) from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) from .balances import ( Balances, AsyncBalances, @@ -24,7 +28,27 @@ AsyncBalancesWithStreamingResponse, ) from ..._compat import cached_property -from .statements import ( +from .loan_tapes import ( + LoanTapes, + AsyncLoanTapes, + LoanTapesWithRawResponse, + AsyncLoanTapesWithRawResponse, + LoanTapesWithStreamingResponse, + AsyncLoanTapesWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncSinglePage, AsyncSinglePage +from ..._base_client import AsyncPaginator, make_request_options +from .credit_configuration import ( + CreditConfiguration, + AsyncCreditConfiguration, + CreditConfigurationWithRawResponse, + AsyncCreditConfigurationWithRawResponse, + CreditConfigurationWithStreamingResponse, + AsyncCreditConfigurationWithStreamingResponse, +) +from .statements.statements import ( Statements, AsyncStatements, StatementsWithRawResponse, @@ -32,14 +56,6 @@ StatementsWithStreamingResponse, AsyncStatementsWithStreamingResponse, ) -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncSinglePage, AsyncSinglePage -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) -from .statements.statements import Statements, AsyncStatements from .financial_transactions import ( FinancialTransactions, AsyncFinancialTransactions, @@ -48,6 +64,7 @@ FinancialTransactionsWithStreamingResponse, AsyncFinancialTransactionsWithStreamingResponse, ) +from ...types.financial_account import FinancialAccount __all__ = ["FinancialAccounts", "AsyncFinancialAccounts"] @@ -61,16 +78,35 @@ def balances(self) -> Balances: def financial_transactions(self) -> FinancialTransactions: return FinancialTransactions(self._client) + @cached_property + def credit_configuration(self) -> CreditConfiguration: + return CreditConfiguration(self._client) + @cached_property def statements(self) -> Statements: return Statements(self._client) + @cached_property + def loan_tapes(self) -> LoanTapes: + return LoanTapes(self._client) + @cached_property def with_raw_response(self) -> FinancialAccountsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return FinancialAccountsWithRawResponse(self) @cached_property def with_streaming_response(self) -> FinancialAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return FinancialAccountsWithStreamingResponse(self) def create( @@ -79,6 +115,7 @@ def create( nickname: str, type: Literal["OPERATING"], account_token: str | NotGiven = NOT_GIVEN, + is_for_benefit_of: bool | 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, @@ -99,12 +136,13 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/financial_accounts", + "/v1/financial_accounts", body=maybe_transform( { "nickname": nickname, "type": type, "account_token": account_token, + "is_for_benefit_of": is_for_benefit_of, }, financial_account_create_params.FinancialAccountCreateParams, ), @@ -142,7 +180,7 @@ def retrieve( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return self._get( - f"/financial_accounts/{financial_account_token}", + f"/v1/financial_accounts/{financial_account_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -178,7 +216,7 @@ def update( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return self._patch( - f"/financial_accounts/{financial_account_token}", + f"/v1/financial_accounts/{financial_account_token}", body=maybe_transform({"nickname": nickname}, financial_account_update_params.FinancialAccountUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -219,7 +257,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/financial_accounts", + "/v1/financial_accounts", page=SyncSinglePage[FinancialAccount], options=make_request_options( extra_headers=extra_headers, @@ -238,6 +276,54 @@ def list( model=FinancialAccount, ) + def update_status( + self, + financial_account_token: str, + *, + status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"], + substatus: Optional[Literal["CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "CHARGED_OFF_DELINQUENT"]], + # 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, + ) -> FinancialAccount: + """ + Update financial account status + + Args: + status: Status of the financial account + + substatus: Substatus for the financial account + + 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 financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return self._post( + f"/v1/financial_accounts/{financial_account_token}/update_status", + body=maybe_transform( + { + "status": status, + "substatus": substatus, + }, + financial_account_update_status_params.FinancialAccountUpdateStatusParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FinancialAccount, + ) + class AsyncFinancialAccounts(AsyncAPIResource): @cached_property @@ -248,16 +334,35 @@ def balances(self) -> AsyncBalances: def financial_transactions(self) -> AsyncFinancialTransactions: return AsyncFinancialTransactions(self._client) + @cached_property + def credit_configuration(self) -> AsyncCreditConfiguration: + return AsyncCreditConfiguration(self._client) + @cached_property def statements(self) -> AsyncStatements: return AsyncStatements(self._client) + @cached_property + def loan_tapes(self) -> AsyncLoanTapes: + return AsyncLoanTapes(self._client) + @cached_property def with_raw_response(self) -> AsyncFinancialAccountsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncFinancialAccountsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncFinancialAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncFinancialAccountsWithStreamingResponse(self) async def create( @@ -266,6 +371,7 @@ async def create( nickname: str, type: Literal["OPERATING"], account_token: str | NotGiven = NOT_GIVEN, + is_for_benefit_of: bool | 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, @@ -286,12 +392,13 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/financial_accounts", - body=maybe_transform( + "/v1/financial_accounts", + body=await async_maybe_transform( { "nickname": nickname, "type": type, "account_token": account_token, + "is_for_benefit_of": is_for_benefit_of, }, financial_account_create_params.FinancialAccountCreateParams, ), @@ -329,7 +436,7 @@ async def retrieve( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return await self._get( - f"/financial_accounts/{financial_account_token}", + f"/v1/financial_accounts/{financial_account_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -365,8 +472,10 @@ async def update( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return await self._patch( - f"/financial_accounts/{financial_account_token}", - body=maybe_transform({"nickname": nickname}, financial_account_update_params.FinancialAccountUpdateParams), + f"/v1/financial_accounts/{financial_account_token}", + body=await async_maybe_transform( + {"nickname": nickname}, financial_account_update_params.FinancialAccountUpdateParams + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -406,7 +515,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/financial_accounts", + "/v1/financial_accounts", page=AsyncSinglePage[FinancialAccount], options=make_request_options( extra_headers=extra_headers, @@ -425,6 +534,54 @@ def list( model=FinancialAccount, ) + async def update_status( + self, + financial_account_token: str, + *, + status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"], + substatus: Optional[Literal["CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "CHARGED_OFF_DELINQUENT"]], + # 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, + ) -> FinancialAccount: + """ + Update financial account status + + Args: + status: Status of the financial account + + substatus: Substatus for the financial account + + 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 financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return await self._post( + f"/v1/financial_accounts/{financial_account_token}/update_status", + body=await async_maybe_transform( + { + "status": status, + "substatus": substatus, + }, + financial_account_update_status_params.FinancialAccountUpdateStatusParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FinancialAccount, + ) + class FinancialAccountsWithRawResponse: def __init__(self, financial_accounts: FinancialAccounts) -> None: @@ -442,6 +599,9 @@ def __init__(self, financial_accounts: FinancialAccounts) -> None: self.list = _legacy_response.to_raw_response_wrapper( financial_accounts.list, ) + self.update_status = _legacy_response.to_raw_response_wrapper( + financial_accounts.update_status, + ) @cached_property def balances(self) -> BalancesWithRawResponse: @@ -451,10 +611,18 @@ def balances(self) -> BalancesWithRawResponse: def financial_transactions(self) -> FinancialTransactionsWithRawResponse: return FinancialTransactionsWithRawResponse(self._financial_accounts.financial_transactions) + @cached_property + def credit_configuration(self) -> CreditConfigurationWithRawResponse: + return CreditConfigurationWithRawResponse(self._financial_accounts.credit_configuration) + @cached_property def statements(self) -> StatementsWithRawResponse: return StatementsWithRawResponse(self._financial_accounts.statements) + @cached_property + def loan_tapes(self) -> LoanTapesWithRawResponse: + return LoanTapesWithRawResponse(self._financial_accounts.loan_tapes) + class AsyncFinancialAccountsWithRawResponse: def __init__(self, financial_accounts: AsyncFinancialAccounts) -> None: @@ -472,6 +640,9 @@ def __init__(self, financial_accounts: AsyncFinancialAccounts) -> None: self.list = _legacy_response.async_to_raw_response_wrapper( financial_accounts.list, ) + self.update_status = _legacy_response.async_to_raw_response_wrapper( + financial_accounts.update_status, + ) @cached_property def balances(self) -> AsyncBalancesWithRawResponse: @@ -481,10 +652,18 @@ def balances(self) -> AsyncBalancesWithRawResponse: def financial_transactions(self) -> AsyncFinancialTransactionsWithRawResponse: return AsyncFinancialTransactionsWithRawResponse(self._financial_accounts.financial_transactions) + @cached_property + def credit_configuration(self) -> AsyncCreditConfigurationWithRawResponse: + return AsyncCreditConfigurationWithRawResponse(self._financial_accounts.credit_configuration) + @cached_property def statements(self) -> AsyncStatementsWithRawResponse: return AsyncStatementsWithRawResponse(self._financial_accounts.statements) + @cached_property + def loan_tapes(self) -> AsyncLoanTapesWithRawResponse: + return AsyncLoanTapesWithRawResponse(self._financial_accounts.loan_tapes) + class FinancialAccountsWithStreamingResponse: def __init__(self, financial_accounts: FinancialAccounts) -> None: @@ -502,6 +681,9 @@ def __init__(self, financial_accounts: FinancialAccounts) -> None: self.list = to_streamed_response_wrapper( financial_accounts.list, ) + self.update_status = to_streamed_response_wrapper( + financial_accounts.update_status, + ) @cached_property def balances(self) -> BalancesWithStreamingResponse: @@ -511,10 +693,18 @@ def balances(self) -> BalancesWithStreamingResponse: def financial_transactions(self) -> FinancialTransactionsWithStreamingResponse: return FinancialTransactionsWithStreamingResponse(self._financial_accounts.financial_transactions) + @cached_property + def credit_configuration(self) -> CreditConfigurationWithStreamingResponse: + return CreditConfigurationWithStreamingResponse(self._financial_accounts.credit_configuration) + @cached_property def statements(self) -> StatementsWithStreamingResponse: return StatementsWithStreamingResponse(self._financial_accounts.statements) + @cached_property + def loan_tapes(self) -> LoanTapesWithStreamingResponse: + return LoanTapesWithStreamingResponse(self._financial_accounts.loan_tapes) + class AsyncFinancialAccountsWithStreamingResponse: def __init__(self, financial_accounts: AsyncFinancialAccounts) -> None: @@ -532,6 +722,9 @@ def __init__(self, financial_accounts: AsyncFinancialAccounts) -> None: self.list = async_to_streamed_response_wrapper( financial_accounts.list, ) + self.update_status = async_to_streamed_response_wrapper( + financial_accounts.update_status, + ) @cached_property def balances(self) -> AsyncBalancesWithStreamingResponse: @@ -541,6 +734,14 @@ def balances(self) -> AsyncBalancesWithStreamingResponse: def financial_transactions(self) -> AsyncFinancialTransactionsWithStreamingResponse: return AsyncFinancialTransactionsWithStreamingResponse(self._financial_accounts.financial_transactions) + @cached_property + def credit_configuration(self) -> AsyncCreditConfigurationWithStreamingResponse: + return AsyncCreditConfigurationWithStreamingResponse(self._financial_accounts.credit_configuration) + @cached_property def statements(self) -> AsyncStatementsWithStreamingResponse: return AsyncStatementsWithStreamingResponse(self._financial_accounts.statements) + + @cached_property + def loan_tapes(self) -> AsyncLoanTapesWithStreamingResponse: + return AsyncLoanTapesWithStreamingResponse(self._financial_accounts.loan_tapes) diff --git a/src/lithic/resources/financial_accounts/financial_transactions.py b/src/lithic/resources/financial_accounts/financial_transactions.py index 4065f43c..167ec9fe 100644 --- a/src/lithic/resources/financial_accounts/financial_transactions.py +++ b/src/lithic/resources/financial_accounts/financial_transactions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,18 +9,15 @@ import httpx from ... import _legacy_response -from ...types import FinancialTransaction from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncSinglePage, AsyncSinglePage -from ..._base_client import ( - AsyncPaginator, - make_request_options, -) +from ..._base_client import AsyncPaginator, make_request_options from ...types.financial_accounts import financial_transaction_list_params +from ...types.financial_transaction import FinancialTransaction __all__ = ["FinancialTransactions", "AsyncFinancialTransactions"] @@ -28,10 +25,21 @@ class FinancialTransactions(SyncAPIResource): @cached_property def with_raw_response(self) -> FinancialTransactionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return FinancialTransactionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> FinancialTransactionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return FinancialTransactionsWithStreamingResponse(self) def retrieve( @@ -67,7 +75,7 @@ def retrieve( f"Expected a non-empty value for `financial_transaction_token` but received {financial_transaction_token!r}" ) return self._get( - f"/financial_accounts/{financial_account_token}/financial_transactions/{financial_transaction_token}", + f"/v1/financial_accounts/{financial_account_token}/financial_transactions/{financial_transaction_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -79,7 +87,7 @@ def list( financial_account_token: str, *, begin: Union[str, datetime] | NotGiven = NOT_GIVEN, - category: Literal["ACH", "CARD", "TRANSFER"] | NotGiven = NOT_GIVEN, + category: Literal["ACH", "CARD", "INTERNAL", "TRANSFER"] | NotGiven = NOT_GIVEN, end: Union[str, datetime] | NotGiven = NOT_GIVEN, ending_before: str | NotGiven = NOT_GIVEN, result: Literal["APPROVED", "DECLINED"] | NotGiven = NOT_GIVEN, @@ -127,7 +135,7 @@ def list( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return self._get_api_list( - f"/financial_accounts/{financial_account_token}/financial_transactions", + f"/v1/financial_accounts/{financial_account_token}/financial_transactions", page=SyncSinglePage[FinancialTransaction], options=make_request_options( extra_headers=extra_headers, @@ -154,10 +162,21 @@ def list( class AsyncFinancialTransactions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFinancialTransactionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncFinancialTransactionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncFinancialTransactionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncFinancialTransactionsWithStreamingResponse(self) async def retrieve( @@ -193,7 +212,7 @@ async def retrieve( f"Expected a non-empty value for `financial_transaction_token` but received {financial_transaction_token!r}" ) return await self._get( - f"/financial_accounts/{financial_account_token}/financial_transactions/{financial_transaction_token}", + f"/v1/financial_accounts/{financial_account_token}/financial_transactions/{financial_transaction_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -205,7 +224,7 @@ def list( financial_account_token: str, *, begin: Union[str, datetime] | NotGiven = NOT_GIVEN, - category: Literal["ACH", "CARD", "TRANSFER"] | NotGiven = NOT_GIVEN, + category: Literal["ACH", "CARD", "INTERNAL", "TRANSFER"] | NotGiven = NOT_GIVEN, end: Union[str, datetime] | NotGiven = NOT_GIVEN, ending_before: str | NotGiven = NOT_GIVEN, result: Literal["APPROVED", "DECLINED"] | NotGiven = NOT_GIVEN, @@ -253,7 +272,7 @@ def list( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return self._get_api_list( - f"/financial_accounts/{financial_account_token}/financial_transactions", + f"/v1/financial_accounts/{financial_account_token}/financial_transactions", page=AsyncSinglePage[FinancialTransaction], options=make_request_options( extra_headers=extra_headers, diff --git a/src/lithic/resources/financial_accounts/loan_tapes.py b/src/lithic/resources/financial_accounts/loan_tapes.py new file mode 100644 index 00000000..40622728 --- /dev/null +++ b/src/lithic/resources/financial_accounts/loan_tapes.py @@ -0,0 +1,335 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.financial_accounts import loan_tape_list_params +from ...types.financial_accounts.loan_tape import LoanTape + +__all__ = ["LoanTapes", "AsyncLoanTapes"] + + +class LoanTapes(SyncAPIResource): + @cached_property + def with_raw_response(self) -> LoanTapesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return LoanTapesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LoanTapesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return LoanTapesWithStreamingResponse(self) + + def retrieve( + self, + loan_tape_token: str, + *, + financial_account_token: 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, + ) -> LoanTape: + """ + Get a specific loan tape for a given financial account. + + Args: + financial_account_token: Globally unique identifier for financial account. + + loan_tape_token: Globally unique identifier for loan tape. + + 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 financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + if not loan_tape_token: + raise ValueError(f"Expected a non-empty value for `loan_tape_token` but received {loan_tape_token!r}") + return self._get( + f"/v1/financial_accounts/{financial_account_token}/loan_tapes/{loan_tape_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoanTape, + ) + + def list( + self, + financial_account_token: str, + *, + begin: Union[str, date] | NotGiven = NOT_GIVEN, + end: Union[str, date] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: 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, + ) -> SyncCursorPage[LoanTape]: + """ + List the loan tapes for a given financial account. + + Args: + financial_account_token: Globally unique identifier for financial account. + + begin: Date string in RFC 3339 format. Only entries created after the specified date + will be included. + + end: Date string in RFC 3339 format. Only entries created before the specified date + will be included. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + page_size: Page size (for pagination). + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + 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 financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return self._get_api_list( + f"/v1/financial_accounts/{financial_account_token}/loan_tapes", + page=SyncCursorPage[LoanTape], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "begin": begin, + "end": end, + "ending_before": ending_before, + "page_size": page_size, + "starting_after": starting_after, + }, + loan_tape_list_params.LoanTapeListParams, + ), + ), + model=LoanTape, + ) + + +class AsyncLoanTapes(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncLoanTapesWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncLoanTapesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLoanTapesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncLoanTapesWithStreamingResponse(self) + + async def retrieve( + self, + loan_tape_token: str, + *, + financial_account_token: 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, + ) -> LoanTape: + """ + Get a specific loan tape for a given financial account. + + Args: + financial_account_token: Globally unique identifier for financial account. + + loan_tape_token: Globally unique identifier for loan tape. + + 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 financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + if not loan_tape_token: + raise ValueError(f"Expected a non-empty value for `loan_tape_token` but received {loan_tape_token!r}") + return await self._get( + f"/v1/financial_accounts/{financial_account_token}/loan_tapes/{loan_tape_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoanTape, + ) + + def list( + self, + financial_account_token: str, + *, + begin: Union[str, date] | NotGiven = NOT_GIVEN, + end: Union[str, date] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: 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, + ) -> AsyncPaginator[LoanTape, AsyncCursorPage[LoanTape]]: + """ + List the loan tapes for a given financial account. + + Args: + financial_account_token: Globally unique identifier for financial account. + + begin: Date string in RFC 3339 format. Only entries created after the specified date + will be included. + + end: Date string in RFC 3339 format. Only entries created before the specified date + will be included. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + page_size: Page size (for pagination). + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + 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 financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return self._get_api_list( + f"/v1/financial_accounts/{financial_account_token}/loan_tapes", + page=AsyncCursorPage[LoanTape], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "begin": begin, + "end": end, + "ending_before": ending_before, + "page_size": page_size, + "starting_after": starting_after, + }, + loan_tape_list_params.LoanTapeListParams, + ), + ), + model=LoanTape, + ) + + +class LoanTapesWithRawResponse: + def __init__(self, loan_tapes: LoanTapes) -> None: + self._loan_tapes = loan_tapes + + self.retrieve = _legacy_response.to_raw_response_wrapper( + loan_tapes.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + loan_tapes.list, + ) + + +class AsyncLoanTapesWithRawResponse: + def __init__(self, loan_tapes: AsyncLoanTapes) -> None: + self._loan_tapes = loan_tapes + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + loan_tapes.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + loan_tapes.list, + ) + + +class LoanTapesWithStreamingResponse: + def __init__(self, loan_tapes: LoanTapes) -> None: + self._loan_tapes = loan_tapes + + self.retrieve = to_streamed_response_wrapper( + loan_tapes.retrieve, + ) + self.list = to_streamed_response_wrapper( + loan_tapes.list, + ) + + +class AsyncLoanTapesWithStreamingResponse: + def __init__(self, loan_tapes: AsyncLoanTapes) -> None: + self._loan_tapes = loan_tapes + + self.retrieve = async_to_streamed_response_wrapper( + loan_tapes.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + loan_tapes.list, + ) diff --git a/src/lithic/resources/financial_accounts/statements/__init__.py b/src/lithic/resources/financial_accounts/statements/__init__.py index dbc3ec38..2882f28f 100644 --- a/src/lithic/resources/financial_accounts/statements/__init__.py +++ b/src/lithic/resources/financial_accounts/statements/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .line_items import ( LineItems, diff --git a/src/lithic/resources/financial_accounts/statements/line_items.py b/src/lithic/resources/financial_accounts/statements/line_items.py index a0080864..9ed8a66d 100644 --- a/src/lithic/resources/financial_accounts/statements/line_items.py +++ b/src/lithic/resources/financial_accounts/statements/line_items.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -11,11 +11,9 @@ from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage -from ...._base_client import ( - AsyncPaginator, - make_request_options, -) -from ....types.financial_accounts.statements import LineItemListResponse, line_item_list_params +from ...._base_client import AsyncPaginator, make_request_options +from ....types.financial_accounts.statements import line_item_list_params +from ....types.financial_accounts.statements.statement_line_items import Data __all__ = ["LineItems", "AsyncLineItems"] @@ -23,10 +21,21 @@ class LineItems(SyncAPIResource): @cached_property def with_raw_response(self) -> LineItemsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return LineItemsWithRawResponse(self) @cached_property def with_streaming_response(self) -> LineItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return LineItemsWithStreamingResponse(self) def list( @@ -43,11 +52,15 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncCursorPage[LineItemListResponse]: + ) -> SyncCursorPage[Data]: """ List the line items for a given statement within a given financial account. Args: + financial_account_token: Globally unique identifier for financial account. + + statement_token: Globally unique identifier for statements. + ending_before: A cursor representing an item's token before which a page of results should end. Used to retrieve the previous page of results before this item. @@ -71,8 +84,8 @@ def list( if not statement_token: raise ValueError(f"Expected a non-empty value for `statement_token` but received {statement_token!r}") return self._get_api_list( - f"/financial_accounts/{financial_account_token}/statements/{statement_token}/line_items", - page=SyncCursorPage[LineItemListResponse], + f"/v1/financial_accounts/{financial_account_token}/statements/{statement_token}/line_items", + page=SyncCursorPage[Data], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -87,17 +100,28 @@ def list( line_item_list_params.LineItemListParams, ), ), - model=LineItemListResponse, + model=Data, ) class AsyncLineItems(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncLineItemsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncLineItemsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncLineItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncLineItemsWithStreamingResponse(self) def list( @@ -114,11 +138,15 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[LineItemListResponse, AsyncCursorPage[LineItemListResponse]]: + ) -> AsyncPaginator[Data, AsyncCursorPage[Data]]: """ List the line items for a given statement within a given financial account. Args: + financial_account_token: Globally unique identifier for financial account. + + statement_token: Globally unique identifier for statements. + ending_before: A cursor representing an item's token before which a page of results should end. Used to retrieve the previous page of results before this item. @@ -142,8 +170,8 @@ def list( if not statement_token: raise ValueError(f"Expected a non-empty value for `statement_token` but received {statement_token!r}") return self._get_api_list( - f"/financial_accounts/{financial_account_token}/statements/{statement_token}/line_items", - page=AsyncCursorPage[LineItemListResponse], + f"/v1/financial_accounts/{financial_account_token}/statements/{statement_token}/line_items", + page=AsyncCursorPage[Data], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -158,7 +186,7 @@ def list( line_item_list_params.LineItemListParams, ), ), - model=LineItemListResponse, + model=Data, ) diff --git a/src/lithic/resources/financial_accounts/statements/statements.py b/src/lithic/resources/financial_accounts/statements/statements.py index 3af46fe2..a9b27dde 100644 --- a/src/lithic/resources/financial_accounts/statements/statements.py +++ b/src/lithic/resources/financial_accounts/statements/statements.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -22,11 +22,9 @@ from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncCursorPage, AsyncCursorPage -from ...._base_client import ( - AsyncPaginator, - make_request_options, -) -from ....types.financial_accounts import Statement, statement_list_params +from ...._base_client import AsyncPaginator, make_request_options +from ....types.financial_accounts import statement_list_params +from ....types.financial_accounts.statement import Statement __all__ = ["Statements", "AsyncStatements"] @@ -38,10 +36,21 @@ def line_items(self) -> LineItems: @cached_property def with_raw_response(self) -> StatementsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return StatementsWithRawResponse(self) @cached_property def with_streaming_response(self) -> StatementsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return StatementsWithStreamingResponse(self) def retrieve( @@ -60,6 +69,10 @@ def retrieve( Get a specific statement for a given financial account. Args: + financial_account_token: Globally unique identifier for financial account. + + statement_token: Globally unique identifier for statements. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -75,7 +88,7 @@ def retrieve( if not statement_token: raise ValueError(f"Expected a non-empty value for `statement_token` but received {statement_token!r}") return self._get( - f"/financial_accounts/{financial_account_token}/statements/{statement_token}", + f"/v1/financial_accounts/{financial_account_token}/statements/{statement_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -89,6 +102,7 @@ def list( begin: Union[str, date] | NotGiven = NOT_GIVEN, end: Union[str, date] | NotGiven = NOT_GIVEN, ending_before: str | NotGiven = NOT_GIVEN, + include_initial_statements: bool | NotGiven = NOT_GIVEN, page_size: int | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -102,6 +116,8 @@ def list( List the statements for a given financial account. Args: + financial_account_token: Globally unique identifier for financial account. + begin: Date string in RFC 3339 format. Only entries created after the specified date will be included. @@ -111,6 +127,8 @@ def list( ending_before: A cursor representing an item's token before which a page of results should end. Used to retrieve the previous page of results before this item. + include_initial_statements: Whether to include the initial statement. It is not included by default. + page_size: Page size (for pagination). starting_after: A cursor representing an item's token after which a page of results should @@ -129,7 +147,7 @@ def list( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return self._get_api_list( - f"/financial_accounts/{financial_account_token}/statements", + f"/v1/financial_accounts/{financial_account_token}/statements", page=SyncCursorPage[Statement], options=make_request_options( extra_headers=extra_headers, @@ -141,6 +159,7 @@ def list( "begin": begin, "end": end, "ending_before": ending_before, + "include_initial_statements": include_initial_statements, "page_size": page_size, "starting_after": starting_after, }, @@ -158,10 +177,21 @@ def line_items(self) -> AsyncLineItems: @cached_property def with_raw_response(self) -> AsyncStatementsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncStatementsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncStatementsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncStatementsWithStreamingResponse(self) async def retrieve( @@ -180,6 +210,10 @@ async def retrieve( Get a specific statement for a given financial account. Args: + financial_account_token: Globally unique identifier for financial account. + + statement_token: Globally unique identifier for statements. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -195,7 +229,7 @@ async def retrieve( if not statement_token: raise ValueError(f"Expected a non-empty value for `statement_token` but received {statement_token!r}") return await self._get( - f"/financial_accounts/{financial_account_token}/statements/{statement_token}", + f"/v1/financial_accounts/{financial_account_token}/statements/{statement_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -209,6 +243,7 @@ def list( begin: Union[str, date] | NotGiven = NOT_GIVEN, end: Union[str, date] | NotGiven = NOT_GIVEN, ending_before: str | NotGiven = NOT_GIVEN, + include_initial_statements: bool | NotGiven = NOT_GIVEN, page_size: int | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -222,6 +257,8 @@ def list( List the statements for a given financial account. Args: + financial_account_token: Globally unique identifier for financial account. + begin: Date string in RFC 3339 format. Only entries created after the specified date will be included. @@ -231,6 +268,8 @@ def list( ending_before: A cursor representing an item's token before which a page of results should end. Used to retrieve the previous page of results before this item. + include_initial_statements: Whether to include the initial statement. It is not included by default. + page_size: Page size (for pagination). starting_after: A cursor representing an item's token after which a page of results should @@ -249,7 +288,7 @@ def list( f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" ) return self._get_api_list( - f"/financial_accounts/{financial_account_token}/statements", + f"/v1/financial_accounts/{financial_account_token}/statements", page=AsyncCursorPage[Statement], options=make_request_options( extra_headers=extra_headers, @@ -261,6 +300,7 @@ def list( "begin": begin, "end": end, "ending_before": ending_before, + "include_initial_statements": include_initial_statements, "page_size": page_size, "starting_after": starting_after, }, diff --git a/src/lithic/resources/management_operations.py b/src/lithic/resources/management_operations.py new file mode 100644 index 00000000..f40679a6 --- /dev/null +++ b/src/lithic/resources/management_operations.py @@ -0,0 +1,603 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date, datetime +from typing_extensions import Literal + +import httpx + +from .. import _legacy_response +from ..types import ( + management_operation_list_params, + management_operation_create_params, + management_operation_reverse_params, +) +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + maybe_transform, + async_maybe_transform, +) +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.management_operation_transaction import ManagementOperationTransaction + +__all__ = ["ManagementOperations", "AsyncManagementOperations"] + + +class ManagementOperations(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ManagementOperationsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return ManagementOperationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ManagementOperationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return ManagementOperationsWithStreamingResponse(self) + + def create( + self, + *, + amount: int, + category: Literal["MANAGEMENT_FEE", "MANAGEMENT_DISPUTE", "MANAGEMENT_REWARD", "MANAGEMENT_ADJUSTMENT"], + direction: Literal["CREDIT", "DEBIT"], + effective_date: Union[str, date], + event_type: Literal[ + "CASH_BACK", + "CURRENCY_CONVERSION", + "INTEREST", + "LATE_PAYMENT", + "BILLING_ERROR", + "PROVISIONAL_CREDIT", + "LOSS_WRITE_OFF", + "CASH_BACK_REVERSAL", + "CURRENCY_CONVERSION_REVERSAL", + "INTEREST_REVERSAL", + "LATE_PAYMENT_REVERSAL", + "BILLING_ERROR_REVERSAL", + "PROVISIONAL_CREDIT_REVERSAL", + "RETURNED_PAYMENT", + "RETURNED_PAYMENT_REVERSAL", + ], + financial_account_token: str, + token: str | NotGiven = NOT_GIVEN, + memo: str | NotGiven = NOT_GIVEN, + subtype: str | NotGiven = NOT_GIVEN, + user_defined_id: 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, + ) -> ManagementOperationTransaction: + """ + Create management operation + + 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 + """ + return self._post( + "/v1/management_operations", + body=maybe_transform( + { + "amount": amount, + "category": category, + "direction": direction, + "effective_date": effective_date, + "event_type": event_type, + "financial_account_token": financial_account_token, + "token": token, + "memo": memo, + "subtype": subtype, + "user_defined_id": user_defined_id, + }, + management_operation_create_params.ManagementOperationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ManagementOperationTransaction, + ) + + def retrieve( + self, + management_operation_token: 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, + ) -> ManagementOperationTransaction: + """ + Get management operation + + 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 management_operation_token: + raise ValueError( + f"Expected a non-empty value for `management_operation_token` but received {management_operation_token!r}" + ) + return self._get( + f"/v1/management_operations/{management_operation_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ManagementOperationTransaction, + ) + + def list( + self, + *, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + category: Literal["MANAGEMENT_FEE", "MANAGEMENT_DISPUTE", "MANAGEMENT_REWARD", "MANAGEMENT_ADJUSTMENT"] + | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + financial_account_token: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED"] | 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, + ) -> SyncCursorPage[ManagementOperationTransaction]: + """List management operations + + Args: + begin: Date string in RFC 3339 format. + + Only entries created after the specified time + will be included. UTC time zone. + + category: Management operation category to be returned. + + end: Date string in RFC 3339 format. Only entries created before the specified time + will be included. UTC time zone. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + financial_account_token: Globally unique identifier for the financial account. Accepted type dependent on + the program's use case. + + page_size: Page size (for pagination). + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + status: Management operation status to be returned. + + 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( + "/v1/management_operations", + page=SyncCursorPage[ManagementOperationTransaction], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "begin": begin, + "business_account_token": business_account_token, + "category": category, + "end": end, + "ending_before": ending_before, + "financial_account_token": financial_account_token, + "page_size": page_size, + "starting_after": starting_after, + "status": status, + }, + management_operation_list_params.ManagementOperationListParams, + ), + ), + model=ManagementOperationTransaction, + ) + + def reverse( + self, + management_operation_token: str, + *, + effective_date: Union[str, date], + memo: 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, + ) -> ManagementOperationTransaction: + """ + Reverse a management operation + + 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 management_operation_token: + raise ValueError( + f"Expected a non-empty value for `management_operation_token` but received {management_operation_token!r}" + ) + return self._post( + f"/v1/management_operations/{management_operation_token}/reverse", + body=maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + }, + management_operation_reverse_params.ManagementOperationReverseParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ManagementOperationTransaction, + ) + + +class AsyncManagementOperations(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncManagementOperationsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncManagementOperationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncManagementOperationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncManagementOperationsWithStreamingResponse(self) + + async def create( + self, + *, + amount: int, + category: Literal["MANAGEMENT_FEE", "MANAGEMENT_DISPUTE", "MANAGEMENT_REWARD", "MANAGEMENT_ADJUSTMENT"], + direction: Literal["CREDIT", "DEBIT"], + effective_date: Union[str, date], + event_type: Literal[ + "CASH_BACK", + "CURRENCY_CONVERSION", + "INTEREST", + "LATE_PAYMENT", + "BILLING_ERROR", + "PROVISIONAL_CREDIT", + "LOSS_WRITE_OFF", + "CASH_BACK_REVERSAL", + "CURRENCY_CONVERSION_REVERSAL", + "INTEREST_REVERSAL", + "LATE_PAYMENT_REVERSAL", + "BILLING_ERROR_REVERSAL", + "PROVISIONAL_CREDIT_REVERSAL", + "RETURNED_PAYMENT", + "RETURNED_PAYMENT_REVERSAL", + ], + financial_account_token: str, + token: str | NotGiven = NOT_GIVEN, + memo: str | NotGiven = NOT_GIVEN, + subtype: str | NotGiven = NOT_GIVEN, + user_defined_id: 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, + ) -> ManagementOperationTransaction: + """ + Create management operation + + 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 + """ + return await self._post( + "/v1/management_operations", + body=await async_maybe_transform( + { + "amount": amount, + "category": category, + "direction": direction, + "effective_date": effective_date, + "event_type": event_type, + "financial_account_token": financial_account_token, + "token": token, + "memo": memo, + "subtype": subtype, + "user_defined_id": user_defined_id, + }, + management_operation_create_params.ManagementOperationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ManagementOperationTransaction, + ) + + async def retrieve( + self, + management_operation_token: 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, + ) -> ManagementOperationTransaction: + """ + Get management operation + + 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 management_operation_token: + raise ValueError( + f"Expected a non-empty value for `management_operation_token` but received {management_operation_token!r}" + ) + return await self._get( + f"/v1/management_operations/{management_operation_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ManagementOperationTransaction, + ) + + def list( + self, + *, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + category: Literal["MANAGEMENT_FEE", "MANAGEMENT_DISPUTE", "MANAGEMENT_REWARD", "MANAGEMENT_ADJUSTMENT"] + | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + financial_account_token: str | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + starting_after: str | NotGiven = NOT_GIVEN, + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED"] | 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, + ) -> AsyncPaginator[ManagementOperationTransaction, AsyncCursorPage[ManagementOperationTransaction]]: + """List management operations + + Args: + begin: Date string in RFC 3339 format. + + Only entries created after the specified time + will be included. UTC time zone. + + category: Management operation category to be returned. + + end: Date string in RFC 3339 format. Only entries created before the specified time + will be included. UTC time zone. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + financial_account_token: Globally unique identifier for the financial account. Accepted type dependent on + the program's use case. + + page_size: Page size (for pagination). + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + status: Management operation status to be returned. + + 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( + "/v1/management_operations", + page=AsyncCursorPage[ManagementOperationTransaction], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "begin": begin, + "business_account_token": business_account_token, + "category": category, + "end": end, + "ending_before": ending_before, + "financial_account_token": financial_account_token, + "page_size": page_size, + "starting_after": starting_after, + "status": status, + }, + management_operation_list_params.ManagementOperationListParams, + ), + ), + model=ManagementOperationTransaction, + ) + + async def reverse( + self, + management_operation_token: str, + *, + effective_date: Union[str, date], + memo: 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, + ) -> ManagementOperationTransaction: + """ + Reverse a management operation + + 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 management_operation_token: + raise ValueError( + f"Expected a non-empty value for `management_operation_token` but received {management_operation_token!r}" + ) + return await self._post( + f"/v1/management_operations/{management_operation_token}/reverse", + body=await async_maybe_transform( + { + "effective_date": effective_date, + "memo": memo, + }, + management_operation_reverse_params.ManagementOperationReverseParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ManagementOperationTransaction, + ) + + +class ManagementOperationsWithRawResponse: + def __init__(self, management_operations: ManagementOperations) -> None: + self._management_operations = management_operations + + self.create = _legacy_response.to_raw_response_wrapper( + management_operations.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + management_operations.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + management_operations.list, + ) + self.reverse = _legacy_response.to_raw_response_wrapper( + management_operations.reverse, + ) + + +class AsyncManagementOperationsWithRawResponse: + def __init__(self, management_operations: AsyncManagementOperations) -> None: + self._management_operations = management_operations + + self.create = _legacy_response.async_to_raw_response_wrapper( + management_operations.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + management_operations.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + management_operations.list, + ) + self.reverse = _legacy_response.async_to_raw_response_wrapper( + management_operations.reverse, + ) + + +class ManagementOperationsWithStreamingResponse: + def __init__(self, management_operations: ManagementOperations) -> None: + self._management_operations = management_operations + + self.create = to_streamed_response_wrapper( + management_operations.create, + ) + self.retrieve = to_streamed_response_wrapper( + management_operations.retrieve, + ) + self.list = to_streamed_response_wrapper( + management_operations.list, + ) + self.reverse = to_streamed_response_wrapper( + management_operations.reverse, + ) + + +class AsyncManagementOperationsWithStreamingResponse: + def __init__(self, management_operations: AsyncManagementOperations) -> None: + self._management_operations = management_operations + + self.create = async_to_streamed_response_wrapper( + management_operations.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + management_operations.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + management_operations.list, + ) + self.reverse = async_to_streamed_response_wrapper( + management_operations.reverse, + ) diff --git a/src/lithic/resources/payments.py b/src/lithic/resources/payments.py index b0aea627..f2261cea 100644 --- a/src/lithic/resources/payments.py +++ b/src/lithic/resources/payments.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -10,26 +10,30 @@ from .. import _legacy_response from ..types import ( - Payment, - PaymentRetryResponse, - PaymentCreateResponse, - PaymentSimulateReturnResponse, - PaymentSimulateReleaseResponse, payment_list_params, payment_create_params, + payment_simulate_action_params, payment_simulate_return_params, + payment_simulate_receipt_params, payment_simulate_release_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 to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncCursorPage, AsyncCursorPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.payment import Payment +from ..types.payment_retry_response import PaymentRetryResponse +from ..types.payment_create_response import PaymentCreateResponse +from ..types.payment_simulate_action_response import PaymentSimulateActionResponse +from ..types.payment_simulate_return_response import PaymentSimulateReturnResponse +from ..types.payment_simulate_receipt_response import PaymentSimulateReceiptResponse +from ..types.payment_simulate_release_response import PaymentSimulateReleaseResponse __all__ = ["Payments", "AsyncPayments"] @@ -37,10 +41,21 @@ class Payments(SyncAPIResource): @cached_property def with_raw_response(self) -> PaymentsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return PaymentsWithRawResponse(self) @cached_property def with_streaming_response(self) -> PaymentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return PaymentsWithStreamingResponse(self) def create( @@ -78,7 +93,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/payments", + "/v1/payments", body=maybe_transform( { "amount": amount, @@ -125,7 +140,7 @@ def retrieve( if not payment_token: raise ValueError(f"Expected a non-empty value for `payment_token` but received {payment_token!r}") return self._get( - f"/payments/{payment_token}", + f"/v1/payments/{payment_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -135,7 +150,10 @@ def retrieve( def list( self, *, + account_token: str | NotGiven = NOT_GIVEN, begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + category: Literal["ACH"] | NotGiven = NOT_GIVEN, end: Union[str, datetime] | NotGiven = NOT_GIVEN, ending_before: str | NotGiven = NOT_GIVEN, financial_account_token: str | NotGiven = NOT_GIVEN, @@ -177,7 +195,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/payments", + "/v1/payments", page=SyncCursorPage[Payment], options=make_request_options( extra_headers=extra_headers, @@ -186,7 +204,10 @@ def list( timeout=timeout, query=maybe_transform( { + "account_token": account_token, "begin": begin, + "business_account_token": business_account_token, + "category": category, "end": end, "ending_before": ending_before, "financial_account_token": financial_account_token, @@ -227,13 +248,130 @@ def retry( if not payment_token: raise ValueError(f"Expected a non-empty value for `payment_token` but received {payment_token!r}") return self._post( - f"/payments/{payment_token}/retry", + f"/v1/payments/{payment_token}/retry", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=PaymentRetryResponse, ) + def simulate_action( + self, + payment_token: str, + *, + event_type: Literal[ + "ACH_ORIGINATION_REVIEWED", + "ACH_ORIGINATION_RELEASED", + "ACH_ORIGINATION_PROCESSED", + "ACH_ORIGINATION_SETTLED", + "ACH_RECEIPT_SETTLED", + "ACH_RETURN_INITIATED", + "ACH_RETURN_PROCESSED", + "ACH_RETURN_SETTLED", + ], + decline_reason: Literal[ + "PROGRAM_TRANSACTION_LIMIT_EXCEEDED", "PROGRAM_DAILY_LIMIT_EXCEEDED", "PROGRAM_MONTHLY_LIMIT_EXCEEDED" + ] + | NotGiven = NOT_GIVEN, + return_reason_code: 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, + ) -> PaymentSimulateActionResponse: + """ + Simulate payment lifecycle event + + Args: + event_type: Event Type + + decline_reason: Decline reason + + return_reason_code: Return Reason Code + + 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 payment_token: + raise ValueError(f"Expected a non-empty value for `payment_token` but received {payment_token!r}") + return self._post( + f"/v1/simulate/payments/{payment_token}/action", + body=maybe_transform( + { + "event_type": event_type, + "decline_reason": decline_reason, + "return_reason_code": return_reason_code, + }, + payment_simulate_action_params.PaymentSimulateActionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PaymentSimulateActionResponse, + ) + + def simulate_receipt( + self, + *, + token: str, + amount: int, + financial_account_token: str, + receipt_type: Literal["RECEIPT_CREDIT", "RECEIPT_DEBIT"], + memo: 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, + ) -> PaymentSimulateReceiptResponse: + """ + Simulates a receipt of a Payment. + + Args: + token: Payment token + + amount: Amount + + financial_account_token: Financial Account Token + + receipt_type: Receipt Type + + memo: Memo + + 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( + "/v1/simulate/payments/receipt", + body=maybe_transform( + { + "token": token, + "amount": amount, + "financial_account_token": financial_account_token, + "receipt_type": receipt_type, + "memo": memo, + }, + payment_simulate_receipt_params.PaymentSimulateReceiptParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PaymentSimulateReceiptResponse, + ) + def simulate_release( self, *, @@ -249,6 +387,8 @@ def simulate_release( Simulates a release of a Payment. Args: + payment_token: Payment Token + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -258,7 +398,7 @@ def simulate_release( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/payments/release", + "/v1/simulate/payments/release", body=maybe_transform( {"payment_token": payment_token}, payment_simulate_release_params.PaymentSimulateReleaseParams ), @@ -284,6 +424,10 @@ def simulate_return( Simulates a return of a Payment. Args: + payment_token: Payment Token + + return_reason_code: Return Reason Code + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -293,7 +437,7 @@ def simulate_return( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/payments/return", + "/v1/simulate/payments/return", body=maybe_transform( { "payment_token": payment_token, @@ -311,10 +455,21 @@ def simulate_return( class AsyncPayments(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncPaymentsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncPaymentsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncPaymentsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncPaymentsWithStreamingResponse(self) async def create( @@ -352,8 +507,8 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/payments", - body=maybe_transform( + "/v1/payments", + body=await async_maybe_transform( { "amount": amount, "external_bank_account_token": external_bank_account_token, @@ -399,7 +554,7 @@ async def retrieve( if not payment_token: raise ValueError(f"Expected a non-empty value for `payment_token` but received {payment_token!r}") return await self._get( - f"/payments/{payment_token}", + f"/v1/payments/{payment_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -409,7 +564,10 @@ async def retrieve( def list( self, *, + account_token: str | NotGiven = NOT_GIVEN, begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + business_account_token: str | NotGiven = NOT_GIVEN, + category: Literal["ACH"] | NotGiven = NOT_GIVEN, end: Union[str, datetime] | NotGiven = NOT_GIVEN, ending_before: str | NotGiven = NOT_GIVEN, financial_account_token: str | NotGiven = NOT_GIVEN, @@ -451,7 +609,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/payments", + "/v1/payments", page=AsyncCursorPage[Payment], options=make_request_options( extra_headers=extra_headers, @@ -460,7 +618,10 @@ def list( timeout=timeout, query=maybe_transform( { + "account_token": account_token, "begin": begin, + "business_account_token": business_account_token, + "category": category, "end": end, "ending_before": ending_before, "financial_account_token": financial_account_token, @@ -501,13 +662,130 @@ async def retry( if not payment_token: raise ValueError(f"Expected a non-empty value for `payment_token` but received {payment_token!r}") return await self._post( - f"/payments/{payment_token}/retry", + f"/v1/payments/{payment_token}/retry", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=PaymentRetryResponse, ) + async def simulate_action( + self, + payment_token: str, + *, + event_type: Literal[ + "ACH_ORIGINATION_REVIEWED", + "ACH_ORIGINATION_RELEASED", + "ACH_ORIGINATION_PROCESSED", + "ACH_ORIGINATION_SETTLED", + "ACH_RECEIPT_SETTLED", + "ACH_RETURN_INITIATED", + "ACH_RETURN_PROCESSED", + "ACH_RETURN_SETTLED", + ], + decline_reason: Literal[ + "PROGRAM_TRANSACTION_LIMIT_EXCEEDED", "PROGRAM_DAILY_LIMIT_EXCEEDED", "PROGRAM_MONTHLY_LIMIT_EXCEEDED" + ] + | NotGiven = NOT_GIVEN, + return_reason_code: 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, + ) -> PaymentSimulateActionResponse: + """ + Simulate payment lifecycle event + + Args: + event_type: Event Type + + decline_reason: Decline reason + + return_reason_code: Return Reason Code + + 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 payment_token: + raise ValueError(f"Expected a non-empty value for `payment_token` but received {payment_token!r}") + return await self._post( + f"/v1/simulate/payments/{payment_token}/action", + body=await async_maybe_transform( + { + "event_type": event_type, + "decline_reason": decline_reason, + "return_reason_code": return_reason_code, + }, + payment_simulate_action_params.PaymentSimulateActionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PaymentSimulateActionResponse, + ) + + async def simulate_receipt( + self, + *, + token: str, + amount: int, + financial_account_token: str, + receipt_type: Literal["RECEIPT_CREDIT", "RECEIPT_DEBIT"], + memo: 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, + ) -> PaymentSimulateReceiptResponse: + """ + Simulates a receipt of a Payment. + + Args: + token: Payment token + + amount: Amount + + financial_account_token: Financial Account Token + + receipt_type: Receipt Type + + memo: Memo + + 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( + "/v1/simulate/payments/receipt", + body=await async_maybe_transform( + { + "token": token, + "amount": amount, + "financial_account_token": financial_account_token, + "receipt_type": receipt_type, + "memo": memo, + }, + payment_simulate_receipt_params.PaymentSimulateReceiptParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PaymentSimulateReceiptResponse, + ) + async def simulate_release( self, *, @@ -523,6 +801,8 @@ async def simulate_release( Simulates a release of a Payment. Args: + payment_token: Payment Token + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -532,8 +812,8 @@ async def simulate_release( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/payments/release", - body=maybe_transform( + "/v1/simulate/payments/release", + body=await async_maybe_transform( {"payment_token": payment_token}, payment_simulate_release_params.PaymentSimulateReleaseParams ), options=make_request_options( @@ -558,6 +838,10 @@ async def simulate_return( Simulates a return of a Payment. Args: + payment_token: Payment Token + + return_reason_code: Return Reason Code + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -567,8 +851,8 @@ async def simulate_return( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/payments/return", - body=maybe_transform( + "/v1/simulate/payments/return", + body=await async_maybe_transform( { "payment_token": payment_token, "return_reason_code": return_reason_code, @@ -598,6 +882,12 @@ def __init__(self, payments: Payments) -> None: self.retry = _legacy_response.to_raw_response_wrapper( payments.retry, ) + self.simulate_action = _legacy_response.to_raw_response_wrapper( + payments.simulate_action, + ) + self.simulate_receipt = _legacy_response.to_raw_response_wrapper( + payments.simulate_receipt, + ) self.simulate_release = _legacy_response.to_raw_response_wrapper( payments.simulate_release, ) @@ -622,6 +912,12 @@ def __init__(self, payments: AsyncPayments) -> None: self.retry = _legacy_response.async_to_raw_response_wrapper( payments.retry, ) + self.simulate_action = _legacy_response.async_to_raw_response_wrapper( + payments.simulate_action, + ) + self.simulate_receipt = _legacy_response.async_to_raw_response_wrapper( + payments.simulate_receipt, + ) self.simulate_release = _legacy_response.async_to_raw_response_wrapper( payments.simulate_release, ) @@ -646,6 +942,12 @@ def __init__(self, payments: Payments) -> None: self.retry = to_streamed_response_wrapper( payments.retry, ) + self.simulate_action = to_streamed_response_wrapper( + payments.simulate_action, + ) + self.simulate_receipt = to_streamed_response_wrapper( + payments.simulate_receipt, + ) self.simulate_release = to_streamed_response_wrapper( payments.simulate_release, ) @@ -670,6 +972,12 @@ def __init__(self, payments: AsyncPayments) -> None: self.retry = async_to_streamed_response_wrapper( payments.retry, ) + self.simulate_action = async_to_streamed_response_wrapper( + payments.simulate_action, + ) + self.simulate_receipt = async_to_streamed_response_wrapper( + payments.simulate_receipt, + ) self.simulate_release = async_to_streamed_response_wrapper( payments.simulate_release, ) diff --git a/src/lithic/resources/reports/__init__.py b/src/lithic/resources/reports/__init__.py index 64661c80..d7bcc5cf 100644 --- a/src/lithic/resources/reports/__init__.py +++ b/src/lithic/resources/reports/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .reports import ( Reports, diff --git a/src/lithic/resources/reports/reports.py b/src/lithic/resources/reports/reports.py index 6df9b401..89700efd 100644 --- a/src/lithic/resources/reports/reports.py +++ b/src/lithic/resources/reports/reports.py @@ -1,9 +1,10 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from ..._compat import cached_property -from .settlement import ( +from ..._resource import SyncAPIResource, AsyncAPIResource +from .settlement.settlement import ( Settlement, AsyncSettlement, SettlementWithRawResponse, @@ -11,7 +12,6 @@ SettlementWithStreamingResponse, AsyncSettlementWithStreamingResponse, ) -from ..._resource import SyncAPIResource, AsyncAPIResource __all__ = ["Reports", "AsyncReports"] @@ -23,10 +23,21 @@ def settlement(self) -> Settlement: @cached_property def with_raw_response(self) -> ReportsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return ReportsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ReportsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return ReportsWithStreamingResponse(self) @@ -37,10 +48,21 @@ def settlement(self) -> AsyncSettlement: @cached_property def with_raw_response(self) -> AsyncReportsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncReportsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncReportsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncReportsWithStreamingResponse(self) diff --git a/src/lithic/resources/reports/settlement/__init__.py b/src/lithic/resources/reports/settlement/__init__.py new file mode 100644 index 00000000..c9a3f798 --- /dev/null +++ b/src/lithic/resources/reports/settlement/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .settlement import ( + Settlement, + AsyncSettlement, + SettlementWithRawResponse, + AsyncSettlementWithRawResponse, + SettlementWithStreamingResponse, + AsyncSettlementWithStreamingResponse, +) +from .network_totals import ( + NetworkTotals, + AsyncNetworkTotals, + NetworkTotalsWithRawResponse, + AsyncNetworkTotalsWithRawResponse, + NetworkTotalsWithStreamingResponse, + AsyncNetworkTotalsWithStreamingResponse, +) + +__all__ = [ + "NetworkTotals", + "AsyncNetworkTotals", + "NetworkTotalsWithRawResponse", + "AsyncNetworkTotalsWithRawResponse", + "NetworkTotalsWithStreamingResponse", + "AsyncNetworkTotalsWithStreamingResponse", + "Settlement", + "AsyncSettlement", + "SettlementWithRawResponse", + "AsyncSettlementWithRawResponse", + "SettlementWithStreamingResponse", + "AsyncSettlementWithStreamingResponse", +] diff --git a/src/lithic/resources/reports/settlement/network_totals.py b/src/lithic/resources/reports/settlement/network_totals.py new file mode 100644 index 00000000..8be090d6 --- /dev/null +++ b/src/lithic/resources/reports/settlement/network_totals.py @@ -0,0 +1,359 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date, datetime +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.reports.settlement import network_total_list_params +from ....types.reports.settlement.network_total_list_response import NetworkTotalListResponse +from ....types.reports.settlement.network_total_retrieve_response import NetworkTotalRetrieveResponse + +__all__ = ["NetworkTotals", "AsyncNetworkTotals"] + + +class NetworkTotals(SyncAPIResource): + @cached_property + def with_raw_response(self) -> NetworkTotalsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return NetworkTotalsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> NetworkTotalsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return NetworkTotalsWithStreamingResponse(self) + + def retrieve( + self, + token: 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, + ) -> NetworkTotalRetrieveResponse: + """Retrieve a specific network total record by token. + + Not available in sandbox. + + 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 token: + raise ValueError(f"Expected a non-empty value for `token` but received {token!r}") + return self._get( + f"/v1/reports/settlement/network_totals/{token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NetworkTotalRetrieveResponse, + ) + + def list( + self, + *, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + institution_id: str | NotGiven = NOT_GIVEN, + network: Literal["VISA", "MASTERCARD", "MAESTRO", "INTERLINK"] | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + report_date: Union[str, date] | NotGiven = NOT_GIVEN, + report_date_begin: Union[str, date] | NotGiven = NOT_GIVEN, + report_date_end: Union[str, date] | NotGiven = NOT_GIVEN, + settlement_institution_id: str | NotGiven = NOT_GIVEN, + starting_after: 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, + ) -> SyncCursorPage[NetworkTotalListResponse]: + """List network total records with optional filters. + + Not available in sandbox. + + Args: + begin: Datetime in RFC 3339 format. Only entries created after the specified time will + be included. UTC time zone. + + end: Datetime in RFC 3339 format. Only entries created before the specified time will + be included. UTC time zone. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + institution_id: Institution ID to filter on. + + network: Network to filter on. + + page_size: Number of records per page. + + report_date: Singular report date to filter on (YYYY-MM-DD). Cannot be populated in + conjunction with report_date_begin or report_date_end. + + report_date_begin: Earliest report date to filter on, inclusive (YYYY-MM-DD). + + report_date_end: Latest report date to filter on, inclusive (YYYY-MM-DD). + + settlement_institution_id: Settlement institution ID to filter on. + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + 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( + "/v1/reports/settlement/network_totals", + page=SyncCursorPage[NetworkTotalListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "begin": begin, + "end": end, + "ending_before": ending_before, + "institution_id": institution_id, + "network": network, + "page_size": page_size, + "report_date": report_date, + "report_date_begin": report_date_begin, + "report_date_end": report_date_end, + "settlement_institution_id": settlement_institution_id, + "starting_after": starting_after, + }, + network_total_list_params.NetworkTotalListParams, + ), + ), + model=NetworkTotalListResponse, + ) + + +class AsyncNetworkTotals(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncNetworkTotalsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncNetworkTotalsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncNetworkTotalsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncNetworkTotalsWithStreamingResponse(self) + + async def retrieve( + self, + token: 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, + ) -> NetworkTotalRetrieveResponse: + """Retrieve a specific network total record by token. + + Not available in sandbox. + + 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 token: + raise ValueError(f"Expected a non-empty value for `token` but received {token!r}") + return await self._get( + f"/v1/reports/settlement/network_totals/{token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NetworkTotalRetrieveResponse, + ) + + def list( + self, + *, + begin: Union[str, datetime] | NotGiven = NOT_GIVEN, + end: Union[str, datetime] | NotGiven = NOT_GIVEN, + ending_before: str | NotGiven = NOT_GIVEN, + institution_id: str | NotGiven = NOT_GIVEN, + network: Literal["VISA", "MASTERCARD", "MAESTRO", "INTERLINK"] | NotGiven = NOT_GIVEN, + page_size: int | NotGiven = NOT_GIVEN, + report_date: Union[str, date] | NotGiven = NOT_GIVEN, + report_date_begin: Union[str, date] | NotGiven = NOT_GIVEN, + report_date_end: Union[str, date] | NotGiven = NOT_GIVEN, + settlement_institution_id: str | NotGiven = NOT_GIVEN, + starting_after: 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, + ) -> AsyncPaginator[NetworkTotalListResponse, AsyncCursorPage[NetworkTotalListResponse]]: + """List network total records with optional filters. + + Not available in sandbox. + + Args: + begin: Datetime in RFC 3339 format. Only entries created after the specified time will + be included. UTC time zone. + + end: Datetime in RFC 3339 format. Only entries created before the specified time will + be included. UTC time zone. + + ending_before: A cursor representing an item's token before which a page of results should end. + Used to retrieve the previous page of results before this item. + + institution_id: Institution ID to filter on. + + network: Network to filter on. + + page_size: Number of records per page. + + report_date: Singular report date to filter on (YYYY-MM-DD). Cannot be populated in + conjunction with report_date_begin or report_date_end. + + report_date_begin: Earliest report date to filter on, inclusive (YYYY-MM-DD). + + report_date_end: Latest report date to filter on, inclusive (YYYY-MM-DD). + + settlement_institution_id: Settlement institution ID to filter on. + + starting_after: A cursor representing an item's token after which a page of results should + begin. Used to retrieve the next page of results after this item. + + 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( + "/v1/reports/settlement/network_totals", + page=AsyncCursorPage[NetworkTotalListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "begin": begin, + "end": end, + "ending_before": ending_before, + "institution_id": institution_id, + "network": network, + "page_size": page_size, + "report_date": report_date, + "report_date_begin": report_date_begin, + "report_date_end": report_date_end, + "settlement_institution_id": settlement_institution_id, + "starting_after": starting_after, + }, + network_total_list_params.NetworkTotalListParams, + ), + ), + model=NetworkTotalListResponse, + ) + + +class NetworkTotalsWithRawResponse: + def __init__(self, network_totals: NetworkTotals) -> None: + self._network_totals = network_totals + + self.retrieve = _legacy_response.to_raw_response_wrapper( + network_totals.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + network_totals.list, + ) + + +class AsyncNetworkTotalsWithRawResponse: + def __init__(self, network_totals: AsyncNetworkTotals) -> None: + self._network_totals = network_totals + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + network_totals.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + network_totals.list, + ) + + +class NetworkTotalsWithStreamingResponse: + def __init__(self, network_totals: NetworkTotals) -> None: + self._network_totals = network_totals + + self.retrieve = to_streamed_response_wrapper( + network_totals.retrieve, + ) + self.list = to_streamed_response_wrapper( + network_totals.list, + ) + + +class AsyncNetworkTotalsWithStreamingResponse: + def __init__(self, network_totals: AsyncNetworkTotals) -> None: + self._network_totals = network_totals + + self.retrieve = async_to_streamed_response_wrapper( + network_totals.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + network_totals.list, + ) diff --git a/src/lithic/resources/reports/settlement.py b/src/lithic/resources/reports/settlement/settlement.py similarity index 73% rename from src/lithic/resources/reports/settlement.py rename to src/lithic/resources/reports/settlement/settlement.py index 3010d53e..fee2eff8 100644 --- a/src/lithic/resources/reports/settlement.py +++ b/src/lithic/resources/reports/settlement/settlement.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -7,30 +7,51 @@ import httpx -from ... import _legacy_response -from ...types import SettlementDetail, SettlementReport -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import ( - AsyncPaginator, - make_request_options, +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from .network_totals import ( + NetworkTotals, + AsyncNetworkTotals, + NetworkTotalsWithRawResponse, + AsyncNetworkTotalsWithRawResponse, + NetworkTotalsWithStreamingResponse, + AsyncNetworkTotalsWithStreamingResponse, ) -from ...types.reports import settlement_list_details_params +from ...._base_client import AsyncPaginator, make_request_options +from ....types.reports import settlement_list_details_params +from ....types.settlement_detail import SettlementDetail +from ....types.settlement_report import SettlementReport __all__ = ["Settlement", "AsyncSettlement"] class Settlement(SyncAPIResource): + @cached_property + def network_totals(self) -> NetworkTotals: + return NetworkTotals(self._client) + @cached_property def with_raw_response(self) -> SettlementWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return SettlementWithRawResponse(self) @cached_property def with_streaming_response(self) -> SettlementWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return SettlementWithStreamingResponse(self) def list_details( @@ -70,7 +91,7 @@ def list_details( if not report_date: raise ValueError(f"Expected a non-empty value for `report_date` but received {report_date!r}") return self._get_api_list( - f"/reports/settlement/details/{report_date}", + f"/v1/reports/settlement/details/{report_date}", page=SyncCursorPage[SettlementDetail], options=make_request_options( extra_headers=extra_headers, @@ -100,8 +121,9 @@ def summary( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SettlementReport: - """ - Get the settlement report for a specified report date. + """Get the settlement report for a specified report date. + + Not available in sandbox. Args: extra_headers: Send extra headers @@ -115,7 +137,7 @@ def summary( if not report_date: raise ValueError(f"Expected a non-empty value for `report_date` but received {report_date!r}") return self._get( - f"/reports/settlement/summary/{report_date}", + f"/v1/reports/settlement/summary/{report_date}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -124,12 +146,27 @@ def summary( class AsyncSettlement(AsyncAPIResource): + @cached_property + def network_totals(self) -> AsyncNetworkTotals: + return AsyncNetworkTotals(self._client) + @cached_property def with_raw_response(self) -> AsyncSettlementWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncSettlementWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncSettlementWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncSettlementWithStreamingResponse(self) def list_details( @@ -169,7 +206,7 @@ def list_details( if not report_date: raise ValueError(f"Expected a non-empty value for `report_date` but received {report_date!r}") return self._get_api_list( - f"/reports/settlement/details/{report_date}", + f"/v1/reports/settlement/details/{report_date}", page=AsyncCursorPage[SettlementDetail], options=make_request_options( extra_headers=extra_headers, @@ -199,8 +236,9 @@ async def summary( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SettlementReport: - """ - Get the settlement report for a specified report date. + """Get the settlement report for a specified report date. + + Not available in sandbox. Args: extra_headers: Send extra headers @@ -214,7 +252,7 @@ async def summary( if not report_date: raise ValueError(f"Expected a non-empty value for `report_date` but received {report_date!r}") return await self._get( - f"/reports/settlement/summary/{report_date}", + f"/v1/reports/settlement/summary/{report_date}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -233,6 +271,10 @@ def __init__(self, settlement: Settlement) -> None: settlement.summary, ) + @cached_property + def network_totals(self) -> NetworkTotalsWithRawResponse: + return NetworkTotalsWithRawResponse(self._settlement.network_totals) + class AsyncSettlementWithRawResponse: def __init__(self, settlement: AsyncSettlement) -> None: @@ -245,6 +287,10 @@ def __init__(self, settlement: AsyncSettlement) -> None: settlement.summary, ) + @cached_property + def network_totals(self) -> AsyncNetworkTotalsWithRawResponse: + return AsyncNetworkTotalsWithRawResponse(self._settlement.network_totals) + class SettlementWithStreamingResponse: def __init__(self, settlement: Settlement) -> None: @@ -257,6 +303,10 @@ def __init__(self, settlement: Settlement) -> None: settlement.summary, ) + @cached_property + def network_totals(self) -> NetworkTotalsWithStreamingResponse: + return NetworkTotalsWithStreamingResponse(self._settlement.network_totals) + class AsyncSettlementWithStreamingResponse: def __init__(self, settlement: AsyncSettlement) -> None: @@ -268,3 +318,7 @@ def __init__(self, settlement: AsyncSettlement) -> None: self.summary = async_to_streamed_response_wrapper( settlement.summary, ) + + @cached_property + def network_totals(self) -> AsyncNetworkTotalsWithStreamingResponse: + return AsyncNetworkTotalsWithStreamingResponse(self._settlement.network_totals) diff --git a/src/lithic/resources/responder_endpoints.py b/src/lithic/resources/responder_endpoints.py index 4e9d4d8b..2307b112 100644 --- a/src/lithic/resources/responder_endpoints.py +++ b/src/lithic/resources/responder_endpoints.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -8,20 +8,21 @@ from .. import _legacy_response from ..types import ( - ResponderEndpointStatus, - ResponderEndpointCreateResponse, responder_endpoint_create_params, responder_endpoint_delete_params, responder_endpoint_check_status_params, ) from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, 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 to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options +from ..types.responder_endpoint_status import ResponderEndpointStatus +from ..types.responder_endpoint_create_response import ResponderEndpointCreateResponse __all__ = ["ResponderEndpoints", "AsyncResponderEndpoints"] @@ -29,10 +30,21 @@ class ResponderEndpoints(SyncAPIResource): @cached_property def with_raw_response(self) -> ResponderEndpointsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return ResponderEndpointsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ResponderEndpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return ResponderEndpointsWithStreamingResponse(self) def create( @@ -64,7 +76,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/responder_endpoints", + "/v1/responder_endpoints", body=maybe_transform( { "type": type, @@ -104,7 +116,7 @@ def delete( timeout: Override the client-level default timeout for this request, in seconds """ return self._delete( - "/responder_endpoints", + "/v1/responder_endpoints", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -141,7 +153,7 @@ def check_status( timeout: Override the client-level default timeout for this request, in seconds """ return self._get( - "/responder_endpoints", + "/v1/responder_endpoints", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -158,10 +170,21 @@ def check_status( class AsyncResponderEndpoints(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncResponderEndpointsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncResponderEndpointsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncResponderEndpointsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncResponderEndpointsWithStreamingResponse(self) async def create( @@ -193,8 +216,8 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/responder_endpoints", - body=maybe_transform( + "/v1/responder_endpoints", + body=await async_maybe_transform( { "type": type, "url": url, @@ -233,13 +256,15 @@ async def delete( timeout: Override the client-level default timeout for this request, in seconds """ return await self._delete( - "/responder_endpoints", + "/v1/responder_endpoints", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"type": type}, responder_endpoint_delete_params.ResponderEndpointDeleteParams), + query=await async_maybe_transform( + {"type": type}, responder_endpoint_delete_params.ResponderEndpointDeleteParams + ), ), cast_to=NoneType, ) @@ -270,13 +295,13 @@ async def check_status( timeout: Override the client-level default timeout for this request, in seconds """ return await self._get( - "/responder_endpoints", + "/v1/responder_endpoints", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform( + query=await async_maybe_transform( {"type": type}, responder_endpoint_check_status_params.ResponderEndpointCheckStatusParams ), ), diff --git a/src/lithic/resources/three_ds/__init__.py b/src/lithic/resources/three_ds/__init__.py index 32d6cf86..517059d5 100644 --- a/src/lithic/resources/three_ds/__init__.py +++ b/src/lithic/resources/three_ds/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .three_ds import ( ThreeDS, diff --git a/src/lithic/resources/three_ds/authentication.py b/src/lithic/resources/three_ds/authentication.py index a84141ce..eb4150cf 100644 --- a/src/lithic/resources/three_ds/authentication.py +++ b/src/lithic/resources/three_ds/authentication.py @@ -1,23 +1,24 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations +from typing_extensions import Literal + import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..._base_client import ( - make_request_options, -) -from ...types.three_ds import ( - AuthenticationRetrieveResponse, - AuthenticationSimulateResponse, - authentication_simulate_params, -) +from ..._base_client import make_request_options +from ...types.three_ds import authentication_simulate_params, authentication_simulate_otp_entry_params +from ...types.three_ds.authentication_retrieve_response import AuthenticationRetrieveResponse +from ...types.three_ds.authentication_simulate_response import AuthenticationSimulateResponse __all__ = ["Authentication", "AsyncAuthentication"] @@ -25,10 +26,21 @@ class Authentication(SyncAPIResource): @cached_property def with_raw_response(self) -> AuthenticationWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AuthenticationWithRawResponse(self) @cached_property def with_streaming_response(self) -> AuthenticationWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AuthenticationWithStreamingResponse(self) def retrieve( @@ -59,7 +71,7 @@ def retrieve( f"Expected a non-empty value for `three_ds_authentication_token` but received {three_ds_authentication_token!r}" ) return self._get( - f"/three_ds_authentication/{three_ds_authentication_token}", + f"/v1/three_ds_authentication/{three_ds_authentication_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -72,6 +84,7 @@ def simulate( merchant: authentication_simulate_params.Merchant, pan: str, transaction: authentication_simulate_params.Transaction, + card_expiry_check: Literal["MATCH", "MISMATCH", "NOT_PRESENT"] | 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, @@ -88,6 +101,9 @@ def simulate( Args: pan: Sixteen digit card number. + card_expiry_check: When set will use the following values as part of the Simulated Authentication. + When not set defaults to MATCH + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -97,12 +113,13 @@ def simulate( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/three_ds_authentication/simulate", + "/v1/three_ds_authentication/simulate", body=maybe_transform( { "merchant": merchant, "pan": pan, "transaction": transaction, + "card_expiry_check": card_expiry_check, }, authentication_simulate_params.AuthenticationSimulateParams, ), @@ -112,14 +129,73 @@ def simulate( cast_to=AuthenticationSimulateResponse, ) + def simulate_otp_entry( + self, + *, + token: str, + otp: 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: + """Endpoint for simulating entering OTP into 3DS Challenge UI. + + A call to + /v1/three_ds_authentication/simulate that resulted in triggered SMS-OTP + challenge must precede. Only a single attempt is supported; upon entering OTP, + the challenge is either approved or declined. + + Args: + token: A unique token returned as part of a /v1/three_ds_authentication/simulate call + that resulted in PENDING_CHALLENGE authentication result. + + otp: The OTP entered by the cardholder + + 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( + "/v1/three_ds_decisioning/simulate/enter_otp", + body=maybe_transform( + { + "token": token, + "otp": otp, + }, + authentication_simulate_otp_entry_params.AuthenticationSimulateOtpEntryParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + class AsyncAuthentication(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncAuthenticationWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncAuthenticationWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAuthenticationWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncAuthenticationWithStreamingResponse(self) async def retrieve( @@ -150,7 +226,7 @@ async def retrieve( f"Expected a non-empty value for `three_ds_authentication_token` but received {three_ds_authentication_token!r}" ) return await self._get( - f"/three_ds_authentication/{three_ds_authentication_token}", + f"/v1/three_ds_authentication/{three_ds_authentication_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -163,6 +239,7 @@ async def simulate( merchant: authentication_simulate_params.Merchant, pan: str, transaction: authentication_simulate_params.Transaction, + card_expiry_check: Literal["MATCH", "MISMATCH", "NOT_PRESENT"] | 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, @@ -179,6 +256,9 @@ async def simulate( Args: pan: Sixteen digit card number. + card_expiry_check: When set will use the following values as part of the Simulated Authentication. + When not set defaults to MATCH + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -188,12 +268,13 @@ async def simulate( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/three_ds_authentication/simulate", - body=maybe_transform( + "/v1/three_ds_authentication/simulate", + body=await async_maybe_transform( { "merchant": merchant, "pan": pan, "transaction": transaction, + "card_expiry_check": card_expiry_check, }, authentication_simulate_params.AuthenticationSimulateParams, ), @@ -203,6 +284,54 @@ async def simulate( cast_to=AuthenticationSimulateResponse, ) + async def simulate_otp_entry( + self, + *, + token: str, + otp: 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: + """Endpoint for simulating entering OTP into 3DS Challenge UI. + + A call to + /v1/three_ds_authentication/simulate that resulted in triggered SMS-OTP + challenge must precede. Only a single attempt is supported; upon entering OTP, + the challenge is either approved or declined. + + Args: + token: A unique token returned as part of a /v1/three_ds_authentication/simulate call + that resulted in PENDING_CHALLENGE authentication result. + + otp: The OTP entered by the cardholder + + 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( + "/v1/three_ds_decisioning/simulate/enter_otp", + body=await async_maybe_transform( + { + "token": token, + "otp": otp, + }, + authentication_simulate_otp_entry_params.AuthenticationSimulateOtpEntryParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + class AuthenticationWithRawResponse: def __init__(self, authentication: Authentication) -> None: @@ -214,6 +343,9 @@ def __init__(self, authentication: Authentication) -> None: self.simulate = _legacy_response.to_raw_response_wrapper( authentication.simulate, ) + self.simulate_otp_entry = _legacy_response.to_raw_response_wrapper( + authentication.simulate_otp_entry, + ) class AsyncAuthenticationWithRawResponse: @@ -226,6 +358,9 @@ def __init__(self, authentication: AsyncAuthentication) -> None: self.simulate = _legacy_response.async_to_raw_response_wrapper( authentication.simulate, ) + self.simulate_otp_entry = _legacy_response.async_to_raw_response_wrapper( + authentication.simulate_otp_entry, + ) class AuthenticationWithStreamingResponse: @@ -238,6 +373,9 @@ def __init__(self, authentication: Authentication) -> None: self.simulate = to_streamed_response_wrapper( authentication.simulate, ) + self.simulate_otp_entry = to_streamed_response_wrapper( + authentication.simulate_otp_entry, + ) class AsyncAuthenticationWithStreamingResponse: @@ -250,3 +388,6 @@ def __init__(self, authentication: AsyncAuthentication) -> None: self.simulate = async_to_streamed_response_wrapper( authentication.simulate, ) + self.simulate_otp_entry = async_to_streamed_response_wrapper( + authentication.simulate_otp_entry, + ) diff --git a/src/lithic/resources/three_ds/decisioning.py b/src/lithic/resources/three_ds/decisioning.py index 34349c91..766bc934 100644 --- a/src/lithic/resources/three_ds/decisioning.py +++ b/src/lithic/resources/three_ds/decisioning.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -6,13 +6,17 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..._base_client import ( - make_request_options, -) -from ...types.three_ds import DecisioningRetrieveSecretResponse +from ..._base_client import make_request_options +from ...types.three_ds import ChallengeResult, decisioning_challenge_response_params +from ...types.three_ds.challenge_result import ChallengeResult +from ...types.three_ds.decisioning_retrieve_secret_response import DecisioningRetrieveSecretResponse __all__ = ["Decisioning", "AsyncDecisioning"] @@ -20,12 +24,69 @@ class Decisioning(SyncAPIResource): @cached_property def with_raw_response(self) -> DecisioningWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return DecisioningWithRawResponse(self) @cached_property def with_streaming_response(self) -> DecisioningWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return DecisioningWithStreamingResponse(self) + def challenge_response( + self, + *, + token: str, + challenge_response: ChallengeResult, + # 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: + """ + Card program's response to a 3DS Challenge Request (CReq) + + Args: + token: Globally unique identifier for the 3DS authentication. This token is sent as + part of the initial 3DS Decisioning Request and as part of the 3DS Challenge + Event in the [ThreeDSAuthentication](#/components/schemas/ThreeDSAuthentication) + object + + challenge_response: Whether the Cardholder has Approved or Declined the issued Challenge + + 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( + "/v1/three_ds_decisioning/challenge_response", + body=maybe_transform( + { + "token": token, + "challenge_response": challenge_response, + }, + decisioning_challenge_response_params.DecisioningChallengeResponseParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + def retrieve_secret( self, *, @@ -46,7 +107,7 @@ def retrieve_secret( for more detail about verifying 3DS Decisioning requests. """ return self._get( - "/three_ds_decisioning/secret", + "/v1/three_ds_decisioning/secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -71,7 +132,7 @@ def rotate_secret( request to retrieve the new secret key. """ return self._post( - "/three_ds_decisioning/secret/rotate", + "/v1/three_ds_decisioning/secret/rotate", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -82,12 +143,69 @@ def rotate_secret( class AsyncDecisioning(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncDecisioningWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncDecisioningWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncDecisioningWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncDecisioningWithStreamingResponse(self) + async def challenge_response( + self, + *, + token: str, + challenge_response: ChallengeResult, + # 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: + """ + Card program's response to a 3DS Challenge Request (CReq) + + Args: + token: Globally unique identifier for the 3DS authentication. This token is sent as + part of the initial 3DS Decisioning Request and as part of the 3DS Challenge + Event in the [ThreeDSAuthentication](#/components/schemas/ThreeDSAuthentication) + object + + challenge_response: Whether the Cardholder has Approved or Declined the issued Challenge + + 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( + "/v1/three_ds_decisioning/challenge_response", + body=await async_maybe_transform( + { + "token": token, + "challenge_response": challenge_response, + }, + decisioning_challenge_response_params.DecisioningChallengeResponseParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + async def retrieve_secret( self, *, @@ -108,7 +226,7 @@ async def retrieve_secret( for more detail about verifying 3DS Decisioning requests. """ return await self._get( - "/three_ds_decisioning/secret", + "/v1/three_ds_decisioning/secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -133,7 +251,7 @@ async def rotate_secret( request to retrieve the new secret key. """ return await self._post( - "/three_ds_decisioning/secret/rotate", + "/v1/three_ds_decisioning/secret/rotate", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -145,6 +263,9 @@ class DecisioningWithRawResponse: def __init__(self, decisioning: Decisioning) -> None: self._decisioning = decisioning + self.challenge_response = _legacy_response.to_raw_response_wrapper( + decisioning.challenge_response, + ) self.retrieve_secret = _legacy_response.to_raw_response_wrapper( decisioning.retrieve_secret, ) @@ -157,6 +278,9 @@ class AsyncDecisioningWithRawResponse: def __init__(self, decisioning: AsyncDecisioning) -> None: self._decisioning = decisioning + self.challenge_response = _legacy_response.async_to_raw_response_wrapper( + decisioning.challenge_response, + ) self.retrieve_secret = _legacy_response.async_to_raw_response_wrapper( decisioning.retrieve_secret, ) @@ -169,6 +293,9 @@ class DecisioningWithStreamingResponse: def __init__(self, decisioning: Decisioning) -> None: self._decisioning = decisioning + self.challenge_response = to_streamed_response_wrapper( + decisioning.challenge_response, + ) self.retrieve_secret = to_streamed_response_wrapper( decisioning.retrieve_secret, ) @@ -181,6 +308,9 @@ class AsyncDecisioningWithStreamingResponse: def __init__(self, decisioning: AsyncDecisioning) -> None: self._decisioning = decisioning + self.challenge_response = async_to_streamed_response_wrapper( + decisioning.challenge_response, + ) self.retrieve_secret = async_to_streamed_response_wrapper( decisioning.retrieve_secret, ) diff --git a/src/lithic/resources/three_ds/three_ds.py b/src/lithic/resources/three_ds/three_ds.py index a4597fb3..ea68a29c 100644 --- a/src/lithic/resources/three_ds/three_ds.py +++ b/src/lithic/resources/three_ds/three_ds.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -35,10 +35,21 @@ def decisioning(self) -> Decisioning: @cached_property def with_raw_response(self) -> ThreeDSWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return ThreeDSWithRawResponse(self) @cached_property def with_streaming_response(self) -> ThreeDSWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return ThreeDSWithStreamingResponse(self) @@ -53,10 +64,21 @@ def decisioning(self) -> AsyncDecisioning: @cached_property def with_raw_response(self) -> AsyncThreeDSWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncThreeDSWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncThreeDSWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncThreeDSWithStreamingResponse(self) diff --git a/src/lithic/resources/tokenization_decisioning.py b/src/lithic/resources/tokenization_decisioning.py index e05c3b57..9e376c10 100644 --- a/src/lithic/resources/tokenization_decisioning.py +++ b/src/lithic/resources/tokenization_decisioning.py @@ -1,18 +1,17 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import httpx from .. import _legacy_response -from ..types import TokenizationSecret, TokenizationDecisioningRotateSecretResponse from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .._base_client import ( - make_request_options, -) +from .._base_client import make_request_options +from ..types.tokenization_secret import TokenizationSecret +from ..types.tokenization_decisioning_rotate_secret_response import TokenizationDecisioningRotateSecretResponse __all__ = ["TokenizationDecisioning", "AsyncTokenizationDecisioning"] @@ -20,10 +19,21 @@ class TokenizationDecisioning(SyncAPIResource): @cached_property def with_raw_response(self) -> TokenizationDecisioningWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return TokenizationDecisioningWithRawResponse(self) @cached_property def with_streaming_response(self) -> TokenizationDecisioningWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return TokenizationDecisioningWithStreamingResponse(self) def retrieve_secret( @@ -46,7 +56,7 @@ def retrieve_secret( detail about verifying Tokenization Decisioning requests. """ return self._get( - "/tokenization_decisioning/secret", + "/v1/tokenization_decisioning/secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -70,7 +80,7 @@ def rotate_secret( to this endpoint. """ return self._post( - "/tokenization_decisioning/secret/rotate", + "/v1/tokenization_decisioning/secret/rotate", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -81,10 +91,21 @@ def rotate_secret( class AsyncTokenizationDecisioning(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncTokenizationDecisioningWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncTokenizationDecisioningWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncTokenizationDecisioningWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncTokenizationDecisioningWithStreamingResponse(self) async def retrieve_secret( @@ -107,7 +128,7 @@ async def retrieve_secret( detail about verifying Tokenization Decisioning requests. """ return await self._get( - "/tokenization_decisioning/secret", + "/v1/tokenization_decisioning/secret", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -131,7 +152,7 @@ async def rotate_secret( to this endpoint. """ return await self._post( - "/tokenization_decisioning/secret/rotate", + "/v1/tokenization_decisioning/secret/rotate", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/lithic/resources/tokenizations.py b/src/lithic/resources/tokenizations.py index 2aec5963..7207d4be 100644 --- a/src/lithic/resources/tokenizations.py +++ b/src/lithic/resources/tokenizations.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -10,22 +10,25 @@ from .. import _legacy_response from ..types import ( - Tokenization, - TokenizationRetrieveResponse, - TokenizationSimulateResponse, tokenization_list_params, tokenization_simulate_params, + tokenization_resend_activation_code_params, + tokenization_update_digital_card_art_params, +) +from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from .._utils import ( + maybe_transform, + async_maybe_transform, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncCursorPage, AsyncCursorPage -from .._base_client import ( - AsyncPaginator, - make_request_options, -) +from .._base_client import AsyncPaginator, make_request_options +from ..types.tokenization import Tokenization +from ..types.tokenization_retrieve_response import TokenizationRetrieveResponse +from ..types.tokenization_simulate_response import TokenizationSimulateResponse +from ..types.tokenization_update_digital_card_art_response import TokenizationUpdateDigitalCardArtResponse __all__ = ["Tokenizations", "AsyncTokenizations"] @@ -33,10 +36,21 @@ class Tokenizations(SyncAPIResource): @cached_property def with_raw_response(self) -> TokenizationsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return TokenizationsWithRawResponse(self) @cached_property def with_streaming_response(self) -> TokenizationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return TokenizationsWithStreamingResponse(self) def retrieve( @@ -65,7 +79,7 @@ def retrieve( if not tokenization_token: raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") return self._get( - f"/tokenizations/{tokenization_token}", + f"/v1/tokenizations/{tokenization_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -82,6 +96,7 @@ def list( ending_before: str | NotGiven = NOT_GIVEN, page_size: int | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, + tokenization_channel: Literal["DIGITAL_WALLET", "MERCHANT", "ALL"] | 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, @@ -109,6 +124,9 @@ def list( starting_after: A cursor representing an item's token after which a page of results should begin. Used to retrieve the next page of results after this item. + tokenization_channel: Filter for tokenizations by tokenization channel. If this is not specified, only + DIGITAL_WALLET tokenizations will be returned. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -118,7 +136,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/tokenizations", + "/v1/tokenizations", page=SyncCursorPage[Tokenization], options=make_request_options( extra_headers=extra_headers, @@ -134,6 +152,7 @@ def list( "ending_before": ending_before, "page_size": page_size, "starting_after": starting_after, + "tokenization_channel": tokenization_channel, }, tokenization_list_params.TokenizationListParams, ), @@ -141,15 +160,192 @@ def list( model=Tokenization, ) + def activate( + self, + tokenization_token: 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: + """This endpoint is used to ask the card network to activate a tokenization. + + A + successful response indicates that the request was successfully delivered to the + card network. When the card network activates the tokenization, the state will + be updated and a tokenization.updated event will be sent. The endpoint may only + be used on digital wallet tokenizations with status `INACTIVE`, + `PENDING_ACTIVATION`, or `PENDING_2FA`. This will put the tokenization in an + active state, and transactions will be allowed. Reach out at + [lithic.com/contact](https://lithic.com/contact) for more information. + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return self._post( + f"/v1/tokenizations/{tokenization_token}/activate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def deactivate( + self, + tokenization_token: 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: + """This endpoint is used to ask the card network to deactivate a tokenization. + + A + successful response indicates that the request was successfully delivered to the + card network. When the card network deactivates the tokenization, the state will + be updated and a tokenization.updated event will be sent. Authorizations + attempted with a deactivated tokenization will be blocked and will not be + forwarded to Lithic from the network. Deactivating the token is a permanent + operation. If the target is a digital wallet tokenization, it will be removed + from its device. Reach out at [lithic.com/contact](https://lithic.com/contact) + for more information. + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return self._post( + f"/v1/tokenizations/{tokenization_token}/deactivate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def pause( + self, + tokenization_token: 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: + """This endpoint is used to ask the card network to pause a tokenization. + + A + successful response indicates that the request was successfully delivered to the + card network. When the card network pauses the tokenization, the state will be + updated and a tokenization.updated event will be sent. The endpoint may only be + used on tokenizations with status `ACTIVE`. A paused token will prevent + merchants from sending authorizations, and is a temporary status that can be + changed. Reach out at [lithic.com/contact](https://lithic.com/contact) for more + information. + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return self._post( + f"/v1/tokenizations/{tokenization_token}/pause", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def resend_activation_code( + self, + tokenization_token: str, + *, + activation_method_type: Literal["EMAIL_TO_CARDHOLDER_ADDRESS", "TEXT_TO_CARDHOLDER_NUMBER"] + | 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, + ) -> None: + """ + This endpoint is used to ask the card network to send another activation code to + a cardholder that has already tried tokenizing a card. A successful response + indicates that the request was successfully delivered to the card network. The + endpoint may only be used on Mastercard digital wallet tokenizations with status + `INACTIVE`, `PENDING_ACTIVATION`, or `PENDING_2FA`. The network will send a new + activation code to the one of the contact methods provided in the initial + tokenization flow. If a user fails to enter the code correctly 3 times, the + contact method will not be eligible for resending the activation code, and the + cardholder must restart the provision process. Reach out at + [lithic.com/contact](https://lithic.com/contact) for more information. + + Args: + activation_method_type: The communication method that the user has selected to use to receive the + authentication code. Supported Values: Sms = "TEXT_TO_CARDHOLDER_NUMBER". Email + = "EMAIL_TO_CARDHOLDER_ADDRESS" + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return self._post( + f"/v1/tokenizations/{tokenization_token}/resend_activation_code", + body=maybe_transform( + {"activation_method_type": activation_method_type}, + tokenization_resend_activation_code_params.TokenizationResendActivationCodeParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + def simulate( self, *, cvv: str, expiration_date: str, pan: str, - tokenization_source: Literal["APPLE_PAY", "GOOGLE", "SAMSUNG_PAY"], + tokenization_source: Literal["APPLE_PAY", "GOOGLE", "SAMSUNG_PAY", "MERCHANT"], account_score: int | NotGiven = NOT_GIVEN, device_score: int | NotGiven = NOT_GIVEN, + entity: str | NotGiven = NOT_GIVEN, wallet_recommended_decision: Literal["APPROVED", "DECLINED", "REQUIRE_ADDITIONAL_AUTHENTICATION"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -178,6 +374,9 @@ def simulate( device_score: The device score (1-5) that represents how the Digital Wallet's view on how reputable an end user's device is. + entity: Optional field to specify the token requestor name for a merchant token + simulation. Ignored when tokenization_source is not MERCHANT. + wallet_recommended_decision: The decision that the Digital Wallet's recommend extra_headers: Send extra headers @@ -189,7 +388,7 @@ def simulate( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/tokenizations", + "/v1/simulate/tokenizations", body=maybe_transform( { "cvv": cvv, @@ -198,6 +397,7 @@ def simulate( "tokenization_source": tokenization_source, "account_score": account_score, "device_score": device_score, + "entity": entity, "wallet_recommended_decision": wallet_recommended_decision, }, tokenization_simulate_params.TokenizationSimulateParams, @@ -208,14 +408,115 @@ def simulate( cast_to=TokenizationSimulateResponse, ) + def unpause( + self, + tokenization_token: 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: + """This endpoint is used to ask the card network to unpause a tokenization. + + A + successful response indicates that the request was successfully delivered to the + card network. When the card network unpauses the tokenization, the state will be + updated and a tokenization.updated event will be sent. The endpoint may only be + used on tokenizations with status `PAUSED`. This will put the tokenization in an + active state, and transactions may resume. Reach out at + [lithic.com/contact](https://lithic.com/contact) for more information. + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return self._post( + f"/v1/tokenizations/{tokenization_token}/unpause", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def update_digital_card_art( + self, + tokenization_token: str, + *, + digital_card_art_token: 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, + ) -> TokenizationUpdateDigitalCardArtResponse: + """ + This endpoint is used update the digital card art for a digital wallet + tokenization. A successful response indicates that the card network has updated + the tokenization's art, and the tokenization's `digital_cart_art_token` field + was updated. The endpoint may not be used on tokenizations with status + `DEACTIVATED`. Note that this updates the art for one specific tokenization, not + all tokenizations for a card. New tokenizations for a card will be created with + the art referenced in the card object's `digital_card_art_token` field. Reach + out at [lithic.com/contact](https://lithic.com/contact) for more information. + + Args: + digital_card_art_token: Specifies the digital card art to be displayed in the user’s digital wallet for + a tokenization. This artwork must be approved by the network and configured by + Lithic to use. See + [Flexible Card Art Guide](https://docs.lithic.com/docs/about-digital-wallets#flexible-card-art). + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return self._post( + f"/v1/tokenizations/{tokenization_token}/update_digital_card_art", + body=maybe_transform( + {"digital_card_art_token": digital_card_art_token}, + tokenization_update_digital_card_art_params.TokenizationUpdateDigitalCardArtParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TokenizationUpdateDigitalCardArtResponse, + ) + class AsyncTokenizations(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncTokenizationsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncTokenizationsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncTokenizationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncTokenizationsWithStreamingResponse(self) async def retrieve( @@ -244,7 +545,7 @@ async def retrieve( if not tokenization_token: raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") return await self._get( - f"/tokenizations/{tokenization_token}", + f"/v1/tokenizations/{tokenization_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -261,6 +562,7 @@ def list( ending_before: str | NotGiven = NOT_GIVEN, page_size: int | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, + tokenization_channel: Literal["DIGITAL_WALLET", "MERCHANT", "ALL"] | 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, @@ -288,6 +590,9 @@ def list( starting_after: A cursor representing an item's token after which a page of results should begin. Used to retrieve the next page of results after this item. + tokenization_channel: Filter for tokenizations by tokenization channel. If this is not specified, only + DIGITAL_WALLET tokenizations will be returned. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -297,7 +602,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/tokenizations", + "/v1/tokenizations", page=AsyncCursorPage[Tokenization], options=make_request_options( extra_headers=extra_headers, @@ -313,6 +618,7 @@ def list( "ending_before": ending_before, "page_size": page_size, "starting_after": starting_after, + "tokenization_channel": tokenization_channel, }, tokenization_list_params.TokenizationListParams, ), @@ -320,15 +626,192 @@ def list( model=Tokenization, ) + async def activate( + self, + tokenization_token: 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: + """This endpoint is used to ask the card network to activate a tokenization. + + A + successful response indicates that the request was successfully delivered to the + card network. When the card network activates the tokenization, the state will + be updated and a tokenization.updated event will be sent. The endpoint may only + be used on digital wallet tokenizations with status `INACTIVE`, + `PENDING_ACTIVATION`, or `PENDING_2FA`. This will put the tokenization in an + active state, and transactions will be allowed. Reach out at + [lithic.com/contact](https://lithic.com/contact) for more information. + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return await self._post( + f"/v1/tokenizations/{tokenization_token}/activate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def deactivate( + self, + tokenization_token: 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: + """This endpoint is used to ask the card network to deactivate a tokenization. + + A + successful response indicates that the request was successfully delivered to the + card network. When the card network deactivates the tokenization, the state will + be updated and a tokenization.updated event will be sent. Authorizations + attempted with a deactivated tokenization will be blocked and will not be + forwarded to Lithic from the network. Deactivating the token is a permanent + operation. If the target is a digital wallet tokenization, it will be removed + from its device. Reach out at [lithic.com/contact](https://lithic.com/contact) + for more information. + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return await self._post( + f"/v1/tokenizations/{tokenization_token}/deactivate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def pause( + self, + tokenization_token: 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: + """This endpoint is used to ask the card network to pause a tokenization. + + A + successful response indicates that the request was successfully delivered to the + card network. When the card network pauses the tokenization, the state will be + updated and a tokenization.updated event will be sent. The endpoint may only be + used on tokenizations with status `ACTIVE`. A paused token will prevent + merchants from sending authorizations, and is a temporary status that can be + changed. Reach out at [lithic.com/contact](https://lithic.com/contact) for more + information. + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return await self._post( + f"/v1/tokenizations/{tokenization_token}/pause", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def resend_activation_code( + self, + tokenization_token: str, + *, + activation_method_type: Literal["EMAIL_TO_CARDHOLDER_ADDRESS", "TEXT_TO_CARDHOLDER_NUMBER"] + | 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, + ) -> None: + """ + This endpoint is used to ask the card network to send another activation code to + a cardholder that has already tried tokenizing a card. A successful response + indicates that the request was successfully delivered to the card network. The + endpoint may only be used on Mastercard digital wallet tokenizations with status + `INACTIVE`, `PENDING_ACTIVATION`, or `PENDING_2FA`. The network will send a new + activation code to the one of the contact methods provided in the initial + tokenization flow. If a user fails to enter the code correctly 3 times, the + contact method will not be eligible for resending the activation code, and the + cardholder must restart the provision process. Reach out at + [lithic.com/contact](https://lithic.com/contact) for more information. + + Args: + activation_method_type: The communication method that the user has selected to use to receive the + authentication code. Supported Values: Sms = "TEXT_TO_CARDHOLDER_NUMBER". Email + = "EMAIL_TO_CARDHOLDER_ADDRESS" + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return await self._post( + f"/v1/tokenizations/{tokenization_token}/resend_activation_code", + body=await async_maybe_transform( + {"activation_method_type": activation_method_type}, + tokenization_resend_activation_code_params.TokenizationResendActivationCodeParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + async def simulate( self, *, cvv: str, expiration_date: str, pan: str, - tokenization_source: Literal["APPLE_PAY", "GOOGLE", "SAMSUNG_PAY"], + tokenization_source: Literal["APPLE_PAY", "GOOGLE", "SAMSUNG_PAY", "MERCHANT"], account_score: int | NotGiven = NOT_GIVEN, device_score: int | NotGiven = NOT_GIVEN, + entity: str | NotGiven = NOT_GIVEN, wallet_recommended_decision: Literal["APPROVED", "DECLINED", "REQUIRE_ADDITIONAL_AUTHENTICATION"] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -357,6 +840,9 @@ async def simulate( device_score: The device score (1-5) that represents how the Digital Wallet's view on how reputable an end user's device is. + entity: Optional field to specify the token requestor name for a merchant token + simulation. Ignored when tokenization_source is not MERCHANT. + wallet_recommended_decision: The decision that the Digital Wallet's recommend extra_headers: Send extra headers @@ -368,8 +854,8 @@ async def simulate( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/tokenizations", - body=maybe_transform( + "/v1/simulate/tokenizations", + body=await async_maybe_transform( { "cvv": cvv, "expiration_date": expiration_date, @@ -377,6 +863,7 @@ async def simulate( "tokenization_source": tokenization_source, "account_score": account_score, "device_score": device_score, + "entity": entity, "wallet_recommended_decision": wallet_recommended_decision, }, tokenization_simulate_params.TokenizationSimulateParams, @@ -387,6 +874,96 @@ async def simulate( cast_to=TokenizationSimulateResponse, ) + async def unpause( + self, + tokenization_token: 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: + """This endpoint is used to ask the card network to unpause a tokenization. + + A + successful response indicates that the request was successfully delivered to the + card network. When the card network unpauses the tokenization, the state will be + updated and a tokenization.updated event will be sent. The endpoint may only be + used on tokenizations with status `PAUSED`. This will put the tokenization in an + active state, and transactions may resume. Reach out at + [lithic.com/contact](https://lithic.com/contact) for more information. + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return await self._post( + f"/v1/tokenizations/{tokenization_token}/unpause", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def update_digital_card_art( + self, + tokenization_token: str, + *, + digital_card_art_token: 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, + ) -> TokenizationUpdateDigitalCardArtResponse: + """ + This endpoint is used update the digital card art for a digital wallet + tokenization. A successful response indicates that the card network has updated + the tokenization's art, and the tokenization's `digital_cart_art_token` field + was updated. The endpoint may not be used on tokenizations with status + `DEACTIVATED`. Note that this updates the art for one specific tokenization, not + all tokenizations for a card. New tokenizations for a card will be created with + the art referenced in the card object's `digital_card_art_token` field. Reach + out at [lithic.com/contact](https://lithic.com/contact) for more information. + + Args: + digital_card_art_token: Specifies the digital card art to be displayed in the user’s digital wallet for + a tokenization. This artwork must be approved by the network and configured by + Lithic to use. See + [Flexible Card Art Guide](https://docs.lithic.com/docs/about-digital-wallets#flexible-card-art). + + 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 tokenization_token: + raise ValueError(f"Expected a non-empty value for `tokenization_token` but received {tokenization_token!r}") + return await self._post( + f"/v1/tokenizations/{tokenization_token}/update_digital_card_art", + body=await async_maybe_transform( + {"digital_card_art_token": digital_card_art_token}, + tokenization_update_digital_card_art_params.TokenizationUpdateDigitalCardArtParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TokenizationUpdateDigitalCardArtResponse, + ) + class TokenizationsWithRawResponse: def __init__(self, tokenizations: Tokenizations) -> None: @@ -398,9 +975,27 @@ def __init__(self, tokenizations: Tokenizations) -> None: self.list = _legacy_response.to_raw_response_wrapper( tokenizations.list, ) + self.activate = _legacy_response.to_raw_response_wrapper( + tokenizations.activate, + ) + self.deactivate = _legacy_response.to_raw_response_wrapper( + tokenizations.deactivate, + ) + self.pause = _legacy_response.to_raw_response_wrapper( + tokenizations.pause, + ) + self.resend_activation_code = _legacy_response.to_raw_response_wrapper( + tokenizations.resend_activation_code, + ) self.simulate = _legacy_response.to_raw_response_wrapper( tokenizations.simulate, ) + self.unpause = _legacy_response.to_raw_response_wrapper( + tokenizations.unpause, + ) + self.update_digital_card_art = _legacy_response.to_raw_response_wrapper( + tokenizations.update_digital_card_art, + ) class AsyncTokenizationsWithRawResponse: @@ -413,9 +1008,27 @@ def __init__(self, tokenizations: AsyncTokenizations) -> None: self.list = _legacy_response.async_to_raw_response_wrapper( tokenizations.list, ) + self.activate = _legacy_response.async_to_raw_response_wrapper( + tokenizations.activate, + ) + self.deactivate = _legacy_response.async_to_raw_response_wrapper( + tokenizations.deactivate, + ) + self.pause = _legacy_response.async_to_raw_response_wrapper( + tokenizations.pause, + ) + self.resend_activation_code = _legacy_response.async_to_raw_response_wrapper( + tokenizations.resend_activation_code, + ) self.simulate = _legacy_response.async_to_raw_response_wrapper( tokenizations.simulate, ) + self.unpause = _legacy_response.async_to_raw_response_wrapper( + tokenizations.unpause, + ) + self.update_digital_card_art = _legacy_response.async_to_raw_response_wrapper( + tokenizations.update_digital_card_art, + ) class TokenizationsWithStreamingResponse: @@ -428,9 +1041,27 @@ def __init__(self, tokenizations: Tokenizations) -> None: self.list = to_streamed_response_wrapper( tokenizations.list, ) + self.activate = to_streamed_response_wrapper( + tokenizations.activate, + ) + self.deactivate = to_streamed_response_wrapper( + tokenizations.deactivate, + ) + self.pause = to_streamed_response_wrapper( + tokenizations.pause, + ) + self.resend_activation_code = to_streamed_response_wrapper( + tokenizations.resend_activation_code, + ) self.simulate = to_streamed_response_wrapper( tokenizations.simulate, ) + self.unpause = to_streamed_response_wrapper( + tokenizations.unpause, + ) + self.update_digital_card_art = to_streamed_response_wrapper( + tokenizations.update_digital_card_art, + ) class AsyncTokenizationsWithStreamingResponse: @@ -443,6 +1074,24 @@ def __init__(self, tokenizations: AsyncTokenizations) -> None: self.list = async_to_streamed_response_wrapper( tokenizations.list, ) + self.activate = async_to_streamed_response_wrapper( + tokenizations.activate, + ) + self.deactivate = async_to_streamed_response_wrapper( + tokenizations.deactivate, + ) + self.pause = async_to_streamed_response_wrapper( + tokenizations.pause, + ) + self.resend_activation_code = async_to_streamed_response_wrapper( + tokenizations.resend_activation_code, + ) self.simulate = async_to_streamed_response_wrapper( tokenizations.simulate, ) + self.unpause = async_to_streamed_response_wrapper( + tokenizations.unpause, + ) + self.update_digital_card_art = async_to_streamed_response_wrapper( + tokenizations.update_digital_card_art, + ) diff --git a/src/lithic/resources/transactions/__init__.py b/src/lithic/resources/transactions/__init__.py new file mode 100644 index 00000000..eb3253a2 --- /dev/null +++ b/src/lithic/resources/transactions/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .events import ( + Events, + AsyncEvents, + EventsWithRawResponse, + AsyncEventsWithRawResponse, + EventsWithStreamingResponse, + AsyncEventsWithStreamingResponse, +) +from .transactions import ( + Transactions, + AsyncTransactions, + TransactionsWithRawResponse, + AsyncTransactionsWithRawResponse, + TransactionsWithStreamingResponse, + AsyncTransactionsWithStreamingResponse, +) +from .enhanced_commercial_data import ( + EnhancedCommercialData, + AsyncEnhancedCommercialData, + EnhancedCommercialDataWithRawResponse, + AsyncEnhancedCommercialDataWithRawResponse, + EnhancedCommercialDataWithStreamingResponse, + AsyncEnhancedCommercialDataWithStreamingResponse, +) + +__all__ = [ + "EnhancedCommercialData", + "AsyncEnhancedCommercialData", + "EnhancedCommercialDataWithRawResponse", + "AsyncEnhancedCommercialDataWithRawResponse", + "EnhancedCommercialDataWithStreamingResponse", + "AsyncEnhancedCommercialDataWithStreamingResponse", + "Events", + "AsyncEvents", + "EventsWithRawResponse", + "AsyncEventsWithRawResponse", + "EventsWithStreamingResponse", + "AsyncEventsWithStreamingResponse", + "Transactions", + "AsyncTransactions", + "TransactionsWithRawResponse", + "AsyncTransactionsWithRawResponse", + "TransactionsWithStreamingResponse", + "AsyncTransactionsWithStreamingResponse", +] diff --git a/src/lithic/resources/transactions/enhanced_commercial_data.py b/src/lithic/resources/transactions/enhanced_commercial_data.py new file mode 100644 index 00000000..e4f2d464 --- /dev/null +++ b/src/lithic/resources/transactions/enhanced_commercial_data.py @@ -0,0 +1,163 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.transactions.enhanced_commercial_data_retrieve_response import EnhancedCommercialDataRetrieveResponse + +__all__ = ["EnhancedCommercialData", "AsyncEnhancedCommercialData"] + + +class EnhancedCommercialData(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EnhancedCommercialDataWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return EnhancedCommercialDataWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EnhancedCommercialDataWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return EnhancedCommercialDataWithStreamingResponse(self) + + def retrieve( + self, + transaction_token: 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, + ) -> EnhancedCommercialDataRetrieveResponse: + """Get all L2/L3 enhanced commercial data associated with a transaction. + + Not + available in sandbox. + + 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 transaction_token: + raise ValueError(f"Expected a non-empty value for `transaction_token` but received {transaction_token!r}") + return self._get( + f"/v1/transactions/{transaction_token}/enhanced_commercial_data", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EnhancedCommercialDataRetrieveResponse, + ) + + +class AsyncEnhancedCommercialData(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEnhancedCommercialDataWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncEnhancedCommercialDataWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEnhancedCommercialDataWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncEnhancedCommercialDataWithStreamingResponse(self) + + async def retrieve( + self, + transaction_token: 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, + ) -> EnhancedCommercialDataRetrieveResponse: + """Get all L2/L3 enhanced commercial data associated with a transaction. + + Not + available in sandbox. + + 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 transaction_token: + raise ValueError(f"Expected a non-empty value for `transaction_token` but received {transaction_token!r}") + return await self._get( + f"/v1/transactions/{transaction_token}/enhanced_commercial_data", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EnhancedCommercialDataRetrieveResponse, + ) + + +class EnhancedCommercialDataWithRawResponse: + def __init__(self, enhanced_commercial_data: EnhancedCommercialData) -> None: + self._enhanced_commercial_data = enhanced_commercial_data + + self.retrieve = _legacy_response.to_raw_response_wrapper( + enhanced_commercial_data.retrieve, + ) + + +class AsyncEnhancedCommercialDataWithRawResponse: + def __init__(self, enhanced_commercial_data: AsyncEnhancedCommercialData) -> None: + self._enhanced_commercial_data = enhanced_commercial_data + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + enhanced_commercial_data.retrieve, + ) + + +class EnhancedCommercialDataWithStreamingResponse: + def __init__(self, enhanced_commercial_data: EnhancedCommercialData) -> None: + self._enhanced_commercial_data = enhanced_commercial_data + + self.retrieve = to_streamed_response_wrapper( + enhanced_commercial_data.retrieve, + ) + + +class AsyncEnhancedCommercialDataWithStreamingResponse: + def __init__(self, enhanced_commercial_data: AsyncEnhancedCommercialData) -> None: + self._enhanced_commercial_data = enhanced_commercial_data + + self.retrieve = async_to_streamed_response_wrapper( + enhanced_commercial_data.retrieve, + ) diff --git a/src/lithic/resources/transactions/events/__init__.py b/src/lithic/resources/transactions/events/__init__.py new file mode 100644 index 00000000..6affbb9a --- /dev/null +++ b/src/lithic/resources/transactions/events/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .events import ( + Events, + AsyncEvents, + EventsWithRawResponse, + AsyncEventsWithRawResponse, + EventsWithStreamingResponse, + AsyncEventsWithStreamingResponse, +) +from .enhanced_commercial_data import ( + EnhancedCommercialData, + AsyncEnhancedCommercialData, + EnhancedCommercialDataWithRawResponse, + AsyncEnhancedCommercialDataWithRawResponse, + EnhancedCommercialDataWithStreamingResponse, + AsyncEnhancedCommercialDataWithStreamingResponse, +) + +__all__ = [ + "EnhancedCommercialData", + "AsyncEnhancedCommercialData", + "EnhancedCommercialDataWithRawResponse", + "AsyncEnhancedCommercialDataWithRawResponse", + "EnhancedCommercialDataWithStreamingResponse", + "AsyncEnhancedCommercialDataWithStreamingResponse", + "Events", + "AsyncEvents", + "EventsWithRawResponse", + "AsyncEventsWithRawResponse", + "EventsWithStreamingResponse", + "AsyncEventsWithStreamingResponse", +] diff --git a/src/lithic/resources/transactions/events/enhanced_commercial_data.py b/src/lithic/resources/transactions/events/enhanced_commercial_data.py new file mode 100644 index 00000000..6b604757 --- /dev/null +++ b/src/lithic/resources/transactions/events/enhanced_commercial_data.py @@ -0,0 +1,163 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .... import _legacy_response +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.transactions.events.enhanced_data import EnhancedData + +__all__ = ["EnhancedCommercialData", "AsyncEnhancedCommercialData"] + + +class EnhancedCommercialData(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EnhancedCommercialDataWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return EnhancedCommercialDataWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EnhancedCommercialDataWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return EnhancedCommercialDataWithStreamingResponse(self) + + def retrieve( + self, + event_token: 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, + ) -> EnhancedData: + """Get L2/L3 enhanced commercial data associated with a transaction event. + + Not + available in sandbox. + + 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 event_token: + raise ValueError(f"Expected a non-empty value for `event_token` but received {event_token!r}") + return self._get( + f"/v1/transactions/events/{event_token}/enhanced_commercial_data", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EnhancedData, + ) + + +class AsyncEnhancedCommercialData(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEnhancedCommercialDataWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncEnhancedCommercialDataWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEnhancedCommercialDataWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncEnhancedCommercialDataWithStreamingResponse(self) + + async def retrieve( + self, + event_token: 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, + ) -> EnhancedData: + """Get L2/L3 enhanced commercial data associated with a transaction event. + + Not + available in sandbox. + + 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 event_token: + raise ValueError(f"Expected a non-empty value for `event_token` but received {event_token!r}") + return await self._get( + f"/v1/transactions/events/{event_token}/enhanced_commercial_data", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EnhancedData, + ) + + +class EnhancedCommercialDataWithRawResponse: + def __init__(self, enhanced_commercial_data: EnhancedCommercialData) -> None: + self._enhanced_commercial_data = enhanced_commercial_data + + self.retrieve = _legacy_response.to_raw_response_wrapper( + enhanced_commercial_data.retrieve, + ) + + +class AsyncEnhancedCommercialDataWithRawResponse: + def __init__(self, enhanced_commercial_data: AsyncEnhancedCommercialData) -> None: + self._enhanced_commercial_data = enhanced_commercial_data + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + enhanced_commercial_data.retrieve, + ) + + +class EnhancedCommercialDataWithStreamingResponse: + def __init__(self, enhanced_commercial_data: EnhancedCommercialData) -> None: + self._enhanced_commercial_data = enhanced_commercial_data + + self.retrieve = to_streamed_response_wrapper( + enhanced_commercial_data.retrieve, + ) + + +class AsyncEnhancedCommercialDataWithStreamingResponse: + def __init__(self, enhanced_commercial_data: AsyncEnhancedCommercialData) -> None: + self._enhanced_commercial_data = enhanced_commercial_data + + self.retrieve = async_to_streamed_response_wrapper( + enhanced_commercial_data.retrieve, + ) diff --git a/src/lithic/resources/transactions/events/events.py b/src/lithic/resources/transactions/events/events.py new file mode 100644 index 00000000..42f61b32 --- /dev/null +++ b/src/lithic/resources/transactions/events/events.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from .enhanced_commercial_data import ( + EnhancedCommercialData, + AsyncEnhancedCommercialData, + EnhancedCommercialDataWithRawResponse, + AsyncEnhancedCommercialDataWithRawResponse, + EnhancedCommercialDataWithStreamingResponse, + AsyncEnhancedCommercialDataWithStreamingResponse, +) + +__all__ = ["Events", "AsyncEvents"] + + +class Events(SyncAPIResource): + @cached_property + def enhanced_commercial_data(self) -> EnhancedCommercialData: + return EnhancedCommercialData(self._client) + + @cached_property + def with_raw_response(self) -> EventsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return EventsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EventsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return EventsWithStreamingResponse(self) + + +class AsyncEvents(AsyncAPIResource): + @cached_property + def enhanced_commercial_data(self) -> AsyncEnhancedCommercialData: + return AsyncEnhancedCommercialData(self._client) + + @cached_property + def with_raw_response(self) -> AsyncEventsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncEventsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEventsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncEventsWithStreamingResponse(self) + + +class EventsWithRawResponse: + def __init__(self, events: Events) -> None: + self._events = events + + @cached_property + def enhanced_commercial_data(self) -> EnhancedCommercialDataWithRawResponse: + return EnhancedCommercialDataWithRawResponse(self._events.enhanced_commercial_data) + + +class AsyncEventsWithRawResponse: + def __init__(self, events: AsyncEvents) -> None: + self._events = events + + @cached_property + def enhanced_commercial_data(self) -> AsyncEnhancedCommercialDataWithRawResponse: + return AsyncEnhancedCommercialDataWithRawResponse(self._events.enhanced_commercial_data) + + +class EventsWithStreamingResponse: + def __init__(self, events: Events) -> None: + self._events = events + + @cached_property + def enhanced_commercial_data(self) -> EnhancedCommercialDataWithStreamingResponse: + return EnhancedCommercialDataWithStreamingResponse(self._events.enhanced_commercial_data) + + +class AsyncEventsWithStreamingResponse: + def __init__(self, events: AsyncEvents) -> None: + self._events = events + + @cached_property + def enhanced_commercial_data(self) -> AsyncEnhancedCommercialDataWithStreamingResponse: + return AsyncEnhancedCommercialDataWithStreamingResponse(self._events.enhanced_commercial_data) diff --git a/src/lithic/resources/transactions.py b/src/lithic/resources/transactions/transactions.py similarity index 72% rename from src/lithic/resources/transactions.py rename to src/lithic/resources/transactions/transactions.py index 2cb36675..ea6bf8fc 100644 --- a/src/lithic/resources/transactions.py +++ b/src/lithic/resources/transactions/transactions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -8,16 +8,8 @@ import httpx -from .. import _legacy_response -from ..types import ( - Transaction, - TransactionSimulateVoidResponse, - TransactionSimulateReturnResponse, - TransactionSimulateClearingResponse, - TransactionSimulateAuthorizationResponse, - TransactionSimulateReturnReversalResponse, - TransactionSimulateAuthorizationAdviceResponse, - TransactionSimulateCreditAuthorizationResponse, +from ... import _legacy_response +from ...types import ( transaction_list_params, transaction_simulate_void_params, transaction_simulate_return_params, @@ -27,27 +19,70 @@ transaction_simulate_authorization_advice_params, transaction_simulate_credit_authorization_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..pagination import SyncCursorPage, AsyncCursorPage -from .._base_client import ( - AsyncPaginator, - make_request_options, +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, ) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from .events.events import ( + Events, + AsyncEvents, + EventsWithRawResponse, + AsyncEventsWithRawResponse, + EventsWithStreamingResponse, + AsyncEventsWithStreamingResponse, +) +from ..._base_client import AsyncPaginator, make_request_options +from ...types.transaction import Transaction +from .enhanced_commercial_data import ( + EnhancedCommercialData, + AsyncEnhancedCommercialData, + EnhancedCommercialDataWithRawResponse, + AsyncEnhancedCommercialDataWithRawResponse, + EnhancedCommercialDataWithStreamingResponse, + AsyncEnhancedCommercialDataWithStreamingResponse, +) +from ...types.transaction_simulate_void_response import TransactionSimulateVoidResponse +from ...types.transaction_simulate_return_response import TransactionSimulateReturnResponse +from ...types.transaction_simulate_clearing_response import TransactionSimulateClearingResponse +from ...types.transaction_simulate_authorization_response import TransactionSimulateAuthorizationResponse +from ...types.transaction_simulate_return_reversal_response import TransactionSimulateReturnReversalResponse +from ...types.transaction_simulate_authorization_advice_response import TransactionSimulateAuthorizationAdviceResponse +from ...types.transaction_simulate_credit_authorization_response import TransactionSimulateCreditAuthorizationResponse __all__ = ["Transactions", "AsyncTransactions"] class Transactions(SyncAPIResource): + @cached_property + def enhanced_commercial_data(self) -> EnhancedCommercialData: + return EnhancedCommercialData(self._client) + + @cached_property + def events(self) -> Events: + return Events(self._client) + @cached_property def with_raw_response(self) -> TransactionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return TransactionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> TransactionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return TransactionsWithStreamingResponse(self) def retrieve( @@ -61,8 +96,10 @@ def retrieve( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Transaction: - """ - Get specific card transaction. + """Get a specific card transaction. + + All amounts are in the smallest unit of their + respective currency (e.g., cents for USD). Args: extra_headers: Send extra headers @@ -76,7 +113,7 @@ def retrieve( if not transaction_token: raise ValueError(f"Expected a non-empty value for `transaction_token` but received {transaction_token!r}") return self._get( - f"/transactions/{transaction_token}", + f"/v1/transactions/{transaction_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -94,6 +131,7 @@ def list( page_size: int | NotGiven = NOT_GIVEN, result: Literal["APPROVED", "DECLINED"] | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, + status: Literal["PENDING", "VOIDED", "SETTLED", "DECLINED", "EXPIRED"] | 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, @@ -101,8 +139,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SyncCursorPage[Transaction]: - """ - List card transactions. + """List card transactions. + + All amounts are in the smallest unit of their respective + currency (e.g., cents for USD) and inclusive of any acquirer fees. Args: account_token: Filters for transactions associated with a specific account. @@ -126,6 +166,8 @@ def list( starting_after: A cursor representing an item's token after which a page of results should begin. Used to retrieve the next page of results after this item. + status: Filters for transactions using transaction status field. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -135,7 +177,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/transactions", + "/v1/transactions", page=SyncCursorPage[Transaction], options=make_request_options( extra_headers=extra_headers, @@ -152,6 +194,7 @@ def list( "page_size": page_size, "result": result, "starting_after": starting_after, + "status": status, }, transaction_list_params.TransactionListParams, ), @@ -159,6 +202,39 @@ def list( model=Transaction, ) + def expire_authorization( + self, + transaction_token: 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: + """ + Expire authorization + + 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 transaction_token: + raise ValueError(f"Expected a non-empty value for `transaction_token` but received {transaction_token!r}") + return self._post( + f"/v1/transactions/{transaction_token}/expire_authorization", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + def simulate_authorization( self, *, @@ -170,6 +246,7 @@ def simulate_authorization( merchant_amount: int | NotGiven = NOT_GIVEN, merchant_currency: str | NotGiven = NOT_GIVEN, partial_approval_capable: bool | NotGiven = NOT_GIVEN, + pin: str | NotGiven = NOT_GIVEN, status: Literal[ "AUTHORIZATION", "BALANCE_INQUIRY", @@ -186,19 +263,20 @@ def simulate_authorization( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateAuthorizationResponse: """ - Simulates an authorization request from the payment network as if it came from a - merchant acquirer. If you're configured for ASA, simulating auths requires your - ASA client to be set up properly (respond with a valid JSON to the ASA request). - For users that are not configured for ASA, a daily transaction limit of $5000 - USD is applied by default. This limit can be modified via the + Simulates an authorization request from the card network as if it came from a + merchant acquirer. If you are configured for ASA, simulating authorizations + requires your ASA client to be set up properly, i.e. be able to respond to the + ASA request with a valid JSON. For users that are not configured for ASA, a + daily transaction limit of $5000 USD is applied by default. You can update this + limit via the [update account](https://docs.lithic.com/reference/patchaccountbytoken) endpoint. Args: amount: Amount (in cents) to authorize. For credit authorizations and financial credit authorizations, any value entered will be converted into a negative amount in - the simulated transaction. For example, entering 100 in this field will appear - as a -100 amount in the transaction. For balance inquiries, this field must be + the simulated transaction. For example, entering 100 in this field will result + in a -100 amount in the transaction. For balance inquiries, this field must be set to 0. descriptor: Merchant descriptor. @@ -214,22 +292,25 @@ def simulate_authorization( merchant_amount: Amount of the transaction to be simulated in currency specified in merchant_currency, including any acquirer fees. - merchant_currency: 3-digit alphabetic ISO 4217 currency code. + merchant_currency: 3-character alphabetic ISO 4217 currency code. Note: Simulator only accepts USD, + GBP, EUR and defaults to GBP if another ISO 4217 code is provided partial_approval_capable: Set to true if the terminal is capable of partial approval otherwise false. Partial approval is when part of a transaction is approved and another payment must be used for the remainder. + pin: Simulate entering a PIN. If omitted, PIN check will not be performed. + status: Type of event to simulate. - `AUTHORIZATION` is a dual message purchase authorization, meaning a subsequent clearing step is required to settle the transaction. - - `BALANCE_INQUIRY` is a $0 authorization that includes a request for the - balance held on the card, and is most typically seen when a cardholder - requests to view a card's balance at an ATM. + - `BALANCE_INQUIRY` is a $0 authorization requesting the balance held on the + card, and is most often observed when a cardholder requests to view a card's + balance at an ATM. - `CREDIT_AUTHORIZATION` is a dual message request from a merchant to authorize - a refund or credit, meaning a subsequent clearing step is required to settle - the transaction. + a refund, meaning a subsequent clearing step is required to settle the + transaction. - `FINANCIAL_AUTHORIZATION` is a single message request from a merchant to debit funds immediately (such as an ATM withdrawal), and no subsequent clearing is required to settle the transaction. @@ -246,7 +327,7 @@ def simulate_authorization( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/authorize", + "/v1/simulate/authorize", body=maybe_transform( { "amount": amount, @@ -257,6 +338,7 @@ def simulate_authorization( "merchant_amount": merchant_amount, "merchant_currency": merchant_currency, "partial_approval_capable": partial_approval_capable, + "pin": pin, "status": status, }, transaction_simulate_authorization_params.TransactionSimulateAuthorizationParams, @@ -280,12 +362,12 @@ def simulate_authorization_advice( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateAuthorizationAdviceResponse: """ - Simulates an authorization advice request from the payment network as if it came - from a merchant acquirer. An authorization advice request changes the amount of - the transaction. + Simulates an authorization advice from the card network as if it came from a + merchant acquirer. An authorization advice changes the pending amount of the + transaction. Args: - token: The transaction token returned from the /v1/simulate/authorize response. + token: The transaction token returned from the /v1/simulate/authorize. response. amount: Amount (in cents) to authorize. This amount will override the transaction's amount that was originally set by /v1/simulate/authorize. @@ -299,7 +381,7 @@ def simulate_authorization_advice( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/authorization_advice", + "/v1/simulate/authorization_advice", body=maybe_transform( { "token": token, @@ -325,24 +407,27 @@ def simulate_clearing( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateClearingResponse: - """Clears an existing authorization. + """Clears an existing authorization, either debit or credit. - After this event, the transaction is no longer - pending. + After this event, the + transaction transitions from `PENDING` to `SETTLED` status. - If no `amount` is supplied to this endpoint, the amount of the transaction will - be captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. Args: token: The transaction token returned from the /v1/simulate/authorize response. - amount: Amount (in cents) to complete. Typically this will match the original - authorization, but may be more or less. + amount: Amount (in cents) to clear. Typically this will match the amount in the original + authorization, but can be higher or lower. The sign of this amount will + automatically match the sign of the original authorization's amount. For + example, entering 100 in this field will result in a -100 amount in the + transaction, if the original authorization is a credit authorization. - If no amount is supplied to this endpoint, the amount of the transaction will be - captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. extra_headers: Send extra headers @@ -353,7 +438,7 @@ def simulate_clearing( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/clearing", + "/v1/simulate/clearing", body=maybe_transform( { "token": token, @@ -382,11 +467,10 @@ def simulate_credit_authorization( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateCreditAuthorizationResponse: - """Simulates a credit authorization advice message from the payment network. + """Simulates a credit authorization advice from the card network. - This - message indicates that a credit authorization was approved on your behalf by the - network. + This message + indicates that the network approved a credit authorization on your behalf. Args: amount: Amount (in cents). Any value entered will be converted into a negative amount in @@ -412,7 +496,7 @@ def simulate_credit_authorization( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/credit_authorization_advice", + "/v1/simulate/credit_authorization_advice", body=maybe_transform( { "amount": amount, @@ -442,10 +526,11 @@ def simulate_return( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateReturnResponse: - """Returns (aka refunds) an amount back to a card. + """Returns, or refunds, an amount back to a card. - Returns are cleared immediately - and do not spend time in a `PENDING` state. + Returns simulated via this + endpoint clear immediately, without prior authorization, and result in a + `SETTLED` transaction status. Args: amount: Amount (in cents) to authorize. @@ -463,7 +548,7 @@ def simulate_return( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/return", + "/v1/simulate/return", body=maybe_transform( { "amount": amount, @@ -489,10 +574,11 @@ def simulate_return_reversal( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateReturnReversalResponse: - """ - Voids a settled credit transaction – i.e., a transaction with a negative amount - and `SETTLED` status. These can be credit authorizations that have already - cleared or financial credit authorizations. + """Reverses a return, i.e. + + a credit transaction with a `SETTLED` status. Returns + can be financial credit authorizations, or credit authorizations that have + cleared. Args: token: The transaction token returned from the /v1/simulate/authorize response. @@ -506,7 +592,7 @@ def simulate_return_reversal( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/return_reversal", + "/v1/simulate/return_reversal", body=maybe_transform( {"token": token}, transaction_simulate_return_reversal_params.TransactionSimulateReturnReversalParams ), @@ -529,19 +615,18 @@ def simulate_void( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateVoidResponse: - """Voids an existing, uncleared (aka pending) authorization. + """Voids a pending authorization. - If amount is not sent - the full amount will be voided. Cannot be used on partially completed - transactions, but can be used on partially voided transactions. _Note that - simulating an authorization expiry on credit authorizations or credit - authorization advice is not currently supported but will be added soon._ + If `amount` is not set, the full amount will be + voided. Can be used on partially voided transactions but not partially cleared + transactions. _Simulating an authorization expiry on credit authorizations or + credit authorization advice is not currently supported but will be added soon._ Args: token: The transaction token returned from the /v1/simulate/authorize response. - amount: Amount (in cents) to void. Typically this will match the original authorization, - but may be less. + amount: Amount (in cents) to void. Typically this will match the amount in the original + authorization, but can be less. type: Type of event to simulate. Defaults to `AUTHORIZATION_REVERSAL`. @@ -558,7 +643,7 @@ def simulate_void( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/simulate/void", + "/v1/simulate/void", body=maybe_transform( { "token": token, @@ -575,12 +660,31 @@ def simulate_void( class AsyncTransactions(AsyncAPIResource): + @cached_property + def enhanced_commercial_data(self) -> AsyncEnhancedCommercialData: + return AsyncEnhancedCommercialData(self._client) + + @cached_property + def events(self) -> AsyncEvents: + return AsyncEvents(self._client) + @cached_property def with_raw_response(self) -> AsyncTransactionsWithRawResponse: + """ + 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/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ return AsyncTransactionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncTransactionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ return AsyncTransactionsWithStreamingResponse(self) async def retrieve( @@ -594,8 +698,10 @@ async def retrieve( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Transaction: - """ - Get specific card transaction. + """Get a specific card transaction. + + All amounts are in the smallest unit of their + respective currency (e.g., cents for USD). Args: extra_headers: Send extra headers @@ -609,7 +715,7 @@ async def retrieve( if not transaction_token: raise ValueError(f"Expected a non-empty value for `transaction_token` but received {transaction_token!r}") return await self._get( - f"/transactions/{transaction_token}", + f"/v1/transactions/{transaction_token}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -627,6 +733,7 @@ def list( page_size: int | NotGiven = NOT_GIVEN, result: Literal["APPROVED", "DECLINED"] | NotGiven = NOT_GIVEN, starting_after: str | NotGiven = NOT_GIVEN, + status: Literal["PENDING", "VOIDED", "SETTLED", "DECLINED", "EXPIRED"] | 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, @@ -634,8 +741,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncPaginator[Transaction, AsyncCursorPage[Transaction]]: - """ - List card transactions. + """List card transactions. + + All amounts are in the smallest unit of their respective + currency (e.g., cents for USD) and inclusive of any acquirer fees. Args: account_token: Filters for transactions associated with a specific account. @@ -659,6 +768,8 @@ def list( starting_after: A cursor representing an item's token after which a page of results should begin. Used to retrieve the next page of results after this item. + status: Filters for transactions using transaction status field. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -668,7 +779,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/transactions", + "/v1/transactions", page=AsyncCursorPage[Transaction], options=make_request_options( extra_headers=extra_headers, @@ -685,6 +796,7 @@ def list( "page_size": page_size, "result": result, "starting_after": starting_after, + "status": status, }, transaction_list_params.TransactionListParams, ), @@ -692,6 +804,39 @@ def list( model=Transaction, ) + async def expire_authorization( + self, + transaction_token: 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: + """ + Expire authorization + + 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 transaction_token: + raise ValueError(f"Expected a non-empty value for `transaction_token` but received {transaction_token!r}") + return await self._post( + f"/v1/transactions/{transaction_token}/expire_authorization", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + async def simulate_authorization( self, *, @@ -703,6 +848,7 @@ async def simulate_authorization( merchant_amount: int | NotGiven = NOT_GIVEN, merchant_currency: str | NotGiven = NOT_GIVEN, partial_approval_capable: bool | NotGiven = NOT_GIVEN, + pin: str | NotGiven = NOT_GIVEN, status: Literal[ "AUTHORIZATION", "BALANCE_INQUIRY", @@ -719,19 +865,20 @@ async def simulate_authorization( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateAuthorizationResponse: """ - Simulates an authorization request from the payment network as if it came from a - merchant acquirer. If you're configured for ASA, simulating auths requires your - ASA client to be set up properly (respond with a valid JSON to the ASA request). - For users that are not configured for ASA, a daily transaction limit of $5000 - USD is applied by default. This limit can be modified via the + Simulates an authorization request from the card network as if it came from a + merchant acquirer. If you are configured for ASA, simulating authorizations + requires your ASA client to be set up properly, i.e. be able to respond to the + ASA request with a valid JSON. For users that are not configured for ASA, a + daily transaction limit of $5000 USD is applied by default. You can update this + limit via the [update account](https://docs.lithic.com/reference/patchaccountbytoken) endpoint. Args: amount: Amount (in cents) to authorize. For credit authorizations and financial credit authorizations, any value entered will be converted into a negative amount in - the simulated transaction. For example, entering 100 in this field will appear - as a -100 amount in the transaction. For balance inquiries, this field must be + the simulated transaction. For example, entering 100 in this field will result + in a -100 amount in the transaction. For balance inquiries, this field must be set to 0. descriptor: Merchant descriptor. @@ -747,22 +894,25 @@ async def simulate_authorization( merchant_amount: Amount of the transaction to be simulated in currency specified in merchant_currency, including any acquirer fees. - merchant_currency: 3-digit alphabetic ISO 4217 currency code. + merchant_currency: 3-character alphabetic ISO 4217 currency code. Note: Simulator only accepts USD, + GBP, EUR and defaults to GBP if another ISO 4217 code is provided partial_approval_capable: Set to true if the terminal is capable of partial approval otherwise false. Partial approval is when part of a transaction is approved and another payment must be used for the remainder. + pin: Simulate entering a PIN. If omitted, PIN check will not be performed. + status: Type of event to simulate. - `AUTHORIZATION` is a dual message purchase authorization, meaning a subsequent clearing step is required to settle the transaction. - - `BALANCE_INQUIRY` is a $0 authorization that includes a request for the - balance held on the card, and is most typically seen when a cardholder - requests to view a card's balance at an ATM. + - `BALANCE_INQUIRY` is a $0 authorization requesting the balance held on the + card, and is most often observed when a cardholder requests to view a card's + balance at an ATM. - `CREDIT_AUTHORIZATION` is a dual message request from a merchant to authorize - a refund or credit, meaning a subsequent clearing step is required to settle - the transaction. + a refund, meaning a subsequent clearing step is required to settle the + transaction. - `FINANCIAL_AUTHORIZATION` is a single message request from a merchant to debit funds immediately (such as an ATM withdrawal), and no subsequent clearing is required to settle the transaction. @@ -779,8 +929,8 @@ async def simulate_authorization( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/authorize", - body=maybe_transform( + "/v1/simulate/authorize", + body=await async_maybe_transform( { "amount": amount, "descriptor": descriptor, @@ -790,6 +940,7 @@ async def simulate_authorization( "merchant_amount": merchant_amount, "merchant_currency": merchant_currency, "partial_approval_capable": partial_approval_capable, + "pin": pin, "status": status, }, transaction_simulate_authorization_params.TransactionSimulateAuthorizationParams, @@ -813,12 +964,12 @@ async def simulate_authorization_advice( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateAuthorizationAdviceResponse: """ - Simulates an authorization advice request from the payment network as if it came - from a merchant acquirer. An authorization advice request changes the amount of - the transaction. + Simulates an authorization advice from the card network as if it came from a + merchant acquirer. An authorization advice changes the pending amount of the + transaction. Args: - token: The transaction token returned from the /v1/simulate/authorize response. + token: The transaction token returned from the /v1/simulate/authorize. response. amount: Amount (in cents) to authorize. This amount will override the transaction's amount that was originally set by /v1/simulate/authorize. @@ -832,8 +983,8 @@ async def simulate_authorization_advice( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/authorization_advice", - body=maybe_transform( + "/v1/simulate/authorization_advice", + body=await async_maybe_transform( { "token": token, "amount": amount, @@ -858,24 +1009,27 @@ async def simulate_clearing( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateClearingResponse: - """Clears an existing authorization. + """Clears an existing authorization, either debit or credit. - After this event, the transaction is no longer - pending. + After this event, the + transaction transitions from `PENDING` to `SETTLED` status. - If no `amount` is supplied to this endpoint, the amount of the transaction will - be captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. Args: token: The transaction token returned from the /v1/simulate/authorize response. - amount: Amount (in cents) to complete. Typically this will match the original - authorization, but may be more or less. + amount: Amount (in cents) to clear. Typically this will match the amount in the original + authorization, but can be higher or lower. The sign of this amount will + automatically match the sign of the original authorization's amount. For + example, entering 100 in this field will result in a -100 amount in the + transaction, if the original authorization is a credit authorization. - If no amount is supplied to this endpoint, the amount of the transaction will be - captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. extra_headers: Send extra headers @@ -886,8 +1040,8 @@ async def simulate_clearing( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/clearing", - body=maybe_transform( + "/v1/simulate/clearing", + body=await async_maybe_transform( { "token": token, "amount": amount, @@ -915,11 +1069,10 @@ async def simulate_credit_authorization( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateCreditAuthorizationResponse: - """Simulates a credit authorization advice message from the payment network. + """Simulates a credit authorization advice from the card network. - This - message indicates that a credit authorization was approved on your behalf by the - network. + This message + indicates that the network approved a credit authorization on your behalf. Args: amount: Amount (in cents). Any value entered will be converted into a negative amount in @@ -945,8 +1098,8 @@ async def simulate_credit_authorization( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/credit_authorization_advice", - body=maybe_transform( + "/v1/simulate/credit_authorization_advice", + body=await async_maybe_transform( { "amount": amount, "descriptor": descriptor, @@ -975,10 +1128,11 @@ async def simulate_return( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateReturnResponse: - """Returns (aka refunds) an amount back to a card. + """Returns, or refunds, an amount back to a card. - Returns are cleared immediately - and do not spend time in a `PENDING` state. + Returns simulated via this + endpoint clear immediately, without prior authorization, and result in a + `SETTLED` transaction status. Args: amount: Amount (in cents) to authorize. @@ -996,8 +1150,8 @@ async def simulate_return( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/return", - body=maybe_transform( + "/v1/simulate/return", + body=await async_maybe_transform( { "amount": amount, "descriptor": descriptor, @@ -1022,10 +1176,11 @@ async def simulate_return_reversal( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateReturnReversalResponse: - """ - Voids a settled credit transaction – i.e., a transaction with a negative amount - and `SETTLED` status. These can be credit authorizations that have already - cleared or financial credit authorizations. + """Reverses a return, i.e. + + a credit transaction with a `SETTLED` status. Returns + can be financial credit authorizations, or credit authorizations that have + cleared. Args: token: The transaction token returned from the /v1/simulate/authorize response. @@ -1039,8 +1194,8 @@ async def simulate_return_reversal( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/return_reversal", - body=maybe_transform( + "/v1/simulate/return_reversal", + body=await async_maybe_transform( {"token": token}, transaction_simulate_return_reversal_params.TransactionSimulateReturnReversalParams ), options=make_request_options( @@ -1062,19 +1217,18 @@ async def simulate_void( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> TransactionSimulateVoidResponse: - """Voids an existing, uncleared (aka pending) authorization. + """Voids a pending authorization. - If amount is not sent - the full amount will be voided. Cannot be used on partially completed - transactions, but can be used on partially voided transactions. _Note that - simulating an authorization expiry on credit authorizations or credit - authorization advice is not currently supported but will be added soon._ + If `amount` is not set, the full amount will be + voided. Can be used on partially voided transactions but not partially cleared + transactions. _Simulating an authorization expiry on credit authorizations or + credit authorization advice is not currently supported but will be added soon._ Args: token: The transaction token returned from the /v1/simulate/authorize response. - amount: Amount (in cents) to void. Typically this will match the original authorization, - but may be less. + amount: Amount (in cents) to void. Typically this will match the amount in the original + authorization, but can be less. type: Type of event to simulate. Defaults to `AUTHORIZATION_REVERSAL`. @@ -1091,8 +1245,8 @@ async def simulate_void( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/simulate/void", - body=maybe_transform( + "/v1/simulate/void", + body=await async_maybe_transform( { "token": token, "amount": amount, @@ -1117,6 +1271,9 @@ def __init__(self, transactions: Transactions) -> None: self.list = _legacy_response.to_raw_response_wrapper( transactions.list, ) + self.expire_authorization = _legacy_response.to_raw_response_wrapper( + transactions.expire_authorization, + ) self.simulate_authorization = _legacy_response.to_raw_response_wrapper( transactions.simulate_authorization, ) @@ -1139,6 +1296,14 @@ def __init__(self, transactions: Transactions) -> None: transactions.simulate_void, ) + @cached_property + def enhanced_commercial_data(self) -> EnhancedCommercialDataWithRawResponse: + return EnhancedCommercialDataWithRawResponse(self._transactions.enhanced_commercial_data) + + @cached_property + def events(self) -> EventsWithRawResponse: + return EventsWithRawResponse(self._transactions.events) + class AsyncTransactionsWithRawResponse: def __init__(self, transactions: AsyncTransactions) -> None: @@ -1150,6 +1315,9 @@ def __init__(self, transactions: AsyncTransactions) -> None: self.list = _legacy_response.async_to_raw_response_wrapper( transactions.list, ) + self.expire_authorization = _legacy_response.async_to_raw_response_wrapper( + transactions.expire_authorization, + ) self.simulate_authorization = _legacy_response.async_to_raw_response_wrapper( transactions.simulate_authorization, ) @@ -1172,6 +1340,14 @@ def __init__(self, transactions: AsyncTransactions) -> None: transactions.simulate_void, ) + @cached_property + def enhanced_commercial_data(self) -> AsyncEnhancedCommercialDataWithRawResponse: + return AsyncEnhancedCommercialDataWithRawResponse(self._transactions.enhanced_commercial_data) + + @cached_property + def events(self) -> AsyncEventsWithRawResponse: + return AsyncEventsWithRawResponse(self._transactions.events) + class TransactionsWithStreamingResponse: def __init__(self, transactions: Transactions) -> None: @@ -1183,6 +1359,9 @@ def __init__(self, transactions: Transactions) -> None: self.list = to_streamed_response_wrapper( transactions.list, ) + self.expire_authorization = to_streamed_response_wrapper( + transactions.expire_authorization, + ) self.simulate_authorization = to_streamed_response_wrapper( transactions.simulate_authorization, ) @@ -1205,6 +1384,14 @@ def __init__(self, transactions: Transactions) -> None: transactions.simulate_void, ) + @cached_property + def enhanced_commercial_data(self) -> EnhancedCommercialDataWithStreamingResponse: + return EnhancedCommercialDataWithStreamingResponse(self._transactions.enhanced_commercial_data) + + @cached_property + def events(self) -> EventsWithStreamingResponse: + return EventsWithStreamingResponse(self._transactions.events) + class AsyncTransactionsWithStreamingResponse: def __init__(self, transactions: AsyncTransactions) -> None: @@ -1216,6 +1403,9 @@ def __init__(self, transactions: AsyncTransactions) -> None: self.list = async_to_streamed_response_wrapper( transactions.list, ) + self.expire_authorization = async_to_streamed_response_wrapper( + transactions.expire_authorization, + ) self.simulate_authorization = async_to_streamed_response_wrapper( transactions.simulate_authorization, ) @@ -1237,3 +1427,11 @@ def __init__(self, transactions: AsyncTransactions) -> None: self.simulate_void = async_to_streamed_response_wrapper( transactions.simulate_void, ) + + @cached_property + def enhanced_commercial_data(self) -> AsyncEnhancedCommercialDataWithStreamingResponse: + return AsyncEnhancedCommercialDataWithStreamingResponse(self._transactions.enhanced_commercial_data) + + @cached_property + def events(self) -> AsyncEventsWithStreamingResponse: + return AsyncEventsWithStreamingResponse(self._transactions.events) diff --git a/src/lithic/resources/webhooks.py b/src/lithic/resources/webhooks.py deleted file mode 100644 index 8b5094f8..00000000 --- a/src/lithic/resources/webhooks.py +++ /dev/null @@ -1,207 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import hmac -import json -import math -import base64 -import hashlib -from datetime import datetime, timezone, timedelta - -from .._types import ( - HeadersLike, -) -from .._utils import ( - removeprefix, - get_required_header, -) -from .._resource import SyncAPIResource, AsyncAPIResource - -__all__ = ["Webhooks", "AsyncWebhooks"] - - -class Webhooks(SyncAPIResource): - def unwrap( - self, - payload: str | bytes, - headers: HeadersLike, - *, - secret: str | None = None, - ) -> object: - """Validates that the given payload was sent by Lithic and parses the payload.""" - self.verify_signature(payload=payload, headers=headers, secret=secret) - return json.loads(payload) - - def verify_signature( - self, - payload: str | bytes, - headers: HeadersLike, - *, - secret: str | None = None, - ) -> None: - """Validates whether or not the webhook payload was sent by Lithic. - - An error will be raised if the webhook payload was not sent by Lithic. - """ - if secret is None: - secret = self._client.webhook_secret - - if secret is None: - raise ValueError( - "The webhook secret must either be set using the env var, LITHIC_WEBHOOK_SECRET, on the client class, Lithic(webhook_secret='123'), or passed to this function" - ) - - try: - whsecret = base64.b64decode(removeprefix(secret, "whsec_")) - except Exception as err: - raise ValueError("Bad secret") from err - - msg_id = get_required_header(headers, "webhook-id") - msg_timestamp = get_required_header(headers, "webhook-timestamp") - - # validate the timestamp - webhook_tolerance = timedelta(minutes=5) - now = datetime.now(tz=timezone.utc) - - try: - timestamp = datetime.fromtimestamp(float(msg_timestamp), tz=timezone.utc) - except Exception as err: - raise ValueError("Invalid signature headers. Could not convert to timestamp") from err - - # too old - if timestamp < (now - webhook_tolerance): - raise ValueError("Webhook timestamp is too old") - - # too new - if timestamp > (now + webhook_tolerance): - raise ValueError("Webhook timestamp is too new") - - # create the signature - body = payload.decode("utf-8") if isinstance(payload, bytes) else payload - if not isinstance(body, str): # pyright: ignore[reportUnnecessaryIsInstance] - raise ValueError( - "Webhook body should be a string of JSON (or bytes which can be decoded to a utf-8 string), not a parsed dictionary." - ) - - timestamp_str = str(math.floor(timestamp.replace(tzinfo=timezone.utc).timestamp())) - - to_sign = f"{msg_id}.{timestamp_str}.{body}".encode() - expected_signature = hmac.new(whsecret, to_sign, hashlib.sha256).digest() - - msg_signature = get_required_header(headers, "webhook-signature") - - # Signature header can contain multiple signatures delimited by spaces - passed_sigs = msg_signature.split(" ") - - for versioned_sig in passed_sigs: - values = versioned_sig.split(",") - if len(values) != 2: - # signature is not formatted like {version},{signature} - continue - - (version, signature) = values - - # Only verify prefix v1 - if version != "v1": - continue - - sig_bytes = base64.b64decode(signature) - if hmac.compare_digest(expected_signature, sig_bytes): - # valid! - return None - - raise ValueError("None of the given webhook signatures match the expected signature") - - -class AsyncWebhooks(AsyncAPIResource): - def unwrap( - self, - payload: str | bytes, - headers: HeadersLike, - *, - secret: str | None = None, - ) -> object: - """Validates that the given payload was sent by Lithic and parses the payload.""" - self.verify_signature(payload=payload, headers=headers, secret=secret) - return json.loads(payload) - - def verify_signature( - self, - payload: str | bytes, - headers: HeadersLike, - *, - secret: str | None = None, - ) -> None: - """Validates whether or not the webhook payload was sent by Lithic. - - An error will be raised if the webhook payload was not sent by Lithic. - """ - if secret is None: - secret = self._client.webhook_secret - - if secret is None: - raise ValueError( - "The webhook secret must either be set using the env var, LITHIC_WEBHOOK_SECRET, on the client class, Lithic(webhook_secret='123'), or passed to this function" - ) - - try: - whsecret = base64.b64decode(removeprefix(secret, "whsec_")) - except Exception as err: - raise ValueError("Bad secret") from err - - msg_id = get_required_header(headers, "webhook-id") - msg_timestamp = get_required_header(headers, "webhook-timestamp") - - # validate the timestamp - webhook_tolerance = timedelta(minutes=5) - now = datetime.now(tz=timezone.utc) - - try: - timestamp = datetime.fromtimestamp(float(msg_timestamp), tz=timezone.utc) - except Exception as err: - raise ValueError("Invalid signature headers. Could not convert to timestamp") from err - - # too old - if timestamp < (now - webhook_tolerance): - raise ValueError("Webhook timestamp is too old") - - # too new - if timestamp > (now + webhook_tolerance): - raise ValueError("Webhook timestamp is too new") - - # create the signature - body = payload.decode("utf-8") if isinstance(payload, bytes) else payload - if not isinstance(body, str): # pyright: ignore[reportUnnecessaryIsInstance] - raise ValueError( - "Webhook body should be a string of JSON (or bytes which can be decoded to a utf-8 string), not a parsed dictionary." - ) - - timestamp_str = str(math.floor(timestamp.replace(tzinfo=timezone.utc).timestamp())) - - to_sign = f"{msg_id}.{timestamp_str}.{body}".encode() - expected_signature = hmac.new(whsecret, to_sign, hashlib.sha256).digest() - - msg_signature = get_required_header(headers, "webhook-signature") - - # Signature header can contain multiple signatures delimited by spaces - passed_sigs = msg_signature.split(" ") - - for versioned_sig in passed_sigs: - values = versioned_sig.split(",") - if len(values) != 2: - # signature is not formatted like {version},{signature} - continue - - (version, signature) = values - - # Only verify prefix v1 - if version != "v1": - continue - - sig_bytes = base64.b64decode(signature) - if hmac.compare_digest(expected_signature, sig_bytes): - # valid! - return None - - raise ValueError("None of the given webhook signatures match the expected signature") diff --git a/src/lithic/types/__init__.py b/src/lithic/types/__init__.py index bc1b39f6..7097d293 100644 --- a/src/lithic/types/__init__.py +++ b/src/lithic/types/__init__.py @@ -1,15 +1,24 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .card import Card as Card from .event import Event as Event -from .shared import Address as Address, Carrier as Carrier, ShippingAddress as ShippingAddress +from .shared import ( + Address as Address, + Carrier as Carrier, + Currency as Currency, + Document as Document, + ShippingAddress as ShippingAddress, + AccountFinancialAccountType as AccountFinancialAccountType, + InstanceFinancialAccountType as InstanceFinancialAccountType, +) from .account import Account as Account from .balance import Balance as Balance from .dispute import Dispute as Dispute from .payment import Payment as Payment -from .auth_rule import AuthRule as AuthRule +from .kyb_param import KYBParam as KYBParam +from .kyc_param import KYCParam as KYCParam from .api_status import APIStatus as APIStatus from .owner_type import OwnerType as OwnerType from .transaction import Transaction as Transaction @@ -17,16 +26,18 @@ from .tokenization import Tokenization as Tokenization from .account_holder import AccountHolder as AccountHolder from .message_attempt import MessageAttempt as MessageAttempt -from .business_account import BusinessAccount as BusinessAccount from .card_list_params import CardListParams as CardListParams from .digital_card_art import DigitalCardArt as DigitalCardArt from .dispute_evidence import DisputeEvidence as DisputeEvidence +from .external_payment import ExternalPayment as ExternalPayment +from .kyc_exempt_param import KYCExemptParam as KYCExemptParam from .aggregate_balance import AggregateBalance as AggregateBalance from .card_embed_params import CardEmbedParams as CardEmbedParams from .card_renew_params import CardRenewParams as CardRenewParams from .card_spend_limits import CardSpendLimits as CardSpendLimits from .event_list_params import EventListParams as EventListParams from .financial_account import FinancialAccount as FinancialAccount +from .required_document import RequiredDocument as RequiredDocument from .settlement_detail import SettlementDetail as SettlementDetail from .settlement_report import SettlementReport as SettlementReport from .auth_stream_secret import AuthStreamSecret as AuthStreamSecret @@ -38,70 +49,78 @@ from .card_embed_response import CardEmbedResponse as CardEmbedResponse from .card_reissue_params import CardReissueParams as CardReissueParams from .dispute_list_params import DisputeListParams as DisputeListParams -from .event_resend_params import EventResendParams as EventResendParams +from .kyb_business_entity import KYBBusinessEntity as KYBBusinessEntity from .payment_list_params import PaymentListParams as PaymentListParams from .tokenization_secret import TokenizationSecret as TokenizationSecret from .verification_method import VerificationMethod as VerificationMethod from .account_spend_limits import AccountSpendLimits as AccountSpendLimits +from .address_update_param import AddressUpdateParam as AddressUpdateParam from .spend_limit_duration import SpendLimitDuration as SpendLimitDuration from .account_update_params import AccountUpdateParams as AccountUpdateParams -from .auth_rule_list_params import AuthRuleListParams as AuthRuleListParams from .card_provision_params import CardProvisionParams as CardProvisionParams from .dispute_create_params import DisputeCreateParams as DisputeCreateParams from .dispute_update_params import DisputeUpdateParams as DisputeUpdateParams from .financial_transaction import FinancialTransaction as FinancialTransaction from .payment_create_params import PaymentCreateParams as PaymentCreateParams -from .auth_rule_apply_params import AuthRuleApplyParams as AuthRuleApplyParams +from .book_transfer_response import BookTransferResponse as BookTransferResponse from .payment_retry_response import PaymentRetryResponse as PaymentRetryResponse -from .account_holder_document import AccountHolderDocument as AccountHolderDocument -from .auth_rule_create_params import AuthRuleCreateParams as AuthRuleCreateParams -from .auth_rule_remove_params import AuthRuleRemoveParams as AuthRuleRemoveParams -from .auth_rule_update_params import AuthRuleUpdateParams as AuthRuleUpdateParams from .card_provision_response import CardProvisionResponse as CardProvisionResponse from .payment_create_response import PaymentCreateResponse as PaymentCreateResponse from .transaction_list_params import TransactionListParams as TransactionListParams from .card_program_list_params import CardProgramListParams as CardProgramListParams from .tokenization_list_params import TokenizationListParams as TokenizationListParams -from .auth_rule_remove_response import AuthRuleRemoveResponse as AuthRuleRemoveResponse -from .card_get_embed_url_params import CardGetEmbedURLParams as CardGetEmbedURLParams +from .book_transfer_list_params import BookTransferListParams as BookTransferListParams from .card_search_by_pan_params import CardSearchByPanParams as CardSearchByPanParams from .responder_endpoint_status import ResponderEndpointStatus as ResponderEndpointStatus from .account_holder_list_params import AccountHolderListParams as AccountHolderListParams -from .card_get_embed_html_params import CardGetEmbedHTMLParams as CardGetEmbedHTMLParams from .event_list_attempts_params import EventListAttemptsParams as EventListAttemptsParams from .settlement_summary_details import SettlementSummaryDetails as SettlementSummaryDetails -from .auth_rule_retrieve_response import AuthRuleRetrieveResponse as AuthRuleRetrieveResponse +from .book_transfer_create_params import BookTransferCreateParams as BookTransferCreateParams from .account_holder_create_params import AccountHolderCreateParams as AccountHolderCreateParams from .account_holder_update_params import AccountHolderUpdateParams as AccountHolderUpdateParams +from .book_transfer_reverse_params import BookTransferReverseParams as BookTransferReverseParams +from .card_convert_physical_params import CardConvertPhysicalParams as CardConvertPhysicalParams from .digital_card_art_list_params import DigitalCardArtListParams as DigitalCardArtListParams +from .external_payment_list_params import ExternalPaymentListParams as ExternalPaymentListParams from .tokenization_simulate_params import TokenizationSimulateParams as TokenizationSimulateParams from .aggregate_balance_list_params import AggregateBalanceListParams as AggregateBalanceListParams from .dispute_list_evidences_params import DisputeListEvidencesParams as DisputeListEvidencesParams from .external_bank_account_address import ExternalBankAccountAddress as ExternalBankAccountAddress from .financial_account_list_params import FinancialAccountListParams as FinancialAccountListParams from .account_holder_create_response import AccountHolderCreateResponse as AccountHolderCreateResponse -from .account_holder_resubmit_params import AccountHolderResubmitParams as AccountHolderResubmitParams from .account_holder_update_response import AccountHolderUpdateResponse as AccountHolderUpdateResponse +from .external_payment_cancel_params import ExternalPaymentCancelParams as ExternalPaymentCancelParams +from .external_payment_create_params import ExternalPaymentCreateParams as ExternalPaymentCreateParams +from .external_payment_settle_params import ExternalPaymentSettleParams as ExternalPaymentSettleParams +from .payment_simulate_action_params import PaymentSimulateActionParams as PaymentSimulateActionParams from .payment_simulate_return_params import PaymentSimulateReturnParams as PaymentSimulateReturnParams from .tokenization_retrieve_response import TokenizationRetrieveResponse as TokenizationRetrieveResponse from .tokenization_simulate_response import TokenizationSimulateResponse as TokenizationSimulateResponse +from .external_payment_release_params import ExternalPaymentReleaseParams as ExternalPaymentReleaseParams +from .external_payment_reverse_params import ExternalPaymentReverseParams as ExternalPaymentReverseParams from .financial_account_create_params import FinancialAccountCreateParams as FinancialAccountCreateParams from .financial_account_update_params import FinancialAccountUpdateParams as FinancialAccountUpdateParams +from .payment_simulate_receipt_params import PaymentSimulateReceiptParams as PaymentSimulateReceiptParams from .payment_simulate_release_params import PaymentSimulateReleaseParams as PaymentSimulateReleaseParams +from .management_operation_list_params import ManagementOperationListParams as ManagementOperationListParams +from .management_operation_transaction import ManagementOperationTransaction as ManagementOperationTransaction +from .payment_simulate_action_response import PaymentSimulateActionResponse as PaymentSimulateActionResponse from .payment_simulate_return_response import PaymentSimulateReturnResponse as PaymentSimulateReturnResponse from .responder_endpoint_create_params import ResponderEndpointCreateParams as ResponderEndpointCreateParams from .responder_endpoint_delete_params import ResponderEndpointDeleteParams as ResponderEndpointDeleteParams from .transaction_simulate_void_params import TransactionSimulateVoidParams as TransactionSimulateVoidParams from .external_bank_account_list_params import ExternalBankAccountListParams as ExternalBankAccountListParams +from .payment_simulate_receipt_response import PaymentSimulateReceiptResponse as PaymentSimulateReceiptResponse from .payment_simulate_release_response import PaymentSimulateReleaseResponse as PaymentSimulateReleaseResponse +from .management_operation_create_params import ManagementOperationCreateParams as ManagementOperationCreateParams from .responder_endpoint_create_response import ResponderEndpointCreateResponse as ResponderEndpointCreateResponse from .transaction_simulate_return_params import TransactionSimulateReturnParams as TransactionSimulateReturnParams from .transaction_simulate_void_response import TransactionSimulateVoidResponse as TransactionSimulateVoidResponse -from .card_product_credit_detail_response import CardProductCreditDetailResponse as CardProductCreditDetailResponse from .external_bank_account_address_param import ExternalBankAccountAddressParam as ExternalBankAccountAddressParam from .external_bank_account_create_params import ExternalBankAccountCreateParams as ExternalBankAccountCreateParams from .external_bank_account_list_response import ExternalBankAccountListResponse as ExternalBankAccountListResponse from .external_bank_account_update_params import ExternalBankAccountUpdateParams as ExternalBankAccountUpdateParams +from .management_operation_reverse_params import ManagementOperationReverseParams as ManagementOperationReverseParams from .transaction_simulate_clearing_params import TransactionSimulateClearingParams as TransactionSimulateClearingParams from .transaction_simulate_return_response import TransactionSimulateReturnResponse as TransactionSimulateReturnResponse from .account_holder_upload_document_params import ( @@ -116,6 +135,9 @@ from .account_holder_list_documents_response import ( AccountHolderListDocumentsResponse as AccountHolderListDocumentsResponse, ) +from .financial_account_update_status_params import ( + FinancialAccountUpdateStatusParams as FinancialAccountUpdateStatusParams, +) from .responder_endpoint_check_status_params import ( ResponderEndpointCheckStatusParams as ResponderEndpointCheckStatusParams, ) @@ -131,24 +153,48 @@ from .transaction_simulate_authorization_params import ( TransactionSimulateAuthorizationParams as TransactionSimulateAuthorizationParams, ) +from .external_bank_account_retry_prenote_params import ( + ExternalBankAccountRetryPrenoteParams as ExternalBankAccountRetryPrenoteParams, +) +from .tokenization_resend_activation_code_params import ( + TokenizationResendActivationCodeParams as TokenizationResendActivationCodeParams, +) +from .tokenization_update_digital_card_art_params import ( + TokenizationUpdateDigitalCardArtParams as TokenizationUpdateDigitalCardArtParams, +) from .transaction_simulate_authorization_response import ( TransactionSimulateAuthorizationResponse as TransactionSimulateAuthorizationResponse, ) from .transaction_simulate_return_reversal_params import ( TransactionSimulateReturnReversalParams as TransactionSimulateReturnReversalParams, ) +from .external_bank_account_retry_prenote_response import ( + ExternalBankAccountRetryPrenoteResponse as ExternalBankAccountRetryPrenoteResponse, +) +from .tokenization_update_digital_card_art_response import ( + TokenizationUpdateDigitalCardArtResponse as TokenizationUpdateDigitalCardArtResponse, +) from .transaction_simulate_return_reversal_response import ( TransactionSimulateReturnReversalResponse as TransactionSimulateReturnReversalResponse, ) from .tokenization_decisioning_rotate_secret_response import ( TokenizationDecisioningRotateSecretResponse as TokenizationDecisioningRotateSecretResponse, ) +from .account_holder_simulate_enrollment_review_params import ( + AccountHolderSimulateEnrollmentReviewParams as AccountHolderSimulateEnrollmentReviewParams, +) from .transaction_simulate_authorization_advice_params import ( TransactionSimulateAuthorizationAdviceParams as TransactionSimulateAuthorizationAdviceParams, ) from .transaction_simulate_credit_authorization_params import ( TransactionSimulateCreditAuthorizationParams as TransactionSimulateCreditAuthorizationParams, ) +from .external_bank_account_retry_micro_deposits_params import ( + ExternalBankAccountRetryMicroDepositsParams as ExternalBankAccountRetryMicroDepositsParams, +) +from .account_holder_simulate_enrollment_review_response import ( + AccountHolderSimulateEnrollmentReviewResponse as AccountHolderSimulateEnrollmentReviewResponse, +) from .transaction_simulate_authorization_advice_response import ( TransactionSimulateAuthorizationAdviceResponse as TransactionSimulateAuthorizationAdviceResponse, ) @@ -158,3 +204,6 @@ from .external_bank_account_retry_micro_deposits_response import ( ExternalBankAccountRetryMicroDepositsResponse as ExternalBankAccountRetryMicroDepositsResponse, ) +from .account_holder_simulate_enrollment_document_review_params import ( + AccountHolderSimulateEnrollmentDocumentReviewParams as AccountHolderSimulateEnrollmentDocumentReviewParams, +) diff --git a/src/lithic/types/account.py b/src/lithic/types/account.py index aa53d79c..42cfa81c 100644 --- a/src/lithic/types/account.py +++ b/src/lithic/types/account.py @@ -1,6 +1,7 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional +from datetime import datetime from typing_extensions import Literal from .._models import BaseModel @@ -50,8 +51,9 @@ class VerificationAddress(BaseModel): postal_code: str """Valid postal code. - Only USA ZIP codes are currently supported, entered as a five-digit ZIP or - nine-digit ZIP+4. + Only USA postal codes (ZIP codes) are currently supported, entered as a + five-digit postal code or nine-digit postal code (ZIP+4) using the format + 12345-1234. """ state: str @@ -73,6 +75,12 @@ class Account(BaseModel): this parameter, do not include pagination. """ + created: Optional[datetime] = None + """Timestamp of when the account was created. + + For accounts created before 2023-05-11, this field will be null. + """ + spend_limit: SpendLimit """ Spend limit information for the user containing the daily, monthly, and lifetime @@ -82,17 +90,31 @@ class Account(BaseModel): feature is disabled. """ - state: Literal["ACTIVE", "PAUSED"] + state: Literal["ACTIVE", "PAUSED", "CLOSED"] """Account state: - `ACTIVE` - Account is able to transact and create new cards. - `PAUSED` - Account will not be able to transact or create new cards. It can be set back to `ACTIVE`. + - `CLOSED` - Account will not be able to transact or create new cards. `CLOSED` + accounts are also unable to be transitioned to `ACTIVE` or `PAUSED` states. + `CLOSED` accounts result from failing to pass KYB/KYC or Lithic closing for + risk/compliance reasons. Please contact + [support@lithic.com](mailto:support@lithic.com) if you believe this was in + error. """ account_holder: Optional[AccountHolder] = None auth_rule_tokens: Optional[List[str]] = None - """List of identifiers for the Auth Rule(s) that are applied on the account.""" + """ + List of identifiers for the Auth Rule(s) that are applied on the account. This + field is deprecated and will no longer be populated in the `account_holder` + object. The key will be removed from the schema in a future release. Use the + `/auth_rules` endpoints to fetch Auth Rule information instead. + """ + + cardholder_currency: Optional[str] = None + """3-character alphabetic ISO 4217 code for the currency of the cardholder.""" verification_address: Optional[VerificationAddress] = None diff --git a/src/lithic/types/account_holder.py b/src/lithic/types/account_holder.py index 685da4ec..627df7fc 100644 --- a/src/lithic/types/account_holder.py +++ b/src/lithic/types/account_holder.py @@ -1,11 +1,12 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime from typing_extensions import Literal -from .shared import Address from .._models import BaseModel +from .shared.address import Address +from .required_document import RequiredDocument __all__ = [ "AccountHolder", @@ -25,6 +26,15 @@ class BeneficialOwnerEntity(BaseModel): acceptable; APO/FPO are acceptable. """ + dba_business_name: str + """ + Any name that the business operates under that is not its legal business name + (if applicable). + """ + + entity_token: str + """Globally unique identifier for the entity.""" + government_id: str """Government-issued identification number. @@ -41,33 +51,30 @@ class BeneficialOwnerEntity(BaseModel): format. """ - dba_business_name: Optional[str] = None - """ - Any name that the business operates under that is not its legal business name - (if applicable). - """ - parent_company: Optional[str] = None """Parent company name (if applicable).""" class BeneficialOwnerIndividual(BaseModel): - address: Optional[Address] = None + address: Address """Individual's current address""" - dob: Optional[str] = None + dob: str """Individual's date of birth, as an RFC 3339 date.""" - email: Optional[str] = None + email: str """Individual's email address.""" - first_name: Optional[str] = None + entity_token: str + """Globally unique identifier for the entity.""" + + first_name: str """Individual's first name, as it appears on government-issued identity documents.""" - last_name: Optional[str] = None + last_name: str """Individual's last name, as it appears on government-issued identity documents.""" - phone_number: Optional[str] = None + phone_number: str """Individual's phone number, entered in E.164 format.""" @@ -78,6 +85,15 @@ class BusinessEntity(BaseModel): acceptable; APO/FPO are acceptable. """ + dba_business_name: str + """ + Any name that the business operates under that is not its legal business name + (if applicable). + """ + + entity_token: str + """Globally unique identifier for the entity.""" + government_id: str """Government-issued identification number. @@ -94,53 +110,53 @@ class BusinessEntity(BaseModel): format. """ - dba_business_name: Optional[str] = None - """ - Any name that the business operates under that is not its legal business name - (if applicable). - """ - parent_company: Optional[str] = None """Parent company name (if applicable).""" class ControlPerson(BaseModel): - address: Optional[Address] = None + address: Address """Individual's current address""" - dob: Optional[str] = None + dob: str """Individual's date of birth, as an RFC 3339 date.""" - email: Optional[str] = None + email: str """Individual's email address.""" - first_name: Optional[str] = None + entity_token: str + """Globally unique identifier for the entity.""" + + first_name: str """Individual's first name, as it appears on government-issued identity documents.""" - last_name: Optional[str] = None + last_name: str """Individual's last name, as it appears on government-issued identity documents.""" - phone_number: Optional[str] = None + phone_number: str """Individual's phone number, entered in E.164 format.""" class Individual(BaseModel): - address: Optional[Address] = None + address: Address """Individual's current address""" - dob: Optional[str] = None + dob: str """Individual's date of birth, as an RFC 3339 date.""" - email: Optional[str] = None + email: str """Individual's email address.""" - first_name: Optional[str] = None + entity_token: str + """Globally unique identifier for the entity.""" + + first_name: str """Individual's first name, as it appears on government-issued identity documents.""" - last_name: Optional[str] = None + last_name: str """Individual's last name, as it appears on government-issued identity documents.""" - phone_number: Optional[str] = None + phone_number: str """Individual's phone number, entered in E.164 format.""" @@ -148,10 +164,12 @@ class VerificationApplication(BaseModel): created: Optional[datetime] = None """Timestamp of when the application was created.""" - status: Optional[Literal["ACCEPTED", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"]] = None - """ - KYC and KYB evaluation states. Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` - are only applicable for the `ADVANCED` workflow. + status: Optional[Literal["ACCEPTED", "PENDING_REVIEW", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"]] = None + """KYC and KYB evaluation states. + + Note: + + - `PENDING_REVIEW` is only applicable for the `KYB_BASIC` workflow. """ status_reasons: Optional[ @@ -181,14 +199,14 @@ class AccountHolder(BaseModel): token: str """Globally unique identifier for the account holder.""" + created: datetime + """Timestamp of when the account holder was created.""" + account_token: Optional[str] = None """Globally unique identifier for the account.""" beneficial_owner_entities: Optional[List[BeneficialOwnerEntity]] = None - """Only present when user_type == "BUSINESS". - - List of all entities with >25% ownership in the company. - """ + """Deprecated. Only present when user_type == "BUSINESS".""" beneficial_owner_individuals: Optional[List[BeneficialOwnerIndividual]] = None """Only present when user_type == "BUSINESS". @@ -220,9 +238,6 @@ class AccountHolder(BaseModel): In some cases, this individual could also be a beneficial owner listed above. """ - created: Optional[datetime] = None - """Timestamp of when the account holder was created.""" - email: Optional[str] = None """ < Deprecated. Use control_person.email when user_type == "BUSINESS". Use @@ -261,12 +276,20 @@ class AccountHolder(BaseModel): > Primary phone of Account Holder, entered in E.164 format. """ - status: Optional[Literal["ACCEPTED", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"]] = None - """ + + KYC and KYB evaluation states. + + Note: - Use verification_application.status instead> KYC and KYB evaluation states. - Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for the - `ADVANCED` workflow. + - `PENDING_REVIEW` is only applicable for the `KYB_BASIC` workflow. """ status_reasons: Optional[ diff --git a/src/lithic/types/account_holder_create_params.py b/src/lithic/types/account_holder_create_params.py index eefc7324..4a328871 100644 --- a/src/lithic/types/account_holder_create_params.py +++ b/src/lithic/types/account_holder_create_params.py @@ -1,19 +1,19 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union, Iterable -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict -from ..types import shared_params +from .shared_params.address import Address __all__ = [ "AccountHolderCreateParams", "KYB", - "KYBBeneficialOwnerEntity", "KYBBeneficialOwnerIndividual", "KYBBusinessEntity", "KYBControlPerson", + "KYBBeneficialOwnerEntity", "KYC", "KYCIndividual", "KYCExempt", @@ -21,26 +21,13 @@ class KYB(TypedDict, total=False): - beneficial_owner_entities: Required[Iterable[KYBBeneficialOwnerEntity]] - """List of all entities with >25% ownership in the company. - - If no entity or individual owns >25% of the company, and the largest shareholder - is an entity, please identify them in this field. See - [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) - (Section I) for more background. If no business owner is an entity, pass in an - empty list. However, either this parameter or `beneficial_owner_individuals` - must be populated. on entities that should be included. - """ - beneficial_owner_individuals: Required[Iterable[KYBBeneficialOwnerIndividual]] - """List of all individuals with >25% ownership in the company. + """List of all direct and indirect individuals with >25% ownership in the company. - If no entity or individual owns >25% of the company, and the largest shareholder - is an individual, please identify them in this field. See + If no individual owns >25% of the company, please identify the largest + shareholder in this field. See [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) - (Section I) for more background on individuals that should be included. If no - individual is an entity, pass in an empty list. However, either this parameter - or `beneficial_owner_entities` must be populated. + (Section I) for more background on individuals that should be included. """ business_entity: Required[KYBBusinessEntity] @@ -77,6 +64,9 @@ class KYB(TypedDict, total=False): workflow: Required[Literal["KYB_BASIC", "KYB_BYO"]] """Specifies the type of KYB workflow to run.""" + beneficial_owner_entities: Iterable[KYBBeneficialOwnerEntity] + """Deprecated.""" + external_id: str """ A user provided id that can be used to link an account holder with an external @@ -95,41 +85,8 @@ class KYB(TypedDict, total=False): """Company website URL.""" -class KYBBeneficialOwnerEntity(TypedDict, total=False): - address: Required[shared_params.Address] - """ - Business's physical address - PO boxes, UPS drops, and FedEx drops are not - acceptable; APO/FPO are acceptable. - """ - - government_id: Required[str] - """Government-issued identification number. - - US Federal Employer Identification Numbers (EIN) are currently supported, - entered as full nine-digits, with or without hyphens. - """ - - legal_business_name: Required[str] - """Legal (formal) business name.""" - - phone_numbers: Required[List[str]] - """ - One or more of the business's phone number(s), entered as a list in E.164 - format. - """ - - dba_business_name: str - """ - Any name that the business operates under that is not its legal business name - (if applicable). - """ - - parent_company: str - """Parent company name (if applicable).""" - - class KYBBeneficialOwnerIndividual(TypedDict, total=False): - address: Required[shared_params.Address] + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. @@ -163,7 +120,7 @@ class KYBBeneficialOwnerIndividual(TypedDict, total=False): class KYBBusinessEntity(TypedDict, total=False): - address: Required[shared_params.Address] + address: Required[Address] """ Business's physical address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. @@ -196,7 +153,7 @@ class KYBBusinessEntity(TypedDict, total=False): class KYBControlPerson(TypedDict, total=False): - address: Required[shared_params.Address] + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. @@ -229,6 +186,39 @@ class KYBControlPerson(TypedDict, total=False): """Individual's phone number, entered in E.164 format.""" +class KYBBeneficialOwnerEntity(TypedDict, total=False): + address: Required[Address] + """ + Business's physical address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + """ + + government_id: Required[str] + """Government-issued identification number. + + US Federal Employer Identification Numbers (EIN) are currently supported, + entered as full nine-digits, with or without hyphens. + """ + + legal_business_name: Required[str] + """Legal (formal) business name.""" + + phone_numbers: Required[List[str]] + """ + One or more of the business's phone number(s), entered as a list in E.164 + format. + """ + + dba_business_name: str + """ + Any name that the business operates under that is not its legal business name + (if applicable). + """ + + parent_company: str + """Parent company name (if applicable).""" + + class KYC(TypedDict, total=False): individual: Required[KYCIndividual] """ @@ -243,7 +233,7 @@ class KYC(TypedDict, total=False): implementation with Lithic. """ - workflow: Required[Literal["KYC_ADVANCED", "KYC_BASIC", "KYC_BYO"]] + workflow: Required[Literal["KYC_BASIC", "KYC_BYO"]] """Specifies the type of KYC workflow to run.""" external_id: str @@ -262,7 +252,7 @@ class KYC(TypedDict, total=False): class KYCIndividual(TypedDict, total=False): - address: Required[shared_params.Address] + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. @@ -296,6 +286,12 @@ class KYCIndividual(TypedDict, total=False): class KYCExempt(TypedDict, total=False): + address: Required[Address] + """ + KYC Exempt user's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + """ + email: Required[str] """The KYC Exempt user's email""" @@ -309,17 +305,11 @@ class KYCExempt(TypedDict, total=False): """The KYC Exempt user's last name""" phone_number: Required[str] - """The KYC Exempt user's phone number""" + """The KYC Exempt user's phone number, entered in E.164 format.""" workflow: Required[Literal["KYC_EXEMPT"]] """Specifies the workflow type. This must be 'KYC_EXEMPT'""" - address: shared_params.Address - """ - KYC Exempt user's current address - PO boxes, UPS drops, and FedEx drops are not - acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. - """ - business_account_token: str """ Only applicable for customers using the KYC-Exempt workflow to enroll authorized @@ -334,4 +324,4 @@ class KYCExempt(TypedDict, total=False): """ -AccountHolderCreateParams = Union[KYB, KYC, KYCExempt] +AccountHolderCreateParams: TypeAlias = Union[KYB, KYC, KYCExempt] diff --git a/src/lithic/types/account_holder_create_response.py b/src/lithic/types/account_holder_create_response.py index f98c7caa..e3245796 100644 --- a/src/lithic/types/account_holder_create_response.py +++ b/src/lithic/types/account_holder_create_response.py @@ -1,10 +1,11 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime from typing_extensions import Literal from .._models import BaseModel +from .required_document import RequiredDocument __all__ = ["AccountHolderCreateResponse"] @@ -16,10 +17,12 @@ class AccountHolderCreateResponse(BaseModel): account_token: str """Globally unique identifier for the account.""" - status: Literal["ACCEPTED", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"] - """ - KYC and KYB evaluation states. Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` - are only applicable for the `ADVANCED` workflow. + status: Literal["ACCEPTED", "PENDING_REVIEW", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"] + """KYC and KYB evaluation states. + + Note: + + - `PENDING_REVIEW` is only applicable for the `KYB_BASIC` workflow. """ status_reasons: List[ @@ -35,6 +38,19 @@ class AccountHolderCreateResponse(BaseModel): "OTHER_VERIFICATION_FAILURE", "RISK_THRESHOLD_FAILURE", "WATCHLIST_ALERT_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ADDRESS_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_NAME_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_BUSINESS_OFFICERS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_SOS_FILING_INACTIVE", + "PRIMARY_BUSINESS_ENTITY_SOS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_CMRA_FAILURE", + "PRIMARY_BUSINESS_ENTITY_WATCHLIST_FAILURE", + "PRIMARY_BUSINESS_ENTITY_REGISTERED_AGENT_FAILURE", + "CONTROL_PERSON_BLOCKLIST_ALERT_FAILURE", + "CONTROL_PERSON_ID_VERIFICATION_FAILURE", + "CONTROL_PERSON_DOB_VERIFICATION_FAILURE", + "CONTROL_PERSON_NAME_VERIFICATION_FAILURE", ] ] """Reason for the evaluation status.""" @@ -47,3 +63,9 @@ class AccountHolderCreateResponse(BaseModel): Customer-provided token that indicates a relationship with an object outside of the Lithic ecosystem. """ + + required_documents: Optional[List[RequiredDocument]] = None + """Only present for "KYB_BASIC" workflow. + + A list of documents required for the account holder to be approved. + """ diff --git a/src/lithic/types/account_holder_document.py b/src/lithic/types/account_holder_document.py deleted file mode 100644 index d703e7ea..00000000 --- a/src/lithic/types/account_holder_document.py +++ /dev/null @@ -1,52 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import List, Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["AccountHolderDocument", "RequiredDocumentUpload"] - - -class RequiredDocumentUpload(BaseModel): - image_type: Optional[Literal["back", "front"]] = None - """Type of image to upload.""" - - status: Optional[Literal["COMPLETED", "FAILED", "PENDING", "UPLOADED"]] = None - """Status of document image upload.""" - - status_reasons: Optional[ - List[ - Literal[ - "BACK_IMAGE_BLURRY", - "FILE_SIZE_TOO_LARGE", - "FRONT_IMAGE_BLURRY", - "FRONT_IMAGE_GLARE", - "INVALID_FILE_TYPE", - "UNKNOWN_ERROR", - ] - ] - ] = None - - upload_url: Optional[str] = None - """URL to upload document image to. - - Note that the upload URLs expire after 7 days. If an upload URL expires, you can - refresh the URLs by retrieving the document upload from - `GET /account_holders/{account_holder_token}/documents`. - """ - - -class AccountHolderDocument(BaseModel): - token: Optional[str] = None - """Globally unique identifier for the document.""" - - account_holder_token: Optional[str] = None - """Globally unique identifier for the account holder.""" - - document_type: Optional[ - Literal["commercial_license", "drivers_license", "passport", "passport_card", "visa"] - ] = None - """Type of documentation to be submitted for verification.""" - - required_document_uploads: Optional[List[RequiredDocumentUpload]] = None diff --git a/src/lithic/types/account_holder_list_documents_response.py b/src/lithic/types/account_holder_list_documents_response.py index 538f46b0..e94d7d58 100644 --- a/src/lithic/types/account_holder_list_documents_response.py +++ b/src/lithic/types/account_holder_list_documents_response.py @@ -1,12 +1,12 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from .._models import BaseModel -from .account_holder_document import AccountHolderDocument +from .shared.document import Document __all__ = ["AccountHolderListDocumentsResponse"] class AccountHolderListDocumentsResponse(BaseModel): - data: Optional[List[AccountHolderDocument]] = None + data: Optional[List[Document]] = None diff --git a/src/lithic/types/account_holder_list_params.py b/src/lithic/types/account_holder_list_params.py index ebd5b9e6..225eb5d6 100644 --- a/src/lithic/types/account_holder_list_params.py +++ b/src/lithic/types/account_holder_list_params.py @@ -1,13 +1,35 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing_extensions import TypedDict +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo __all__ = ["AccountHolderListParams"] class AccountHolderListParams(TypedDict, total=False): + begin: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created after the specified time will be included. UTC time zone. + """ + + email: str + """Email address of the account holder. + + The query must be an exact match, case insensitive. + """ + + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created before the specified time will be included. UTC time zone. + """ + ending_before: str """A cursor representing an item's token before which a page of results should end. @@ -17,9 +39,30 @@ class AccountHolderListParams(TypedDict, total=False): external_id: str """If applicable, represents the external_id associated with the account_holder.""" + first_name: str + """(Individual Account Holders only) The first name of the account holder. + + The query is case insensitive and supports partial matches. + """ + + last_name: str + """(Individual Account Holders only) The last name of the account holder. + + The query is case insensitive and supports partial matches. + """ + + legal_business_name: str + """(Business Account Holders only) The legal business name of the account holder. + + The query is case insensitive and supports partial matches. + """ + limit: int """The number of account_holders to limit the response to.""" + phone_number: str + """Phone number of the account holder. The query must be an exact match.""" + starting_after: str """A cursor representing an item's token after which a page of results should begin. diff --git a/src/lithic/types/account_holder_simulate_enrollment_document_review_params.py b/src/lithic/types/account_holder_simulate_enrollment_document_review_params.py new file mode 100644 index 00000000..3843d08f --- /dev/null +++ b/src/lithic/types/account_holder_simulate_enrollment_document_review_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["AccountHolderSimulateEnrollmentDocumentReviewParams"] + + +class AccountHolderSimulateEnrollmentDocumentReviewParams(TypedDict, total=False): + document_upload_token: Required[str] + """The account holder document upload which to perform the simulation upon.""" + + status: Required[Literal["UPLOADED", "ACCEPTED", "REJECTED", "PARTIAL_APPROVAL"]] + """An account holder document's upload status for use within the simulation.""" + + accepted_entity_status_reasons: List[str] + """A list of status reasons associated with a KYB account holder in PENDING_REVIEW""" + + status_reason: Literal[ + "DOCUMENT_MISSING_REQUIRED_DATA", + "DOCUMENT_UPLOAD_TOO_BLURRY", + "FILE_SIZE_TOO_LARGE", + "INVALID_DOCUMENT_TYPE", + "INVALID_DOCUMENT_UPLOAD", + "INVALID_ENTITY", + "DOCUMENT_EXPIRED", + "DOCUMENT_ISSUED_GREATER_THAN_30_DAYS", + "DOCUMENT_TYPE_NOT_SUPPORTED", + "UNKNOWN_FAILURE_REASON", + "UNKNOWN_ERROR", + ] + """Status reason that will be associated with the simulated account holder status. + + Only required for a `REJECTED` status or `PARTIAL_APPROVAL` status. + """ diff --git a/src/lithic/types/account_holder_simulate_enrollment_review_params.py b/src/lithic/types/account_holder_simulate_enrollment_review_params.py new file mode 100644 index 00000000..d098b2e0 --- /dev/null +++ b/src/lithic/types/account_holder_simulate_enrollment_review_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = ["AccountHolderSimulateEnrollmentReviewParams"] + + +class AccountHolderSimulateEnrollmentReviewParams(TypedDict, total=False): + account_holder_token: str + """The account holder which to perform the simulation upon.""" + + status: Literal["ACCEPTED", "REJECTED"] + """An account holder's status for use within the simulation.""" + + status_reasons: List[ + Literal[ + "PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ADDRESS_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_NAME_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_BUSINESS_OFFICERS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_SOS_FILING_INACTIVE", + "PRIMARY_BUSINESS_ENTITY_SOS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_CMRA_FAILURE", + "PRIMARY_BUSINESS_ENTITY_WATCHLIST_FAILURE", + "PRIMARY_BUSINESS_ENTITY_REGISTERED_AGENT_FAILURE", + "CONTROL_PERSON_BLOCKLIST_ALERT_FAILURE", + "CONTROL_PERSON_ID_VERIFICATION_FAILURE", + "CONTROL_PERSON_DOB_VERIFICATION_FAILURE", + "CONTROL_PERSON_NAME_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_DOB_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_BLOCKLIST_ALERT_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_ID_VERIFICATION_FAILURE", + "BENEFICIAL_OWNER_INDIVIDUAL_NAME_VERIFICATION_FAILURE", + ] + ] + """Status reason that will be associated with the simulated account holder status. + + Only required for a `REJECTED` status. + """ diff --git a/src/lithic/types/account_holder_simulate_enrollment_review_response.py b/src/lithic/types/account_holder_simulate_enrollment_review_response.py new file mode 100644 index 00000000..ae15bc3d --- /dev/null +++ b/src/lithic/types/account_holder_simulate_enrollment_review_response.py @@ -0,0 +1,401 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .required_document import RequiredDocument +from .kyb_business_entity import KYBBusinessEntity + +__all__ = [ + "AccountHolderSimulateEnrollmentReviewResponse", + "BeneficialOwnerIndividual", + "BeneficialOwnerIndividualAddress", + "ControlPerson", + "ControlPersonAddress", + "Individual", + "IndividualAddress", + "VerificationApplication", +] + + +class BeneficialOwnerIndividualAddress(BaseModel): + address1: str + """Valid deliverable address (no PO boxes).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ + + address2: Optional[str] = None + """Unit or apartment number (if applicable).""" + + +class BeneficialOwnerIndividual(BaseModel): + address: Optional[BeneficialOwnerIndividualAddress] = None + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: Optional[str] = None + """Individual's date of birth, as an RFC 3339 date.""" + + email: Optional[str] = None + """Individual's email address. + + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. + """ + + first_name: Optional[str] = None + """Individual's first name, as it appears on government-issued identity documents.""" + + last_name: Optional[str] = None + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: Optional[str] = None + """Individual's phone number, entered in E.164 format.""" + + +class ControlPersonAddress(BaseModel): + address1: str + """Valid deliverable address (no PO boxes).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ + + address2: Optional[str] = None + """Unit or apartment number (if applicable).""" + + +class ControlPerson(BaseModel): + address: Optional[ControlPersonAddress] = None + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: Optional[str] = None + """Individual's date of birth, as an RFC 3339 date.""" + + email: Optional[str] = None + """Individual's email address. + + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. + """ + + first_name: Optional[str] = None + """Individual's first name, as it appears on government-issued identity documents.""" + + last_name: Optional[str] = None + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: Optional[str] = None + """Individual's phone number, entered in E.164 format.""" + + +class IndividualAddress(BaseModel): + address1: str + """Valid deliverable address (no PO boxes).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ + + address2: Optional[str] = None + """Unit or apartment number (if applicable).""" + + +class Individual(BaseModel): + address: Optional[IndividualAddress] = None + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: Optional[str] = None + """Individual's date of birth, as an RFC 3339 date.""" + + email: Optional[str] = None + """Individual's email address. + + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. + """ + + first_name: Optional[str] = None + """Individual's first name, as it appears on government-issued identity documents.""" + + last_name: Optional[str] = None + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: Optional[str] = None + """Individual's phone number, entered in E.164 format.""" + + +class VerificationApplication(BaseModel): + created: datetime + """Timestamp of when the application was created.""" + + status: Literal["ACCEPTED", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"] + """KYC and KYB evaluation states. + + Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for the + `ADVANCED` workflow. + """ + + status_reasons: List[ + Literal[ + "ADDRESS_VERIFICATION_FAILURE", + "AGE_THRESHOLD_FAILURE", + "COMPLETE_VERIFICATION_FAILURE", + "DOB_VERIFICATION_FAILURE", + "ID_VERIFICATION_FAILURE", + "MAX_DOCUMENT_ATTEMPTS", + "MAX_RESUBMISSION_ATTEMPTS", + "NAME_VERIFICATION_FAILURE", + "OTHER_VERIFICATION_FAILURE", + "RISK_THRESHOLD_FAILURE", + "WATCHLIST_ALERT_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ADDRESS_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_NAME_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_BUSINESS_OFFICERS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_SOS_FILING_INACTIVE", + "PRIMARY_BUSINESS_ENTITY_SOS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_CMRA_FAILURE", + "PRIMARY_BUSINESS_ENTITY_WATCHLIST_FAILURE", + "PRIMARY_BUSINESS_ENTITY_REGISTERED_AGENT_FAILURE", + "CONTROL_PERSON_BLOCKLIST_ALERT_FAILURE", + "CONTROL_PERSON_ID_VERIFICATION_FAILURE", + "CONTROL_PERSON_DOB_VERIFICATION_FAILURE", + "CONTROL_PERSON_NAME_VERIFICATION_FAILURE", + ] + ] + """Reason for the evaluation status.""" + + updated: datetime + """Timestamp of when the application was last updated.""" + + +class AccountHolderSimulateEnrollmentReviewResponse(BaseModel): + token: Optional[str] = None + """Globally unique identifier for the account holder.""" + + account_token: Optional[str] = None + """Globally unique identifier for the account.""" + + beneficial_owner_entities: Optional[List[KYBBusinessEntity]] = None + """Only present when user_type == "BUSINESS". + + List of all entities with >25% ownership in the company. + """ + + beneficial_owner_individuals: Optional[List[BeneficialOwnerIndividual]] = None + """Only present when user_type == "BUSINESS". + + List of all individuals with >25% ownership in the company. + """ + + business_account_token: Optional[str] = None + """ + Only applicable for customers using the KYC-Exempt workflow to enroll authorized + users of businesses. Pass the account_token of the enrolled business associated + with the AUTHORIZED_USER in this field. + """ + + business_entity: Optional[KYBBusinessEntity] = None + """Only present when user_type == "BUSINESS". + + Information about the business for which the account is being opened and KYB is + being run. + """ + + control_person: Optional[ControlPerson] = None + """Only present when user_type == "BUSINESS". + + An individual with significant responsibility for managing the legal entity + (e.g., a Chief Executive Officer, Chief Financial Officer, Chief Operating + Officer, + + Managing Member, General Partner, President, Vice President, or Treasurer). This + can be an executive, or someone who will have program-wide access + + to the cards that Lithic will provide. In some cases, this individual could also + be a beneficial owner listed above. + """ + + created: Optional[datetime] = None + """Timestamp of when the account holder was created.""" + + email: Optional[str] = None + """ + < Deprecated. Use control_person.email when user_type == "BUSINESS". Use + individual.phone_number when user_type == "INDIVIDUAL". + + > Primary email of Account Holder. + """ + + exemption_type: Optional[Literal["AUTHORIZED_USER", "PREPAID_CARD_USER"]] = None + """The type of KYC exemption for a KYC-Exempt Account Holder. + + "None" if the account holder is not KYC-Exempt. + """ + + external_id: Optional[str] = None + """ + Customer-provided token that indicates a relationship with an object outside of + the Lithic ecosystem. + """ + + individual: Optional[Individual] = None + """Only present when user_type == "INDIVIDUAL". + + Information about the individual for which the account is being opened and KYC + is being run. + """ + + nature_of_business: Optional[str] = None + """Only present when user_type == "BUSINESS". + + User-submitted description of the business. + """ + + phone_number: Optional[str] = None + """ + < Deprecated. Use control_person.phone_number when user_type == "BUSINESS". Use + individual.phone_number when user_type == "INDIVIDUAL". + + > Primary phone of Account Holder, entered in E.164 format. + """ + + required_documents: Optional[List[RequiredDocument]] = None + """Only present for "KYB_BASIC" and "KYC_ADVANCED" workflows. + + A list of documents required for the account holder to be approved. + """ + + status: Optional[Literal["ACCEPTED", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"]] = None + """ + + KYC and KYB evaluation states. + + Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for the + `ADVANCED` workflow. + """ + + status_reasons: Optional[ + List[ + Literal[ + "ADDRESS_VERIFICATION_FAILURE", + "AGE_THRESHOLD_FAILURE", + "COMPLETE_VERIFICATION_FAILURE", + "DOB_VERIFICATION_FAILURE", + "ID_VERIFICATION_FAILURE", + "MAX_DOCUMENT_ATTEMPTS", + "MAX_RESUBMISSION_ATTEMPTS", + "NAME_VERIFICATION_FAILURE", + "OTHER_VERIFICATION_FAILURE", + "RISK_THRESHOLD_FAILURE", + "WATCHLIST_ALERT_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ADDRESS_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_NAME_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_BUSINESS_OFFICERS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_SOS_FILING_INACTIVE", + "PRIMARY_BUSINESS_ENTITY_SOS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_CMRA_FAILURE", + "PRIMARY_BUSINESS_ENTITY_WATCHLIST_FAILURE", + "PRIMARY_BUSINESS_ENTITY_REGISTERED_AGENT_FAILURE", + "CONTROL_PERSON_BLOCKLIST_ALERT_FAILURE", + "CONTROL_PERSON_ID_VERIFICATION_FAILURE", + "CONTROL_PERSON_DOB_VERIFICATION_FAILURE", + "CONTROL_PERSON_NAME_VERIFICATION_FAILURE", + ] + ] + ] = None + """ Reason for the evaluation status. + """ + + user_type: Optional[Literal["BUSINESS", "INDIVIDUAL"]] = None + """The type of Account Holder. + + If the type is "INDIVIDUAL", the "individual" attribute will be present. + + If the type is "BUSINESS" then the "business_entity", "control_person", + "beneficial_owner_individuals", "beneficial_owner_entities", + + "nature_of_business", and "website_url" attributes will be present. + """ + + verification_application: Optional[VerificationApplication] = None + """Information about the most recent identity verification attempt""" + + website_url: Optional[str] = None + """Only present when user_type == "BUSINESS". Business's primary website.""" diff --git a/src/lithic/types/account_holder_update_params.py b/src/lithic/types/account_holder_update_params.py index cfe22fc2..62453efd 100644 --- a/src/lithic/types/account_holder_update_params.py +++ b/src/lithic/types/account_holder_update_params.py @@ -1,30 +1,316 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing_extensions import TypedDict +from typing import List, Union, Iterable +from typing_extensions import Required, TypeAlias, TypedDict -__all__ = ["AccountHolderUpdateParams"] +from .address_update_param import AddressUpdateParam +__all__ = [ + "AccountHolderUpdateParams", + "KYBPatchRequest", + "KYBPatchRequestBeneficialOwnerEntity", + "KYBPatchRequestBeneficialOwnerIndividual", + "KYBPatchRequestBusinessEntity", + "KYBPatchRequestControlPerson", + "KYCPatchRequest", + "KYCPatchRequestIndividual", + "PatchRequest", +] -class AccountHolderUpdateParams(TypedDict, total=False): - business_account_token: str + +class KYBPatchRequest(TypedDict, total=False): + beneficial_owner_entities: Iterable[KYBPatchRequestBeneficialOwnerEntity] + """List of all entities with >25% ownership in the company. + + If no entity or individual owns >25% of the company, and the largest shareholder + is an entity, please identify them in this field. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf)(Section + I) for more background. If no business owner is an entity, pass in an empty + list. However, either this parameter or `beneficial_owner_individuals` must be + populated. on entities that should be included. + """ + + beneficial_owner_individuals: Iterable[KYBPatchRequestBeneficialOwnerIndividual] + """List of all individuals with >25% ownership in the company. + + If no entity or individual owns >25% of the company, and the largest shareholder + is an individual, please identify them in this field. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf)(Section + I) for more background on individuals that should be included. If no individual + is an entity, pass in an empty list. However, either this parameter or + `beneficial_owner_entities` must be populated. + """ + + business_entity: KYBPatchRequestBusinessEntity + """ + Information for business for which the account is being opened and KYB is being + run. + """ + + control_person: KYBPatchRequestControlPerson + """ + An individual with significant responsibility for managing the legal entity + (e.g., a Chief Executive Officer, Chief Financial Officer, Chief Operating + Officer, Managing Member, General Partner, President, Vice President, or + Treasurer). This can be an executive, or someone who will have program-wide + access to the cards that Lithic will provide. In some cases, this individual + could also be a beneficial owner listed above. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) + (Section II) for more background. + """ + + external_id: str + """ + A user provided id that can be used to link an account holder with an external + system + """ + + nature_of_business: str + """ + Short description of the company's line of business (i.e., what does the company + do?). + """ + + website_url: str + """Company website URL.""" + + +class KYBPatchRequestBeneficialOwnerEntity(TypedDict, total=False): + entity_token: Required[str] + """Globally unique identifier for an entity.""" + + address: AddressUpdateParam + """ + Business''s physical address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + """ + + dba_business_name: str + """ + Any name that the business operates under that is not its legal business name + (if applicable). + """ + + government_id: str + """Government-issued identification number. + + US Federal Employer Identification Numbers (EIN) are currently supported, + entered as full nine-digits, with or without hyphens. """ - Only applicable for customers using the KYC-Exempt workflow to enroll authorized - users of businesses. Pass the account_token of the enrolled business associated - with the AUTHORIZED_USER in this field. + + legal_business_name: str + """Legal (formal) business name.""" + + parent_company: str + """Parent company name (if applicable).""" + + phone_numbers: List[str] + """ + One or more of the business's phone number(s), entered as a list in E.164 + format. """ + +class KYBPatchRequestBeneficialOwnerIndividual(TypedDict, total=False): + entity_token: Required[str] + """Globally unique identifier for an entity.""" + + address: AddressUpdateParam + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: str + """Individual's date of birth, as an RFC 3339 date.""" + email: str - """Account holder's email address. + """Individual's email address. - The primary purpose of this field is for cardholder identification and - verification during the digital wallet tokenization process. + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. """ + first_name: str + """Individual's first name, as it appears on government-issued identity documents.""" + + government_id: str + """ + Government-issued identification number (required for identity verification and + compliance with banking regulations). Social Security Numbers (SSN) and + Individual Taxpayer Identification Numbers (ITIN) are currently supported, + entered as full nine-digits, with or without hyphens + """ + + last_name: str + """Individual's last name, as it appears on government-issued identity documents.""" + phone_number: str - """Account holder's phone number, entered in E.164 format. + """Individual's phone number, entered in E.164 format.""" - The primary purpose of this field is for cardholder identification and - verification during the digital wallet tokenization process. + +class KYBPatchRequestBusinessEntity(TypedDict, total=False): + entity_token: Required[str] + """Globally unique identifier for an entity.""" + + address: AddressUpdateParam + """ + Business''s physical address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + """ + + dba_business_name: str + """ + Any name that the business operates under that is not its legal business name + (if applicable). """ + + government_id: str + """Government-issued identification number. + + US Federal Employer Identification Numbers (EIN) are currently supported, + entered as full nine-digits, with or without hyphens. + """ + + legal_business_name: str + """Legal (formal) business name.""" + + parent_company: str + """Parent company name (if applicable).""" + + phone_numbers: List[str] + """ + One or more of the business's phone number(s), entered as a list in E.164 + format. + """ + + +class KYBPatchRequestControlPerson(TypedDict, total=False): + entity_token: Required[str] + """Globally unique identifier for an entity.""" + + address: AddressUpdateParam + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: str + """Individual's date of birth, as an RFC 3339 date.""" + + email: str + """Individual's email address. + + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. + """ + + first_name: str + """Individual's first name, as it appears on government-issued identity documents.""" + + government_id: str + """ + Government-issued identification number (required for identity verification and + compliance with banking regulations). Social Security Numbers (SSN) and + Individual Taxpayer Identification Numbers (ITIN) are currently supported, + entered as full nine-digits, with or without hyphens + """ + + last_name: str + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: str + """Individual's phone number, entered in E.164 format.""" + + +class KYCPatchRequest(TypedDict, total=False): + external_id: str + """ + A user provided id that can be used to link an account holder with an external + system + """ + + individual: KYCPatchRequestIndividual + """ + Information on the individual for whom the account is being opened and KYC is + being run. + """ + + +class KYCPatchRequestIndividual(TypedDict, total=False): + entity_token: Required[str] + """Globally unique identifier for an entity.""" + + address: AddressUpdateParam + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: str + """Individual's date of birth, as an RFC 3339 date.""" + + email: str + """Individual's email address. + + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. + """ + + first_name: str + """Individual's first name, as it appears on government-issued identity documents.""" + + government_id: str + """ + Government-issued identification number (required for identity verification and + compliance with banking regulations). Social Security Numbers (SSN) and + Individual Taxpayer Identification Numbers (ITIN) are currently supported, + entered as full nine-digits, with or without hyphens + """ + + last_name: str + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: str + """Individual's phone number, entered in E.164 format.""" + + +class PatchRequest(TypedDict, total=False): + address: AddressUpdateParam + """Allowed for: KYC-Exempt, BYO-KYC, BYO-KYB.""" + + business_account_token: str + """Allowed for: KYC-Exempt, BYO-KYC. + + The token of the business account to which the account holder is associated. + """ + + email: str + """Allowed for all Account Holders. + + Account holder's email address. The primary purpose of this field is for + cardholder identification and verification during the digital wallet + tokenization process. + """ + + first_name: str + """Allowed for KYC-Exempt, BYO-KYC. Account holder's first name.""" + + last_name: str + """Allowed for KYC-Exempt, BYO-KYC. Account holder's last name.""" + + legal_business_name: str + """Allowed for BYO-KYB. Legal business name of the account holder.""" + + phone_number: str + """Allowed for all Account Holders. + + Account holder's phone number, entered in E.164 format. The primary purpose of + this field is for cardholder identification and verification during the digital + wallet tokenization process. + """ + + +AccountHolderUpdateParams: TypeAlias = Union[KYBPatchRequest, KYCPatchRequest, PatchRequest] diff --git a/src/lithic/types/account_holder_update_response.py b/src/lithic/types/account_holder_update_response.py index c443f0c6..2c0bddfb 100644 --- a/src/lithic/types/account_holder_update_response.py +++ b/src/lithic/types/account_holder_update_response.py @@ -1,25 +1,465 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, TypeAlias from .._models import BaseModel +from .required_document import RequiredDocument +from .kyb_business_entity import KYBBusinessEntity -__all__ = ["AccountHolderUpdateResponse"] +__all__ = [ + "AccountHolderUpdateResponse", + "KYBKYCPatchResponse", + "KybkycPatchResponseBeneficialOwnerIndividual", + "KybkycPatchResponseBeneficialOwnerIndividualAddress", + "KYBKYCPatchResponseControlPerson", + "KYBKYCPatchResponseControlPersonAddress", + "KYBKYCPatchResponseIndividual", + "KYBKYCPatchResponseIndividualAddress", + "KYBKYCPatchResponseVerificationApplication", + "PatchResponse", + "PatchResponseAddress", +] -class AccountHolderUpdateResponse(BaseModel): +class KybkycPatchResponseBeneficialOwnerIndividualAddress(BaseModel): + address1: str + """Valid deliverable address (no PO boxes).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ + + address2: Optional[str] = None + """Unit or apartment number (if applicable).""" + + +class KybkycPatchResponseBeneficialOwnerIndividual(BaseModel): + address: Optional[KybkycPatchResponseBeneficialOwnerIndividualAddress] = None + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: Optional[str] = None + """Individual's date of birth, as an RFC 3339 date.""" + + email: Optional[str] = None + """Individual's email address. + + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. + """ + + first_name: Optional[str] = None + """Individual's first name, as it appears on government-issued identity documents.""" + + last_name: Optional[str] = None + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: Optional[str] = None + """Individual's phone number, entered in E.164 format.""" + + +class KYBKYCPatchResponseControlPersonAddress(BaseModel): + address1: str + """Valid deliverable address (no PO boxes).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ + + address2: Optional[str] = None + """Unit or apartment number (if applicable).""" + + +class KYBKYCPatchResponseControlPerson(BaseModel): + address: Optional[KYBKYCPatchResponseControlPersonAddress] = None + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: Optional[str] = None + """Individual's date of birth, as an RFC 3339 date.""" + + email: Optional[str] = None + """Individual's email address. + + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. + """ + + first_name: Optional[str] = None + """Individual's first name, as it appears on government-issued identity documents.""" + + last_name: Optional[str] = None + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: Optional[str] = None + """Individual's phone number, entered in E.164 format.""" + + +class KYBKYCPatchResponseIndividualAddress(BaseModel): + address1: str + """Valid deliverable address (no PO boxes).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ + + address2: Optional[str] = None + """Unit or apartment number (if applicable).""" + + +class KYBKYCPatchResponseIndividual(BaseModel): + address: Optional[KYBKYCPatchResponseIndividualAddress] = None + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: Optional[str] = None + """Individual's date of birth, as an RFC 3339 date.""" + + email: Optional[str] = None + """Individual's email address. + + If utilizing Lithic for chargeback processing, this customer email address may + be used to communicate dispute status and resolution. + """ + + first_name: Optional[str] = None + """Individual's first name, as it appears on government-issued identity documents.""" + + last_name: Optional[str] = None + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: Optional[str] = None + """Individual's phone number, entered in E.164 format.""" + + +class KYBKYCPatchResponseVerificationApplication(BaseModel): + created: datetime + """Timestamp of when the application was created.""" + + status: Literal["ACCEPTED", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"] + """KYC and KYB evaluation states. + + Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for the + `ADVANCED` workflow. + """ + + status_reasons: List[ + Literal[ + "ADDRESS_VERIFICATION_FAILURE", + "AGE_THRESHOLD_FAILURE", + "COMPLETE_VERIFICATION_FAILURE", + "DOB_VERIFICATION_FAILURE", + "ID_VERIFICATION_FAILURE", + "MAX_DOCUMENT_ATTEMPTS", + "MAX_RESUBMISSION_ATTEMPTS", + "NAME_VERIFICATION_FAILURE", + "OTHER_VERIFICATION_FAILURE", + "RISK_THRESHOLD_FAILURE", + "WATCHLIST_ALERT_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ADDRESS_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_NAME_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_BUSINESS_OFFICERS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_SOS_FILING_INACTIVE", + "PRIMARY_BUSINESS_ENTITY_SOS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_CMRA_FAILURE", + "PRIMARY_BUSINESS_ENTITY_WATCHLIST_FAILURE", + "PRIMARY_BUSINESS_ENTITY_REGISTERED_AGENT_FAILURE", + "CONTROL_PERSON_BLOCKLIST_ALERT_FAILURE", + "CONTROL_PERSON_ID_VERIFICATION_FAILURE", + "CONTROL_PERSON_DOB_VERIFICATION_FAILURE", + "CONTROL_PERSON_NAME_VERIFICATION_FAILURE", + ] + ] + """Reason for the evaluation status.""" + + updated: datetime + """Timestamp of when the application was last updated.""" + + +class KYBKYCPatchResponse(BaseModel): token: Optional[str] = None - """The token for the account holder that was updated""" + """Globally unique identifier for the account holder.""" + + account_token: Optional[str] = None + """Globally unique identifier for the account.""" + + beneficial_owner_entities: Optional[List[KYBBusinessEntity]] = None + """Only present when user_type == "BUSINESS". + + List of all entities with >25% ownership in the company. + """ + + beneficial_owner_individuals: Optional[List[KybkycPatchResponseBeneficialOwnerIndividual]] = None + """Only present when user_type == "BUSINESS". + + List of all individuals with >25% ownership in the company. + """ business_account_token: Optional[str] = None """ - Only applicable for customers using the KYC-Exempt workflow to enroll businesses - with authorized users. Pass the account_token of the enrolled business - associated with the AUTHORIZED_USER in this field. + Only applicable for customers using the KYC-Exempt workflow to enroll authorized + users of businesses. Pass the account_token of the enrolled business associated + with the AUTHORIZED_USER in this field. """ + business_entity: Optional[KYBBusinessEntity] = None + """Only present when user_type == "BUSINESS". + + Information about the business for which the account is being opened and KYB is + being run. + """ + + control_person: Optional[KYBKYCPatchResponseControlPerson] = None + """Only present when user_type == "BUSINESS". + + An individual with significant responsibility for managing the legal entity + (e.g., a Chief Executive Officer, Chief Financial Officer, Chief Operating + Officer, + + Managing Member, General Partner, President, Vice President, or Treasurer). This + can be an executive, or someone who will have program-wide access + + to the cards that Lithic will provide. In some cases, this individual could also + be a beneficial owner listed above. + """ + + created: Optional[datetime] = None + """Timestamp of when the account holder was created.""" + email: Optional[str] = None - """The newly updated email for the account holder""" + """ + < Deprecated. Use control_person.email when user_type == "BUSINESS". Use + individual.phone_number when user_type == "INDIVIDUAL". + + > Primary email of Account Holder. + """ + + exemption_type: Optional[Literal["AUTHORIZED_USER", "PREPAID_CARD_USER"]] = None + """The type of KYC exemption for a KYC-Exempt Account Holder. + + "None" if the account holder is not KYC-Exempt. + """ + + external_id: Optional[str] = None + """ + Customer-provided token that indicates a relationship with an object outside of + the Lithic ecosystem. + """ + + individual: Optional[KYBKYCPatchResponseIndividual] = None + """Only present when user_type == "INDIVIDUAL". + + Information about the individual for which the account is being opened and KYC + is being run. + """ + + nature_of_business: Optional[str] = None + """Only present when user_type == "BUSINESS". + + User-submitted description of the business. + """ phone_number: Optional[str] = None - """The newly updated phone_number for the account holder""" + """ + < Deprecated. Use control_person.phone_number when user_type == "BUSINESS". Use + individual.phone_number when user_type == "INDIVIDUAL". + + > Primary phone of Account Holder, entered in E.164 format. + """ + + required_documents: Optional[List[RequiredDocument]] = None + """Only present for "KYB_BASIC" and "KYC_ADVANCED" workflows. + + A list of documents required for the account holder to be approved. + """ + + status: Optional[Literal["ACCEPTED", "PENDING_DOCUMENT", "PENDING_RESUBMIT", "REJECTED"]] = None + """ + + KYC and KYB evaluation states. + + Note: `PENDING_RESUBMIT` and `PENDING_DOCUMENT` are only applicable for the + `ADVANCED` workflow. + """ + + status_reasons: Optional[ + List[ + Literal[ + "ADDRESS_VERIFICATION_FAILURE", + "AGE_THRESHOLD_FAILURE", + "COMPLETE_VERIFICATION_FAILURE", + "DOB_VERIFICATION_FAILURE", + "ID_VERIFICATION_FAILURE", + "MAX_DOCUMENT_ATTEMPTS", + "MAX_RESUBMISSION_ATTEMPTS", + "NAME_VERIFICATION_FAILURE", + "OTHER_VERIFICATION_FAILURE", + "RISK_THRESHOLD_FAILURE", + "WATCHLIST_ALERT_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_ADDRESS_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_NAME_VERIFICATION_FAILURE", + "PRIMARY_BUSINESS_ENTITY_BUSINESS_OFFICERS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_SOS_FILING_INACTIVE", + "PRIMARY_BUSINESS_ENTITY_SOS_NOT_MATCHED", + "PRIMARY_BUSINESS_ENTITY_CMRA_FAILURE", + "PRIMARY_BUSINESS_ENTITY_WATCHLIST_FAILURE", + "PRIMARY_BUSINESS_ENTITY_REGISTERED_AGENT_FAILURE", + "CONTROL_PERSON_BLOCKLIST_ALERT_FAILURE", + "CONTROL_PERSON_ID_VERIFICATION_FAILURE", + "CONTROL_PERSON_DOB_VERIFICATION_FAILURE", + "CONTROL_PERSON_NAME_VERIFICATION_FAILURE", + ] + ] + ] = None + """ Reason for the evaluation status. + """ + + user_type: Optional[Literal["BUSINESS", "INDIVIDUAL"]] = None + """The type of Account Holder. + + If the type is "INDIVIDUAL", the "individual" attribute will be present. + + If the type is "BUSINESS" then the "business_entity", "control_person", + "beneficial_owner_individuals", "beneficial_owner_entities", + + "nature_of_business", and "website_url" attributes will be present. + """ + + verification_application: Optional[KYBKYCPatchResponseVerificationApplication] = None + """Information about the most recent identity verification attempt""" + + website_url: Optional[str] = None + """Only present when user_type == "BUSINESS". Business's primary website.""" + + +class PatchResponseAddress(BaseModel): + address1: str + """Valid deliverable address (no PO boxes).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ + + address2: Optional[str] = None + """Unit or apartment number (if applicable).""" + + +class PatchResponse(BaseModel): + token: Optional[str] = None + """The token for the account holder that was updated""" + + address: Optional[PatchResponseAddress] = None + """The address for the account holder""" + + business_account_token: Optional[str] = None + """The token for the business account that the account holder is associated with""" + + email: Optional[str] = None + """The email for the account holder""" + + first_name: Optional[str] = None + """The first name for the account holder""" + + last_name: Optional[str] = None + """The last name for the account holder""" + + legal_business_name: Optional[str] = None + """The legal business name for the account holder""" + + phone_number: Optional[str] = None + """The phone_number for the account holder""" + + +AccountHolderUpdateResponse: TypeAlias = Union[KYBKYCPatchResponse, PatchResponse] diff --git a/src/lithic/types/account_holder_upload_document_params.py b/src/lithic/types/account_holder_upload_document_params.py index 380e8804..21c30873 100644 --- a/src/lithic/types/account_holder_upload_document_params.py +++ b/src/lithic/types/account_holder_upload_document_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -8,5 +8,30 @@ class AccountHolderUploadDocumentParams(TypedDict, total=False): - document_type: Required[Literal["commercial_license", "drivers_license", "passport", "passport_card", "visa"]] - """Type of the document to upload.""" + document_type: Required[ + Literal[ + "EIN_LETTER", + "TAX_RETURN", + "OPERATING_AGREEMENT", + "CERTIFICATE_OF_FORMATION", + "DRIVERS_LICENSE", + "PASSPORT", + "PASSPORT_CARD", + "CERTIFICATE_OF_GOOD_STANDING", + "ARTICLES_OF_INCORPORATION", + "ARTICLES_OF_ORGANIZATION", + "BYLAWS", + "GOVERNMENT_BUSINESS_LICENSE", + "PARTNERSHIP_AGREEMENT", + "SS4_FORM", + "BANK_STATEMENT", + "UTILITY_BILL_STATEMENT", + "SSN_CARD", + "ITIN_LETTER", + "FINCEN_BOI_REPORT", + ] + ] + """The type of document to upload""" + + entity_token: Required[str] + """Globally unique identifier for the entity.""" diff --git a/src/lithic/types/account_list_params.py b/src/lithic/types/account_list_params.py index ff8f3f68..44e75c2c 100644 --- a/src/lithic/types/account_list_params.py +++ b/src/lithic/types/account_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/account_spend_limits.py b/src/lithic/types/account_spend_limits.py index 0e74a8f5..8635e927 100644 --- a/src/lithic/types/account_spend_limits.py +++ b/src/lithic/types/account_spend_limits.py @@ -1,30 +1,66 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from .._models import BaseModel -__all__ = ["AccountSpendLimits", "AvailableSpendLimit"] +__all__ = ["AccountSpendLimits", "AvailableSpendLimit", "SpendLimit", "SpendVelocity"] class AvailableSpendLimit(BaseModel): daily: Optional[int] = None """ - The available spend limit relative to the daily limit configured on the Account. + The available spend limit (in cents) relative to the daily limit configured on + the Account (e.g. 100000 would be a $1,000 limit). """ lifetime: Optional[int] = None """ - The available spend limit relative to the lifetime limit configured on the - Account. + The available spend limit (in cents) relative to the lifetime limit configured + on the Account. """ monthly: Optional[int] = None """ - The available spend limit relative to the monthly limit configured on the - Account. + The available spend limit (in cents) relative to the monthly limit configured on + the Account. + """ + + +class SpendLimit(BaseModel): + daily: Optional[int] = None + """The configured daily spend limit (in cents) on the Account.""" + + lifetime: Optional[int] = None + """The configured lifetime spend limit (in cents) on the Account.""" + + monthly: Optional[int] = None + """The configured monthly spend limit (in cents) on the Account.""" + + +class SpendVelocity(BaseModel): + daily: Optional[int] = None + """Current daily spend velocity (in cents) on the Account. + + Present if daily spend limit is set. + """ + + lifetime: Optional[int] = None + """Current lifetime spend velocity (in cents) on the Account. + + Present if lifetime spend limit is set. + """ + + monthly: Optional[int] = None + """Current monthly spend velocity (in cents) on the Account. + + Present if monthly spend limit is set. """ class AccountSpendLimits(BaseModel): available_spend_limit: AvailableSpendLimit + + spend_limit: Optional[SpendLimit] = None + + spend_velocity: Optional[SpendVelocity] = None diff --git a/src/lithic/types/account_update_params.py b/src/lithic/types/account_update_params.py index 3beb9101..ed9e95f8 100644 --- a/src/lithic/types/account_update_params.py +++ b/src/lithic/types/account_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,26 +9,28 @@ class AccountUpdateParams(TypedDict, total=False): daily_spend_limit: int - """ - Amount (in cents) for the account's daily spend limit. By default the daily - spend limit is set to $1,250. + """Amount (in cents) for the account's daily spend limit (e.g. + + 100000 would be a $1,000 limit). By default the daily spend limit is set to + $1,250. """ lifetime_spend_limit: int - """Amount (in cents) for the account's lifetime spend limit. - - Once this limit is reached, no transactions will be accepted on any card created - for this account until the limit is updated. Note that a spend limit of 0 is - effectively no limit, and should only be used to reset or remove a prior limit. - Only a limit of 1 or above will result in declined transactions due to checks - against the account limit. This behavior differs from the daily spend limit and - the monthly spend limit. + """Amount (in cents) for the account's lifetime spend limit (e.g. + + 100000 would be a $1,000 limit). Once this limit is reached, no transactions + will be accepted on any card created for this account until the limit is + updated. Note that a spend limit of 0 is effectively no limit, and should only + be used to reset or remove a prior limit. Only a limit of 1 or above will result + in declined transactions due to checks against the account limit. This behavior + differs from the daily spend limit and the monthly spend limit. """ monthly_spend_limit: int - """ - Amount (in cents) for the account's monthly spend limit. By default the monthly - spend limit is set to $5,000. + """Amount (in cents) for the account's monthly spend limit (e.g. + + 100000 would be a $1,000 limit). By default the monthly spend limit is set to + $5,000. """ state: Literal["ACTIVE", "PAUSED"] @@ -37,7 +39,9 @@ class AccountUpdateParams(TypedDict, total=False): verification_address: VerificationAddress """ Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. + transactions if enabled via Auth Rules. This field is deprecated as AVS checks + are no longer supported by Authorization Rules. The field will be removed from + the schema in a future release. """ diff --git a/src/lithic/types/accounts/__init__.py b/src/lithic/types/accounts/__init__.py deleted file mode 100644 index 0eb7dec1..00000000 --- a/src/lithic/types/accounts/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from .credit_configuration_update_params import CreditConfigurationUpdateParams as CreditConfigurationUpdateParams diff --git a/src/lithic/types/accounts/credit_configuration_update_params.py b/src/lithic/types/accounts/credit_configuration_update_params.py deleted file mode 100644 index f5d74b47..00000000 --- a/src/lithic/types/accounts/credit_configuration_update_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["CreditConfigurationUpdateParams"] - - -class CreditConfigurationUpdateParams(TypedDict, total=False): - billing_period: int - """Number of days within the billing period""" - - credit_limit: int - """Credit limit extended to the Business Account""" - - external_bank_account_token: str - """The external bank account token to use for auto-collections""" - - payment_period: int - """Number of days after the billing period ends that a payment is required""" diff --git a/src/lithic/types/address_update_param.py b/src/lithic/types/address_update_param.py new file mode 100644 index 00000000..221e9050 --- /dev/null +++ b/src/lithic/types/address_update_param.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AddressUpdateParam"] + + +class AddressUpdateParam(TypedDict, total=False): + address1: str + """Valid deliverable address (no PO boxes).""" + + address2: str + """Unit or apartment number (if applicable).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ diff --git a/src/lithic/types/aggregate_balance.py b/src/lithic/types/aggregate_balance.py index 32829ace..2c12c10a 100644 --- a/src/lithic/types/aggregate_balance.py +++ b/src/lithic/types/aggregate_balance.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal @@ -16,7 +16,7 @@ class AggregateBalance(BaseModel): """Date and time for when the balance was first created.""" currency: str - """3-digit alphabetic ISO 4217 code for the local currency of the balance.""" + """3-character alphabetic ISO 4217 code for the local currency of the balance.""" financial_account_type: Literal["ISSUING", "OPERATING", "RESERVE"] """Type of financial account""" diff --git a/src/lithic/types/aggregate_balance_list_params.py b/src/lithic/types/aggregate_balance_list_params.py index c093df18..d098e821 100644 --- a/src/lithic/types/aggregate_balance_list_params.py +++ b/src/lithic/types/aggregate_balance_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/api_status.py b/src/lithic/types/api_status.py index 7c7a3750..180f1470 100644 --- a/src/lithic/types/api_status.py +++ b/src/lithic/types/api_status.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/auth_rule.py b/src/lithic/types/auth_rule.py deleted file mode 100644 index 2017f101..00000000 --- a/src/lithic/types/auth_rule.py +++ /dev/null @@ -1,54 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import List, Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["AuthRule"] - - -class AuthRule(BaseModel): - token: str - """Globally unique identifier.""" - - state: Literal["ACTIVE", "INACTIVE"] - """Indicates whether the Auth Rule is ACTIVE or INACTIVE""" - - account_tokens: Optional[List[str]] = None - """Array of account_token(s) identifying the accounts that the Auth Rule applies - to. - - Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - """ - - allowed_countries: Optional[List[str]] = None - """Countries in which the Auth Rule permits transactions. - - Note that Lithic maintains a list of countries in which all transactions are - blocked; "allowing" those countries in an Auth Rule does not override the - Lithic-wide restrictions. - """ - - allowed_mcc: Optional[List[str]] = None - """Merchant category codes for which the Auth Rule permits transactions.""" - - blocked_countries: Optional[List[str]] = None - """Countries in which the Auth Rule automatically declines transactions.""" - - blocked_mcc: Optional[List[str]] = None - """ - Merchant category codes for which the Auth Rule automatically declines - transactions. - """ - - card_tokens: Optional[List[str]] = None - """Array of card_token(s) identifying the cards that the Auth Rule applies to. - - Note that only this field or `account_tokens` can be provided for a given Auth - Rule. - """ - - program_level: Optional[bool] = None - """Boolean indicating whether the Auth Rule is applied at the program level.""" diff --git a/src/lithic/types/auth_rule_apply_params.py b/src/lithic/types/auth_rule_apply_params.py deleted file mode 100644 index 9cb97db5..00000000 --- a/src/lithic/types/auth_rule_apply_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import List -from typing_extensions import TypedDict - -__all__ = ["AuthRuleApplyParams"] - - -class AuthRuleApplyParams(TypedDict, total=False): - account_tokens: List[str] - """ - Array of account_token(s) identifying the accounts that the Auth Rule applies - to. Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - """ - - card_tokens: List[str] - """ - Array of card_token(s) identifying the cards that the Auth Rule applies to. Note - that only this field or `account_tokens` can be provided for a given Auth Rule. - """ - - program_level: bool - """Boolean indicating whether the Auth Rule is applied at the program level.""" diff --git a/src/lithic/types/auth_rule_create_params.py b/src/lithic/types/auth_rule_create_params.py deleted file mode 100644 index 308a1eab..00000000 --- a/src/lithic/types/auth_rule_create_params.py +++ /dev/null @@ -1,48 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import List -from typing_extensions import TypedDict - -__all__ = ["AuthRuleCreateParams"] - - -class AuthRuleCreateParams(TypedDict, total=False): - account_tokens: List[str] - """Array of account_token(s) identifying the accounts that the Auth Rule applies - to. - - Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - """ - - allowed_countries: List[str] - """Countries in which the Auth Rule permits transactions. - - Note that Lithic maintains a list of countries in which all transactions are - blocked; "allowing" those countries in an Auth Rule does not override the - Lithic-wide restrictions. - """ - - allowed_mcc: List[str] - """Merchant category codes for which the Auth Rule permits transactions.""" - - blocked_countries: List[str] - """Countries in which the Auth Rule automatically declines transactions.""" - - blocked_mcc: List[str] - """ - Merchant category codes for which the Auth Rule automatically declines - transactions. - """ - - card_tokens: List[str] - """Array of card_token(s) identifying the cards that the Auth Rule applies to. - - Note that only this field or `account_tokens` can be provided for a given Auth - Rule. - """ - - program_level: bool - """Boolean indicating whether the Auth Rule is applied at the program level.""" diff --git a/src/lithic/types/auth_rule_remove_params.py b/src/lithic/types/auth_rule_remove_params.py deleted file mode 100644 index 82b23227..00000000 --- a/src/lithic/types/auth_rule_remove_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import List -from typing_extensions import TypedDict - -__all__ = ["AuthRuleRemoveParams"] - - -class AuthRuleRemoveParams(TypedDict, total=False): - account_tokens: List[str] - """ - Array of account_token(s) identifying the accounts that the Auth Rule applies - to. Note that only this field or `card_tokens` can be provided for a given Auth - Rule. - """ - - card_tokens: List[str] - """ - Array of card_token(s) identifying the cards that the Auth Rule applies to. Note - that only this field or `account_tokens` can be provided for a given Auth Rule. - """ - - program_level: bool - """Boolean indicating whether the Auth Rule is applied at the program level.""" diff --git a/src/lithic/types/auth_rule_remove_response.py b/src/lithic/types/auth_rule_remove_response.py deleted file mode 100644 index 13961e1b..00000000 --- a/src/lithic/types/auth_rule_remove_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import List, Optional - -from .._models import BaseModel - -__all__ = ["AuthRuleRemoveResponse"] - - -class AuthRuleRemoveResponse(BaseModel): - account_tokens: Optional[List[str]] = None - - card_tokens: Optional[List[str]] = None - - program_level: Optional[bool] = None diff --git a/src/lithic/types/auth_rule_retrieve_response.py b/src/lithic/types/auth_rule_retrieve_response.py deleted file mode 100644 index 31e16484..00000000 --- a/src/lithic/types/auth_rule_retrieve_response.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import List, Optional - -from .._models import BaseModel -from .auth_rule import AuthRule - -__all__ = ["AuthRuleRetrieveResponse"] - - -class AuthRuleRetrieveResponse(BaseModel): - data: Optional[List[AuthRule]] = None diff --git a/src/lithic/types/auth_rule_update_params.py b/src/lithic/types/auth_rule_update_params.py deleted file mode 100644 index 0503c6d9..00000000 --- a/src/lithic/types/auth_rule_update_params.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import List -from typing_extensions import TypedDict - -__all__ = ["AuthRuleUpdateParams"] - - -class AuthRuleUpdateParams(TypedDict, total=False): - allowed_countries: List[str] - """ - Array of country codes for which the Auth Rule will permit transactions. Note - that only this field or `blocked_countries` can be used for a given Auth Rule. - """ - - allowed_mcc: List[str] - """ - Array of merchant category codes for which the Auth Rule will permit - transactions. Note that only this field or `blocked_mcc` can be used for a given - Auth Rule. - """ - - blocked_countries: List[str] - """ - Array of country codes for which the Auth Rule will automatically decline - transactions. Note that only this field or `allowed_countries` can be used for a - given Auth Rule. - """ - - blocked_mcc: List[str] - """ - Array of merchant category codes for which the Auth Rule will automatically - decline transactions. Note that only this field or `allowed_mcc` can be used for - a given Auth Rule. - """ diff --git a/src/lithic/types/auth_rules/__init__.py b/src/lithic/types/auth_rules/__init__.py new file mode 100644 index 00000000..b2982d4a --- /dev/null +++ b/src/lithic/types/auth_rules/__init__.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .v2_list_params import V2ListParams as V2ListParams +from .v2_apply_params import V2ApplyParams as V2ApplyParams +from .v2_draft_params import V2DraftParams as V2DraftParams +from .v2_create_params import V2CreateParams as V2CreateParams +from .v2_list_response import V2ListResponse as V2ListResponse +from .v2_update_params import V2UpdateParams as V2UpdateParams +from .v2_apply_response import V2ApplyResponse as V2ApplyResponse +from .v2_draft_response import V2DraftResponse as V2DraftResponse +from .v2_create_response import V2CreateResponse as V2CreateResponse +from .v2_report_response import V2ReportResponse as V2ReportResponse +from .v2_update_response import V2UpdateResponse as V2UpdateResponse +from .auth_rule_condition import AuthRuleCondition as AuthRuleCondition +from .v2_promote_response import V2PromoteResponse as V2PromoteResponse +from .v2_retrieve_response import V2RetrieveResponse as V2RetrieveResponse +from .conditional_attribute import ConditionalAttribute as ConditionalAttribute +from .velocity_limit_params import VelocityLimitParams as VelocityLimitParams +from .auth_rule_condition_param import AuthRuleConditionParam as AuthRuleConditionParam +from .velocity_limit_params_param import VelocityLimitParamsParam as VelocityLimitParamsParam +from .conditional_block_parameters import ConditionalBlockParameters as ConditionalBlockParameters +from .conditional_block_parameters_param import ConditionalBlockParametersParam as ConditionalBlockParametersParam +from .velocity_limit_params_period_window import VelocityLimitParamsPeriodWindow as VelocityLimitParamsPeriodWindow diff --git a/src/lithic/types/auth_rules/auth_rule_condition.py b/src/lithic/types/auth_rules/auth_rule_condition.py new file mode 100644 index 00000000..7de1a850 --- /dev/null +++ b/src/lithic/types/auth_rules/auth_rule_condition.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conditional_attribute import ConditionalAttribute + +__all__ = ["AuthRuleCondition"] + + +class AuthRuleCondition(BaseModel): + attribute: Optional[ConditionalAttribute] = None + """The attribute to target. + + The following attributes may be targeted: + + - `MCC`: A four-digit number listed in ISO 18245. An MCC is used to classify a + business by the types of goods or services it provides. + - `COUNTRY`: Country of entity of card acceptor. Possible values are: (1) all + ISO 3166-1 alpha-3 country codes, (2) QZZ for Kosovo, and (3) ANT for + Netherlands Antilles. + - `CURRENCY`: 3-character alphabetic ISO 4217 code for the merchant currency of + the transaction. + - `MERCHANT_ID`: Unique alphanumeric identifier for the payment card acceptor + (merchant). + - `DESCRIPTOR`: Short description of card acceptor. + - `LIABILITY_SHIFT`: Indicates whether chargeback liability shift to the issuer + applies to the transaction. Valid values are `NONE`, `3DS_AUTHENTICATED`, or + `TOKEN_AUTHENTICATED`. + - `PAN_ENTRY_MODE`: The method by which the cardholder's primary account number + (PAN) was entered. Valid values are `AUTO_ENTRY`, `BAR_CODE`, `CONTACTLESS`, + `ECOMMERCE`, `ERROR_KEYED`, `ERROR_MAGNETIC_STRIPE`, `ICC`, `KEY_ENTERED`, + `MAGNETIC_STRIPE`, `MANUAL`, `OCR`, `SECURE_CARDLESS`, `UNSPECIFIED`, + `UNKNOWN`, `CREDENTIAL_ON_FILE`, or `ECOMMERCE`. + - `TRANSACTION_AMOUNT`: The base transaction amount (in cents) plus the acquirer + fee field in the settlement/cardholder billing currency. This is the amount + the issuer should authorize against unless the issuer is paying the acquirer + fee on behalf of the cardholder. + - `RISK_SCORE`: Network-provided score assessing risk level associated with a + given authorization. Scores are on a range of 0-999, with 0 representing the + lowest risk and 999 representing the highest risk. For Visa transactions, + where the raw score has a range of 0-99, Lithic will normalize the score by + multiplying the raw score by 10x. + - `CARD_TRANSACTION_COUNT_15M`: The number of transactions on the card in the + trailing 15 minutes before the authorization. + - `CARD_TRANSACTION_COUNT_1H`: The number of transactions on the card in the + trailing hour up and until the authorization. + - `CARD_TRANSACTION_COUNT_24H`: The number of transactions on the card in the + trailing 24 hours up and until the authorization. + - `CARD_STATE`: The current state of the card associated with the transaction. + Valid values are `CLOSED`, `OPEN`, `PAUSED`, `PENDING_ACTIVATION`, + `PENDING_FULFILLMENT`. + - `PIN_ENTERED`: Indicates whether a PIN was entered during the transaction. + Valid values are `TRUE`, `FALSE`. + - `PIN_STATUS`: The current state of card's PIN. Valid values are `NOT_SET`, + `OK`, `BLOCKED`. + - `WALLET_TYPE`: For transactions using a digital wallet token, indicates the + source of the token. Valid values are `APPLE_PAY`, `GOOGLE_PAY`, + `SAMSUNG_PAY`, `MASTERPASS`, `MERCHANT`, `OTHER`, `NONE`. + """ + + operation: Optional[ + Literal["IS_ONE_OF", "IS_NOT_ONE_OF", "MATCHES", "DOES_NOT_MATCH", "IS_GREATER_THAN", "IS_LESS_THAN"] + ] = None + """The operation to apply to the attribute""" + + value: Union[str, int, List[str], None] = None + """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/auth_rule_condition_param.py b/src/lithic/types/auth_rules/auth_rule_condition_param.py new file mode 100644 index 00000000..960d4e71 --- /dev/null +++ b/src/lithic/types/auth_rules/auth_rule_condition_param.py @@ -0,0 +1,68 @@ +# 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 Literal, TypedDict + +from .conditional_attribute import ConditionalAttribute + +__all__ = ["AuthRuleConditionParam"] + + +class AuthRuleConditionParam(TypedDict, total=False): + attribute: ConditionalAttribute + """The attribute to target. + + The following attributes may be targeted: + + - `MCC`: A four-digit number listed in ISO 18245. An MCC is used to classify a + business by the types of goods or services it provides. + - `COUNTRY`: Country of entity of card acceptor. Possible values are: (1) all + ISO 3166-1 alpha-3 country codes, (2) QZZ for Kosovo, and (3) ANT for + Netherlands Antilles. + - `CURRENCY`: 3-character alphabetic ISO 4217 code for the merchant currency of + the transaction. + - `MERCHANT_ID`: Unique alphanumeric identifier for the payment card acceptor + (merchant). + - `DESCRIPTOR`: Short description of card acceptor. + - `LIABILITY_SHIFT`: Indicates whether chargeback liability shift to the issuer + applies to the transaction. Valid values are `NONE`, `3DS_AUTHENTICATED`, or + `TOKEN_AUTHENTICATED`. + - `PAN_ENTRY_MODE`: The method by which the cardholder's primary account number + (PAN) was entered. Valid values are `AUTO_ENTRY`, `BAR_CODE`, `CONTACTLESS`, + `ECOMMERCE`, `ERROR_KEYED`, `ERROR_MAGNETIC_STRIPE`, `ICC`, `KEY_ENTERED`, + `MAGNETIC_STRIPE`, `MANUAL`, `OCR`, `SECURE_CARDLESS`, `UNSPECIFIED`, + `UNKNOWN`, `CREDENTIAL_ON_FILE`, or `ECOMMERCE`. + - `TRANSACTION_AMOUNT`: The base transaction amount (in cents) plus the acquirer + fee field in the settlement/cardholder billing currency. This is the amount + the issuer should authorize against unless the issuer is paying the acquirer + fee on behalf of the cardholder. + - `RISK_SCORE`: Network-provided score assessing risk level associated with a + given authorization. Scores are on a range of 0-999, with 0 representing the + lowest risk and 999 representing the highest risk. For Visa transactions, + where the raw score has a range of 0-99, Lithic will normalize the score by + multiplying the raw score by 10x. + - `CARD_TRANSACTION_COUNT_15M`: The number of transactions on the card in the + trailing 15 minutes before the authorization. + - `CARD_TRANSACTION_COUNT_1H`: The number of transactions on the card in the + trailing hour up and until the authorization. + - `CARD_TRANSACTION_COUNT_24H`: The number of transactions on the card in the + trailing 24 hours up and until the authorization. + - `CARD_STATE`: The current state of the card associated with the transaction. + Valid values are `CLOSED`, `OPEN`, `PAUSED`, `PENDING_ACTIVATION`, + `PENDING_FULFILLMENT`. + - `PIN_ENTERED`: Indicates whether a PIN was entered during the transaction. + Valid values are `TRUE`, `FALSE`. + - `PIN_STATUS`: The current state of card's PIN. Valid values are `NOT_SET`, + `OK`, `BLOCKED`. + - `WALLET_TYPE`: For transactions using a digital wallet token, indicates the + source of the token. Valid values are `APPLE_PAY`, `GOOGLE_PAY`, + `SAMSUNG_PAY`, `MASTERPASS`, `MERCHANT`, `OTHER`, `NONE`. + """ + + operation: Literal["IS_ONE_OF", "IS_NOT_ONE_OF", "MATCHES", "DOES_NOT_MATCH", "IS_GREATER_THAN", "IS_LESS_THAN"] + """The operation to apply to the attribute""" + + value: Union[str, int, List[str]] + """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/conditional_attribute.py b/src/lithic/types/auth_rules/conditional_attribute.py new file mode 100644 index 00000000..626ff59a --- /dev/null +++ b/src/lithic/types/auth_rules/conditional_attribute.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ConditionalAttribute"] + +ConditionalAttribute: TypeAlias = Literal[ + "MCC", + "COUNTRY", + "CURRENCY", + "MERCHANT_ID", + "DESCRIPTOR", + "LIABILITY_SHIFT", + "PAN_ENTRY_MODE", + "TRANSACTION_AMOUNT", + "RISK_SCORE", + "CARD_TRANSACTION_COUNT_15M", + "CARD_TRANSACTION_COUNT_1H", + "CARD_TRANSACTION_COUNT_24H", + "CARD_STATE", + "PIN_ENTERED", + "PIN_STATUS", + "WALLET_TYPE", +] diff --git a/src/lithic/types/auth_rules/conditional_block_parameters.py b/src/lithic/types/auth_rules/conditional_block_parameters.py new file mode 100644 index 00000000..b615fbe2 --- /dev/null +++ b/src/lithic/types/auth_rules/conditional_block_parameters.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel +from .auth_rule_condition import AuthRuleCondition + +__all__ = ["ConditionalBlockParameters"] + + +class ConditionalBlockParameters(BaseModel): + conditions: List[AuthRuleCondition] diff --git a/src/lithic/types/auth_rules/conditional_block_parameters_param.py b/src/lithic/types/auth_rules/conditional_block_parameters_param.py new file mode 100644 index 00000000..b9a456a3 --- /dev/null +++ b/src/lithic/types/auth_rules/conditional_block_parameters_param.py @@ -0,0 +1,14 @@ +# 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 .auth_rule_condition_param import AuthRuleConditionParam + +__all__ = ["ConditionalBlockParametersParam"] + + +class ConditionalBlockParametersParam(TypedDict, total=False): + conditions: Required[Iterable[AuthRuleConditionParam]] diff --git a/src/lithic/types/auth_rules/v2/__init__.py b/src/lithic/types/auth_rules/v2/__init__.py new file mode 100644 index 00000000..c665d2de --- /dev/null +++ b/src/lithic/types/auth_rules/v2/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .backtest_results import BacktestResults as BacktestResults +from .backtest_create_params import BacktestCreateParams as BacktestCreateParams +from .backtest_create_response import BacktestCreateResponse as BacktestCreateResponse diff --git a/src/lithic/types/auth_rules/v2/backtest_create_params.py b/src/lithic/types/auth_rules/v2/backtest_create_params.py new file mode 100644 index 00000000..fec2e270 --- /dev/null +++ b/src/lithic/types/auth_rules/v2/backtest_create_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["BacktestCreateParams"] + + +class BacktestCreateParams(TypedDict, total=False): + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """The end time of the backtest.""" + + start: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """The start time of the backtest.""" diff --git a/src/lithic/types/auth_rules/v2/backtest_create_response.py b/src/lithic/types/auth_rules/v2/backtest_create_response.py new file mode 100644 index 00000000..28ee75ea --- /dev/null +++ b/src/lithic/types/auth_rules/v2/backtest_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["BacktestCreateResponse"] + + +class BacktestCreateResponse(BaseModel): + backtest_token: Optional[str] = None + """Auth Rule Backtest Token""" diff --git a/src/lithic/types/auth_rules/v2/backtest_results.py b/src/lithic/types/auth_rules/v2/backtest_results.py new file mode 100644 index 00000000..20f6916a --- /dev/null +++ b/src/lithic/types/auth_rules/v2/backtest_results.py @@ -0,0 +1,114 @@ +# 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 + +__all__ = [ + "BacktestResults", + "Results", + "ResultsCurrentVersion", + "ResultsCurrentVersionExample", + "ResultsDraftVersion", + "ResultsDraftVersionExample", + "SimulationParameters", +] + + +class ResultsCurrentVersionExample(BaseModel): + approved: Optional[bool] = None + """Whether the rule would have approved the authorization request.""" + + event_token: Optional[str] = None + """The authorization request event token.""" + + timestamp: Optional[datetime] = None + """The timestamp of the authorization request event.""" + + +class ResultsCurrentVersion(BaseModel): + approved: Optional[int] = None + """ + The total number of historical transactions approved by this rule during the + backtest period, or the number of transactions that would have been approved if + the rule was evaluated in shadow mode. + """ + + declined: Optional[int] = None + """ + The total number of historical transactions declined by this rule during the + backtest period, or the number of transactions that would have been declined if + the rule was evaluated in shadow mode. + """ + + examples: Optional[List[ResultsCurrentVersionExample]] = None + """Example authorization request events that would have been approved or declined.""" + + version: Optional[int] = None + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class ResultsDraftVersionExample(BaseModel): + approved: Optional[bool] = None + """Whether the rule would have approved the authorization request.""" + + event_token: Optional[str] = None + """The authorization request event token.""" + + timestamp: Optional[datetime] = None + """The timestamp of the authorization request event.""" + + +class ResultsDraftVersion(BaseModel): + approved: Optional[int] = None + """ + The total number of historical transactions approved by this rule during the + backtest period, or the number of transactions that would have been approved if + the rule was evaluated in shadow mode. + """ + + declined: Optional[int] = None + """ + The total number of historical transactions declined by this rule during the + backtest period, or the number of transactions that would have been declined if + the rule was evaluated in shadow mode. + """ + + examples: Optional[List[ResultsDraftVersionExample]] = None + """Example authorization request events that would have been approved or declined.""" + + version: Optional[int] = None + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class Results(BaseModel): + current_version: Optional[ResultsCurrentVersion] = None + + draft_version: Optional[ResultsDraftVersion] = None + + +class SimulationParameters(BaseModel): + auth_rule_token: Optional[str] = None + """Auth Rule Token""" + + end: Optional[datetime] = None + """The end time of the simulation.""" + + start: Optional[datetime] = None + """The start time of the simulation.""" + + +class BacktestResults(BaseModel): + backtest_token: str + """Auth Rule Backtest Token""" + + results: Results + + simulation_parameters: SimulationParameters diff --git a/src/lithic/types/auth_rules/v2_apply_params.py b/src/lithic/types/auth_rules/v2_apply_params.py new file mode 100644 index 00000000..e44e79e7 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_apply_params.py @@ -0,0 +1,36 @@ +# 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 Required, TypeAlias, TypedDict + +__all__ = [ + "V2ApplyParams", + "ApplyAuthRuleRequestAccountTokens", + "ApplyAuthRuleRequestCardTokens", + "ApplyAuthRuleRequestProgramLevel", +] + + +class ApplyAuthRuleRequestAccountTokens(TypedDict, total=False): + account_tokens: Required[List[str]] + """Account tokens to which the Auth Rule applies.""" + + +class ApplyAuthRuleRequestCardTokens(TypedDict, total=False): + card_tokens: Required[List[str]] + """Card tokens to which the Auth Rule applies.""" + + +class ApplyAuthRuleRequestProgramLevel(TypedDict, total=False): + program_level: Required[bool] + """Whether the Auth Rule applies to all authorizations on the card program.""" + + excluded_card_tokens: List[str] + """Card tokens to which the Auth Rule does not apply.""" + + +V2ApplyParams: TypeAlias = Union[ + ApplyAuthRuleRequestAccountTokens, ApplyAuthRuleRequestCardTokens, ApplyAuthRuleRequestProgramLevel +] diff --git a/src/lithic/types/auth_rules/v2_apply_response.py b/src/lithic/types/auth_rules/v2_apply_response.py new file mode 100644 index 00000000..153c3d2b --- /dev/null +++ b/src/lithic/types/auth_rules/v2_apply_response.py @@ -0,0 +1,67 @@ +# 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 ..._models import BaseModel +from .velocity_limit_params import VelocityLimitParams +from .conditional_block_parameters import ConditionalBlockParameters + +__all__ = ["V2ApplyResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] + +CurrentVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class CurrentVersion(BaseModel): + parameters: CurrentVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +DraftVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class DraftVersion(BaseModel): + parameters: DraftVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class V2ApplyResponse(BaseModel): + token: str + """Auth Rule Token""" + + account_tokens: List[str] + """Account tokens to which the Auth Rule applies.""" + + card_tokens: List[str] + """Card tokens to which the Auth Rule applies.""" + + current_version: Optional[CurrentVersion] = None + + draft_version: Optional[DraftVersion] = None + + name: Optional[str] = None + """Auth Rule Name""" + + program_level: bool + """Whether the Auth Rule applies to all authorizations on the card program.""" + + state: Literal["ACTIVE", "INACTIVE"] + """The state of the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + excluded_card_tokens: Optional[List[str]] = None + """Card tokens to which the Auth Rule does not apply.""" diff --git a/src/lithic/types/auth_rules/v2_create_params.py b/src/lithic/types/auth_rules/v2_create_params.py new file mode 100644 index 00000000..dff961f4 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_create_params.py @@ -0,0 +1,81 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .velocity_limit_params_param import VelocityLimitParamsParam +from .conditional_block_parameters_param import ConditionalBlockParametersParam + +__all__ = [ + "V2CreateParams", + "CreateAuthRuleRequestAccountTokens", + "CreateAuthRuleRequestAccountTokensParameters", + "CreateAuthRuleRequestCardTokens", + "CreateAuthRuleRequestCardTokensParameters", + "CreateAuthRuleRequestProgramLevel", + "CreateAuthRuleRequestProgramLevelParameters", +] + + +class CreateAuthRuleRequestAccountTokens(TypedDict, total=False): + account_tokens: Required[List[str]] + """Account tokens to which the Auth Rule applies.""" + + name: Optional[str] + """Auth Rule Name""" + + parameters: CreateAuthRuleRequestAccountTokensParameters + """Parameters for the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + +CreateAuthRuleRequestAccountTokensParameters: TypeAlias = Union[ + ConditionalBlockParametersParam, VelocityLimitParamsParam +] + + +class CreateAuthRuleRequestCardTokens(TypedDict, total=False): + card_tokens: Required[List[str]] + """Card tokens to which the Auth Rule applies.""" + + name: Optional[str] + """Auth Rule Name""" + + parameters: CreateAuthRuleRequestCardTokensParameters + """Parameters for the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + +CreateAuthRuleRequestCardTokensParameters: TypeAlias = Union[ConditionalBlockParametersParam, VelocityLimitParamsParam] + + +class CreateAuthRuleRequestProgramLevel(TypedDict, total=False): + program_level: Required[bool] + """Whether the Auth Rule applies to all authorizations on the card program.""" + + excluded_card_tokens: List[str] + """Card tokens to which the Auth Rule does not apply.""" + + name: Optional[str] + """Auth Rule Name""" + + parameters: CreateAuthRuleRequestProgramLevelParameters + """Parameters for the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + +CreateAuthRuleRequestProgramLevelParameters: TypeAlias = Union[ + ConditionalBlockParametersParam, VelocityLimitParamsParam +] + +V2CreateParams: TypeAlias = Union[ + CreateAuthRuleRequestAccountTokens, CreateAuthRuleRequestCardTokens, CreateAuthRuleRequestProgramLevel +] diff --git a/src/lithic/types/auth_rules/v2_create_response.py b/src/lithic/types/auth_rules/v2_create_response.py new file mode 100644 index 00000000..b2078622 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_create_response.py @@ -0,0 +1,67 @@ +# 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 ..._models import BaseModel +from .velocity_limit_params import VelocityLimitParams +from .conditional_block_parameters import ConditionalBlockParameters + +__all__ = ["V2CreateResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] + +CurrentVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class CurrentVersion(BaseModel): + parameters: CurrentVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +DraftVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class DraftVersion(BaseModel): + parameters: DraftVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class V2CreateResponse(BaseModel): + token: str + """Auth Rule Token""" + + account_tokens: List[str] + """Account tokens to which the Auth Rule applies.""" + + card_tokens: List[str] + """Card tokens to which the Auth Rule applies.""" + + current_version: Optional[CurrentVersion] = None + + draft_version: Optional[DraftVersion] = None + + name: Optional[str] = None + """Auth Rule Name""" + + program_level: bool + """Whether the Auth Rule applies to all authorizations on the card program.""" + + state: Literal["ACTIVE", "INACTIVE"] + """The state of the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + excluded_card_tokens: Optional[List[str]] = None + """Card tokens to which the Auth Rule does not apply.""" diff --git a/src/lithic/types/auth_rules/v2_draft_params.py b/src/lithic/types/auth_rules/v2_draft_params.py new file mode 100644 index 00000000..5d7a5559 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_draft_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import TypeAlias, TypedDict + +from .velocity_limit_params_param import VelocityLimitParamsParam +from .conditional_block_parameters_param import ConditionalBlockParametersParam + +__all__ = ["V2DraftParams", "Parameters"] + + +class V2DraftParams(TypedDict, total=False): + parameters: Optional[Parameters] + """Parameters for the Auth Rule""" + + +Parameters: TypeAlias = Union[ConditionalBlockParametersParam, VelocityLimitParamsParam] diff --git a/src/lithic/types/auth_rules/v2_draft_response.py b/src/lithic/types/auth_rules/v2_draft_response.py new file mode 100644 index 00000000..4226de04 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_draft_response.py @@ -0,0 +1,67 @@ +# 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 ..._models import BaseModel +from .velocity_limit_params import VelocityLimitParams +from .conditional_block_parameters import ConditionalBlockParameters + +__all__ = ["V2DraftResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] + +CurrentVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class CurrentVersion(BaseModel): + parameters: CurrentVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +DraftVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class DraftVersion(BaseModel): + parameters: DraftVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class V2DraftResponse(BaseModel): + token: str + """Auth Rule Token""" + + account_tokens: List[str] + """Account tokens to which the Auth Rule applies.""" + + card_tokens: List[str] + """Card tokens to which the Auth Rule applies.""" + + current_version: Optional[CurrentVersion] = None + + draft_version: Optional[DraftVersion] = None + + name: Optional[str] = None + """Auth Rule Name""" + + program_level: bool + """Whether the Auth Rule applies to all authorizations on the card program.""" + + state: Literal["ACTIVE", "INACTIVE"] + """The state of the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + excluded_card_tokens: Optional[List[str]] = None + """Card tokens to which the Auth Rule does not apply.""" diff --git a/src/lithic/types/auth_rule_list_params.py b/src/lithic/types/auth_rules/v2_list_params.py similarity index 57% rename from src/lithic/types/auth_rule_list_params.py rename to src/lithic/types/auth_rules/v2_list_params.py index 581c186f..f080a4f6 100644 --- a/src/lithic/types/auth_rule_list_params.py +++ b/src/lithic/types/auth_rules/v2_list_params.py @@ -1,13 +1,19 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import TypedDict -__all__ = ["AuthRuleListParams"] +__all__ = ["V2ListParams"] -class AuthRuleListParams(TypedDict, total=False): +class V2ListParams(TypedDict, total=False): + account_token: str + """Only return Authorization Rules that are bound to the provided account token.""" + + card_token: str + """Only return Authorization Rules that are bound to the provided card token.""" + ending_before: str """A cursor representing an item's token before which a page of results should end. diff --git a/src/lithic/types/auth_rules/v2_list_response.py b/src/lithic/types/auth_rules/v2_list_response.py new file mode 100644 index 00000000..d1039175 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_list_response.py @@ -0,0 +1,67 @@ +# 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 ..._models import BaseModel +from .velocity_limit_params import VelocityLimitParams +from .conditional_block_parameters import ConditionalBlockParameters + +__all__ = ["V2ListResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] + +CurrentVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class CurrentVersion(BaseModel): + parameters: CurrentVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +DraftVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class DraftVersion(BaseModel): + parameters: DraftVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class V2ListResponse(BaseModel): + token: str + """Auth Rule Token""" + + account_tokens: List[str] + """Account tokens to which the Auth Rule applies.""" + + card_tokens: List[str] + """Card tokens to which the Auth Rule applies.""" + + current_version: Optional[CurrentVersion] = None + + draft_version: Optional[DraftVersion] = None + + name: Optional[str] = None + """Auth Rule Name""" + + program_level: bool + """Whether the Auth Rule applies to all authorizations on the card program.""" + + state: Literal["ACTIVE", "INACTIVE"] + """The state of the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + excluded_card_tokens: Optional[List[str]] = None + """Card tokens to which the Auth Rule does not apply.""" diff --git a/src/lithic/types/auth_rules/v2_promote_response.py b/src/lithic/types/auth_rules/v2_promote_response.py new file mode 100644 index 00000000..53c27487 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_promote_response.py @@ -0,0 +1,67 @@ +# 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 ..._models import BaseModel +from .velocity_limit_params import VelocityLimitParams +from .conditional_block_parameters import ConditionalBlockParameters + +__all__ = ["V2PromoteResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] + +CurrentVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class CurrentVersion(BaseModel): + parameters: CurrentVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +DraftVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class DraftVersion(BaseModel): + parameters: DraftVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class V2PromoteResponse(BaseModel): + token: str + """Auth Rule Token""" + + account_tokens: List[str] + """Account tokens to which the Auth Rule applies.""" + + card_tokens: List[str] + """Card tokens to which the Auth Rule applies.""" + + current_version: Optional[CurrentVersion] = None + + draft_version: Optional[DraftVersion] = None + + name: Optional[str] = None + """Auth Rule Name""" + + program_level: bool + """Whether the Auth Rule applies to all authorizations on the card program.""" + + state: Literal["ACTIVE", "INACTIVE"] + """The state of the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + excluded_card_tokens: Optional[List[str]] = None + """Card tokens to which the Auth Rule does not apply.""" diff --git a/src/lithic/types/auth_rules/v2_report_response.py b/src/lithic/types/auth_rules/v2_report_response.py new file mode 100644 index 00000000..32aed0d1 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_report_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["V2ReportResponse"] + + +class V2ReportResponse(BaseModel): + report_token: Optional[str] = None diff --git a/src/lithic/types/auth_rules/v2_retrieve_response.py b/src/lithic/types/auth_rules/v2_retrieve_response.py new file mode 100644 index 00000000..c4793dcc --- /dev/null +++ b/src/lithic/types/auth_rules/v2_retrieve_response.py @@ -0,0 +1,67 @@ +# 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 ..._models import BaseModel +from .velocity_limit_params import VelocityLimitParams +from .conditional_block_parameters import ConditionalBlockParameters + +__all__ = ["V2RetrieveResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] + +CurrentVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class CurrentVersion(BaseModel): + parameters: CurrentVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +DraftVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class DraftVersion(BaseModel): + parameters: DraftVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class V2RetrieveResponse(BaseModel): + token: str + """Auth Rule Token""" + + account_tokens: List[str] + """Account tokens to which the Auth Rule applies.""" + + card_tokens: List[str] + """Card tokens to which the Auth Rule applies.""" + + current_version: Optional[CurrentVersion] = None + + draft_version: Optional[DraftVersion] = None + + name: Optional[str] = None + """Auth Rule Name""" + + program_level: bool + """Whether the Auth Rule applies to all authorizations on the card program.""" + + state: Literal["ACTIVE", "INACTIVE"] + """The state of the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + excluded_card_tokens: Optional[List[str]] = None + """Card tokens to which the Auth Rule does not apply.""" diff --git a/src/lithic/types/auth_rules/v2_update_params.py b/src/lithic/types/auth_rules/v2_update_params.py new file mode 100644 index 00000000..042cf558 --- /dev/null +++ b/src/lithic/types/auth_rules/v2_update_params.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias, TypedDict + +__all__ = ["V2UpdateParams", "AccountLevelRule", "CardLevelRule", "ProgramLevelRule"] + + +class AccountLevelRule(TypedDict, total=False): + account_tokens: List[str] + """Account tokens to which the Auth Rule applies.""" + + name: Optional[str] + """Auth Rule Name""" + + state: Literal["INACTIVE"] + """The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + """ + + +class CardLevelRule(TypedDict, total=False): + card_tokens: List[str] + """Card tokens to which the Auth Rule applies.""" + + name: Optional[str] + """Auth Rule Name""" + + state: Literal["INACTIVE"] + """The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + """ + + +class ProgramLevelRule(TypedDict, total=False): + excluded_card_tokens: List[str] + """Card tokens to which the Auth Rule does not apply.""" + + name: Optional[str] + """Auth Rule Name""" + + program_level: bool + """Whether the Auth Rule applies to all authorizations on the card program.""" + + state: Literal["INACTIVE"] + """The desired state of the Auth Rule. + + Note that only deactivating an Auth Rule through this endpoint is supported at + this time. If you need to (re-)activate an Auth Rule the /promote endpoint + should be used to promote a draft to the currently active version. + """ + + +V2UpdateParams: TypeAlias = Union[AccountLevelRule, CardLevelRule, ProgramLevelRule] diff --git a/src/lithic/types/auth_rules/v2_update_response.py b/src/lithic/types/auth_rules/v2_update_response.py new file mode 100644 index 00000000..e5a5456f --- /dev/null +++ b/src/lithic/types/auth_rules/v2_update_response.py @@ -0,0 +1,67 @@ +# 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 ..._models import BaseModel +from .velocity_limit_params import VelocityLimitParams +from .conditional_block_parameters import ConditionalBlockParameters + +__all__ = ["V2UpdateResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] + +CurrentVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class CurrentVersion(BaseModel): + parameters: CurrentVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +DraftVersionParameters: TypeAlias = Union[ConditionalBlockParameters, VelocityLimitParams] + + +class DraftVersion(BaseModel): + parameters: DraftVersionParameters + """Parameters for the Auth Rule""" + + version: int + """ + The version of the rule, this is incremented whenever the rule's parameters + change. + """ + + +class V2UpdateResponse(BaseModel): + token: str + """Auth Rule Token""" + + account_tokens: List[str] + """Account tokens to which the Auth Rule applies.""" + + card_tokens: List[str] + """Card tokens to which the Auth Rule applies.""" + + current_version: Optional[CurrentVersion] = None + + draft_version: Optional[DraftVersion] = None + + name: Optional[str] = None + """Auth Rule Name""" + + program_level: bool + """Whether the Auth Rule applies to all authorizations on the card program.""" + + state: Literal["ACTIVE", "INACTIVE"] + """The state of the Auth Rule""" + + type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT"] + """The type of Auth Rule""" + + excluded_card_tokens: Optional[List[str]] = None + """Card tokens to which the Auth Rule does not apply.""" diff --git a/src/lithic/types/auth_rules/velocity_limit_params.py b/src/lithic/types/auth_rules/velocity_limit_params.py new file mode 100644 index 00000000..134fe9ac --- /dev/null +++ b/src/lithic/types/auth_rules/velocity_limit_params.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .velocity_limit_params_period_window import VelocityLimitParamsPeriodWindow + +__all__ = ["VelocityLimitParams", "Filters"] + + +class Filters(BaseModel): + exclude_countries: Optional[List[str]] = None + """ISO-3166-1 alpha-3 Country Codes to exclude from the velocity calculation. + + Transactions matching any of the provided will be excluded from the calculated + velocity. + """ + + exclude_mccs: Optional[List[str]] = None + """Merchant Category Codes to exclude from the velocity calculation. + + Transactions matching this MCC will be excluded from the calculated velocity. + """ + + include_countries: Optional[List[str]] = None + """ISO-3166-1 alpha-3 Country Codes to include in the velocity calculation. + + Transactions not matching any of the provided will not be included in the + calculated velocity. + """ + + include_mccs: Optional[List[str]] = None + """Merchant Category Codes to include in the velocity calculation. + + Transactions not matching this MCC will not be included in the calculated + velocity. + """ + + +class VelocityLimitParams(BaseModel): + filters: Filters + + period: Union[int, VelocityLimitParamsPeriodWindow] + """The size of the trailing window to calculate Spend Velocity over in seconds. + + The minimum value is 10 seconds, and the maximum value is 2678400 seconds (31 + days). + """ + + scope: Literal["CARD", "ACCOUNT"] + + limit_amount: Optional[int] = None + """ + The maximum amount of spend velocity allowed in the period in minor units (the + smallest unit of a currency, e.g. cents for USD). Transactions exceeding this + limit will be declined. + """ + + limit_count: Optional[int] = None + """ + The number of spend velocity impacting transactions may not exceed this limit in + the period. Transactions exceeding this limit will be declined. A spend velocity + impacting transaction is a transaction that has been authorized, and optionally + settled, or a force post (a transaction that settled without prior + authorization). + """ diff --git a/src/lithic/types/auth_rules/velocity_limit_params_param.py b/src/lithic/types/auth_rules/velocity_limit_params_param.py new file mode 100644 index 00000000..2914fddb --- /dev/null +++ b/src/lithic/types/auth_rules/velocity_limit_params_param.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .velocity_limit_params_period_window import VelocityLimitParamsPeriodWindow + +__all__ = ["VelocityLimitParamsParam", "Filters"] + + +class Filters(TypedDict, total=False): + exclude_countries: Optional[List[str]] + """ISO-3166-1 alpha-3 Country Codes to exclude from the velocity calculation. + + Transactions matching any of the provided will be excluded from the calculated + velocity. + """ + + exclude_mccs: Optional[List[str]] + """Merchant Category Codes to exclude from the velocity calculation. + + Transactions matching this MCC will be excluded from the calculated velocity. + """ + + include_countries: Optional[List[str]] + """ISO-3166-1 alpha-3 Country Codes to include in the velocity calculation. + + Transactions not matching any of the provided will not be included in the + calculated velocity. + """ + + include_mccs: Optional[List[str]] + """Merchant Category Codes to include in the velocity calculation. + + Transactions not matching this MCC will not be included in the calculated + velocity. + """ + + +class VelocityLimitParamsParam(TypedDict, total=False): + filters: Required[Filters] + + period: Required[Union[int, VelocityLimitParamsPeriodWindow]] + """The size of the trailing window to calculate Spend Velocity over in seconds. + + The minimum value is 10 seconds, and the maximum value is 2678400 seconds (31 + days). + """ + + scope: Required[Literal["CARD", "ACCOUNT"]] + + limit_amount: Optional[int] + """ + The maximum amount of spend velocity allowed in the period in minor units (the + smallest unit of a currency, e.g. cents for USD). Transactions exceeding this + limit will be declined. + """ + + limit_count: Optional[int] + """ + The number of spend velocity impacting transactions may not exceed this limit in + the period. Transactions exceeding this limit will be declined. A spend velocity + impacting transaction is a transaction that has been authorized, and optionally + settled, or a force post (a transaction that settled without prior + authorization). + """ diff --git a/src/lithic/types/auth_rules/velocity_limit_params_period_window.py b/src/lithic/types/auth_rules/velocity_limit_params_period_window.py new file mode 100644 index 00000000..09db6bb2 --- /dev/null +++ b/src/lithic/types/auth_rules/velocity_limit_params_period_window.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__ = ["VelocityLimitParamsPeriodWindow"] + +VelocityLimitParamsPeriodWindow: TypeAlias = Literal["DAY", "WEEK", "MONTH"] diff --git a/src/lithic/types/auth_stream_secret.py b/src/lithic/types/auth_stream_secret.py index df85dc69..700172ab 100644 --- a/src/lithic/types/auth_stream_secret.py +++ b/src/lithic/types/auth_stream_secret.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/balance.py b/src/lithic/types/balance.py index edc571e1..7be9fcc8 100644 --- a/src/lithic/types/balance.py +++ b/src/lithic/types/balance.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal @@ -16,7 +16,7 @@ class Balance(BaseModel): """Date and time for when the balance was first created.""" currency: str - """3-digit alphabetic ISO 4217 code for the local currency of the balance.""" + """3-character alphabetic ISO 4217 code for the local currency of the balance.""" financial_account_token: str """Globally unique identifier for the financial account that holds this balance.""" diff --git a/src/lithic/types/balance_list_params.py b/src/lithic/types/balance_list_params.py index 94a52f3a..e2829698 100644 --- a/src/lithic/types/balance_list_params.py +++ b/src/lithic/types/balance_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -21,5 +21,8 @@ class BalanceListParams(TypedDict, total=False): Defaults to latest available balances """ + business_account_token: str + """List balances for all financial accounts of a given business_account_token.""" + financial_account_type: Literal["ISSUING", "OPERATING", "RESERVE"] """List balances for a given Financial Account type.""" diff --git a/src/lithic/types/book_transfer_create_params.py b/src/lithic/types/book_transfer_create_params.py new file mode 100644 index 00000000..672fb095 --- /dev/null +++ b/src/lithic/types/book_transfer_create_params.py @@ -0,0 +1,82 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BookTransferCreateParams"] + + +class BookTransferCreateParams(TypedDict, total=False): + amount: Required[int] + """Amount to be transferred in the currency’s smallest unit (e.g., cents for USD). + + This should always be a positive value. + """ + + category: Required[ + Literal["ADJUSTMENT", "BALANCE_OR_FUNDING", "DERECOGNITION", "DISPUTE", "FEE", "REWARD", "TRANSFER"] + ] + """Category of the book transfer""" + + from_financial_account_token: Required[str] + """ + Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + """ + + subtype: Required[str] + """The program specific subtype code for the specified category/type.""" + + to_financial_account_token: Required[str] + """ + Globally unique identifier for the financial account or card that will receive + the funds. Accepted type dependent on the program's use case. + """ + + type: Required[ + Literal[ + "ATM_WITHDRAWAL", + "ATM_DECLINE", + "INTERNATIONAL_ATM_WITHDRAWAL", + "INACTIVITY", + "STATEMENT", + "MONTHLY", + "QUARTERLY", + "ANNUAL", + "CUSTOMER_SERVICE", + "ACCOUNT_MAINTENANCE", + "ACCOUNT_ACTIVATION", + "ACCOUNT_CLOSURE", + "CARD_REPLACEMENT", + "CARD_DELIVERY", + "CARD_CREATE", + "CURRENCY_CONVERSION", + "INTEREST", + "LATE_PAYMENT", + "BILL_PAYMENT", + "CASH_BACK", + "ACCOUNT_TO_ACCOUNT", + "CARD_TO_CARD", + "DISBURSE", + "BILLING_ERROR", + "LOSS_WRITE_OFF", + "EXPIRED_CARD", + "EARLY_DERECOGNITION", + "ESCHEATMENT", + "INACTIVITY_FEE_DOWN", + "PROVISIONAL_CREDIT", + "DISPUTE_WON", + "TRANSFER", + ] + ] + """Type of book_transfer""" + + token: str + """Customer-provided token that will serve as an idempotency token. + + This token will become the transaction token. + """ + + memo: str + """Optional descriptor for the transfer.""" diff --git a/src/lithic/types/book_transfer_list_params.py b/src/lithic/types/book_transfer_list_params.py new file mode 100644 index 00000000..36b406f7 --- /dev/null +++ b/src/lithic/types/book_transfer_list_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["BookTransferListParams"] + + +class BookTransferListParams(TypedDict, total=False): + account_token: str + + begin: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created after the specified time will be included. UTC time zone. + """ + + business_account_token: str + + category: Literal["BALANCE_OR_FUNDING", "FEE", "REWARD", "ADJUSTMENT", "DERECOGNITION", "DISPUTE", "INTERNAL"] + """Book Transfer category to be returned.""" + + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created before the specified time will be included. UTC time zone. + """ + + ending_before: str + """A cursor representing an item's token before which a page of results should end. + + Used to retrieve the previous page of results before this item. + """ + + financial_account_token: str + """ + Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + """ + + page_size: int + """Page size (for pagination).""" + + result: Literal["APPROVED", "DECLINED"] + """Book transfer result to be returned.""" + + starting_after: str + """A cursor representing an item's token after which a page of results should + begin. + + Used to retrieve the next page of results after this item. + """ + + status: Literal["DECLINED", "SETTLED"] + """Book transfer status to be returned.""" diff --git a/src/lithic/types/book_transfer_response.py b/src/lithic/types/book_transfer_response.py new file mode 100644 index 00000000..49fb5663 --- /dev/null +++ b/src/lithic/types/book_transfer_response.py @@ -0,0 +1,105 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["BookTransferResponse", "Event"] + + +class Event(BaseModel): + token: str + """Globally unique identifier.""" + + amount: int + """ + Amount of the financial event that has been settled in the currency's smallest + unit (e.g., cents). + """ + + created: datetime + """Date and time when the financial event occurred. UTC time zone.""" + + detailed_results: List[Literal["APPROVED", "FUNDS_INSUFFICIENT"]] + """Detailed Results""" + + memo: str + """Memo for the transfer.""" + + result: Literal["APPROVED", "DECLINED"] + """ + APPROVED financial events were successful while DECLINED financial events were + declined by user, Lithic, or the network. + """ + + subtype: str + """The program specific subtype code for the specified category/type.""" + + type: str + """Type of the book transfer""" + + +class BookTransferResponse(BaseModel): + token: str + """Customer-provided token that will serve as an idempotency token. + + This token will become the transaction token. + """ + + category: Literal["ADJUSTMENT", "BALANCE_OR_FUNDING", "DERECOGNITION", "DISPUTE", "FEE", "REWARD", "TRANSFER"] + """Category of the book transfer""" + + created: datetime + """Date and time when the transfer occurred. UTC time zone.""" + + currency: str + """ + 3-character alphabetic ISO 4217 code for the settling currency of the + transaction. + """ + + events: List[Event] + """A list of all financial events that have modified this transfer.""" + + from_financial_account_token: str + """ + Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + """ + + pending_amount: int + """ + Pending amount of the transaction in the currency's smallest unit (e.g., cents), + including any acquirer fees. The value of this field will go to zero over time + once the financial transaction is settled. + """ + + result: Literal["APPROVED", "DECLINED"] + """ + APPROVED transactions were successful while DECLINED transactions were declined + by user, Lithic, or the network. + """ + + settled_amount: int + """ + Amount of the transaction that has been settled in the currency's smallest unit + (e.g., cents). + """ + + status: Literal["DECLINED", "REVERSED", "SETTLED"] + """Status types: \\** `DECLINED` - The transfer was declined. + + - `REVERSED` - The transfer was reversed \\** `SETTLED` - The transfer is + completed. + """ + + to_financial_account_token: object + """ + Globally unique identifier for the financial account or card that will receive + the funds. Accepted type dependent on the program's use case. + """ + + updated: datetime + """Date and time when the financial transaction was last updated. UTC time zone.""" diff --git a/src/lithic/types/book_transfer_reverse_params.py b/src/lithic/types/book_transfer_reverse_params.py new file mode 100644 index 00000000..11ec3df6 --- /dev/null +++ b/src/lithic/types/book_transfer_reverse_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BookTransferReverseParams"] + + +class BookTransferReverseParams(TypedDict, total=False): + memo: str + """Optional descriptor for the reversal.""" diff --git a/src/lithic/types/business_account.py b/src/lithic/types/business_account.py deleted file mode 100644 index 2203e0c1..00000000 --- a/src/lithic/types/business_account.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import Optional - -from .._models import BaseModel - -__all__ = ["BusinessAccount", "CollectionsConfiguration"] - - -class CollectionsConfiguration(BaseModel): - billing_period: int - """Number of days within the billing period""" - - payment_period: int - """Number of days after the billing period ends that a payment is required""" - - external_bank_account_token: Optional[str] = None - """The external bank account token to use for auto-collections""" - - -class BusinessAccount(BaseModel): - token: str - """Account token""" - - collections_configuration: Optional[CollectionsConfiguration] = None - - credit_limit: Optional[int] = None - """Credit limit extended to the Account""" diff --git a/src/lithic/types/card.py b/src/lithic/types/card.py index 65a45b61..e722490d 100644 --- a/src/lithic/types/card.py +++ b/src/lithic/types/card.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime @@ -66,14 +66,22 @@ class Card(BaseModel): """An RFC 3339 timestamp for when the card was created. UTC time zone.""" funding: Funding + """Deprecated: Funding account for the card.""" last_four: str """Last four digits of the card number.""" + pin_status: Literal["OK", "BLOCKED", "NOT_SET"] + """Indicates if a card is blocked due a PIN status issue (e.g. + + excessive incorrect attempts). + """ + spend_limit: int - """Amount (in cents) to limit approved authorizations. + """Amount (in cents) to limit approved authorizations (e.g. - Transaction requests above the spend limit will be declined. + 100000 would be a $1,000 limit). Transaction requests above the spend limit will + be declined. """ spend_limit_duration: SpendLimitDuration @@ -104,18 +112,18 @@ class Card(BaseModel): card is provisioned pending manufacturing and fulfillment. Cards in this state can accept authorizations for e-commerce purchases, but not for "Card Present" purchases where the physical card itself is present. - - `PENDING_ACTIVATION` - Each business day at 2pm Eastern Time Zone (ET), cards - of type `PHYSICAL` in state `PENDING_FULFILLMENT` are sent to the card - production warehouse and updated to state `PENDING_ACTIVATION` . Similar to - `PENDING_FULFILLMENT`, cards in this state can be used for e-commerce - transactions. API clients should update the card's state to `OPEN` only after - the cardholder confirms receipt of the card. + - `PENDING_ACTIVATION` - At regular intervals, cards of type `PHYSICAL` in state + `PENDING_FULFILLMENT` are sent to the card production warehouse and updated to + state `PENDING_ACTIVATION` . Similar to `PENDING_FULFILLMENT`, cards in this + state can be used for e-commerce transactions or can be added to mobile + wallets. API clients should update the card's state to `OPEN` only after the + cardholder confirms receipt of the card. In sandbox, the same daily batch fulfillment occurs, but no cards are actually manufactured. """ - type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL"] + type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"] """Card types: - `VIRTUAL` - Card will authorize at any merchant and can be added to a digital @@ -128,10 +136,22 @@ class Card(BaseModel): - `SINGLE_USE` - Card is closed upon first successful authorization. - `MERCHANT_LOCKED` - _[Deprecated]_ Card is locked to the first merchant that successfully authorizes the card. + - `UNLOCKED` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please use + VIRTUAL instead. + - `DIGITAL_WALLET` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please + use VIRTUAL instead. """ auth_rule_tokens: Optional[List[str]] = None - """List of identifiers for the Auth Rule(s) that are applied on the card.""" + """ + List of identifiers for the Auth Rule(s) that are applied on the card. This + field is deprecated and will no longer be populated in the `Card` object. The + key will be removed from the schema in a future release. Use the `/auth_rules` + endpoints to fetch Auth Rule information instead. + """ + + cardholder_currency: Optional[str] = None + """3-character alphabetic ISO 4217 code for the currency of the cardholder.""" cvv: Optional[str] = None """Three digit cvv printed on the back of the card.""" @@ -154,11 +174,7 @@ class Card(BaseModel): """Hostname of card’s locked merchant (will be empty if not applicable).""" memo: Optional[str] = None - """Friendly name to identify the card. - - We recommend against using this field to store JSON data as it can cause - unexpected behavior. - """ + """Friendly name to identify the card.""" pan: Optional[str] = None """Primary Account Number (PAN) (i.e. @@ -168,9 +184,22 @@ class Card(BaseModel): [support@lithic.com](mailto:support@lithic.com) for questions. """ + pending_commands: Optional[List[str]] = None + """ + Indicates if there are offline PIN changes pending card interaction with an + offline PIN terminal. Possible commands are: CHANGE_PIN, UNBLOCK_PIN. Applicable + only to cards issued in markets supporting offline PINs. + """ + product_id: Optional[str] = None """Only applicable to cards of type `PHYSICAL`. This must be configured with Lithic before use. Specifies the configuration (i.e., physical card art) that the card should be manufactured with. """ + + replacement_for: Optional[str] = None + """ + If the card is a replacement for another card, the globally unique identifier + for the card that was replaced. + """ diff --git a/src/lithic/types/card_convert_physical_params.py b/src/lithic/types/card_convert_physical_params.py new file mode 100644 index 00000000..745c0426 --- /dev/null +++ b/src/lithic/types/card_convert_physical_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .shared_params.carrier import Carrier +from .shared_params.shipping_address import ShippingAddress + +__all__ = ["CardConvertPhysicalParams"] + + +class CardConvertPhysicalParams(TypedDict, total=False): + shipping_address: Required[ShippingAddress] + """The shipping address this card will be sent to.""" + + carrier: Carrier + """If omitted, the previous carrier will be used.""" + + product_id: str + """Specifies the configuration (e.g. + + physical card art) that the card should be manufactured with, and only applies + to cards of type `PHYSICAL`. This must be configured with Lithic before use. + """ + + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + """Shipping method for the card. + + Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` + require additional permissions. + + - `STANDARD` - USPS regular mail or similar international option, with no + tracking + - `STANDARD_WITH_TRACKING` - USPS regular mail or similar international option, + with tracking + - `PRIORITY` - USPS Priority, 1-3 day shipping, with tracking + - `EXPRESS` - FedEx Express, 3-day shipping, with tracking + - `2_DAY` - FedEx 2-day shipping, with tracking + - `EXPEDITED` - FedEx Standard Overnight or similar international option, with + tracking + """ diff --git a/src/lithic/types/card_create_params.py b/src/lithic/types/card_create_params.py index 02ff4c76..fb295a5b 100644 --- a/src/lithic/types/card_create_params.py +++ b/src/lithic/types/card_create_params.py @@ -1,17 +1,18 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict -from ..types import shared_params from .spend_limit_duration import SpendLimitDuration +from .shared_params.carrier import Carrier +from .shared_params.shipping_address import ShippingAddress __all__ = ["CardCreateParams"] class CardCreateParams(TypedDict, total=False): - type: Required[Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL"]] + type: Required[Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"]] """Card types: - `VIRTUAL` - Card will authorize at any merchant and can be added to a digital @@ -24,6 +25,10 @@ class CardCreateParams(TypedDict, total=False): - `SINGLE_USE` - Card is closed upon first successful authorization. - `MERCHANT_LOCKED` - _[Deprecated]_ Card is locked to the first merchant that successfully authorizes the card. + - `UNLOCKED` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please use + VIRTUAL instead. + - `DIGITAL_WALLET` - _[Deprecated]_ Similar behavior to VIRTUAL cards, please + use VIRTUAL instead. """ account_token: str @@ -45,7 +50,7 @@ class CardCreateParams(TypedDict, total=False): test creating cards on specific card programs. """ - carrier: shared_params.Carrier + carrier: Carrier digital_card_art_token: str """ @@ -70,17 +75,13 @@ class CardCreateParams(TypedDict, total=False): """ memo: str - """Friendly name to identify the card. - - We recommend against using this field to store JSON data as it can cause - unexpected behavior. - """ + """Friendly name to identify the card.""" pin: str """Encrypted PIN block (in base64). - Only applies to cards of type `PHYSICAL` and `VIRTUAL`. See - [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + Applies to cards of type `PHYSICAL` and `VIRTUAL`. See + [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). """ product_id: str @@ -90,13 +91,24 @@ class CardCreateParams(TypedDict, total=False): (i.e., physical card art) that the card should be manufactured with. """ + replacement_account_token: str + """Restricted field limited to select use cases. + + Lithic will reach out directly if this field should be used. Globally unique + identifier for the replacement card's account. If this field is specified, + `replacement_for` must also be specified. If `replacement_for` is specified and + this field is omitted, the replacement card's account will be inferred from the + card being replaced. + """ + replacement_for: str - """Only applicable to cards of type `PHYSICAL`. + """Globally unique identifier for the card that this card will replace. - Globally unique identifier for the card that this physical card will replace. + If the card type is `PHYSICAL` it will be replaced by a `PHYSICAL` card. If the + card type is `VIRTUAL` it will be replaced by a `VIRTUAL` card. """ - shipping_address: shared_params.ShippingAddress + shipping_address: ShippingAddress shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] """Shipping method for the card. @@ -116,12 +128,12 @@ class CardCreateParams(TypedDict, total=False): """ spend_limit: int - """Amount (in cents) to limit approved authorizations. + """Amount (in cents) to limit approved authorizations (e.g. - Transaction requests above the spend limit will be declined. Note that a spend - limit of 0 is effectively no limit, and should only be used to reset or remove a - prior limit. Only a limit of 1 or above will result in declined transactions due - to checks against the card limit. + 100000 would be a $1,000 limit). Transaction requests above the spend limit will + be declined. Note that a spend limit of 0 is effectively no limit, and should + only be used to reset or remove a prior limit. Only a limit of 1 or above will + result in declined transactions due to checks against the card limit. """ spend_limit_duration: SpendLimitDuration diff --git a/src/lithic/types/card_embed_params.py b/src/lithic/types/card_embed_params.py index 97b9c764..7e21ab6d 100644 --- a/src/lithic/types/card_embed_params.py +++ b/src/lithic/types/card_embed_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/card_embed_response.py b/src/lithic/types/card_embed_response.py index 5e480f33..7336c3be 100644 --- a/src/lithic/types/card_embed_response.py +++ b/src/lithic/types/card_embed_response.py @@ -1,6 +1,7 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing_extensions import TypeAlias __all__ = ["CardEmbedResponse"] -CardEmbedResponse = str +CardEmbedResponse: TypeAlias = str diff --git a/src/lithic/types/card_get_embed_html_params.py b/src/lithic/types/card_get_embed_html_params.py deleted file mode 100644 index 86bb86d3..00000000 --- a/src/lithic/types/card_get_embed_html_params.py +++ /dev/null @@ -1,41 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import Union -from datetime import datetime -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["CardGetEmbedHTMLParams"] - - -class CardGetEmbedHTMLParams(TypedDict, total=False): - token: Required[str] - """Globally unique identifier for the card to be displayed.""" - - css: str - """ - A publicly available URI, so the white-labeled card element can be styled with - the client's branding. - """ - - expiration: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] - """An RFC 3339 timestamp for when the request should expire. UTC time zone. - - If no timezone is specified, UTC will be used. If payload does not contain an - expiration, the request will never expire. - - Using an `expiration` reduces the risk of a - [replay attack](https://en.wikipedia.org/wiki/Replay_attack). Without supplying - the `expiration`, in the event that a malicious user gets a copy of your request - in transit, they will be able to obtain the response data indefinitely. - """ - - target_origin: str - """Required if you want to post the element clicked to the parent iframe. - - If you supply this param, you can also capture click events in the parent iframe - by adding an event listener. - """ diff --git a/src/lithic/types/card_get_embed_url_params.py b/src/lithic/types/card_get_embed_url_params.py deleted file mode 100644 index df77c553..00000000 --- a/src/lithic/types/card_get_embed_url_params.py +++ /dev/null @@ -1,41 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import Union -from datetime import datetime -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["CardGetEmbedURLParams"] - - -class CardGetEmbedURLParams(TypedDict, total=False): - token: Required[str] - """Globally unique identifier for the card to be displayed.""" - - css: str - """ - A publicly available URI, so the white-labeled card element can be styled with - the client's branding. - """ - - expiration: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] - """An RFC 3339 timestamp for when the request should expire. UTC time zone. - - If no timezone is specified, UTC will be used. If payload does not contain an - expiration, the request will never expire. - - Using an `expiration` reduces the risk of a - [replay attack](https://en.wikipedia.org/wiki/Replay_attack). Without supplying - the `expiration`, in the event that a malicious user gets a copy of your request - in transit, they will be able to obtain the response data indefinitely. - """ - - target_origin: str - """Required if you want to post the element clicked to the parent iframe. - - If you supply this param, you can also capture click events in the parent iframe - by adding an event listener. - """ diff --git a/src/lithic/types/card_list_params.py b/src/lithic/types/card_list_params.py index df74475a..13dc9e53 100644 --- a/src/lithic/types/card_list_params.py +++ b/src/lithic/types/card_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/card_product_credit_detail_response.py b/src/lithic/types/card_product_credit_detail_response.py deleted file mode 100644 index 025a73b5..00000000 --- a/src/lithic/types/card_product_credit_detail_response.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from .._models import BaseModel - -__all__ = ["CardProductCreditDetailResponse"] - - -class CardProductCreditDetailResponse(BaseModel): - credit_extended: int - """The amount of credit extended within the program""" - - credit_limit: int - """The total credit limit of the program""" diff --git a/src/lithic/types/card_program.py b/src/lithic/types/card_program.py index 858502a1..38a7a063 100644 --- a/src/lithic/types/card_program.py +++ b/src/lithic/types/card_program.py @@ -1,5 +1,6 @@ -# File generated from our OpenAPI spec by Stainless. +# 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 @@ -22,3 +23,12 @@ class CardProgram(BaseModel): pan_range_start: str """The first digits of the card number that this card program starts with.""" + + cardholder_currency: Optional[str] = None + """3-character alphabetic ISO 4217 code for the currency of the cardholder.""" + + settlement_currencies: Optional[List[str]] = None + """ + List of 3-character alphabetic ISO 4217 codes for the currencies that the card + program supports for settlement. + """ diff --git a/src/lithic/types/card_program_list_params.py b/src/lithic/types/card_program_list_params.py index 15257297..cfaa30b8 100644 --- a/src/lithic/types/card_program_list_params.py +++ b/src/lithic/types/card_program_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/card_provision_params.py b/src/lithic/types/card_provision_params.py index bde462a9..48ba1e86 100644 --- a/src/lithic/types/card_provision_params.py +++ b/src/lithic/types/card_provision_params.py @@ -1,14 +1,18 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing_extensions import Literal, TypedDict +from typing import Union +from typing_extensions import Literal, Annotated, TypedDict + +from .._types import Base64FileInput +from .._utils import PropertyInfo __all__ = ["CardProvisionParams"] class CardProvisionParams(TypedDict, total=False): - certificate: str + certificate: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] """Only applicable if `digital_wallet` is `APPLE_PAY`. Omit to receive only `activationData` in the response. Apple's public leaf @@ -17,17 +21,31 @@ class CardProvisionParams(TypedDict, total=False): wallet. """ + client_device_id: str + """ + Only applicable if `digital_wallet` is `GOOGLE_PAY` or `SAMSUNG_PAY` and the + card is on the Visa network. Stable device identification set by the wallet + provider. + """ + + client_wallet_account_id: str + """ + Only applicable if `digital_wallet` is `GOOGLE_PAY` or `SAMSUNG_PAY` and the + card is on the Visa network. Consumer ID that identifies the wallet account + holder entity. + """ + digital_wallet: Literal["APPLE_PAY", "GOOGLE_PAY", "SAMSUNG_PAY"] """Name of digital wallet provider.""" - nonce: str + nonce: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] """Only applicable if `digital_wallet` is `APPLE_PAY`. Omit to receive only `activationData` in the response. Base64 cryptographic nonce provided by the device's wallet. """ - nonce_signature: str + nonce_signature: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] """Only applicable if `digital_wallet` is `APPLE_PAY`. Omit to receive only `activationData` in the response. Base64 cryptographic diff --git a/src/lithic/types/card_provision_response.py b/src/lithic/types/card_provision_response.py index 24c9fa70..fe565e0e 100644 --- a/src/lithic/types/card_provision_response.py +++ b/src/lithic/types/card_provision_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/card_reissue_params.py b/src/lithic/types/card_reissue_params.py index db5da95c..ca25add9 100644 --- a/src/lithic/types/card_reissue_params.py +++ b/src/lithic/types/card_reissue_params.py @@ -1,16 +1,17 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, TypedDict -from ..types import shared_params +from .shared_params.carrier import Carrier +from .shared_params.shipping_address import ShippingAddress __all__ = ["CardReissueParams"] class CardReissueParams(TypedDict, total=False): - carrier: shared_params.Carrier + carrier: Carrier """If omitted, the previous carrier will be used.""" product_id: str @@ -20,13 +21,14 @@ class CardReissueParams(TypedDict, total=False): to cards of type `PHYSICAL`. This must be configured with Lithic before use. """ - shipping_address: shared_params.ShippingAddress + shipping_address: ShippingAddress """If omitted, the previous shipping address will be used.""" - shipping_method: Literal["2-DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] """Shipping method for the card. - Use of options besides `STANDARD` require additional permissions. + Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` + require additional permissions. - `STANDARD` - USPS regular mail or similar international option, with no tracking diff --git a/src/lithic/types/card_renew_params.py b/src/lithic/types/card_renew_params.py index a3d6fd9a..832b2ca6 100644 --- a/src/lithic/types/card_renew_params.py +++ b/src/lithic/types/card_renew_params.py @@ -1,19 +1,20 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict -from ..types import shared_params +from .shared_params.carrier import Carrier +from .shared_params.shipping_address import ShippingAddress __all__ = ["CardRenewParams"] class CardRenewParams(TypedDict, total=False): - shipping_address: Required[shared_params.ShippingAddress] + shipping_address: Required[ShippingAddress] """The shipping address this card will be sent to.""" - carrier: shared_params.Carrier + carrier: Carrier """If omitted, the previous carrier will be used.""" exp_month: str @@ -37,10 +38,11 @@ class CardRenewParams(TypedDict, total=False): to cards of type `PHYSICAL`. This must be configured with Lithic before use. """ - shipping_method: Literal["2-DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] """Shipping method for the card. - Use of options besides `STANDARD` require additional permissions. + Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` + require additional permissions. - `STANDARD` - USPS regular mail or similar international option, with no tracking diff --git a/src/lithic/types/card_search_by_pan_params.py b/src/lithic/types/card_search_by_pan_params.py index c9ec8935..2a0504ef 100644 --- a/src/lithic/types/card_search_by_pan_params.py +++ b/src/lithic/types/card_search_by_pan_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/card_spend_limits.py b/src/lithic/types/card_spend_limits.py index 782d2a7e..4517050e 100644 --- a/src/lithic/types/card_spend_limits.py +++ b/src/lithic/types/card_spend_limits.py @@ -1,22 +1,66 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from .._models import BaseModel -__all__ = ["CardSpendLimits", "AvailableSpendLimit"] +__all__ = ["CardSpendLimits", "AvailableSpendLimit", "SpendLimit", "SpendVelocity"] class AvailableSpendLimit(BaseModel): annually: Optional[int] = None - """The available spend limit relative to the annual limit configured on the Card.""" + """ + The available spend limit (in cents) relative to the annual limit configured on + the Card (e.g. 100000 would be a $1,000 limit). + """ forever: Optional[int] = None - """The available spend limit relative to the forever limit configured on the Card.""" + """ + The available spend limit (in cents) relative to the forever limit configured on + the Card. + """ monthly: Optional[int] = None - """The available spend limit relative to the monthly limit configured on the Card.""" + """ + The available spend limit (in cents) relative to the monthly limit configured on + the Card. + """ + + +class SpendLimit(BaseModel): + annually: Optional[int] = None + """The configured annual spend limit (in cents) on the Card.""" + + forever: Optional[int] = None + """The configured forever spend limit (in cents) on the Card.""" + + monthly: Optional[int] = None + """The configured monthly spend limit (in cents) on the Card.""" + + +class SpendVelocity(BaseModel): + annually: Optional[int] = None + """Current annual spend velocity (in cents) on the Card. + + Present if annual spend limit is set. + """ + + forever: Optional[int] = None + """Current forever spend velocity (in cents) on the Card. + + Present if forever spend limit is set. + """ + + monthly: Optional[int] = None + """Current monthly spend velocity (in cents) on the Card. + + Present if monthly spend limit is set. + """ class CardSpendLimits(BaseModel): available_spend_limit: AvailableSpendLimit + + spend_limit: Optional[SpendLimit] = None + + spend_velocity: Optional[SpendVelocity] = None diff --git a/src/lithic/types/card_update_params.py b/src/lithic/types/card_update_params.py index e5d52a7c..8eb72622 100644 --- a/src/lithic/types/card_update_params.py +++ b/src/lithic/types/card_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -10,12 +10,6 @@ class CardUpdateParams(TypedDict, total=False): - auth_rule_token: str - """ - Identifier for any Auth Rules that will be applied to transactions taking place - with the card. - """ - digital_card_art_token: str """ Specifies the digital card art to be displayed in the user’s digital wallet @@ -25,26 +19,29 @@ class CardUpdateParams(TypedDict, total=False): """ memo: str - """Friendly name to identify the card. - - We recommend against using this field to store JSON data as it can cause - unexpected behavior. - """ + """Friendly name to identify the card.""" pin: str """Encrypted PIN block (in base64). - Only applies to cards of type `PHYSICAL` and `VIRTUAL`. See - [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block-enterprise). + Only applies to cards of type `PHYSICAL` and `VIRTUAL`. Changing PIN also resets + PIN status to `OK`. See + [Encrypted PIN Block](https://docs.lithic.com/docs/cards#encrypted-pin-block). + """ + + pin_status: Literal["OK"] + """Indicates if a card is blocked due a PIN status issue (e.g. + + excessive incorrect attempts). Can only be set to `OK` to unblock a card. """ spend_limit: int - """Amount (in cents) to limit approved authorizations. + """Amount (in cents) to limit approved authorizations (e.g. - Transaction requests above the spend limit will be declined. Note that a spend - limit of 0 is effectively no limit, and should only be used to reset or remove a - prior limit. Only a limit of 1 or above will result in declined transactions due - to checks against the card limit. + 100000 would be a $1,000 limit). Transaction requests above the spend limit will + be declined. Note that a spend limit of 0 is effectively no limit, and should + only be used to reset or remove a prior limit. Only a limit of 1 or above will + result in declined transactions due to checks against the card limit. """ spend_limit_duration: SpendLimitDuration diff --git a/src/lithic/types/cards/__init__.py b/src/lithic/types/cards/__init__.py index be61455b..5c7241a4 100644 --- a/src/lithic/types/cards/__init__.py +++ b/src/lithic/types/cards/__init__.py @@ -1,8 +1,9 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .balance_list_params import BalanceListParams as BalanceListParams +from .balance_list_response import BalanceListResponse as BalanceListResponse from .aggregate_balance_list_params import AggregateBalanceListParams as AggregateBalanceListParams from .aggregate_balance_list_response import AggregateBalanceListResponse as AggregateBalanceListResponse from .financial_transaction_list_params import FinancialTransactionListParams as FinancialTransactionListParams diff --git a/src/lithic/types/cards/aggregate_balance_list_params.py b/src/lithic/types/cards/aggregate_balance_list_params.py index 240c51a4..51b927dc 100644 --- a/src/lithic/types/cards/aggregate_balance_list_params.py +++ b/src/lithic/types/cards/aggregate_balance_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/cards/aggregate_balance_list_response.py b/src/lithic/types/cards/aggregate_balance_list_response.py index 849e15ab..0b3728ca 100644 --- a/src/lithic/types/cards/aggregate_balance_list_response.py +++ b/src/lithic/types/cards/aggregate_balance_list_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime @@ -15,7 +15,7 @@ class AggregateBalanceListResponse(BaseModel): """Date and time for when the balance was first created.""" currency: str - """3-digit alphabetic ISO 4217 code for the local currency of the balance.""" + """3-character alphabetic ISO 4217 code for the local currency of the balance.""" last_card_token: str """ diff --git a/src/lithic/types/cards/balance_list_params.py b/src/lithic/types/cards/balance_list_params.py index b80030a0..be24dded 100644 --- a/src/lithic/types/cards/balance_list_params.py +++ b/src/lithic/types/cards/balance_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/cards/balance_list_response.py b/src/lithic/types/cards/balance_list_response.py new file mode 100644 index 00000000..83f06974 --- /dev/null +++ b/src/lithic/types/cards/balance_list_response.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BalanceListResponse"] + + +class BalanceListResponse(BaseModel): + token: str + """Globally unique identifier for the financial account that holds this balance.""" + + available_amount: int + """Funds available for spend in the currency's smallest unit (e.g., cents for USD)""" + + created: datetime + """Date and time for when the balance was first created.""" + + currency: str + """3-character alphabetic ISO 4217 code for the local currency of the balance.""" + + last_transaction_event_token: str + """ + Globally unique identifier for the last financial transaction event that + impacted this balance. + """ + + last_transaction_token: str + """ + Globally unique identifier for the last financial transaction that impacted this + balance. + """ + + pending_amount: int + """Funds not available for spend due to card authorizations or pending ACH release. + + Shown in the currency's smallest unit (e.g., cents for USD). + """ + + total_amount: int + """ + The sum of available and pending balance in the currency's smallest unit (e.g., + cents for USD). + """ + + type: Literal["ISSUING", "OPERATING", "RESERVE"] + """Type of financial account.""" + + updated: datetime + """Date and time for when the balance was last updated.""" diff --git a/src/lithic/types/cards/financial_transaction_list_params.py b/src/lithic/types/cards/financial_transaction_list_params.py index 8c237520..04bdf49f 100644 --- a/src/lithic/types/cards/financial_transaction_list_params.py +++ b/src/lithic/types/cards/financial_transaction_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/credit_products/__init__.py b/src/lithic/types/credit_products/__init__.py new file mode 100644 index 00000000..adbd039a --- /dev/null +++ b/src/lithic/types/credit_products/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .extended_credit import ExtendedCredit as ExtendedCredit +from .prime_rate_create_params import PrimeRateCreateParams as PrimeRateCreateParams +from .prime_rate_retrieve_params import PrimeRateRetrieveParams as PrimeRateRetrieveParams +from .prime_rate_retrieve_response import PrimeRateRetrieveResponse as PrimeRateRetrieveResponse diff --git a/src/lithic/types/credit_products/extended_credit.py b/src/lithic/types/credit_products/extended_credit.py new file mode 100644 index 00000000..ea070193 --- /dev/null +++ b/src/lithic/types/credit_products/extended_credit.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["ExtendedCredit"] + + +class ExtendedCredit(BaseModel): + credit_extended: int diff --git a/src/lithic/types/credit_products/prime_rate_create_params.py b/src/lithic/types/credit_products/prime_rate_create_params.py new file mode 100644 index 00000000..99ba37c6 --- /dev/null +++ b/src/lithic/types/credit_products/prime_rate_create_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["PrimeRateCreateParams"] + + +class PrimeRateCreateParams(TypedDict, total=False): + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + """Date the rate goes into effect""" + + rate: Required[str] + """The rate in decimal format""" diff --git a/src/lithic/types/credit_products/prime_rate_retrieve_params.py b/src/lithic/types/credit_products/prime_rate_retrieve_params.py new file mode 100644 index 00000000..2b61243a --- /dev/null +++ b/src/lithic/types/credit_products/prime_rate_retrieve_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["PrimeRateRetrieveParams"] + + +class PrimeRateRetrieveParams(TypedDict, total=False): + ending_before: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """The effective date that the prime rates ends before""" + + starting_after: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """The effective date that the prime rate starts after""" diff --git a/src/lithic/types/credit_products/prime_rate_retrieve_response.py b/src/lithic/types/credit_products/prime_rate_retrieve_response.py new file mode 100644 index 00000000..5d4e3d92 --- /dev/null +++ b/src/lithic/types/credit_products/prime_rate_retrieve_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from datetime import date + +from ..._models import BaseModel + +__all__ = ["PrimeRateRetrieveResponse", "Data"] + + +class Data(BaseModel): + effective_date: date + """Date the rate goes into effect""" + + rate: str + """The rate in decimal format""" + + +class PrimeRateRetrieveResponse(BaseModel): + data: List[Data] + """List of prime rates""" + + has_more: bool + """Whether there are more prime rates""" diff --git a/src/lithic/types/digital_card_art.py b/src/lithic/types/digital_card_art.py index a414fa9f..3df0a6d2 100644 --- a/src/lithic/types/digital_card_art.py +++ b/src/lithic/types/digital_card_art.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime diff --git a/src/lithic/types/digital_card_art_list_params.py b/src/lithic/types/digital_card_art_list_params.py index eb3ed626..5233633f 100644 --- a/src/lithic/types/digital_card_art_list_params.py +++ b/src/lithic/types/digital_card_art_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/dispute.py b/src/lithic/types/dispute.py index d9e5a863..e217d8e8 100644 --- a/src/lithic/types/dispute.py +++ b/src/lithic/types/dispute.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime diff --git a/src/lithic/types/dispute_create_params.py b/src/lithic/types/dispute_create_params.py index 4d981410..d515813b 100644 --- a/src/lithic/types/dispute_create_params.py +++ b/src/lithic/types/dispute_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/dispute_evidence.py b/src/lithic/types/dispute_evidence.py index a850e9b9..cd6f1958 100644 --- a/src/lithic/types/dispute_evidence.py +++ b/src/lithic/types/dispute_evidence.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime diff --git a/src/lithic/types/dispute_initiate_evidence_upload_params.py b/src/lithic/types/dispute_initiate_evidence_upload_params.py index 5ed6c9e0..29e610e7 100644 --- a/src/lithic/types/dispute_initiate_evidence_upload_params.py +++ b/src/lithic/types/dispute_initiate_evidence_upload_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/dispute_list_evidences_params.py b/src/lithic/types/dispute_list_evidences_params.py index c9b3f360..9c84266d 100644 --- a/src/lithic/types/dispute_list_evidences_params.py +++ b/src/lithic/types/dispute_list_evidences_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/dispute_list_params.py b/src/lithic/types/dispute_list_params.py index 6ad90569..fd12fdf5 100644 --- a/src/lithic/types/dispute_list_params.py +++ b/src/lithic/types/dispute_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/dispute_update_params.py b/src/lithic/types/dispute_update_params.py index 52b8ba74..de0b45aa 100644 --- a/src/lithic/types/dispute_update_params.py +++ b/src/lithic/types/dispute_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/event.py b/src/lithic/types/event.py index 035a2a26..ce2cbe4a 100644 --- a/src/lithic/types/event.py +++ b/src/lithic/types/event.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict from datetime import datetime @@ -23,20 +23,45 @@ class Event(BaseModel): "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] """Event types: @@ -47,8 +72,11 @@ class Event(BaseModel): verification is complete. - `card.created` - Notification that a card has been created. - `card.renewed` - Notification that a card has been renewed. + - `card.reissued` - Notification that a card has been reissued. - `card.shipped` - Physical card shipment notification. See https://docs.lithic.com/docs/cards#physical-card-shipped-webhook. + - `card.converted` - Notification that a virtual card has been converted to a + physical card. - `card_transaction.updated` - Transaction Lifecycle webhook. See https://docs.lithic.com/docs/transaction-webhooks. - `dispute.updated` - A dispute has been updated. @@ -59,6 +87,11 @@ class Event(BaseModel): - `digital_wallet.tokenization_two_factor_authentication_code` - A code to be passed to an end user to complete digital wallet authentication. See https://docs.lithic.com/docs/tokenization-control#digital-wallet-tokenization-auth-code. + - `digital_wallet.tokenization_two_factor_authentication_code_sent` - + Notification that a two factor authentication code for activating a digital + wallet has been sent to the end user. + - `digital_wallet.tokenization_updated` - Notification that a digital wallet + tokenization's status has changed. """ payload: Dict[str, object] diff --git a/src/lithic/types/event_list_attempts_params.py b/src/lithic/types/event_list_attempts_params.py index d2f594f6..cd087c03 100644 --- a/src/lithic/types/event_list_attempts_params.py +++ b/src/lithic/types/event_list_attempts_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/event_list_params.py b/src/lithic/types/event_list_params.py index e5eadac5..92820896 100644 --- a/src/lithic/types/event_list_params.py +++ b/src/lithic/types/event_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -35,20 +35,45 @@ class EventListParams(TypedDict, total=False): "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] """Event types to filter events by.""" diff --git a/src/lithic/types/event_resend_params.py b/src/lithic/types/event_resend_params.py deleted file mode 100644 index e381da50..00000000 --- a/src/lithic/types/event_resend_params.py +++ /dev/null @@ -1,41 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -from typing import Union -from datetime import datetime -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["EventResendParams"] - - -class EventResendParams(TypedDict, total=False): - token: Required[str] - """Globally unique identifier for the card to be displayed.""" - - css: str - """ - A publicly available URI, so the white-labeled card element can be styled with - the client's branding. - """ - - expiration: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] - """An RFC 3339 timestamp for when the request should expire. UTC time zone. - - If no timezone is specified, UTC will be used. If payload does not contain an - expiration, the request will never expire. - - Using an `expiration` reduces the risk of a - [replay attack](https://en.wikipedia.org/wiki/Replay_attack). Without supplying - the `expiration`, in the event that a malicious user gets a copy of your request - in transit, they will be able to obtain the response data indefinitely. - """ - - target_origin: str - """Required if you want to post the element clicked to the parent iframe. - - If you supply this param, you can also capture click events in the parent iframe - by adding an event listener. - """ diff --git a/src/lithic/types/event_subscription.py b/src/lithic/types/event_subscription.py index 28f8802f..acf5f6e7 100644 --- a/src/lithic/types/event_subscription.py +++ b/src/lithic/types/event_subscription.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal @@ -26,20 +26,45 @@ class EventSubscription(BaseModel): "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] ] = None diff --git a/src/lithic/types/events/__init__.py b/src/lithic/types/events/__init__.py index aea612e2..afe8a307 100644 --- a/src/lithic/types/events/__init__.py +++ b/src/lithic/types/events/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/events/subscription_create_params.py b/src/lithic/types/events/subscription_create_params.py index acd89ebb..00455890 100644 --- a/src/lithic/types/events/subscription_create_params.py +++ b/src/lithic/types/events/subscription_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -23,20 +23,45 @@ class SubscriptionCreateParams(TypedDict, total=False): "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] """Indicates types of events that will be sent to this subscription. diff --git a/src/lithic/types/events/subscription_list_attempts_params.py b/src/lithic/types/events/subscription_list_attempts_params.py index 00523a7d..ce3f2678 100644 --- a/src/lithic/types/events/subscription_list_attempts_params.py +++ b/src/lithic/types/events/subscription_list_attempts_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/events/subscription_list_params.py b/src/lithic/types/events/subscription_list_params.py index feb66ed8..bc25fa49 100644 --- a/src/lithic/types/events/subscription_list_params.py +++ b/src/lithic/types/events/subscription_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/events/subscription_recover_params.py b/src/lithic/types/events/subscription_recover_params.py index 46953dab..dcc4fe80 100644 --- a/src/lithic/types/events/subscription_recover_params.py +++ b/src/lithic/types/events/subscription_recover_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/events/subscription_replay_missing_params.py b/src/lithic/types/events/subscription_replay_missing_params.py index 06fd3b3d..ebb1a04a 100644 --- a/src/lithic/types/events/subscription_replay_missing_params.py +++ b/src/lithic/types/events/subscription_replay_missing_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/events/subscription_retrieve_secret_response.py b/src/lithic/types/events/subscription_retrieve_secret_response.py index 607372ad..467c4f8a 100644 --- a/src/lithic/types/events/subscription_retrieve_secret_response.py +++ b/src/lithic/types/events/subscription_retrieve_secret_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/events/subscription_send_simulated_example_params.py b/src/lithic/types/events/subscription_send_simulated_example_params.py index 6e62a6e8..cd949c85 100644 --- a/src/lithic/types/events/subscription_send_simulated_example_params.py +++ b/src/lithic/types/events/subscription_send_simulated_example_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -12,19 +12,44 @@ class SubscriptionSendSimulatedExampleParams(TypedDict, total=False): "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] """Event type to send example message for.""" diff --git a/src/lithic/types/events/subscription_update_params.py b/src/lithic/types/events/subscription_update_params.py index c6143566..1fd5687a 100644 --- a/src/lithic/types/events/subscription_update_params.py +++ b/src/lithic/types/events/subscription_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -23,20 +23,45 @@ class SubscriptionUpdateParams(TypedDict, total=False): "account_holder.created", "account_holder.updated", "account_holder.verification", + "auth_rules.performance_report.created", "balance.updated", + "book_transfer_transaction.created", "card.created", "card.renewed", + "card.reissued", + "card.converted", "card.shipped", "card_transaction.updated", "digital_wallet.tokenization_approval_request", "digital_wallet.tokenization_result", "digital_wallet.tokenization_two_factor_authentication_code", + "digital_wallet.tokenization_two_factor_authentication_code_sent", + "digital_wallet.tokenization_updated", "dispute.updated", "dispute_evidence.upload_failed", + "external_bank_account.created", + "external_bank_account.updated", + "external_payment.created", + "external_payment.updated", + "financial_account.created", + "financial_account.updated", + "loan_tape.created", + "loan_tape.updated", + "management_operation.created", + "management_operation.updated", "payment_transaction.created", "payment_transaction.updated", + "internal_transaction.created", + "internal_transaction.updated", + "settlement_report.updated", + "statements.created", "three_ds_authentication.created", - "transfer_transaction.created", + "three_ds_authentication.updated", + "tokenization.approval_request", + "tokenization.result", + "tokenization.two_factor_authentication_code", + "tokenization.two_factor_authentication_code_sent", + "tokenization.updated", ] ] """Indicates types of events that will be sent to this subscription. diff --git a/src/lithic/types/external_bank_account_address.py b/src/lithic/types/external_bank_account_address.py index 946a9e98..e755b393 100644 --- a/src/lithic/types/external_bank_account_address.py +++ b/src/lithic/types/external_bank_account_address.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/external_bank_account_address_param.py b/src/lithic/types/external_bank_account_address_param.py index d057e56c..f201ef84 100644 --- a/src/lithic/types/external_bank_account_address_param.py +++ b/src/lithic/types/external_bank_account_address_param.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/external_bank_account_create_params.py b/src/lithic/types/external_bank_account_create_params.py index 78168666..441ce050 100644 --- a/src/lithic/types/external_bank_account_create_params.py +++ b/src/lithic/types/external_bank_account_create_params.py @@ -1,10 +1,10 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from datetime import date -from typing_extensions import Literal, Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._utils import PropertyInfo from .owner_type import OwnerType @@ -15,76 +15,170 @@ "ExternalBankAccountCreateParams", "BankVerifiedCreateBankAccountAPIRequest", "PlaidCreateBankAccountAPIRequest", + "ExternallyVerifiedCreateBankAccountAPIRequest", ] class BankVerifiedCreateBankAccountAPIRequest(TypedDict, total=False): account_number: Required[str] + """Account Number""" country: Required[str] + """The country that the bank account is located in using ISO 3166-1. + + We will only accept USA bank accounts e.g., USA + """ currency: Required[str] + """currency of the external account 3-character alphabetic ISO 4217 code""" + + financial_account_token: Required[str] + """The financial account token of the operating account to fund the micro deposits""" owner: Required[str] + """Legal Name of the business or individual who owns the external account. + + This will appear in statements + """ owner_type: Required[OwnerType] + """Owner Type""" routing_number: Required[str] + """Routing Number""" type: Required[Literal["CHECKING", "SAVINGS"]] + """Account Type""" verification_method: Required[VerificationMethod] + """Verification Method""" account_token: str + """Indicates which Lithic account the external account is associated with. - address: ExternalBankAccountAddressParam - """ - Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. + For external accounts that are associated with the program, account_token field + returned will be null """ + address: ExternalBankAccountAddressParam + """Address""" + company_id: str + """Optional field that helps identify bank accounts in receipts""" dob: Annotated[Union[str, date], PropertyInfo(format="iso8601")] """Date of Birth of the Individual that owns the external bank account""" doing_business_as: str + """Doing Business As""" name: str + """The nickname for this External Bank Account""" user_defined_id: str + """User Defined ID""" verification_enforcement: bool - """Indicates whether verification was enforced for a given association record. - - For MICRO_DEPOSIT, option to disable verification if the external bank account - has already been verified before. By default, verification will be required - unless users pass in a value of false - """ class PlaidCreateBankAccountAPIRequest(TypedDict, total=False): owner: Required[str] + """Legal Name of the business or individual who owns the external account. + + This will appear in statements + """ owner_type: Required[OwnerType] + """Owner Type""" processor_token: Required[str] verification_method: Required[VerificationMethod] + """Verification Method""" account_token: str + """Indicates which Lithic account the external account is associated with. + + For external accounts that are associated with the program, account_token field + returned will be null + """ company_id: str + """Optional field that helps identify bank accounts in receipts""" dob: Annotated[Union[str, date], PropertyInfo(format="iso8601")] """Date of Birth of the Individual that owns the external bank account""" doing_business_as: str + """Doing Business As""" user_defined_id: str + """User Defined ID""" ExternalBankAccountAddress = ExternalBankAccountAddressParam """This type is deprecated, please use ExternalBankAccountAddressParam instead""" -ExternalBankAccountCreateParams = Union[BankVerifiedCreateBankAccountAPIRequest, PlaidCreateBankAccountAPIRequest] + +class ExternallyVerifiedCreateBankAccountAPIRequest(TypedDict, total=False): + account_number: Required[str] + """Account Number""" + + country: Required[str] + """The country that the bank account is located in using ISO 3166-1. + + We will only accept USA bank accounts e.g., USA + """ + + currency: Required[str] + """currency of the external account 3-character alphabetic ISO 4217 code""" + + owner: Required[str] + """Legal Name of the business or individual who owns the external account. + + This will appear in statements + """ + + owner_type: Required[OwnerType] + """Owner Type""" + + routing_number: Required[str] + """Routing Number""" + + type: Required[Literal["CHECKING", "SAVINGS"]] + """Account Type""" + + verification_method: Required[Literal["EXTERNALLY_VERIFIED"]] + """Verification Method""" + + account_token: str + """Indicates which Lithic account the external account is associated with. + + For external accounts that are associated with the program, account_token field + returned will be null + """ + + address: ExternalBankAccountAddressParam + """Address""" + + company_id: str + """Optional field that helps identify bank accounts in receipts""" + + dob: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Date of Birth of the Individual that owns the external bank account""" + + doing_business_as: str + """Doing Business As""" + + name: str + """The nickname for this External Bank Account""" + + user_defined_id: str + """User Defined ID""" + + +ExternalBankAccountCreateParams: TypeAlias = Union[ + BankVerifiedCreateBankAccountAPIRequest, + PlaidCreateBankAccountAPIRequest, + ExternallyVerifiedCreateBankAccountAPIRequest, +] diff --git a/src/lithic/types/external_bank_account_create_response.py b/src/lithic/types/external_bank_account_create_response.py index 593fd169..05a5eb46 100644 --- a/src/lithic/types/external_bank_account_create_response.py +++ b/src/lithic/types/external_bank_account_create_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import date, datetime @@ -32,7 +32,7 @@ class ExternalBankAccountCreateResponse(BaseModel): """ currency: str - """currency of the external account 3-digit alphabetic ISO 4217 code""" + """currency of the external account 3-character alphabetic ISO 4217 code""" last_four: str """The last 4 digits of the bank account. @@ -47,19 +47,25 @@ class ExternalBankAccountCreateResponse(BaseModel): """ owner_type: Literal["BUSINESS", "INDIVIDUAL"] + """Owner Type""" routing_number: str + """Routing Number""" - state: Literal["CLOSED", "ENABLED", "PAUSED"] + state: Literal["ENABLED", "CLOSED", "PAUSED"] + """Account State""" type: Literal["CHECKING", "SAVINGS"] + """Account Type""" verification_attempts: int """The number of attempts at verification""" verification_method: Literal["MANUAL", "MICRO_DEPOSIT", "PLAID", "PRENOTE"] + """Verification Method""" - verification_state: Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"] + verification_state: Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"] + """Verification State""" account_token: Optional[str] = None """Indicates which Lithic account the external account is associated with. @@ -69,10 +75,7 @@ class ExternalBankAccountCreateResponse(BaseModel): """ address: Optional[ExternalBankAccountAddress] = None - """ - Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. - """ + """Address""" company_id: Optional[str] = None """Optional field that helps identify bank accounts in receipts""" @@ -81,11 +84,16 @@ class ExternalBankAccountCreateResponse(BaseModel): """Date of Birth of the Individual that owns the external bank account""" doing_business_as: Optional[str] = None + """Doing Business As""" + + financial_account_token: Optional[str] = None + """The financial account token of the operating account to fund the micro deposits""" name: Optional[str] = None - """The nickname given to this record of External Bank Account""" + """The nickname for this External Bank Account""" user_defined_id: Optional[str] = None + """User Defined ID""" verification_failed_reason: Optional[str] = None """Optional free text description of the reason for the failed verification. diff --git a/src/lithic/types/external_bank_account_list_params.py b/src/lithic/types/external_bank_account_list_params.py index 561b6808..4a0a267d 100644 --- a/src/lithic/types/external_bank_account_list_params.py +++ b/src/lithic/types/external_bank_account_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -35,6 +35,6 @@ class ExternalBankAccountListParams(TypedDict, total=False): Used to retrieve the next page of results after this item. """ - states: List[Literal["CLOSED", "ENABLED", "PAUSED"]] + states: List[Literal["ENABLED", "CLOSED", "PAUSED"]] - verification_states: List[Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"]] + verification_states: List[Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"]] diff --git a/src/lithic/types/external_bank_account_list_response.py b/src/lithic/types/external_bank_account_list_response.py index 867b7937..d83bbb6e 100644 --- a/src/lithic/types/external_bank_account_list_response.py +++ b/src/lithic/types/external_bank_account_list_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import date, datetime @@ -32,7 +32,7 @@ class ExternalBankAccountListResponse(BaseModel): """ currency: str - """currency of the external account 3-digit alphabetic ISO 4217 code""" + """currency of the external account 3-character alphabetic ISO 4217 code""" last_four: str """The last 4 digits of the bank account. @@ -47,19 +47,25 @@ class ExternalBankAccountListResponse(BaseModel): """ owner_type: Literal["BUSINESS", "INDIVIDUAL"] + """Owner Type""" routing_number: str + """Routing Number""" - state: Literal["CLOSED", "ENABLED", "PAUSED"] + state: Literal["ENABLED", "CLOSED", "PAUSED"] + """Account State""" type: Literal["CHECKING", "SAVINGS"] + """Account Type""" verification_attempts: int """The number of attempts at verification""" verification_method: Literal["MANUAL", "MICRO_DEPOSIT", "PLAID", "PRENOTE"] + """Verification Method""" - verification_state: Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"] + verification_state: Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"] + """Verification State""" account_token: Optional[str] = None """Indicates which Lithic account the external account is associated with. @@ -69,10 +75,7 @@ class ExternalBankAccountListResponse(BaseModel): """ address: Optional[ExternalBankAccountAddress] = None - """ - Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. - """ + """Address""" company_id: Optional[str] = None """Optional field that helps identify bank accounts in receipts""" @@ -81,11 +84,16 @@ class ExternalBankAccountListResponse(BaseModel): """Date of Birth of the Individual that owns the external bank account""" doing_business_as: Optional[str] = None + """Doing Business As""" + + financial_account_token: Optional[str] = None + """The financial account token of the operating account to fund the micro deposits""" name: Optional[str] = None - """The nickname given to this record of External Bank Account""" + """The nickname for this External Bank Account""" user_defined_id: Optional[str] = None + """User Defined ID""" verification_failed_reason: Optional[str] = None """Optional free text description of the reason for the failed verification. diff --git a/src/lithic/types/external_bank_account_retrieve_response.py b/src/lithic/types/external_bank_account_retrieve_response.py index 7bb09881..89d46fb3 100644 --- a/src/lithic/types/external_bank_account_retrieve_response.py +++ b/src/lithic/types/external_bank_account_retrieve_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import date, datetime @@ -32,7 +32,7 @@ class ExternalBankAccountRetrieveResponse(BaseModel): """ currency: str - """currency of the external account 3-digit alphabetic ISO 4217 code""" + """currency of the external account 3-character alphabetic ISO 4217 code""" last_four: str """The last 4 digits of the bank account. @@ -47,19 +47,25 @@ class ExternalBankAccountRetrieveResponse(BaseModel): """ owner_type: Literal["BUSINESS", "INDIVIDUAL"] + """Owner Type""" routing_number: str + """Routing Number""" - state: Literal["CLOSED", "ENABLED", "PAUSED"] + state: Literal["ENABLED", "CLOSED", "PAUSED"] + """Account State""" type: Literal["CHECKING", "SAVINGS"] + """Account Type""" verification_attempts: int """The number of attempts at verification""" verification_method: Literal["MANUAL", "MICRO_DEPOSIT", "PLAID", "PRENOTE"] + """Verification Method""" - verification_state: Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"] + verification_state: Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"] + """Verification State""" account_token: Optional[str] = None """Indicates which Lithic account the external account is associated with. @@ -69,10 +75,7 @@ class ExternalBankAccountRetrieveResponse(BaseModel): """ address: Optional[ExternalBankAccountAddress] = None - """ - Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. - """ + """Address""" company_id: Optional[str] = None """Optional field that helps identify bank accounts in receipts""" @@ -81,11 +84,16 @@ class ExternalBankAccountRetrieveResponse(BaseModel): """Date of Birth of the Individual that owns the external bank account""" doing_business_as: Optional[str] = None + """Doing Business As""" + + financial_account_token: Optional[str] = None + """The financial account token of the operating account to fund the micro deposits""" name: Optional[str] = None - """The nickname given to this record of External Bank Account""" + """The nickname for this External Bank Account""" user_defined_id: Optional[str] = None + """User Defined ID""" verification_failed_reason: Optional[str] = None """Optional free text description of the reason for the failed verification. diff --git a/src/lithic/types/external_bank_account_retry_micro_deposits_params.py b/src/lithic/types/external_bank_account_retry_micro_deposits_params.py new file mode 100644 index 00000000..97bfa30b --- /dev/null +++ b/src/lithic/types/external_bank_account_retry_micro_deposits_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ExternalBankAccountRetryMicroDepositsParams"] + + +class ExternalBankAccountRetryMicroDepositsParams(TypedDict, total=False): + financial_account_token: str diff --git a/src/lithic/types/external_bank_account_retry_micro_deposits_response.py b/src/lithic/types/external_bank_account_retry_micro_deposits_response.py index be343ea7..3abe8d31 100644 --- a/src/lithic/types/external_bank_account_retry_micro_deposits_response.py +++ b/src/lithic/types/external_bank_account_retry_micro_deposits_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import date, datetime @@ -32,7 +32,7 @@ class ExternalBankAccountRetryMicroDepositsResponse(BaseModel): """ currency: str - """currency of the external account 3-digit alphabetic ISO 4217 code""" + """currency of the external account 3-character alphabetic ISO 4217 code""" last_four: str """The last 4 digits of the bank account. @@ -47,19 +47,25 @@ class ExternalBankAccountRetryMicroDepositsResponse(BaseModel): """ owner_type: Literal["BUSINESS", "INDIVIDUAL"] + """Owner Type""" routing_number: str + """Routing Number""" - state: Literal["CLOSED", "ENABLED", "PAUSED"] + state: Literal["ENABLED", "CLOSED", "PAUSED"] + """Account State""" type: Literal["CHECKING", "SAVINGS"] + """Account Type""" verification_attempts: int """The number of attempts at verification""" verification_method: Literal["MANUAL", "MICRO_DEPOSIT", "PLAID", "PRENOTE"] + """Verification Method""" - verification_state: Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"] + verification_state: Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"] + """Verification State""" account_token: Optional[str] = None """Indicates which Lithic account the external account is associated with. @@ -69,10 +75,7 @@ class ExternalBankAccountRetryMicroDepositsResponse(BaseModel): """ address: Optional[ExternalBankAccountAddress] = None - """ - Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. - """ + """Address""" company_id: Optional[str] = None """Optional field that helps identify bank accounts in receipts""" @@ -81,11 +84,16 @@ class ExternalBankAccountRetryMicroDepositsResponse(BaseModel): """Date of Birth of the Individual that owns the external bank account""" doing_business_as: Optional[str] = None + """Doing Business As""" + + financial_account_token: Optional[str] = None + """The financial account token of the operating account to fund the micro deposits""" name: Optional[str] = None - """The nickname given to this record of External Bank Account""" + """The nickname for this External Bank Account""" user_defined_id: Optional[str] = None + """User Defined ID""" verification_failed_reason: Optional[str] = None """Optional free text description of the reason for the failed verification. diff --git a/src/lithic/types/external_bank_account_retry_prenote_params.py b/src/lithic/types/external_bank_account_retry_prenote_params.py new file mode 100644 index 00000000..b86f2b44 --- /dev/null +++ b/src/lithic/types/external_bank_account_retry_prenote_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ExternalBankAccountRetryPrenoteParams"] + + +class ExternalBankAccountRetryPrenoteParams(TypedDict, total=False): + financial_account_token: str diff --git a/src/lithic/types/external_bank_account_retry_prenote_response.py b/src/lithic/types/external_bank_account_retry_prenote_response.py new file mode 100644 index 00000000..141d4cf3 --- /dev/null +++ b/src/lithic/types/external_bank_account_retry_prenote_response.py @@ -0,0 +1,105 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import date, datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .owner_type import OwnerType +from .verification_method import VerificationMethod +from .external_bank_account_address import ExternalBankAccountAddress + +__all__ = ["ExternalBankAccountRetryPrenoteResponse"] + + +class ExternalBankAccountRetryPrenoteResponse(BaseModel): + token: str + """ + A globally unique identifier for this record of an external bank account + association. If a program links an external bank account to more than one + end-user or to both the program and the end-user, then Lithic will return each + record of the association + """ + + country: str + """The country that the bank account is located in using ISO 3166-1. + + We will only accept USA bank accounts e.g., USA + """ + + created: datetime + """ + An ISO 8601 string representing when this funding source was added to the Lithic + account. + """ + + currency: str + """currency of the external account 3-character alphabetic ISO 4217 code""" + + last_four: str + """The last 4 digits of the bank account. + + Derived by Lithic from the account number passed + """ + + owner: str + """Legal Name of the business or individual who owns the external account. + + This will appear in statements + """ + + owner_type: OwnerType + """Owner Type""" + + routing_number: str + """Routing Number""" + + state: Literal["ENABLED", "CLOSED", "PAUSED"] + """Account State""" + + type: Literal["CHECKING", "SAVINGS"] + """Account Type""" + + verification_attempts: int + """The number of attempts at verification""" + + verification_method: VerificationMethod + """Verification Method""" + + verification_state: Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"] + """Verification State""" + + account_token: Optional[str] = None + """Indicates which Lithic account the external account is associated with. + + For external accounts that are associated with the program, account_token field + returned will be null + """ + + address: Optional[ExternalBankAccountAddress] = None + """Address""" + + company_id: Optional[str] = None + """Optional field that helps identify bank accounts in receipts""" + + dob: Optional[date] = None + """Date of Birth of the Individual that owns the external bank account""" + + doing_business_as: Optional[str] = None + """Doing Business As""" + + financial_account_token: Optional[str] = None + """The financial account token of the operating account to fund the micro deposits""" + + name: Optional[str] = None + """The nickname for this External Bank Account""" + + user_defined_id: Optional[str] = None + """User Defined ID""" + + verification_failed_reason: Optional[str] = None + """Optional free text description of the reason for the failed verification. + + For ACH micro-deposits returned, this field will display the reason return code + sent by the ACH network + """ diff --git a/src/lithic/types/external_bank_account_update_params.py b/src/lithic/types/external_bank_account_update_params.py index 68ac4e2d..d20a51e4 100644 --- a/src/lithic/types/external_bank_account_update_params.py +++ b/src/lithic/types/external_bank_account_update_params.py @@ -1,10 +1,10 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from datetime import date -from typing_extensions import Annotated, TypedDict +from typing_extensions import Literal, Annotated, TypedDict from .._utils import PropertyInfo from .owner_type import OwnerType @@ -15,25 +15,33 @@ class ExternalBankAccountUpdateParams(TypedDict, total=False): address: ExternalBankAccountAddressParam - """ - Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. - """ + """Address""" company_id: str + """Optional field that helps identify bank accounts in receipts""" dob: Annotated[Union[str, date], PropertyInfo(format="iso8601")] """Date of Birth of the Individual that owns the external bank account""" doing_business_as: str + """Doing Business As""" name: str + """The nickname for this External Bank Account""" owner: str + """Legal Name of the business or individual who owns the external account. + + This will appear in statements + """ owner_type: OwnerType + """Owner Type""" + + type: Literal["CHECKING", "SAVINGS"] user_defined_id: str + """User Defined ID""" ExternalBankAccountAddress = ExternalBankAccountAddressParam diff --git a/src/lithic/types/external_bank_account_update_response.py b/src/lithic/types/external_bank_account_update_response.py index 85d617fe..cda7ab58 100644 --- a/src/lithic/types/external_bank_account_update_response.py +++ b/src/lithic/types/external_bank_account_update_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import date, datetime @@ -32,7 +32,7 @@ class ExternalBankAccountUpdateResponse(BaseModel): """ currency: str - """currency of the external account 3-digit alphabetic ISO 4217 code""" + """currency of the external account 3-character alphabetic ISO 4217 code""" last_four: str """The last 4 digits of the bank account. @@ -47,19 +47,25 @@ class ExternalBankAccountUpdateResponse(BaseModel): """ owner_type: Literal["BUSINESS", "INDIVIDUAL"] + """Owner Type""" routing_number: str + """Routing Number""" - state: Literal["CLOSED", "ENABLED", "PAUSED"] + state: Literal["ENABLED", "CLOSED", "PAUSED"] + """Account State""" type: Literal["CHECKING", "SAVINGS"] + """Account Type""" verification_attempts: int """The number of attempts at verification""" verification_method: Literal["MANUAL", "MICRO_DEPOSIT", "PLAID", "PRENOTE"] + """Verification Method""" - verification_state: Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"] + verification_state: Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"] + """Verification State""" account_token: Optional[str] = None """Indicates which Lithic account the external account is associated with. @@ -69,10 +75,7 @@ class ExternalBankAccountUpdateResponse(BaseModel): """ address: Optional[ExternalBankAccountAddress] = None - """ - Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. - """ + """Address""" company_id: Optional[str] = None """Optional field that helps identify bank accounts in receipts""" @@ -81,11 +84,16 @@ class ExternalBankAccountUpdateResponse(BaseModel): """Date of Birth of the Individual that owns the external bank account""" doing_business_as: Optional[str] = None + """Doing Business As""" + + financial_account_token: Optional[str] = None + """The financial account token of the operating account to fund the micro deposits""" name: Optional[str] = None - """The nickname given to this record of External Bank Account""" + """The nickname for this External Bank Account""" user_defined_id: Optional[str] = None + """User Defined ID""" verification_failed_reason: Optional[str] = None """Optional free text description of the reason for the failed verification. diff --git a/src/lithic/types/external_bank_accounts/__init__.py b/src/lithic/types/external_bank_accounts/__init__.py index 97d834ff..b30011e2 100644 --- a/src/lithic/types/external_bank_accounts/__init__.py +++ b/src/lithic/types/external_bank_accounts/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/external_bank_accounts/micro_deposit_create_params.py b/src/lithic/types/external_bank_accounts/micro_deposit_create_params.py index 3f0ac427..44f17a67 100644 --- a/src/lithic/types/external_bank_accounts/micro_deposit_create_params.py +++ b/src/lithic/types/external_bank_accounts/micro_deposit_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/external_bank_accounts/micro_deposit_create_response.py b/src/lithic/types/external_bank_accounts/micro_deposit_create_response.py index b101ce52..643962ad 100644 --- a/src/lithic/types/external_bank_accounts/micro_deposit_create_response.py +++ b/src/lithic/types/external_bank_accounts/micro_deposit_create_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import date, datetime @@ -32,7 +32,7 @@ class MicroDepositCreateResponse(BaseModel): """ currency: str - """currency of the external account 3-digit alphabetic ISO 4217 code""" + """currency of the external account 3-character alphabetic ISO 4217 code""" last_four: str """The last 4 digits of the bank account. @@ -47,19 +47,25 @@ class MicroDepositCreateResponse(BaseModel): """ owner_type: Literal["BUSINESS", "INDIVIDUAL"] + """Owner Type""" routing_number: str + """Routing Number""" - state: Literal["CLOSED", "ENABLED", "PAUSED"] + state: Literal["ENABLED", "CLOSED", "PAUSED"] + """Account State""" type: Literal["CHECKING", "SAVINGS"] + """Account Type""" verification_attempts: int """The number of attempts at verification""" verification_method: Literal["MANUAL", "MICRO_DEPOSIT", "PLAID", "PRENOTE"] + """Verification Method""" - verification_state: Literal["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS", "PENDING"] + verification_state: Literal["PENDING", "ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"] + """Verification State""" account_token: Optional[str] = None """Indicates which Lithic account the external account is associated with. @@ -69,10 +75,7 @@ class MicroDepositCreateResponse(BaseModel): """ address: Optional[ExternalBankAccountAddress] = None - """ - Address used during Address Verification Service (AVS) checks during - transactions if enabled via Auth Rules. - """ + """Address""" company_id: Optional[str] = None """Optional field that helps identify bank accounts in receipts""" @@ -81,11 +84,16 @@ class MicroDepositCreateResponse(BaseModel): """Date of Birth of the Individual that owns the external bank account""" doing_business_as: Optional[str] = None + """Doing Business As""" + + financial_account_token: Optional[str] = None + """The financial account token of the operating account to fund the micro deposits""" name: Optional[str] = None - """The nickname given to this record of External Bank Account""" + """The nickname for this External Bank Account""" user_defined_id: Optional[str] = None + """User Defined ID""" verification_failed_reason: Optional[str] = None """Optional free text description of the reason for the failed verification. diff --git a/src/lithic/types/external_payment.py b/src/lithic/types/external_payment.py new file mode 100644 index 00000000..18e76652 --- /dev/null +++ b/src/lithic/types/external_payment.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import date, datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ExternalPayment", "Event"] + + +class Event(BaseModel): + token: str + + amount: int + + created: datetime + + detailed_results: List[Literal["APPROVED"]] + + effective_date: date + + memo: str + + result: Literal["APPROVED", "DECLINED"] + + type: Literal[ + "EXTERNAL_WIRE_INITIATED", + "EXTERNAL_WIRE_CANCELED", + "EXTERNAL_WIRE_SETTLED", + "EXTERNAL_WIRE_REVERSED", + "EXTERNAL_WIRE_RELEASED", + "EXTERNAL_ACH_INITIATED", + "EXTERNAL_ACH_CANCELED", + "EXTERNAL_ACH_SETTLED", + "EXTERNAL_ACH_REVERSED", + "EXTERNAL_ACH_RELEASED", + "EXTERNAL_TRANSFER_INITIATED", + "EXTERNAL_TRANSFER_CANCELED", + "EXTERNAL_TRANSFER_SETTLED", + "EXTERNAL_TRANSFER_REVERSED", + "EXTERNAL_TRANSFER_RELEASED", + "EXTERNAL_CHECK_INITIATED", + "EXTERNAL_CHECK_CANCELED", + "EXTERNAL_CHECK_SETTLED", + "EXTERNAL_CHECK_REVERSED", + "EXTERNAL_CHECK_RELEASED", + ] + + +class ExternalPayment(BaseModel): + token: str + + category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"] + + created: datetime + + currency: str + + events: List[Event] + + financial_account_token: str + + payment_type: Literal["DEPOSIT", "WITHDRAWAL"] + + pending_amount: int + + result: Literal["APPROVED", "DECLINED"] + + settled_amount: int + + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED"] + + updated: datetime + + user_defined_id: Optional[str] = None diff --git a/src/lithic/types/external_payment_cancel_params.py b/src/lithic/types/external_payment_cancel_params.py new file mode 100644 index 00000000..23405208 --- /dev/null +++ b/src/lithic/types/external_payment_cancel_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 Union +from datetime import date +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExternalPaymentCancelParams"] + + +class ExternalPaymentCancelParams(TypedDict, total=False): + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + + memo: str diff --git a/src/lithic/types/external_payment_create_params.py b/src/lithic/types/external_payment_create_params.py new file mode 100644 index 00000000..7e384cc7 --- /dev/null +++ b/src/lithic/types/external_payment_create_params.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Literal, Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExternalPaymentCreateParams"] + + +class ExternalPaymentCreateParams(TypedDict, total=False): + amount: Required[int] + + category: Required[Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"]] + + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + + financial_account_token: Required[str] + + payment_type: Required[Literal["DEPOSIT", "WITHDRAWAL"]] + + token: str + + memo: str + + progress_to: Literal["SETTLED", "RELEASED"] + + user_defined_id: str diff --git a/src/lithic/types/external_payment_list_params.py b/src/lithic/types/external_payment_list_params.py new file mode 100644 index 00000000..c1ce26ff --- /dev/null +++ b/src/lithic/types/external_payment_list_params.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExternalPaymentListParams"] + + +class ExternalPaymentListParams(TypedDict, total=False): + begin: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created after the specified time will be included. UTC time zone. + """ + + business_account_token: str + + category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"] + """External Payment category to be returned.""" + + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created before the specified time will be included. UTC time zone. + """ + + ending_before: str + """A cursor representing an item's token before which a page of results should end. + + Used to retrieve the previous page of results before this item. + """ + + financial_account_token: str + """ + Globally unique identifier for the financial account or card that will send the + funds. Accepted type dependent on the program's use case. + """ + + page_size: int + """Page size (for pagination).""" + + result: Literal["APPROVED", "DECLINED"] + """External Payment result to be returned.""" + + starting_after: str + """A cursor representing an item's token after which a page of results should + begin. + + Used to retrieve the next page of results after this item. + """ + + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED"] + """Book transfer status to be returned.""" diff --git a/src/lithic/types/external_payment_release_params.py b/src/lithic/types/external_payment_release_params.py new file mode 100644 index 00000000..18f3ab78 --- /dev/null +++ b/src/lithic/types/external_payment_release_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 Union +from datetime import date +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExternalPaymentReleaseParams"] + + +class ExternalPaymentReleaseParams(TypedDict, total=False): + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + + memo: str diff --git a/src/lithic/types/external_payment_reverse_params.py b/src/lithic/types/external_payment_reverse_params.py new file mode 100644 index 00000000..2949029f --- /dev/null +++ b/src/lithic/types/external_payment_reverse_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 Union +from datetime import date +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExternalPaymentReverseParams"] + + +class ExternalPaymentReverseParams(TypedDict, total=False): + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + + memo: str diff --git a/src/lithic/types/external_payment_settle_params.py b/src/lithic/types/external_payment_settle_params.py new file mode 100644 index 00000000..984cdceb --- /dev/null +++ b/src/lithic/types/external_payment_settle_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Literal, Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExternalPaymentSettleParams"] + + +class ExternalPaymentSettleParams(TypedDict, total=False): + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + + memo: str + + progress_to: Literal["SETTLED", "RELEASED"] diff --git a/src/lithic/types/financial_account.py b/src/lithic/types/financial_account.py index 9c5cee34..bb763758 100644 --- a/src/lithic/types/financial_account.py +++ b/src/lithic/types/financial_account.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime @@ -6,27 +6,58 @@ from .._models import BaseModel -__all__ = ["FinancialAccount"] +__all__ = ["FinancialAccount", "CreditConfiguration"] + + +class CreditConfiguration(BaseModel): + charged_off_reason: Optional[Literal["DELINQUENT", "FRAUD"]] = None + """Reason for the financial account being marked as Charged Off""" + + credit_limit: Optional[int] = None + + credit_product_token: Optional[str] = None + """Globally unique identifier for the credit product""" + + external_bank_account_token: Optional[str] = None + + financial_account_state: Optional[Literal["PENDING", "CURRENT", "DELINQUENT", "CHARGED_OFF"]] = None + """State of the financial account""" + + is_spend_blocked: bool + + tier: Optional[str] = None + """Tier assigned to the financial account""" class FinancialAccount(BaseModel): token: str - """Globally unique identifier for the financial account.""" + """Globally unique identifier for the account""" + + account_token: Optional[str] = None created: datetime - """Date and time for when the financial account was first created.""" - type: Literal["ISSUING", "OPERATING", "RESERVE"] - """Type of financial account""" + credit_configuration: Optional[CreditConfiguration] = None + + is_for_benefit_of: bool + """Whether financial account is for the benefit of another entity""" + + nickname: Optional[str] = None + + status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"] + """Status of the financial account""" + + type: Literal[ + "ISSUING", "RESERVE", "OPERATING", "CHARGED_OFF_FEES", "CHARGED_OFF_INTEREST", "CHARGED_OFF_PRINCIPAL" + ] updated: datetime - """Date and time for when the financial account was last updated.""" account_number: Optional[str] = None - """Account number for your Lithic-assigned bank account number, if applicable.""" - - nickname: Optional[str] = None - """User-defined nickname for the financial account.""" routing_number: Optional[str] = None - """Routing number for your Lithic-assigned bank account number, if applicable.""" + + substatus: Optional[ + Literal["CHARGED_OFF_DELINQUENT", "CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "DELINQUENT"] + ] = None + """Substatus for the financial account""" diff --git a/src/lithic/types/financial_account_create_params.py b/src/lithic/types/financial_account_create_params.py index f854cb14..62e7bec9 100644 --- a/src/lithic/types/financial_account_create_params.py +++ b/src/lithic/types/financial_account_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -13,3 +13,5 @@ class FinancialAccountCreateParams(TypedDict, total=False): type: Required[Literal["OPERATING"]] account_token: str + + is_for_benefit_of: bool diff --git a/src/lithic/types/financial_account_list_params.py b/src/lithic/types/financial_account_list_params.py index 86cb0d10..d465f5be 100644 --- a/src/lithic/types/financial_account_list_params.py +++ b/src/lithic/types/financial_account_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/financial_account_update_params.py b/src/lithic/types/financial_account_update_params.py index b2bd5e08..41bb68a8 100644 --- a/src/lithic/types/financial_account_update_params.py +++ b/src/lithic/types/financial_account_update_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/financial_account_update_status_params.py b/src/lithic/types/financial_account_update_status_params.py new file mode 100644 index 00000000..5ae18b85 --- /dev/null +++ b/src/lithic/types/financial_account_update_status_params.py @@ -0,0 +1,18 @@ +# 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 Literal, Required, TypedDict + +__all__ = ["FinancialAccountUpdateStatusParams"] + + +class FinancialAccountUpdateStatusParams(TypedDict, total=False): + status: Required[Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"]] + """Status of the financial account""" + + substatus: Required[ + Optional[Literal["CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "CHARGED_OFF_DELINQUENT"]] + ] + """Substatus for the financial account""" diff --git a/src/lithic/types/financial_accounts/__init__.py b/src/lithic/types/financial_accounts/__init__.py index 048f281c..75207d61 100644 --- a/src/lithic/types/financial_accounts/__init__.py +++ b/src/lithic/types/financial_accounts/__init__.py @@ -1,8 +1,14 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations +from .loan_tape import LoanTape as LoanTape from .statement import Statement as Statement +from .statements import Statements as Statements from .balance_list_params import BalanceListParams as BalanceListParams +from .balance_list_response import BalanceListResponse as BalanceListResponse +from .loan_tape_list_params import LoanTapeListParams as LoanTapeListParams from .statement_list_params import StatementListParams as StatementListParams +from .financial_account_credit_config import FinancialAccountCreditConfig as FinancialAccountCreditConfig from .financial_transaction_list_params import FinancialTransactionListParams as FinancialTransactionListParams +from .credit_configuration_update_params import CreditConfigurationUpdateParams as CreditConfigurationUpdateParams diff --git a/src/lithic/types/financial_accounts/balance_list_params.py b/src/lithic/types/financial_accounts/balance_list_params.py index b80030a0..be24dded 100644 --- a/src/lithic/types/financial_accounts/balance_list_params.py +++ b/src/lithic/types/financial_accounts/balance_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/financial_accounts/balance_list_response.py b/src/lithic/types/financial_accounts/balance_list_response.py new file mode 100644 index 00000000..83f06974 --- /dev/null +++ b/src/lithic/types/financial_accounts/balance_list_response.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BalanceListResponse"] + + +class BalanceListResponse(BaseModel): + token: str + """Globally unique identifier for the financial account that holds this balance.""" + + available_amount: int + """Funds available for spend in the currency's smallest unit (e.g., cents for USD)""" + + created: datetime + """Date and time for when the balance was first created.""" + + currency: str + """3-character alphabetic ISO 4217 code for the local currency of the balance.""" + + last_transaction_event_token: str + """ + Globally unique identifier for the last financial transaction event that + impacted this balance. + """ + + last_transaction_token: str + """ + Globally unique identifier for the last financial transaction that impacted this + balance. + """ + + pending_amount: int + """Funds not available for spend due to card authorizations or pending ACH release. + + Shown in the currency's smallest unit (e.g., cents for USD). + """ + + total_amount: int + """ + The sum of available and pending balance in the currency's smallest unit (e.g., + cents for USD). + """ + + type: Literal["ISSUING", "OPERATING", "RESERVE"] + """Type of financial account.""" + + updated: datetime + """Date and time for when the balance was last updated.""" diff --git a/src/lithic/types/financial_accounts/credit_configuration_update_params.py b/src/lithic/types/financial_accounts/credit_configuration_update_params.py new file mode 100644 index 00000000..c6a54d33 --- /dev/null +++ b/src/lithic/types/financial_accounts/credit_configuration_update_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["CreditConfigurationUpdateParams"] + + +class CreditConfigurationUpdateParams(TypedDict, total=False): + credit_limit: int + + credit_product_token: str + """Globally unique identifier for the credit product""" + + external_bank_account_token: str + + tier: str + """Tier to assign to a financial account""" diff --git a/src/lithic/types/financial_accounts/financial_account_credit_config.py b/src/lithic/types/financial_accounts/financial_account_credit_config.py new file mode 100644 index 00000000..97a7d38b --- /dev/null +++ b/src/lithic/types/financial_accounts/financial_account_credit_config.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FinancialAccountCreditConfig"] + + +class FinancialAccountCreditConfig(BaseModel): + account_token: str + """Globally unique identifier for the account""" + + charged_off_reason: Optional[Literal["DELINQUENT", "FRAUD"]] = None + """Reason for the financial account being marked as Charged Off""" + + credit_limit: Optional[int] = None + + credit_product_token: Optional[str] = None + """Globally unique identifier for the credit product""" + + external_bank_account_token: Optional[str] = None + + financial_account_state: Literal["PENDING", "CURRENT", "DELINQUENT", "CHARGED_OFF"] + """State of the financial account""" + + is_spend_blocked: bool + + tier: Optional[str] = None + """Tier assigned to the financial account""" diff --git a/src/lithic/types/financial_accounts/financial_transaction_list_params.py b/src/lithic/types/financial_accounts/financial_transaction_list_params.py index 68018ec2..9577fd78 100644 --- a/src/lithic/types/financial_accounts/financial_transaction_list_params.py +++ b/src/lithic/types/financial_accounts/financial_transaction_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -18,7 +18,7 @@ class FinancialTransactionListParams(TypedDict, total=False): Only entries created after the specified time will be included. UTC time zone. """ - category: Literal["ACH", "CARD", "TRANSFER"] + category: Literal["ACH", "CARD", "INTERNAL", "TRANSFER"] """Financial Transaction category to be returned.""" end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] diff --git a/src/lithic/types/financial_accounts/loan_tape.py b/src/lithic/types/financial_accounts/loan_tape.py new file mode 100644 index 00000000..40b2adfb --- /dev/null +++ b/src/lithic/types/financial_accounts/loan_tape.py @@ -0,0 +1,323 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import datetime +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "LoanTape", + "AccountStanding", + "AccountStandingFinancialAccountState", + "Balances", + "BalancesDue", + "BalancesNextStatementDue", + "BalancesPastDue", + "BalancesPastStatementsDue", + "DayTotals", + "InterestDetails", + "InterestDetailsDailyBalanceAmounts", + "InterestDetailsEffectiveApr", + "InterestDetailsInterestForPeriod", + "MinimumPaymentBalance", + "PaymentAllocation", + "PeriodTotals", + "PreviousStatementBalance", + "YtdTotals", +] + + +class AccountStandingFinancialAccountState(BaseModel): + status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"] + """Status of the financial account""" + + substatus: Optional[ + Literal["CHARGED_OFF_DELINQUENT", "CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "DELINQUENT"] + ] = None + """Substatus for the financial account""" + + +class AccountStanding(BaseModel): + consecutive_full_payments_made: int + """Number of consecutive full payments made""" + + consecutive_minimum_payments_made: int + """Number of consecutive minimum payments made""" + + consecutive_minimum_payments_missed: int + """Number of consecutive minimum payments missed""" + + days_past_due: int + """Number of days past due""" + + financial_account_state: AccountStandingFinancialAccountState + """Information about the financial account state""" + + has_grace: bool + """Whether the account currently has grace or not""" + + period_number: int + """Current overall period number""" + + period_state: Literal["STANDARD", "PROMO", "PENALTY"] + + +class BalancesDue(BaseModel): + fees: int + + interest: int + + principal: int + + +class BalancesNextStatementDue(BaseModel): + fees: int + + interest: int + + principal: int + + +class BalancesPastDue(BaseModel): + fees: int + + interest: int + + principal: int + + +class BalancesPastStatementsDue(BaseModel): + fees: int + + interest: int + + principal: int + + +class Balances(BaseModel): + due: BalancesDue + """Amount due for the prior billing cycle. + + Any amounts not fully paid off on this due date will be considered past due the + next day + """ + + next_statement_due: BalancesNextStatementDue + """Amount due for the current billing cycle. + + Any amounts not paid off by early payments or credits will be considered due at + the end of the current billing period + """ + + past_due: BalancesPastDue + """Amount not paid off on previous due dates""" + + past_statements_due: BalancesPastStatementsDue + """Amount due for the past billing cycles.""" + + +class DayTotals(BaseModel): + balance_transfers: int + """Opening balance transferred from previous account in cents""" + + cash_advances: int + """ATM and cashback transactions in cents""" + + credits: int + """ + Volume of credit management operation transactions less any balance transfers in + cents + """ + + fees: int + """Volume of debit management operation transactions less any interest in cents""" + + interest: int + """Interest accrued in cents""" + + payments: int + """Any funds transfers which affective the balance in cents""" + + purchases: int + """Net card transaction volume less any cash advances in cents""" + + +class InterestDetailsDailyBalanceAmounts(BaseModel): + balance_transfers: str + + cash_advances: str + + purchases: str + + +class InterestDetailsEffectiveApr(BaseModel): + balance_transfers: str + + cash_advances: str + + purchases: str + + +class InterestDetailsInterestForPeriod(BaseModel): + balance_transfers: str + + cash_advances: str + + purchases: str + + +class InterestDetails(BaseModel): + actual_interest_charged: Optional[int] = None + + daily_balance_amounts: InterestDetailsDailyBalanceAmounts + + effective_apr: InterestDetailsEffectiveApr + + interest_calculation_method: Literal["DAILY", "AVERAGE_DAILY"] + + interest_for_period: InterestDetailsInterestForPeriod + + prime_rate: Optional[str] = None + + minimum_interest_charged: Optional[int] = None + + +class MinimumPaymentBalance(BaseModel): + amount: int + + remaining: int + + +class PaymentAllocation(BaseModel): + fees: int + + interest: int + + principal: int + + +class PeriodTotals(BaseModel): + balance_transfers: int + """Opening balance transferred from previous account in cents""" + + cash_advances: int + """ATM and cashback transactions in cents""" + + credits: int + """ + Volume of credit management operation transactions less any balance transfers in + cents + """ + + fees: int + """Volume of debit management operation transactions less any interest in cents""" + + interest: int + """Interest accrued in cents""" + + payments: int + """Any funds transfers which affective the balance in cents""" + + purchases: int + """Net card transaction volume less any cash advances in cents""" + + +class PreviousStatementBalance(BaseModel): + amount: int + + remaining: int + + +class YtdTotals(BaseModel): + balance_transfers: int + """Opening balance transferred from previous account in cents""" + + cash_advances: int + """ATM and cashback transactions in cents""" + + credits: int + """ + Volume of credit management operation transactions less any balance transfers in + cents + """ + + fees: int + """Volume of debit management operation transactions less any interest in cents""" + + interest: int + """Interest accrued in cents""" + + payments: int + """Any funds transfers which affective the balance in cents""" + + purchases: int + """Net card transaction volume less any cash advances in cents""" + + +class LoanTape(BaseModel): + token: str + """Globally unique identifier for a loan tape""" + + account_standing: AccountStanding + + available_credit: int + """Amount of credit available to spend in cents""" + + balances: Balances + + created: datetime.datetime + """Timestamp of when the loan tape was created""" + + credit_limit: int + """For prepay accounts, this is the minimum prepay balance that must be maintained. + + For charge card accounts, this is the maximum credit balance extended by a + lender + """ + + credit_product_token: str + """Globally unique identifier for a credit product""" + + date: datetime.date + """Date of transactions that this loan tape covers""" + + day_totals: DayTotals + + ending_balance: int + """Balance at the end of the day""" + + excess_credits: int + """Excess credits in the form of provisional credits, payments, or purchase + refunds. + + If positive, the account is in net credit state with no outstanding balances. An + overpayment could land an account in this state + """ + + financial_account_token: str + """Globally unique identifier for a financial account""" + + interest_details: Optional[InterestDetails] = None + + minimum_payment_balance: MinimumPaymentBalance + + payment_allocation: PaymentAllocation + + period_totals: PeriodTotals + + previous_statement_balance: PreviousStatementBalance + + starting_balance: int + """Balance at the start of the day""" + + updated: datetime.datetime + """Timestamp of when the loan tape was updated""" + + version: int + """Version number of the loan tape. This starts at 1""" + + ytd_totals: YtdTotals + + tier: Optional[str] = None + """Interest tier to which this account belongs to""" diff --git a/src/lithic/types/financial_accounts/loan_tape_list_params.py b/src/lithic/types/financial_accounts/loan_tape_list_params.py new file mode 100644 index 00000000..e45b5bd2 --- /dev/null +++ b/src/lithic/types/financial_accounts/loan_tape_list_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["LoanTapeListParams"] + + +class LoanTapeListParams(TypedDict, total=False): + begin: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created after the specified date will be included. + """ + + end: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created before the specified date will be included. + """ + + ending_before: str + """A cursor representing an item's token before which a page of results should end. + + Used to retrieve the previous page of results before this item. + """ + + page_size: int + """Page size (for pagination).""" + + starting_after: str + """A cursor representing an item's token after which a page of results should + begin. + + Used to retrieve the next page of results after this item. + """ diff --git a/src/lithic/types/financial_accounts/statement.py b/src/lithic/types/financial_accounts/statement.py index b30afc8a..b683f844 100644 --- a/src/lithic/types/financial_accounts/statement.py +++ b/src/lithic/types/financial_accounts/statement.py @@ -1,48 +1,184 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from datetime import date, datetime +from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["Statement"] +__all__ = [ + "Statement", + "AccountStanding", + "AccountStandingFinancialAccountState", + "AmountDue", + "PeriodTotals", + "YtdTotals", + "InterestDetails", + "InterestDetailsDailyBalanceAmounts", + "InterestDetailsEffectiveApr", + "InterestDetailsInterestForPeriod", +] -class Statement(BaseModel): - token: str - """Globally unique identifier for a statement""" +class AccountStandingFinancialAccountState(BaseModel): + status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"] + """Status of the financial account""" + + substatus: Optional[ + Literal["CHARGED_OFF_DELINQUENT", "CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "DELINQUENT"] + ] = None + """Substatus for the financial account""" + + +class AccountStanding(BaseModel): + consecutive_full_payments_made: int + """Number of consecutive full payments made""" + + consecutive_minimum_payments_made: int + """Number of consecutive minimum payments made""" + + consecutive_minimum_payments_missed: int + """Number of consecutive minimum payments missed""" + + days_past_due: int + """Number of days past due""" - ach_period_total: int - """Total payments during this billing period.""" + financial_account_state: AccountStandingFinancialAccountState + """Information about the financial account state""" - ach_ytd_total: int - """Year-to-date settled payment total""" + has_grace: bool + """Whether the account currently has grace or not""" - adjustments_period_total: int - """Total adjustments during this billing period.""" + period_number: int + """Current overall period number""" - adjustments_ytd_total: int - """Year-to-date settled adjustments total""" + period_state: Literal["STANDARD", "PROMO", "PENALTY"] - amount_due: int - """Payment due at the end of the billing period. + +class AmountDue(BaseModel): + amount: int + """Payment due at the end of the billing period in cents. Negative amount indicates something is owed. If the amount owed is positive - (e.g., there was a net credit), then payment should be returned to the - cardholder via ACH. + there was a net credit. If auto-collections are enabled this is the amount that + will be requested on the payment due date + """ + + past_due: int + """Amount past due for statement in cents""" + + +class PeriodTotals(BaseModel): + balance_transfers: int + """Opening balance transferred from previous account in cents""" + + cash_advances: int + """ATM and cashback transactions in cents""" + + credits: int + """ + Volume of credit management operation transactions less any balance transfers in + cents """ + fees: int + """Volume of debit management operation transactions less any interest in cents""" + + interest: int + """Interest accrued in cents""" + + payments: int + """Any funds transfers which affective the balance in cents""" + + purchases: int + """Net card transaction volume less any cash advances in cents""" + + +class YtdTotals(BaseModel): + balance_transfers: int + """Opening balance transferred from previous account in cents""" + + cash_advances: int + """ATM and cashback transactions in cents""" + + credits: int + """ + Volume of credit management operation transactions less any balance transfers in + cents + """ + + fees: int + """Volume of debit management operation transactions less any interest in cents""" + + interest: int + """Interest accrued in cents""" + + payments: int + """Any funds transfers which affective the balance in cents""" + + purchases: int + """Net card transaction volume less any cash advances in cents""" + + +class InterestDetailsDailyBalanceAmounts(BaseModel): + balance_transfers: str + + cash_advances: str + + purchases: str + + +class InterestDetailsEffectiveApr(BaseModel): + balance_transfers: str + + cash_advances: str + + purchases: str + + +class InterestDetailsInterestForPeriod(BaseModel): + balance_transfers: str + + cash_advances: str + + purchases: str + + +class InterestDetails(BaseModel): + actual_interest_charged: Optional[int] = None + + daily_balance_amounts: InterestDetailsDailyBalanceAmounts + + effective_apr: InterestDetailsEffectiveApr + + interest_calculation_method: Literal["DAILY", "AVERAGE_DAILY"] + + interest_for_period: InterestDetailsInterestForPeriod + + prime_rate: Optional[str] = None + + minimum_interest_charged: Optional[int] = None + + +class Statement(BaseModel): + token: str + """Globally unique identifier for a statement""" + + account_standing: AccountStanding + + amount_due: AmountDue + available_credit: int - """Amount of credit available to spend""" + """Amount of credit available to spend in cents""" created: datetime """Timestamp of when the statement was created""" credit_limit: int - """For prepay accounts, this is the minimum prepay balance that must be maintained. + """This is the maximum credit balance extended by the lender in cents""" - For charge card accounts, this is the maximum credit balance extended by a - lender. - """ + credit_product_token: str + """Globally unique identifier for a credit product""" days_in_billing_cycle: int """Number of days in the billing cycle""" @@ -50,23 +186,16 @@ class Statement(BaseModel): ending_balance: int """Balance at the end of the billing period. - For charge cards, this should be the same at the statement amount due. + For charge cards, this should be the same at the statement amount due in cents """ financial_account_token: str """Globally unique identifier for a financial account""" - payment_due_date: date + payment_due_date: Optional[date] = None """Date when the payment is due""" - purchases_period_total: int - """ - Total settled card transactions during this billing period, determined by - liability date. - """ - - purchases_ytd_total: int - """Year-to-date settled card transaction total""" + period_totals: PeriodTotals starting_balance: int """Balance at the start of the billing period""" @@ -77,5 +206,17 @@ class Statement(BaseModel): statement_start_date: date """Date when the billing period began""" + statement_type: Literal["INITIAL", "PERIOD_END", "FINAL"] + updated: datetime """Timestamp of when the statement was updated""" + + ytd_totals: YtdTotals + + interest_details: Optional[InterestDetails] = None + + next_payment_due_date: Optional[date] = None + """Date when the next payment is due""" + + next_statement_end_date: Optional[date] = None + """Date when the next billing period will end""" diff --git a/src/lithic/types/financial_accounts/statement_list_params.py b/src/lithic/types/financial_accounts/statement_list_params.py index 7006945c..47a42258 100644 --- a/src/lithic/types/financial_accounts/statement_list_params.py +++ b/src/lithic/types/financial_accounts/statement_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -30,6 +30,9 @@ class StatementListParams(TypedDict, total=False): Used to retrieve the previous page of results before this item. """ + include_initial_statements: bool + """Whether to include the initial statement. It is not included by default.""" + page_size: int """Page size (for pagination).""" diff --git a/src/lithic/types/financial_accounts/statements/__init__.py b/src/lithic/types/financial_accounts/statements/__init__.py index 1c4ae2ca..b5274717 100644 --- a/src/lithic/types/financial_accounts/statements/__init__.py +++ b/src/lithic/types/financial_accounts/statements/__init__.py @@ -1,6 +1,7 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations +from .statements import Statements as Statements +from .statement_line_items import StatementLineItems as StatementLineItems from .line_item_list_params import LineItemListParams as LineItemListParams -from .line_item_list_response import LineItemListResponse as LineItemListResponse diff --git a/src/lithic/types/financial_accounts/statements/line_item_list_params.py b/src/lithic/types/financial_accounts/statements/line_item_list_params.py index 5c585b1c..1a7a7d2d 100644 --- a/src/lithic/types/financial_accounts/statements/line_item_list_params.py +++ b/src/lithic/types/financial_accounts/statements/line_item_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,6 +9,7 @@ class LineItemListParams(TypedDict, total=False): financial_account_token: Required[str] + """Globally unique identifier for financial account.""" ending_before: str """A cursor representing an item's token before which a page of results should end. diff --git a/src/lithic/types/financial_accounts/statements/line_item_list_response.py b/src/lithic/types/financial_accounts/statements/line_item_list_response.py deleted file mode 100644 index ab713ba3..00000000 --- a/src/lithic/types/financial_accounts/statements/line_item_list_response.py +++ /dev/null @@ -1,100 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from typing import Optional -from datetime import date, datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["LineItemListResponse"] - - -class LineItemListResponse(BaseModel): - token: str - """Globally unique identifier for a Statement Line Item""" - - amount: int - - category: Literal["ACH", "CARD", "TRANSFER"] - - created: datetime - """Timestamp of when the line item was generated""" - - currency: str - """3-digit alphabetic ISO 4217 code for the settling currency of the transaction""" - - event_type: Literal[ - "ACH_INSUFFICIENT_FUNDS", - "ACH_ORIGINATION_PENDING", - "ACH_ORIGINATION_RELEASED", - "ACH_RECEIPT_PENDING", - "ACH_RECEIPT_RELEASED", - "ACH_RETURN", - "AUTHORIZATION", - "AUTHORIZATION_ADVICE", - "AUTHORIZATION_EXPIRY", - "AUTHORIZATION_REVERSAL", - "BALANCE_INQUIRY", - "CLEARING", - "CORRECTION_CREDIT", - "CORRECTION_DEBIT", - "CREDIT_AUTHORIZATION", - "CREDIT_AUTHORIZATION_ADVICE", - "FINANCIAL_AUTHORIZATION", - "FINANCIAL_CREDIT_AUTHORIZATION", - "RETURN", - "RETURN_REVERSAL", - "TRANSFER", - "TRANSFER_INSUFFICIENT_FUNDS", - ] - """Event types: - - - `ACH_INSUFFICIENT_FUNDS` - Attempted ACH origination declined due to - insufficient balance. - - `ACH_ORIGINATION_PENDING` - ACH origination pending release from an ACH hold. - - `ACH_ORIGINATION_RELEASED` - ACH origination released from pending to - available balance. - - `ACH_RECEIPT_PENDING` - ACH receipt pending release from an ACH holder. - - `ACH_RECEIPT_RELEASED` - ACH receipt released from pending to available - balance. - - `ACH_RETURN` - ACH origination returned by the Receiving Depository Financial - Institution. - - `AUTHORIZATION` - Authorize a card transaction. - - `AUTHORIZATION_ADVICE` - Advice on a card transaction. - - `AUTHORIZATION_EXPIRY` - Card Authorization has expired and reversed by - Lithic. - - `AUTHORIZATION_REVERSAL` - Card Authorization was reversed by the merchant. - - `BALANCE_INQUIRY` - A card balance inquiry (typically a $0 authorization) has - occurred on a card. - - `CLEARING` - Card Transaction is settled. - - `CORRECTION_DEBIT` - Manual card transaction correction (Debit). - - `CORRECTION_CREDIT` - Manual card transaction correction (Credit). - - `CREDIT_AUTHORIZATION` - A refund or credit card authorization from a - merchant. - - `CREDIT_AUTHORIZATION_ADVICE` - A credit card authorization was approved on - your behalf by the network. - - `FINANCIAL_AUTHORIZATION` - A request from a merchant to debit card funds - without additional clearing. - - `FINANCIAL_CREDIT_AUTHORIZATION` - A request from a merchant to refund or - credit card funds without additional clearing. - - `RETURN` - A card refund has been processed on the transaction. - - `RETURN_REVERSAL` - A card refund has been reversed (e.g., when a merchant - reverses an incorrect refund). - - `TRANSFER` - Successful internal transfer of funds between financial accounts. - - `TRANSFER_INSUFFICIENT_FUNDS` - Declined internl transfer of funds due to - insufficient balance of the sender. - """ - - financial_account_token: str - """Globally unique identifier for a financial account""" - - financial_transaction_token: str - """Globally unique identifier for a financial transaction""" - - settled_date: date - """Date that the transaction settled""" - - card_token: Optional[str] = None - """Globally unique identifier for a card""" - - descriptor: Optional[str] = None diff --git a/src/lithic/types/financial_accounts/statements/statement_line_items.py b/src/lithic/types/financial_accounts/statements/statement_line_items.py new file mode 100644 index 00000000..ae7da003 --- /dev/null +++ b/src/lithic/types/financial_accounts/statements/statement_line_items.py @@ -0,0 +1,130 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import date, datetime +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["StatementLineItems", "Data"] + + +class Data(BaseModel): + token: str + """Globally unique identifier for a Statement Line Item""" + + amount: int + """Transaction amount in cents""" + + category: Literal[ + "ACH", + "BALANCE_OR_FUNDING", + "CARD", + "EXTERNAL_ACH", + "EXTERNAL_CHECK", + "EXTERNAL_TRANSFER", + "EXTERNAL_WIRE", + "MANAGEMENT_ADJUSTMENT", + "MANAGEMENT_DISPUTE", + "MANAGEMENT_FEE", + "MANAGEMENT_REWARD", + ] + + created: datetime + """Timestamp of when the line item was generated""" + + currency: str + """ + 3-character alphabetic ISO 4217 code for the settling currency of the + transaction + """ + + effective_date: date + """Date that the transaction effected the account balance""" + + event_type: Literal[ + "ACH_ORIGINATION_CANCELLED", + "ACH_ORIGINATION_INITIATED", + "ACH_ORIGINATION_PROCESSED", + "ACH_ORIGINATION_RELEASED", + "ACH_ORIGINATION_REVIEWED", + "ACH_ORIGINATION_SETTLED", + "ACH_RECEIPT_PROCESSED", + "ACH_RECEIPT_SETTLED", + "ACH_RETURN_INITIATED", + "ACH_RETURN_PROCESSED", + "ACH_RETURN_SETTLED", + "AUTHORIZATION", + "AUTHORIZATION_ADVICE", + "AUTHORIZATION_EXPIRY", + "AUTHORIZATION_REVERSAL", + "BALANCE_INQUIRY", + "BILLING_ERROR", + "BILLING_ERROR_REVERSAL", + "CARD_TO_CARD", + "CASH_BACK", + "CASH_BACK_REVERSAL", + "CLEARING", + "CORRECTION_CREDIT", + "CORRECTION_DEBIT", + "CREDIT_AUTHORIZATION", + "CREDIT_AUTHORIZATION_ADVICE", + "CURRENCY_CONVERSION", + "CURRENCY_CONVERSION_REVERSAL", + "DISPUTE_WON", + "EXTERNAL_ACH_CANCELED", + "EXTERNAL_ACH_INITIATED", + "EXTERNAL_ACH_RELEASED", + "EXTERNAL_ACH_REVERSED", + "EXTERNAL_ACH_SETTLED", + "EXTERNAL_CHECK_CANCELED", + "EXTERNAL_CHECK_INITIATED", + "EXTERNAL_CHECK_RELEASED", + "EXTERNAL_CHECK_REVERSED", + "EXTERNAL_CHECK_SETTLED", + "EXTERNAL_TRANSFER_CANCELED", + "EXTERNAL_TRANSFER_INITIATED", + "EXTERNAL_TRANSFER_RELEASED", + "EXTERNAL_TRANSFER_REVERSED", + "EXTERNAL_TRANSFER_SETTLED", + "EXTERNAL_WIRE_CANCELED", + "EXTERNAL_WIRE_INITIATED", + "EXTERNAL_WIRE_RELEASED", + "EXTERNAL_WIRE_REVERSED", + "EXTERNAL_WIRE_SETTLED", + "FINANCIAL_AUTHORIZATION", + "FINANCIAL_CREDIT_AUTHORIZATION", + "INTEREST", + "INTEREST_REVERSAL", + "INTERNAL_ADJUSTMENT", + "LATE_PAYMENT", + "LATE_PAYMENT_REVERSAL", + "PROVISIONAL_CREDIT", + "PROVISIONAL_CREDIT_REVERSAL", + "RETURN", + "RETURN_REVERSAL", + "TRANSFER", + "TRANSFER_INSUFFICIENT_FUNDS", + "RETURNED_PAYMENT", + "RETURNED_PAYMENT_REVERSAL", + ] + + financial_account_token: str + """Globally unique identifier for a financial account""" + + financial_transaction_event_token: str + """Globally unique identifier for a financial transaction event""" + + financial_transaction_token: str + """Globally unique identifier for a financial transaction""" + + card_token: Optional[str] = None + """Globally unique identifier for a card""" + + descriptor: Optional[str] = None + + +class StatementLineItems(BaseModel): + data: List[Data] + + has_more: bool diff --git a/src/lithic/types/financial_accounts/statements/statements.py b/src/lithic/types/financial_accounts/statements/statements.py new file mode 100644 index 00000000..b01ae134 --- /dev/null +++ b/src/lithic/types/financial_accounts/statements/statements.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ...._models import BaseModel +from ..statement import Statement + +__all__ = ["Statements"] + + +class Statements(BaseModel): + data: List[Statement] + + has_more: bool diff --git a/src/lithic/types/financial_transaction.py b/src/lithic/types/financial_transaction.py index 6936858a..4aad3c42 100644 --- a/src/lithic/types/financial_transaction.py +++ b/src/lithic/types/financial_transaction.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime @@ -30,78 +30,84 @@ class Event(BaseModel): type: Optional[ Literal[ - "ACH_INSUFFICIENT_FUNDS", - "ACH_ORIGINATION_PENDING", + "ACH_ORIGINATION_CANCELLED", + "ACH_ORIGINATION_INITIATED", + "ACH_ORIGINATION_PROCESSED", "ACH_ORIGINATION_RELEASED", - "ACH_RECEIPT_PENDING", - "ACH_RECEIPT_RELEASED", - "ACH_RETURN", + "ACH_ORIGINATION_REVIEWED", + "ACH_ORIGINATION_SETTLED", + "ACH_RECEIPT_PROCESSED", + "ACH_RECEIPT_SETTLED", + "ACH_RETURN_INITIATED", + "ACH_RETURN_PROCESSED", + "ACH_RETURN_SETTLED", "AUTHORIZATION", "AUTHORIZATION_ADVICE", "AUTHORIZATION_EXPIRY", "AUTHORIZATION_REVERSAL", "BALANCE_INQUIRY", + "BILLING_ERROR", + "BILLING_ERROR_REVERSAL", + "CARD_TO_CARD", + "CASH_BACK", + "CASH_BACK_REVERSAL", "CLEARING", "CORRECTION_CREDIT", "CORRECTION_DEBIT", "CREDIT_AUTHORIZATION", "CREDIT_AUTHORIZATION_ADVICE", + "CURRENCY_CONVERSION", + "CURRENCY_CONVERSION_REVERSAL", + "DISPUTE_WON", + "EXTERNAL_ACH_CANCELED", + "EXTERNAL_ACH_INITIATED", + "EXTERNAL_ACH_RELEASED", + "EXTERNAL_ACH_REVERSED", + "EXTERNAL_ACH_SETTLED", + "EXTERNAL_CHECK_CANCELED", + "EXTERNAL_CHECK_INITIATED", + "EXTERNAL_CHECK_RELEASED", + "EXTERNAL_CHECK_REVERSED", + "EXTERNAL_CHECK_SETTLED", + "EXTERNAL_TRANSFER_CANCELED", + "EXTERNAL_TRANSFER_INITIATED", + "EXTERNAL_TRANSFER_RELEASED", + "EXTERNAL_TRANSFER_REVERSED", + "EXTERNAL_TRANSFER_SETTLED", + "EXTERNAL_WIRE_CANCELED", + "EXTERNAL_WIRE_INITIATED", + "EXTERNAL_WIRE_RELEASED", + "EXTERNAL_WIRE_REVERSED", + "EXTERNAL_WIRE_SETTLED", "FINANCIAL_AUTHORIZATION", "FINANCIAL_CREDIT_AUTHORIZATION", + "INTEREST", + "INTEREST_REVERSAL", + "INTERNAL_ADJUSTMENT", + "LATE_PAYMENT", + "LATE_PAYMENT_REVERSAL", + "PROVISIONAL_CREDIT", + "PROVISIONAL_CREDIT_REVERSAL", "RETURN", "RETURN_REVERSAL", "TRANSFER", "TRANSFER_INSUFFICIENT_FUNDS", + "RETURNED_PAYMENT", + "RETURNED_PAYMENT_REVERSAL", ] ] = None - """Event types: - - - `ACH_INSUFFICIENT_FUNDS` - Attempted ACH origination declined due to - insufficient balance. - - `ACH_ORIGINATION_PENDING` - ACH origination pending release from an ACH hold. - - `ACH_ORIGINATION_RELEASED` - ACH origination released from pending to - available balance. - - `ACH_RECEIPT_PENDING` - ACH receipt pending release from an ACH holder. - - `ACH_RECEIPT_RELEASED` - ACH receipt released from pending to available - balance. - - `ACH_RETURN` - ACH origination returned by the Receiving Depository Financial - Institution. - - `AUTHORIZATION` - Authorize a card transaction. - - `AUTHORIZATION_ADVICE` - Advice on a card transaction. - - `AUTHORIZATION_EXPIRY` - Card Authorization has expired and reversed by - Lithic. - - `AUTHORIZATION_REVERSAL` - Card Authorization was reversed by the merchant. - - `BALANCE_INQUIRY` - A card balance inquiry (typically a $0 authorization) has - occurred on a card. - - `CLEARING` - Card Transaction is settled. - - `CORRECTION_DEBIT` - Manual card transaction correction (Debit). - - `CORRECTION_CREDIT` - Manual card transaction correction (Credit). - - `CREDIT_AUTHORIZATION` - A refund or credit card authorization from a - merchant. - - `CREDIT_AUTHORIZATION_ADVICE` - A credit card authorization was approved on - your behalf by the network. - - `FINANCIAL_AUTHORIZATION` - A request from a merchant to debit card funds - without additional clearing. - - `FINANCIAL_CREDIT_AUTHORIZATION` - A request from a merchant to refund or - credit card funds without additional clearing. - - `RETURN` - A card refund has been processed on the transaction. - - `RETURN_REVERSAL` - A card refund has been reversed (e.g., when a merchant - reverses an incorrect refund). - - `TRANSFER` - Successful internal transfer of funds between financial accounts. - - `TRANSFER_INSUFFICIENT_FUNDS` - Declined internl transfer of funds due to - insufficient balance of the sender. - """ class FinancialTransaction(BaseModel): token: str """Globally unique identifier.""" - category: Literal["ACH", "CARD", "TRANSFER"] + category: Literal["ACH", "CARD", "INTERNAL", "TRANSFER"] """Status types: - `CARD` - Issuing card transaction. - `ACH` - Transaction over ACH. + - `INTERNAL` - Transaction for internal adjustment. - `TRANSFER` - Internal transfer of funds between financial accounts in your program. """ @@ -110,7 +116,10 @@ class FinancialTransaction(BaseModel): """Date and time when the financial transaction first occurred. UTC time zone.""" currency: str - """3-digit alphabetic ISO 4217 code for the settling currency of the transaction.""" + """ + 3-character alphabetic ISO 4217 code for the settling currency of the + transaction. + """ descriptor: str """ @@ -140,16 +149,16 @@ class FinancialTransaction(BaseModel): (e.g., cents), including any acquirer fees. This may change over time. """ - status: Literal["DECLINED", "EXPIRED", "PENDING", "SETTLED", "VOIDED"] + status: Literal["DECLINED", "EXPIRED", "PENDING", "RETURNED", "SETTLED", "VOIDED"] """Status types: - - `DECLINED` - The card transaction was declined. - - `EXPIRED` - Lithic reversed the card authorization as it has passed its - expiration time. - - `PENDING` - Authorization is pending completion from the merchant or pending - release from ACH hold period - - `SETTLED` - The financial transaction is completed. - - `VOIDED` - The merchant has voided the previously pending card authorization. + - `DECLINED` - The transaction was declined. + - `EXPIRED` - The authorization as it has passed its expiration time. Card + transaction only. + - `PENDING` - The transaction is expected to settle. + - `RETURNED` - The transaction has been returned. + - `SETTLED` - The transaction is completed. + - `VOIDED` - The transaction was voided. Card transaction only. """ updated: datetime diff --git a/src/lithic/types/kyb_business_entity.py b/src/lithic/types/kyb_business_entity.py new file mode 100644 index 00000000..3e7b1d6a --- /dev/null +++ b/src/lithic/types/kyb_business_entity.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel + +__all__ = ["KYBBusinessEntity", "Address"] + + +class Address(BaseModel): + address1: str + """Valid deliverable address (no PO boxes).""" + + city: str + """Name of city.""" + + country: str + """Valid country code. + + Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 + three-character format. + """ + + postal_code: str + """Valid postal code. + + Only USA ZIP codes are currently supported, entered as a five-digit ZIP or + nine-digit ZIP+4. + """ + + state: str + """Valid state code. + + Only USA state codes are currently supported, entered in uppercase ISO 3166-2 + two-character format. + """ + + address2: Optional[str] = None + """Unit or apartment number (if applicable).""" + + +class KYBBusinessEntity(BaseModel): + address: Address + """ + Business''s physical address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + """ + + government_id: str + """Government-issued identification number. + + US Federal Employer Identification Numbers (EIN) are currently supported, + entered as full nine-digits, with or without hyphens. + """ + + legal_business_name: str + """Legal (formal) business name.""" + + phone_numbers: List[str] + """ + One or more of the business's phone number(s), entered as a list in E.164 + format. + """ + + dba_business_name: Optional[str] = None + """ + Any name that the business operates under that is not its legal business name + (if applicable). + """ + + parent_company: Optional[str] = None + """Parent company name (if applicable).""" diff --git a/src/lithic/types/kyb_param.py b/src/lithic/types/kyb_param.py new file mode 100644 index 00000000..864902a9 --- /dev/null +++ b/src/lithic/types/kyb_param.py @@ -0,0 +1,209 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Iterable +from typing_extensions import Literal, Required, TypedDict + +from .shared_params.address import Address + +__all__ = ["KYBParam", "BeneficialOwnerIndividual", "BusinessEntity", "ControlPerson", "BeneficialOwnerEntity"] + + +class BeneficialOwnerIndividual(TypedDict, total=False): + address: Required[Address] + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: Required[str] + """Individual's date of birth, as an RFC 3339 date.""" + + email: Required[str] + """ + Individual's email address. If utilizing Lithic for chargeback processing, this + customer email address may be used to communicate dispute status and resolution. + """ + + first_name: Required[str] + """Individual's first name, as it appears on government-issued identity documents.""" + + government_id: Required[str] + """ + Government-issued identification number (required for identity verification and + compliance with banking regulations). Social Security Numbers (SSN) and + Individual Taxpayer Identification Numbers (ITIN) are currently supported, + entered as full nine-digits, with or without hyphens + """ + + last_name: Required[str] + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: str + """Individual's phone number, entered in E.164 format.""" + + +class BusinessEntity(TypedDict, total=False): + address: Required[Address] + """ + Business's physical address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + """ + + government_id: Required[str] + """Government-issued identification number. + + US Federal Employer Identification Numbers (EIN) are currently supported, + entered as full nine-digits, with or without hyphens. + """ + + legal_business_name: Required[str] + """Legal (formal) business name.""" + + phone_numbers: Required[List[str]] + """ + One or more of the business's phone number(s), entered as a list in E.164 + format. + """ + + dba_business_name: str + """ + Any name that the business operates under that is not its legal business name + (if applicable). + """ + + parent_company: str + """Parent company name (if applicable).""" + + +class ControlPerson(TypedDict, total=False): + address: Required[Address] + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + dob: Required[str] + """Individual's date of birth, as an RFC 3339 date.""" + + email: Required[str] + """ + Individual's email address. If utilizing Lithic for chargeback processing, this + customer email address may be used to communicate dispute status and resolution. + """ + + first_name: Required[str] + """Individual's first name, as it appears on government-issued identity documents.""" + + government_id: Required[str] + """ + Government-issued identification number (required for identity verification and + compliance with banking regulations). Social Security Numbers (SSN) and + Individual Taxpayer Identification Numbers (ITIN) are currently supported, + entered as full nine-digits, with or without hyphens + """ + + last_name: Required[str] + """Individual's last name, as it appears on government-issued identity documents.""" + + phone_number: str + """Individual's phone number, entered in E.164 format.""" + + +class BeneficialOwnerEntity(TypedDict, total=False): + address: Required[Address] + """ + Business's physical address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + """ + + government_id: Required[str] + """Government-issued identification number. + + US Federal Employer Identification Numbers (EIN) are currently supported, + entered as full nine-digits, with or without hyphens. + """ + + legal_business_name: Required[str] + """Legal (formal) business name.""" + + phone_numbers: Required[List[str]] + """ + One or more of the business's phone number(s), entered as a list in E.164 + format. + """ + + dba_business_name: str + """ + Any name that the business operates under that is not its legal business name + (if applicable). + """ + + parent_company: str + """Parent company name (if applicable).""" + + +class KYBParam(TypedDict, total=False): + beneficial_owner_individuals: Required[Iterable[BeneficialOwnerIndividual]] + """List of all direct and indirect individuals with >25% ownership in the company. + + If no individual owns >25% of the company, please identify the largest + shareholder in this field. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) + (Section I) for more background on individuals that should be included. + """ + + business_entity: Required[BusinessEntity] + """ + Information for business for which the account is being opened and KYB is being + run. + """ + + control_person: Required[ControlPerson] + """ + An individual with significant responsibility for managing the legal entity + (e.g., a Chief Executive Officer, Chief Financial Officer, Chief Operating + Officer, Managing Member, General Partner, President, Vice President, or + Treasurer). This can be an executive, or someone who will have program-wide + access to the cards that Lithic will provide. In some cases, this individual + could also be a beneficial owner listed above. See + [FinCEN requirements](https://www.fincen.gov/sites/default/files/shared/CDD_Rev6.7_Sept_2017_Certificate.pdf) + (Section II) for more background. + """ + + nature_of_business: Required[str] + """ + Short description of the company's line of business (i.e., what does the company + do?). + """ + + tos_timestamp: Required[str] + """ + An RFC 3339 timestamp indicating when the account holder accepted the applicable + legal agreements (e.g., cardholder terms) as agreed upon during API customer's + implementation with Lithic. + """ + + workflow: Required[Literal["KYB_BASIC", "KYB_BYO"]] + """Specifies the type of KYB workflow to run.""" + + beneficial_owner_entities: Iterable[BeneficialOwnerEntity] + """Deprecated.""" + + external_id: str + """ + A user provided id that can be used to link an account holder with an external + system + """ + + kyb_passed_timestamp: str + """ + An RFC 3339 timestamp indicating when precomputed KYC was completed on the + business with a pass result. + + This field is required only if workflow type is `KYB_BYO`. + """ + + website_url: str + """Company website URL.""" diff --git a/src/lithic/types/kyc_exempt_param.py b/src/lithic/types/kyc_exempt_param.py new file mode 100644 index 00000000..c0335220 --- /dev/null +++ b/src/lithic/types/kyc_exempt_param.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .shared_params.address import Address + +__all__ = ["KYCExemptParam"] + + +class KYCExemptParam(TypedDict, total=False): + address: Required[Address] + """ + KYC Exempt user's current address - PO boxes, UPS drops, and FedEx drops are not + acceptable; APO/FPO are acceptable. + """ + + email: Required[str] + """The KYC Exempt user's email""" + + first_name: Required[str] + """The KYC Exempt user's first name""" + + kyc_exemption_type: Required[Literal["AUTHORIZED_USER", "PREPAID_CARD_USER"]] + """Specifies the type of KYC Exempt user""" + + last_name: Required[str] + """The KYC Exempt user's last name""" + + phone_number: Required[str] + """The KYC Exempt user's phone number, entered in E.164 format.""" + + workflow: Required[Literal["KYC_EXEMPT"]] + """Specifies the workflow type. This must be 'KYC_EXEMPT'""" + + business_account_token: str + """ + Only applicable for customers using the KYC-Exempt workflow to enroll authorized + users of businesses. Pass the account_token of the enrolled business associated + with the AUTHORIZED_USER in this field. + """ + + external_id: str + """ + A user provided id that can be used to link an account holder with an external + system + """ diff --git a/src/lithic/types/account_holder_resubmit_params.py b/src/lithic/types/kyc_param.py similarity index 70% rename from src/lithic/types/account_holder_resubmit_params.py rename to src/lithic/types/kyc_param.py index acf50828..fef6b452 100644 --- a/src/lithic/types/account_holder_resubmit_params.py +++ b/src/lithic/types/kyc_param.py @@ -1,33 +1,16 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict -from ..types import shared_params +from .shared_params.address import Address -__all__ = ["AccountHolderResubmitParams", "Individual"] - - -class AccountHolderResubmitParams(TypedDict, total=False): - individual: Required[Individual] - """ - Information on individual for whom the account is being opened and KYC is being - re-run. - """ - - tos_timestamp: Required[str] - """ - An RFC 3339 timestamp indicating when the account holder accepted the applicable - legal agreements (e.g., cardholder terms) as agreed upon during API customer's - implementation with Lithic. - """ - - workflow: Required[Literal["KYC_ADVANCED"]] +__all__ = ["KYCParam", "Individual"] class Individual(TypedDict, total=False): - address: Required[shared_params.Address] + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. @@ -58,3 +41,35 @@ class Individual(TypedDict, total=False): phone_number: Required[str] """Individual's phone number, entered in E.164 format.""" + + +class KYCParam(TypedDict, total=False): + individual: Required[Individual] + """ + Information on individual for whom the account is being opened and KYC is being + run. + """ + + tos_timestamp: Required[str] + """ + An RFC 3339 timestamp indicating when the account holder accepted the applicable + legal agreements (e.g., cardholder terms) as agreed upon during API customer's + implementation with Lithic. + """ + + workflow: Required[Literal["KYC_BASIC", "KYC_BYO"]] + """Specifies the type of KYC workflow to run.""" + + external_id: str + """ + A user provided id that can be used to link an account holder with an external + system + """ + + kyc_passed_timestamp: str + """ + An RFC 3339 timestamp indicating when precomputed KYC was completed on the + individual with a pass result. + + This field is required only if workflow type is `KYC_BYO`. + """ diff --git a/src/lithic/types/management_operation_create_params.py b/src/lithic/types/management_operation_create_params.py new file mode 100644 index 00000000..d8e8b559 --- /dev/null +++ b/src/lithic/types/management_operation_create_params.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Literal, Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ManagementOperationCreateParams"] + + +class ManagementOperationCreateParams(TypedDict, total=False): + amount: Required[int] + + category: Required[Literal["MANAGEMENT_FEE", "MANAGEMENT_DISPUTE", "MANAGEMENT_REWARD", "MANAGEMENT_ADJUSTMENT"]] + + direction: Required[Literal["CREDIT", "DEBIT"]] + + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + + event_type: Required[ + Literal[ + "CASH_BACK", + "CURRENCY_CONVERSION", + "INTEREST", + "LATE_PAYMENT", + "BILLING_ERROR", + "PROVISIONAL_CREDIT", + "LOSS_WRITE_OFF", + "CASH_BACK_REVERSAL", + "CURRENCY_CONVERSION_REVERSAL", + "INTEREST_REVERSAL", + "LATE_PAYMENT_REVERSAL", + "BILLING_ERROR_REVERSAL", + "PROVISIONAL_CREDIT_REVERSAL", + "RETURNED_PAYMENT", + "RETURNED_PAYMENT_REVERSAL", + ] + ] + + financial_account_token: Required[str] + + token: str + + memo: str + + subtype: str + + user_defined_id: str diff --git a/src/lithic/types/management_operation_list_params.py b/src/lithic/types/management_operation_list_params.py new file mode 100644 index 00000000..7e460afe --- /dev/null +++ b/src/lithic/types/management_operation_list_params.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ManagementOperationListParams"] + + +class ManagementOperationListParams(TypedDict, total=False): + begin: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created after the specified time will be included. UTC time zone. + """ + + business_account_token: str + + category: Literal["MANAGEMENT_FEE", "MANAGEMENT_DISPUTE", "MANAGEMENT_REWARD", "MANAGEMENT_ADJUSTMENT"] + """Management operation category to be returned.""" + + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Date string in RFC 3339 format. + + Only entries created before the specified time will be included. UTC time zone. + """ + + ending_before: str + """A cursor representing an item's token before which a page of results should end. + + Used to retrieve the previous page of results before this item. + """ + + financial_account_token: str + """Globally unique identifier for the financial account. + + Accepted type dependent on the program's use case. + """ + + page_size: int + """Page size (for pagination).""" + + starting_after: str + """A cursor representing an item's token after which a page of results should + begin. + + Used to retrieve the next page of results after this item. + """ + + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED"] + """Management operation status to be returned.""" diff --git a/src/lithic/types/management_operation_reverse_params.py b/src/lithic/types/management_operation_reverse_params.py new file mode 100644 index 00000000..ed3d2583 --- /dev/null +++ b/src/lithic/types/management_operation_reverse_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 Union +from datetime import date +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ManagementOperationReverseParams"] + + +class ManagementOperationReverseParams(TypedDict, total=False): + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + + memo: str diff --git a/src/lithic/types/management_operation_transaction.py b/src/lithic/types/management_operation_transaction.py new file mode 100644 index 00000000..df533665 --- /dev/null +++ b/src/lithic/types/management_operation_transaction.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import date, datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ManagementOperationTransaction", "Event"] + + +class Event(BaseModel): + token: str + + amount: int + + created: datetime + + detailed_results: List[Literal["APPROVED"]] + + effective_date: date + + memo: str + + result: Literal["APPROVED", "DECLINED"] + + type: Literal[ + "CASH_BACK", + "CURRENCY_CONVERSION", + "INTEREST", + "LATE_PAYMENT", + "BILLING_ERROR", + "PROVISIONAL_CREDIT", + "LOSS_WRITE_OFF", + "CASH_BACK_REVERSAL", + "CURRENCY_CONVERSION_REVERSAL", + "INTEREST_REVERSAL", + "LATE_PAYMENT_REVERSAL", + "BILLING_ERROR_REVERSAL", + "PROVISIONAL_CREDIT_REVERSAL", + "RETURNED_PAYMENT", + "RETURNED_PAYMENT_REVERSAL", + ] + + subtype: Optional[str] = None + + +class ManagementOperationTransaction(BaseModel): + token: str + + category: Literal["MANAGEMENT_FEE", "MANAGEMENT_DISPUTE", "MANAGEMENT_REWARD", "MANAGEMENT_ADJUSTMENT"] + + created: datetime + + currency: str + + direction: Literal["CREDIT", "DEBIT"] + + events: List[Event] + + financial_account_token: str + + pending_amount: int + + result: Literal["APPROVED", "DECLINED"] + + settled_amount: int + + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED"] + + updated: datetime + + user_defined_id: Optional[str] = None diff --git a/src/lithic/types/message_attempt.py b/src/lithic/types/message_attempt.py index 7218ab67..a3443db0 100644 --- a/src/lithic/types/message_attempt.py +++ b/src/lithic/types/message_attempt.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal diff --git a/src/lithic/types/owner_type.py b/src/lithic/types/owner_type.py index 9623609c..129346bf 100644 --- a/src/lithic/types/owner_type.py +++ b/src/lithic/types/owner_type.py @@ -1,7 +1,7 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["OwnerType"] -OwnerType = Literal["BUSINESS", "INDIVIDUAL"] +OwnerType: TypeAlias = Literal["INDIVIDUAL", "BUSINESS"] diff --git a/src/lithic/types/payment.py b/src/lithic/types/payment.py index ea0047c7..ebbf6893 100644 --- a/src/lithic/types/payment.py +++ b/src/lithic/types/payment.py @@ -1,17 +1,84 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional +from datetime import datetime from typing_extensions import Literal from .._models import BaseModel -from .financial_transaction import FinancialTransaction - -__all__ = ["Payment", "PaymentMethodAttributes"] - - -class PaymentMethodAttributes(BaseModel): - sec_code: Literal["CCD", "PPD", "WEB"] +__all__ = ["Payment", "Event", "MethodAttributes"] + + +class Event(BaseModel): + token: str + """Globally unique identifier.""" + + amount: int + """ + Amount of the financial event that has been settled in the currency's smallest + unit (e.g., cents). + """ + + created: datetime + """Date and time when the financial event occurred. UTC time zone.""" + + result: Literal["APPROVED", "DECLINED"] + """ + APPROVED financial events were successful while DECLINED financial events were + declined by user, Lithic, or the network. + """ + + type: Literal[ + "ACH_ORIGINATION_CANCELLED", + "ACH_ORIGINATION_INITIATED", + "ACH_ORIGINATION_PROCESSED", + "ACH_ORIGINATION_SETTLED", + "ACH_ORIGINATION_RELEASED", + "ACH_ORIGINATION_REVIEWED", + "ACH_RECEIPT_PROCESSED", + "ACH_RECEIPT_SETTLED", + "ACH_RETURN_INITIATED", + "ACH_RETURN_PROCESSED", + "ACH_RETURN_SETTLED", + ] + """Event types: + + - `ACH_ORIGINATION_INITIATED` - ACH origination received and pending + approval/release from an ACH hold. + - `ACH_ORIGINATION_REVIEWED` - ACH origination has completed the review process. + - `ACH_ORIGINATION_CANCELLED` - ACH origination has been cancelled. + - `ACH_ORIGINATION_PROCESSED` - ACH origination has been processed and sent to + the fed. + - `ACH_ORIGINATION_SETTLED` - ACH origination has settled. + - `ACH_ORIGINATION_RELEASED` - ACH origination released from pending to + available balance. + - `ACH_RETURN_PROCESSED` - ACH origination returned by the Receiving Depository + Financial Institution. + - `ACH_RECEIPT_PROCESSED` - ACH receipt pending release from an ACH holder. + - `ACH_RETURN_INITIATED` - ACH initiated return for a ACH receipt. + - `ACH_RECEIPT_SETTLED` - ACH receipt funds have settled. + - `ACH_RECEIPT_RELEASED` - ACH receipt released from pending to available + balance. + - `ACH_RETURN_SETTLED` - ACH receipt return settled by the Receiving Depository + Financial Institution. + """ + + detailed_results: Optional[ + List[ + Literal[ + "APPROVED", + "FUNDS_INSUFFICIENT", + "ACCOUNT_INVALID", + "PROGRAM_TRANSACTION_LIMIT_EXCEEDED", + "PROGRAM_DAILY_LIMIT_EXCEEDED", + "PROGRAM_MONTHLY_LIMIT_EXCEEDED", + ] + ] + ] = None + """More detailed reasons for the event""" + + +class MethodAttributes(BaseModel): company_id: Optional[str] = None receipt_routing_number: Optional[str] = None @@ -20,16 +87,74 @@ class PaymentMethodAttributes(BaseModel): return_reason_code: Optional[str] = None + sec_code: Literal["CCD", "PPD", "WEB"] + + trace_numbers: List[Optional[str]] + + +class Payment(BaseModel): + token: str + """Globally unique identifier.""" + + category: Literal["ACH"] + """Payment category""" + + created: datetime + """Date and time when the payment first occurred. UTC time zone.""" + + currency: str + """3-character alphabetic ISO 4217 code for the settling currency of the payment.""" + + descriptor: str + """ + A string that provides a description of the payment; may be useful to display to + users. + """ -class Payment(FinancialTransaction): direction: Literal["CREDIT", "DEBIT"] + events: List[Event] + """A list of all payment events that have modified this payment.""" + + external_bank_account_token: Optional[str] = None + + financial_account_token: str + method: Literal["ACH_NEXT_DAY", "ACH_SAME_DAY"] - method_attributes: PaymentMethodAttributes + method_attributes: MethodAttributes + + pending_amount: int + """ + Pending amount of the payment in the currency's smallest unit (e.g., cents). The + value of this field will go to zero over time once the payment is settled. + """ + + result: Literal["APPROVED", "DECLINED"] + """ + APPROVED payments were successful while DECLINED payments were declined by + Lithic or returned. + """ + + settled_amount: int + """ + Amount of the payment that has been settled in the currency's smallest unit + (e.g., cents). + """ source: Literal["CUSTOMER", "LITHIC"] - external_bank_account_token: Optional[str] = None + status: Literal["DECLINED", "PENDING", "RETURNED", "SETTLED"] + """Status types: + + - `DECLINED` - The payment was declined. + - `PENDING` - The payment is being processed and has yet to settle or release + (origination debit). + - `RETURNED` - The payment has been returned. + - `SETTLED` - The payment is completed. + """ + + updated: datetime + """Date and time when the financial transaction was last updated. UTC time zone.""" user_defined_id: Optional[str] = None diff --git a/src/lithic/types/payment_create_params.py b/src/lithic/types/payment_create_params.py index 8916fa28..97e65835 100644 --- a/src/lithic/types/payment_create_params.py +++ b/src/lithic/types/payment_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -33,11 +33,3 @@ class PaymentCreateParams(TypedDict, total=False): class MethodAttributes(TypedDict, total=False): sec_code: Required[Literal["CCD", "PPD", "WEB"]] - - company_id: str - - receipt_routing_number: str - - retries: int - - return_reason_code: str diff --git a/src/lithic/types/payment_create_response.py b/src/lithic/types/payment_create_response.py index 19acafe9..faffaa24 100644 --- a/src/lithic/types/payment_create_response.py +++ b/src/lithic/types/payment_create_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional @@ -10,4 +10,4 @@ class PaymentCreateResponse(Payment): balance: Optional[Balance] = None - """Balance of a Financial Account""" + """Balance""" diff --git a/src/lithic/types/payment_list_params.py b/src/lithic/types/payment_list_params.py index 11dcd7b7..f2168b52 100644 --- a/src/lithic/types/payment_list_params.py +++ b/src/lithic/types/payment_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -12,12 +12,18 @@ class PaymentListParams(TypedDict, total=False): + account_token: str + begin: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] """Date string in RFC 3339 format. Only entries created after the specified time will be included. UTC time zone. """ + business_account_token: str + + category: Literal["ACH"] + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] """Date string in RFC 3339 format. diff --git a/src/lithic/types/payment_retry_response.py b/src/lithic/types/payment_retry_response.py index e3e0fbce..fe0577c3 100644 --- a/src/lithic/types/payment_retry_response.py +++ b/src/lithic/types/payment_retry_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional @@ -10,4 +10,4 @@ class PaymentRetryResponse(Payment): balance: Optional[Balance] = None - """Balance of a Financial Account""" + """Balance""" diff --git a/src/lithic/types/payment_simulate_action_params.py b/src/lithic/types/payment_simulate_action_params.py new file mode 100644 index 00000000..c8d0c128 --- /dev/null +++ b/src/lithic/types/payment_simulate_action_params.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["PaymentSimulateActionParams"] + + +class PaymentSimulateActionParams(TypedDict, total=False): + event_type: Required[ + Literal[ + "ACH_ORIGINATION_REVIEWED", + "ACH_ORIGINATION_RELEASED", + "ACH_ORIGINATION_PROCESSED", + "ACH_ORIGINATION_SETTLED", + "ACH_RECEIPT_SETTLED", + "ACH_RETURN_INITIATED", + "ACH_RETURN_PROCESSED", + "ACH_RETURN_SETTLED", + ] + ] + """Event Type""" + + decline_reason: Literal[ + "PROGRAM_TRANSACTION_LIMIT_EXCEEDED", "PROGRAM_DAILY_LIMIT_EXCEEDED", "PROGRAM_MONTHLY_LIMIT_EXCEEDED" + ] + """Decline reason""" + + return_reason_code: str + """Return Reason Code""" diff --git a/src/lithic/types/payment_simulate_action_response.py b/src/lithic/types/payment_simulate_action_response.py new file mode 100644 index 00000000..15a8fe99 --- /dev/null +++ b/src/lithic/types/payment_simulate_action_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["PaymentSimulateActionResponse"] + + +class PaymentSimulateActionResponse(BaseModel): + debugging_request_id: str + """Debugging Request Id""" + + result: Literal["APPROVED", "DECLINED"] + """Request Result""" + + transaction_event_token: str + """Transaction Event Token""" diff --git a/src/lithic/types/payment_simulate_receipt_params.py b/src/lithic/types/payment_simulate_receipt_params.py new file mode 100644 index 00000000..86618fc1 --- /dev/null +++ b/src/lithic/types/payment_simulate_receipt_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_extensions import Literal, Required, TypedDict + +__all__ = ["PaymentSimulateReceiptParams"] + + +class PaymentSimulateReceiptParams(TypedDict, total=False): + token: Required[str] + """Payment token""" + + amount: Required[int] + """Amount""" + + financial_account_token: Required[str] + """Financial Account Token""" + + receipt_type: Required[Literal["RECEIPT_CREDIT", "RECEIPT_DEBIT"]] + """Receipt Type""" + + memo: str + """Memo""" diff --git a/src/lithic/types/payment_simulate_receipt_response.py b/src/lithic/types/payment_simulate_receipt_response.py new file mode 100644 index 00000000..e7dffe2d --- /dev/null +++ b/src/lithic/types/payment_simulate_receipt_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["PaymentSimulateReceiptResponse"] + + +class PaymentSimulateReceiptResponse(BaseModel): + debugging_request_id: str + """Debugging Request Id""" + + result: Literal["APPROVED", "DECLINED"] + """Request Result""" + + transaction_event_token: str + """Transaction Event Token""" diff --git a/src/lithic/types/payment_simulate_release_params.py b/src/lithic/types/payment_simulate_release_params.py index 452b2ac8..cec89e02 100644 --- a/src/lithic/types/payment_simulate_release_params.py +++ b/src/lithic/types/payment_simulate_release_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,3 +9,4 @@ class PaymentSimulateReleaseParams(TypedDict, total=False): payment_token: Required[str] + """Payment Token""" diff --git a/src/lithic/types/payment_simulate_release_response.py b/src/lithic/types/payment_simulate_release_response.py index 0326e62f..2d7be0f7 100644 --- a/src/lithic/types/payment_simulate_release_response.py +++ b/src/lithic/types/payment_simulate_release_response.py @@ -1,6 +1,5 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional from typing_extensions import Literal from .._models import BaseModel @@ -9,8 +8,11 @@ class PaymentSimulateReleaseResponse(BaseModel): - debugging_request_id: Optional[str] = None + debugging_request_id: str + """Debugging Request Id""" - result: Optional[Literal["APPROVED", "DECLINED"]] = None + result: Literal["APPROVED", "DECLINED"] + """Request Result""" - transaction_event_token: Optional[str] = None + transaction_event_token: str + """Transaction Event Token""" diff --git a/src/lithic/types/payment_simulate_return_params.py b/src/lithic/types/payment_simulate_return_params.py index d4d094c9..3a880585 100644 --- a/src/lithic/types/payment_simulate_return_params.py +++ b/src/lithic/types/payment_simulate_return_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,5 +9,7 @@ class PaymentSimulateReturnParams(TypedDict, total=False): payment_token: Required[str] + """Payment Token""" return_reason_code: str + """Return Reason Code""" diff --git a/src/lithic/types/payment_simulate_return_response.py b/src/lithic/types/payment_simulate_return_response.py index 9ceb94e9..14043ff6 100644 --- a/src/lithic/types/payment_simulate_return_response.py +++ b/src/lithic/types/payment_simulate_return_response.py @@ -1,6 +1,5 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional from typing_extensions import Literal from .._models import BaseModel @@ -9,8 +8,11 @@ class PaymentSimulateReturnResponse(BaseModel): - debugging_request_id: Optional[str] = None + debugging_request_id: str + """Debugging Request Id""" - result: Optional[Literal["APPROVED", "DECLINED"]] = None + result: Literal["APPROVED", "DECLINED"] + """Request Result""" - transaction_event_token: Optional[str] = None + transaction_event_token: str + """Transaction Event Token""" diff --git a/src/lithic/types/reports/__init__.py b/src/lithic/types/reports/__init__.py index b7d013b8..1d825b95 100644 --- a/src/lithic/types/reports/__init__.py +++ b/src/lithic/types/reports/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/reports/settlement/__init__.py b/src/lithic/types/reports/settlement/__init__.py new file mode 100644 index 00000000..a1847cd0 --- /dev/null +++ b/src/lithic/types/reports/settlement/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .network_total_list_params import NetworkTotalListParams as NetworkTotalListParams +from .network_total_list_response import NetworkTotalListResponse as NetworkTotalListResponse +from .network_total_retrieve_response import NetworkTotalRetrieveResponse as NetworkTotalRetrieveResponse diff --git a/src/lithic/types/reports/settlement/network_total_list_params.py b/src/lithic/types/reports/settlement/network_total_list_params.py new file mode 100644 index 00000000..82a82c55 --- /dev/null +++ b/src/lithic/types/reports/settlement/network_total_list_params.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date, datetime +from typing_extensions import Literal, Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["NetworkTotalListParams"] + + +class NetworkTotalListParams(TypedDict, total=False): + begin: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Datetime in RFC 3339 format. + + Only entries created after the specified time will be included. UTC time zone. + """ + + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Datetime in RFC 3339 format. + + Only entries created before the specified time will be included. UTC time zone. + """ + + ending_before: str + """A cursor representing an item's token before which a page of results should end. + + Used to retrieve the previous page of results before this item. + """ + + institution_id: str + """Institution ID to filter on.""" + + network: Literal["VISA", "MASTERCARD", "MAESTRO", "INTERLINK"] + """Network to filter on.""" + + page_size: int + """Number of records per page.""" + + report_date: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Singular report date to filter on (YYYY-MM-DD). + + Cannot be populated in conjunction with report_date_begin or report_date_end. + """ + + report_date_begin: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Earliest report date to filter on, inclusive (YYYY-MM-DD).""" + + report_date_end: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Latest report date to filter on, inclusive (YYYY-MM-DD).""" + + settlement_institution_id: str + """Settlement institution ID to filter on.""" + + starting_after: str + """A cursor representing an item's token after which a page of results should + begin. + + Used to retrieve the next page of results after this item. + """ diff --git a/src/lithic/types/reports/settlement/network_total_list_response.py b/src/lithic/types/reports/settlement/network_total_list_response.py new file mode 100644 index 00000000..5792fd3c --- /dev/null +++ b/src/lithic/types/reports/settlement/network_total_list_response.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import date, datetime +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["NetworkTotalListResponse", "Amounts"] + + +class Amounts(BaseModel): + gross_settlement: int + """Total settlement amount excluding interchange, in currency's smallest unit.""" + + interchange_fees: int + """Interchange amount, in currency's smallest unit.""" + + net_settlement: int + """ + `gross_settlement` net of `interchange_fees` and `visa_charges` (if applicable), + in currency's smallest unit. + """ + + visa_charges: Optional[int] = None + """Charges specific to Visa/Interlink, in currency's smallest unit.""" + + +class NetworkTotalListResponse(BaseModel): + token: str + """Globally unique identifier.""" + + amounts: Amounts + + created: datetime + """RFC 3339 timestamp for when the record was created. UTC time zone.""" + + currency: str + """3-character alphabetic ISO 4217 code.""" + + institution_id: str + """The institution that activity occurred on. + + For Mastercard: ICA (Interbank Card Association). For Maestro: institution ID. + For Visa: lowest level SRE (Settlement Reporting Entity). + """ + + network: Literal["VISA", "MASTERCARD", "MAESTRO", "INTERLINK"] + """Card network where the transaction took place. + + VISA, MASTERCARD, MAESTRO, or INTERLINK. + """ + + report_date: date + """Date that the network total record applies to. YYYY-MM-DD format.""" + + settlement_institution_id: str + """The institution responsible for settlement. + + For Mastercard: same as `institution_id`. For Maestro: billing ICA. For Visa: + Funds Transfer SRE (FTSRE). + """ + + settlement_service: str + """Settlement service.""" + + updated: datetime + """RFC 3339 timestamp for when the record was last updated. UTC time zone.""" + + cycle: Optional[int] = None + """The clearing cycle that the network total record applies to. Mastercard only.""" diff --git a/src/lithic/types/reports/settlement/network_total_retrieve_response.py b/src/lithic/types/reports/settlement/network_total_retrieve_response.py new file mode 100644 index 00000000..91fc60ac --- /dev/null +++ b/src/lithic/types/reports/settlement/network_total_retrieve_response.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import date, datetime +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["NetworkTotalRetrieveResponse", "Amounts"] + + +class Amounts(BaseModel): + gross_settlement: int + """Total settlement amount excluding interchange, in currency's smallest unit.""" + + interchange_fees: int + """Interchange amount, in currency's smallest unit.""" + + net_settlement: int + """ + `gross_settlement` net of `interchange_fees` and `visa_charges` (if applicable), + in currency's smallest unit. + """ + + visa_charges: Optional[int] = None + """Charges specific to Visa/Interlink, in currency's smallest unit.""" + + +class NetworkTotalRetrieveResponse(BaseModel): + token: str + """Globally unique identifier.""" + + amounts: Amounts + + created: datetime + """RFC 3339 timestamp for when the record was created. UTC time zone.""" + + currency: str + """3-character alphabetic ISO 4217 code.""" + + institution_id: str + """The institution that activity occurred on. + + For Mastercard: ICA (Interbank Card Association). For Maestro: institution ID. + For Visa: lowest level SRE (Settlement Reporting Entity). + """ + + network: Literal["VISA", "MASTERCARD", "MAESTRO", "INTERLINK"] + """Card network where the transaction took place. + + VISA, MASTERCARD, MAESTRO, or INTERLINK. + """ + + report_date: date + """Date that the network total record applies to. YYYY-MM-DD format.""" + + settlement_institution_id: str + """The institution responsible for settlement. + + For Mastercard: same as `institution_id`. For Maestro: billing ICA. For Visa: + Funds Transfer SRE (FTSRE). + """ + + settlement_service: str + """Settlement service.""" + + updated: datetime + """RFC 3339 timestamp for when the record was last updated. UTC time zone.""" + + cycle: Optional[int] = None + """The clearing cycle that the network total record applies to. Mastercard only.""" diff --git a/src/lithic/types/reports/settlement_list_details_params.py b/src/lithic/types/reports/settlement_list_details_params.py index 8f142e31..003f65d0 100644 --- a/src/lithic/types/reports/settlement_list_details_params.py +++ b/src/lithic/types/reports/settlement_list_details_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/required_document.py b/src/lithic/types/required_document.py new file mode 100644 index 00000000..f1143f91 --- /dev/null +++ b/src/lithic/types/required_document.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel + +__all__ = ["RequiredDocument"] + + +class RequiredDocument(BaseModel): + entity_token: str + """Globally unique identifier for an entity.""" + + status_reasons: List[str] + """ + rovides the status reasons that will be satisfied by providing one of the valid + documents. + """ + + valid_documents: List[str] + """ + A list of valid documents that will satisfy the KYC requirements for the + specified entity. + """ diff --git a/src/lithic/types/responder_endpoint_check_status_params.py b/src/lithic/types/responder_endpoint_check_status_params.py index bc4391ee..f21aef7e 100644 --- a/src/lithic/types/responder_endpoint_check_status_params.py +++ b/src/lithic/types/responder_endpoint_check_status_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/responder_endpoint_create_params.py b/src/lithic/types/responder_endpoint_create_params.py index ddda6e7d..36fe9caf 100644 --- a/src/lithic/types/responder_endpoint_create_params.py +++ b/src/lithic/types/responder_endpoint_create_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/responder_endpoint_create_response.py b/src/lithic/types/responder_endpoint_create_response.py index 24f83e30..198faf4e 100644 --- a/src/lithic/types/responder_endpoint_create_response.py +++ b/src/lithic/types/responder_endpoint_create_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/responder_endpoint_delete_params.py b/src/lithic/types/responder_endpoint_delete_params.py index e3482dab..59def61c 100644 --- a/src/lithic/types/responder_endpoint_delete_params.py +++ b/src/lithic/types/responder_endpoint_delete_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/responder_endpoint_status.py b/src/lithic/types/responder_endpoint_status.py index a6fb9929..f2a29aa1 100644 --- a/src/lithic/types/responder_endpoint_status.py +++ b/src/lithic/types/responder_endpoint_status.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/settlement_detail.py b/src/lithic/types/settlement_detail.py index d1ec8ce1..33df1023 100644 --- a/src/lithic/types/settlement_detail.py +++ b/src/lithic/types/settlement_detail.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime @@ -41,7 +41,7 @@ class SettlementDetail(BaseModel): """Date and time when the transaction first occurred. UTC time zone.""" currency: str - """Three-digit alphabetic ISO 4217 code.""" + """Three-character alphabetic ISO 4217 code.""" disputes_gross_amount: int """The total gross amount of disputes settlements.""" @@ -102,3 +102,6 @@ class SettlementDetail(BaseModel): updated: datetime """Date and time when the transaction first occurred. UTC time zone.""" + + fee_description: Optional[str] = None + """Network's description of a fee, only present on records with type `FEE`.""" diff --git a/src/lithic/types/settlement_report.py b/src/lithic/types/settlement_report.py index 432433ba..9c85dd85 100644 --- a/src/lithic/types/settlement_report.py +++ b/src/lithic/types/settlement_report.py @@ -1,6 +1,6 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import List from datetime import datetime from .._models import BaseModel @@ -14,18 +14,39 @@ class SettlementReport(BaseModel): """Date and time when the transaction first occurred. UTC time zone.""" currency: str - """Three-digit alphabetic ISO 4217 code.""" + """3-character alphabetic ISO 4217 code. - details: List[Optional[SettlementSummaryDetails]] + (This field is deprecated and will be removed in a future version of the API.) + """ + + details: List[SettlementSummaryDetails] disputes_gross_amount: int - """The total gross amount of disputes settlements.""" + """The total gross amount of disputes settlements. + + (This field is deprecated and will be removed in a future version of the API. To + compute total amounts, Lithic recommends that customers sum the relevant + settlement amounts found within `details`.) + """ interchange_gross_amount: int - """The total amount of interchange.""" + """The total amount of interchange. + + (This field is deprecated and will be removed in a future version of the API. To + compute total amounts, Lithic recommends that customers sum the relevant + settlement amounts found within `details`.) + """ + + is_complete: bool + """Indicates that all data expected on the given report date is available.""" other_fees_gross_amount: int - """Total amount of gross other fees outside of interchange.""" + """Total amount of gross other fees outside of interchange. + + (This field is deprecated and will be removed in a future version of the API. To + compute total amounts, Lithic recommends that customers sum the relevant + settlement amounts found within `details`.) + """ report_date: str """Date of when the report was first generated.""" @@ -33,13 +54,18 @@ class SettlementReport(BaseModel): settled_net_amount: int """The total net amount of cash moved. - (net value of settled_gross_amount, interchange, fees). + (net value of settled_gross_amount, interchange, fees). (This field is + deprecated and will be removed in a future version of the API. To compute total + amounts, Lithic recommends that customers sum the relevant settlement amounts + found within `details`.) """ transactions_gross_amount: int """ The total amount of settlement impacting transactions (excluding interchange, - fees, and disputes). + fees, and disputes). (This field is deprecated and will be removed in a future + version of the API. To compute total amounts, Lithic recommends that customers + sum the relevant settlement amounts found within `details`.) """ updated: datetime diff --git a/src/lithic/types/settlement_summary_details.py b/src/lithic/types/settlement_summary_details.py index 7da0a02c..1b04c769 100644 --- a/src/lithic/types/settlement_summary_details.py +++ b/src/lithic/types/settlement_summary_details.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal @@ -9,6 +9,9 @@ class SettlementSummaryDetails(BaseModel): + currency: Optional[str] = None + """3-character alphabetic ISO 4217 code.""" + disputes_gross_amount: Optional[int] = None """The total gross amount of disputes settlements.""" diff --git a/src/lithic/types/shared/__init__.py b/src/lithic/types/shared/__init__.py index 7646a183..f9248f1c 100644 --- a/src/lithic/types/shared/__init__.py +++ b/src/lithic/types/shared/__init__.py @@ -1,5 +1,9 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .address import Address as Address from .carrier import Carrier as Carrier +from .currency import Currency as Currency +from .document import Document as Document from .shipping_address import ShippingAddress as ShippingAddress +from .account_financial_account_type import AccountFinancialAccountType as AccountFinancialAccountType +from .instance_financial_account_type import InstanceFinancialAccountType as InstanceFinancialAccountType diff --git a/src/lithic/types/shared/account_financial_account_type.py b/src/lithic/types/shared/account_financial_account_type.py new file mode 100644 index 00000000..2078afab --- /dev/null +++ b/src/lithic/types/shared/account_financial_account_type.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__ = ["AccountFinancialAccountType"] + +AccountFinancialAccountType: TypeAlias = Literal["ISSUING", "OPERATING"] diff --git a/src/lithic/types/shared/address.py b/src/lithic/types/shared/address.py index cabf52f7..3e7be23d 100644 --- a/src/lithic/types/shared/address.py +++ b/src/lithic/types/shared/address.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional @@ -15,24 +15,25 @@ class Address(BaseModel): """Name of city.""" country: str - """Valid country code. - - Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 - three-character format. + """ + Valid country code, entered in uppercase ISO 3166-1 alpha-3 three-character + format. Only USA is currently supported for all workflows. KYC_EXEMPT supports + CAN additionally. """ postal_code: str """Valid postal code. - Only USA ZIP codes are currently supported, entered as a five-digit ZIP or - nine-digit ZIP+4. + USA postal codes (ZIP codes) are supported, entered as a five-digit postal code + or nine-digit postal code (ZIP+4) using the format 12345-1234. KYC_EXEMPT + supports Canadian postal codes. """ state: str """Valid state code. - Only USA state codes are currently supported, entered in uppercase ISO 3166-2 - two-character format. + USA state codes are supported, entered in uppercase ISO 3166-2 two-character + format. KYC_EXEMPT supports Canadian province codes. """ address2: Optional[str] = None diff --git a/src/lithic/types/shared/carrier.py b/src/lithic/types/shared/carrier.py index 40192c24..591cfb25 100644 --- a/src/lithic/types/shared/carrier.py +++ b/src/lithic/types/shared/carrier.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/shared/currency.py b/src/lithic/types/shared/currency.py new file mode 100644 index 00000000..cb3271df --- /dev/null +++ b/src/lithic/types/shared/currency.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["Currency"] + +Currency: TypeAlias = str diff --git a/src/lithic/types/shared/document.py b/src/lithic/types/shared/document.py new file mode 100644 index 00000000..84ba5670 --- /dev/null +++ b/src/lithic/types/shared/document.py @@ -0,0 +1,100 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Document", "RequiredDocumentUpload"] + + +class RequiredDocumentUpload(BaseModel): + token: str + """Globally unique identifier for the document upload.""" + + accepted_entity_status_reasons: List[str] + """ + A list of status reasons associated with a KYB account holder that have been + satisfied by the document upload + """ + + created: datetime + """When the document upload was created""" + + image_type: Literal["FRONT", "BACK"] + """Type of image to upload.""" + + rejected_entity_status_reasons: List[str] + """ + A list of status reasons associated with a KYB account holder that have not been + satisfied by the document upload + """ + + status: Literal["ACCEPTED", "REJECTED", "PENDING_UPLOAD", "UPLOADED", "PARTIAL_APPROVAL"] + """Status of an account holder's document upload.""" + + status_reasons: List[ + Literal[ + "DOCUMENT_MISSING_REQUIRED_DATA", + "DOCUMENT_UPLOAD_TOO_BLURRY", + "FILE_SIZE_TOO_LARGE", + "INVALID_DOCUMENT_TYPE", + "INVALID_DOCUMENT_UPLOAD", + "INVALID_ENTITY", + "DOCUMENT_EXPIRED", + "DOCUMENT_ISSUED_GREATER_THAN_30_DAYS", + "DOCUMENT_TYPE_NOT_SUPPORTED", + "UNKNOWN_FAILURE_REASON", + "UNKNOWN_ERROR", + ] + ] + """Reasons for document image upload status.""" + + updated: datetime + """When the document upload was last updated""" + + upload_url: str + """URL to upload document image to. + + Note that the upload URLs expire after 7 days. If an upload URL expires, you can + refresh the URLs by retrieving the document upload from + `GET /account_holders/{account_holder_token}/documents`. + """ + + +class Document(BaseModel): + token: str + """Globally unique identifier for the document.""" + + account_holder_token: str + """Globally unique identifier for the account holder.""" + + document_type: Literal[ + "DRIVERS_LICENSE", + "PASSPORT", + "PASSPORT_CARD", + "EIN_LETTER", + "TAX_RETURN", + "OPERATING_AGREEMENT", + "CERTIFICATE_OF_FORMATION", + "CERTIFICATE_OF_GOOD_STANDING", + "ARTICLES_OF_INCORPORATION", + "ARTICLES_OF_ORGANIZATION", + "BYLAWS", + "GOVERNMENT_BUSINESS_LICENSE", + "PARTNERSHIP_AGREEMENT", + "SS4_FORM", + "BANK_STATEMENT", + "UTILITY_BILL_STATEMENT", + "SSN_CARD", + "ITIN_LETTER", + "FINCEN_BOI_REPORT", + ] + """Type of documentation to be submitted for verification of an account holder""" + + entity_token: str + """Globally unique identifier for an entity.""" + + required_document_uploads: List[RequiredDocumentUpload] + """Represents a single image of the document to upload.""" diff --git a/src/lithic/types/shared/instance_financial_account_type.py b/src/lithic/types/shared/instance_financial_account_type.py new file mode 100644 index 00000000..912d719c --- /dev/null +++ b/src/lithic/types/shared/instance_financial_account_type.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["InstanceFinancialAccountType"] + +InstanceFinancialAccountType: TypeAlias = Literal[ + "ISSUING", "RESERVE", "OPERATING", "CHARGED_OFF_FEES", "CHARGED_OFF_INTEREST", "CHARGED_OFF_PRINCIPAL" +] diff --git a/src/lithic/types/shared/shipping_address.py b/src/lithic/types/shared/shipping_address.py index 13ef0949..68e69255 100644 --- a/src/lithic/types/shared/shipping_address.py +++ b/src/lithic/types/shared/shipping_address.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional @@ -20,19 +20,22 @@ class ShippingAddress(BaseModel): first_name: str """Customer's first name. - This will be the first name printed on the physical card. + This will be the first name printed on the physical card. The combined length of + `first_name` and `last_name` may not exceed 25 characters. """ last_name: str """Customer's surname (family name). - This will be the last name printed on the physical card. + This will be the last name printed on the physical card. The combined length of + `first_name` and `last_name` may not exceed 25 characters. """ postal_code: str """Postal code (formerly zipcode). - For US addresses, either five-digit zipcode or nine-digit "ZIP+4". + For US addresses, either five-digit postal code or nine-digit postal code + (ZIP+4) using the format 12345-1234. """ state: str diff --git a/src/lithic/types/shared_params/__init__.py b/src/lithic/types/shared_params/__init__.py index 7646a183..3cd36cfe 100644 --- a/src/lithic/types/shared_params/__init__.py +++ b/src/lithic/types/shared_params/__init__.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .address import Address as Address from .carrier import Carrier as Carrier diff --git a/src/lithic/types/shared_params/address.py b/src/lithic/types/shared_params/address.py index f3bff9ee..d438e8bd 100644 --- a/src/lithic/types/shared_params/address.py +++ b/src/lithic/types/shared_params/address.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -15,24 +15,25 @@ class Address(TypedDict, total=False): """Name of city.""" country: Required[str] - """Valid country code. - - Only USA is currently supported, entered in uppercase ISO 3166-1 alpha-3 - three-character format. + """ + Valid country code, entered in uppercase ISO 3166-1 alpha-3 three-character + format. Only USA is currently supported for all workflows. KYC_EXEMPT supports + CAN additionally. """ postal_code: Required[str] """Valid postal code. - Only USA ZIP codes are currently supported, entered as a five-digit ZIP or - nine-digit ZIP+4. + USA postal codes (ZIP codes) are supported, entered as a five-digit postal code + or nine-digit postal code (ZIP+4) using the format 12345-1234. KYC_EXEMPT + supports Canadian postal codes. """ state: Required[str] """Valid state code. - Only USA state codes are currently supported, entered in uppercase ISO 3166-2 - two-character format. + USA state codes are supported, entered in uppercase ISO 3166-2 two-character + format. KYC_EXEMPT supports Canadian province codes. """ address2: str diff --git a/src/lithic/types/shared_params/carrier.py b/src/lithic/types/shared_params/carrier.py index caf5e6fa..9c644c89 100644 --- a/src/lithic/types/shared_params/carrier.py +++ b/src/lithic/types/shared_params/carrier.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/shared_params/shipping_address.py b/src/lithic/types/shared_params/shipping_address.py index 96f980bc..263ad697 100644 --- a/src/lithic/types/shared_params/shipping_address.py +++ b/src/lithic/types/shared_params/shipping_address.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -20,19 +20,22 @@ class ShippingAddress(TypedDict, total=False): first_name: Required[str] """Customer's first name. - This will be the first name printed on the physical card. + This will be the first name printed on the physical card. The combined length of + `first_name` and `last_name` may not exceed 25 characters. """ last_name: Required[str] """Customer's surname (family name). - This will be the last name printed on the physical card. + This will be the last name printed on the physical card. The combined length of + `first_name` and `last_name` may not exceed 25 characters. """ postal_code: Required[str] """Postal code (formerly zipcode). - For US addresses, either five-digit zipcode or nine-digit "ZIP+4". + For US addresses, either five-digit postal code or nine-digit postal code + (ZIP+4) using the format 12345-1234. """ state: Required[str] diff --git a/src/lithic/types/spend_limit_duration.py b/src/lithic/types/spend_limit_duration.py index c299270a..4018870f 100644 --- a/src/lithic/types/spend_limit_duration.py +++ b/src/lithic/types/spend_limit_duration.py @@ -1,7 +1,7 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["SpendLimitDuration"] -SpendLimitDuration = Literal["ANNUALLY", "FOREVER", "MONTHLY", "TRANSACTION"] +SpendLimitDuration: TypeAlias = Literal["ANNUALLY", "FOREVER", "MONTHLY", "TRANSACTION"] diff --git a/src/lithic/types/three_ds/__init__.py b/src/lithic/types/three_ds/__init__.py index d73b4f7c..0060e40a 100644 --- a/src/lithic/types/three_ds/__init__.py +++ b/src/lithic/types/three_ds/__init__.py @@ -1,8 +1,15 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations +from .challenge_result import ChallengeResult as ChallengeResult from .authentication_simulate_params import AuthenticationSimulateParams as AuthenticationSimulateParams from .authentication_retrieve_response import AuthenticationRetrieveResponse as AuthenticationRetrieveResponse from .authentication_simulate_response import AuthenticationSimulateResponse as AuthenticationSimulateResponse from .decisioning_retrieve_secret_response import DecisioningRetrieveSecretResponse as DecisioningRetrieveSecretResponse +from .decisioning_challenge_response_params import ( + DecisioningChallengeResponseParams as DecisioningChallengeResponseParams, +) +from .authentication_simulate_otp_entry_params import ( + AuthenticationSimulateOtpEntryParams as AuthenticationSimulateOtpEntryParams, +) diff --git a/src/lithic/types/three_ds/authentication_retrieve_response.py b/src/lithic/types/three_ds/authentication_retrieve_response.py index fcaf0626..b4ed6923 100644 --- a/src/lithic/types/three_ds/authentication_retrieve_response.py +++ b/src/lithic/types/three_ds/authentication_retrieve_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime @@ -16,6 +16,7 @@ "AdditionalData", "App", "Browser", + "ChallengeMetadata", "Transaction", ] @@ -124,14 +125,14 @@ class MerchantRiskIndicator(BaseModel): Maps to EMV 3DS field deliveryTimeframe. """ - gift_card_amount: Optional[float] = None + gift_card_amount: Optional[int] = None """ In prepaid or gift card purchase transactions, purchase amount total in major units (e.g., a purchase of USD $205.10 would be 205). Maps to EMV 3DS field giftCardAmount. """ - gift_card_count: Optional[float] = None + gift_card_count: Optional[int] = None """ In prepaid or gift card purchase transactions, count of individual prepaid or gift cards/codes purchased. Maps to EMV 3DS field giftCardCount. @@ -218,7 +219,7 @@ class AdditionalData(BaseModel): authentication request to be low risk or not. """ - network_risk_score: Optional[float] = None + network_risk_score: Optional[int] = None """ Mastercard only: Assessment by the network of the authentication risk level, with a higher value indicating a higher amount of risk. @@ -276,6 +277,14 @@ class Browser(BaseModel): """Content of the HTTP user-agent header. Maps to EMV 3DS field browserUserAgent.""" +class ChallengeMetadata(BaseModel): + method_type: Literal["SMS_OTP", "OUT_OF_BAND"] + """The type of challenge method used for authentication.""" + + phone_number: Optional[str] = None + """The phone number used for delivering the OTP. Relevant only for SMS_OTP method.""" + + class Transaction(BaseModel): amount: float """Amount of the purchase in minor units of currency with all punctuation removed. @@ -320,10 +329,10 @@ class AuthenticationRetrieveResponse(BaseModel): account_type: Optional[Literal["CREDIT", "DEBIT", "NOT_APPLICABLE"]] = None """Type of account/card that is being used for the transaction. - Maps to EMV 3DS field acctType. + Maps to EMV 3DS field `acctType`. """ - authentication_result: Optional[Literal["DECLINE", "SUCCESS"]] = None + authentication_result: Literal["DECLINE", "SUCCESS", "PENDING_CHALLENGE", "PENDING_DECISION"] """Indicates the outcome of the 3DS authentication process.""" card_expiry_check: Literal["MATCH", "MISMATCH", "NOT_PRESENT"] @@ -350,11 +359,6 @@ class AuthenticationRetrieveResponse(BaseModel): created: datetime """Date and time when the authentication was created in Lithic's system.""" - decision_made_by: Optional[ - Literal["CUSTOMER_ENDPOINT", "LITHIC_DEFAULT", "LITHIC_RULES", "NETWORK", "UNKNOWN"] - ] = None - """Entity that made the authentication decision.""" - merchant: Merchant """ Object containing data about the merchant involved in the e-commerce @@ -368,6 +372,27 @@ class AuthenticationRetrieveResponse(BaseModel): populated. """ + three_ds_requestor_challenge_indicator: Literal[ + "NO_PREFERENCE", + "NO_CHALLENGE_REQUESTED", + "CHALLENGE_PREFERENCE", + "CHALLENGE_MANDATE", + "NO_CHALLENGE_RISK_ALREADY_ASSESSED", + "DATA_SHARE_ONLY", + "OTHER", + ] + """Indicates whether a challenge is requested for this transaction + + - `NO_PREFERENCE` - No Preference + - `NO_CHALLENGE_REQUESTED` - No Challenge Requested + - `CHALLENGE_PREFERENCE` - Challenge requested (3DS Requestor preference) + - `CHALLENGE_MANDATE` - Challenge requested (Mandate) + - `NO_CHALLENGE_RISK_ALREADY_ASSESSED` - No Challenge requested (Transactional + risk analysis is already performed) + - `DATA_SHARE_ONLY` - No Challenge requested (Data Share Only) + - `OTHER` - Other indicators not captured by above. These are rarely used + """ + additional_data: Optional[AdditionalData] = None """ Object containing additional data about the 3DS request that is beyond the EMV @@ -407,6 +432,17 @@ class AuthenticationRetrieveResponse(BaseModel): Present if the channel is 'BROWSER'. """ + challenge_metadata: Optional[ChallengeMetadata] = None + """Metadata about the challenge method and delivery.""" + + challenge_orchestrated_by: Optional[Literal["LITHIC", "CUSTOMER", "NO_CHALLENGE"]] = None + """Entity that orchestrates the challenge.""" + + decision_made_by: Optional[Literal["CUSTOMER_ENDPOINT", "LITHIC_DEFAULT", "LITHIC_RULES", "NETWORK", "UNKNOWN"]] = ( + None + ) + """Entity that made the authentication decision.""" + three_ri_request_type: Optional[ Literal[ "ACCOUNT_VERIFICATION", diff --git a/src/lithic/types/three_ds/authentication_simulate_otp_entry_params.py b/src/lithic/types/three_ds/authentication_simulate_otp_entry_params.py new file mode 100644 index 00000000..33eefed7 --- /dev/null +++ b/src/lithic/types/three_ds/authentication_simulate_otp_entry_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AuthenticationSimulateOtpEntryParams"] + + +class AuthenticationSimulateOtpEntryParams(TypedDict, total=False): + token: Required[str] + """ + A unique token returned as part of a /v1/three_ds_authentication/simulate call + that resulted in PENDING_CHALLENGE authentication result. + """ + + otp: Required[str] + """The OTP entered by the cardholder""" diff --git a/src/lithic/types/three_ds/authentication_simulate_params.py b/src/lithic/types/three_ds/authentication_simulate_params.py index 5281b070..67030433 100644 --- a/src/lithic/types/three_ds/authentication_simulate_params.py +++ b/src/lithic/types/three_ds/authentication_simulate_params.py @@ -1,8 +1,8 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict __all__ = ["AuthenticationSimulateParams", "Merchant", "Transaction"] @@ -15,6 +15,12 @@ class AuthenticationSimulateParams(TypedDict, total=False): transaction: Required[Transaction] + card_expiry_check: Literal["MATCH", "MISMATCH", "NOT_PRESENT"] + """When set will use the following values as part of the Simulated Authentication. + + When not set defaults to MATCH + """ + class Merchant(TypedDict, total=False): id: Required[str] @@ -38,7 +44,10 @@ class Merchant(TypedDict, total=False): """ name: Required[str] - """Merchant descriptor, corresponds to `descriptor` in authorization.""" + """Merchant descriptor, corresponds to `descriptor` in authorization. + + If CHALLENGE keyword is included, Lithic will trigger a challenge. + """ class Transaction(TypedDict, total=False): @@ -46,4 +55,4 @@ class Transaction(TypedDict, total=False): """Amount (in cents) to authenticate.""" currency: Required[str] - """3-digit alphabetic ISO 4217 currency code.""" + """3-character alphabetic ISO 4217 currency code.""" diff --git a/src/lithic/types/three_ds/authentication_simulate_response.py b/src/lithic/types/three_ds/authentication_simulate_response.py index ac5664ba..3eeeae26 100644 --- a/src/lithic/types/three_ds/authentication_simulate_response.py +++ b/src/lithic/types/three_ds/authentication_simulate_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional @@ -13,6 +13,3 @@ class AuthenticationSimulateResponse(BaseModel): A unique token to reference this transaction with later calls to void or clear the authorization. """ - - debugging_request_id: Optional[str] = None - """Debugging request ID to share with Lithic Support team.""" diff --git a/src/lithic/types/three_ds/challenge_result.py b/src/lithic/types/three_ds/challenge_result.py new file mode 100644 index 00000000..a9383bc1 --- /dev/null +++ b/src/lithic/types/three_ds/challenge_result.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__ = ["ChallengeResult"] + +ChallengeResult: TypeAlias = Literal["APPROVE", "DECLINE_BY_CUSTOMER"] diff --git a/src/lithic/types/three_ds/decisioning_challenge_response_params.py b/src/lithic/types/three_ds/decisioning_challenge_response_params.py new file mode 100644 index 00000000..b68305a5 --- /dev/null +++ b/src/lithic/types/three_ds/decisioning_challenge_response_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 + +from .challenge_result import ChallengeResult + +__all__ = ["DecisioningChallengeResponseParams"] + + +class DecisioningChallengeResponseParams(TypedDict, total=False): + token: Required[str] + """Globally unique identifier for the 3DS authentication. + + This token is sent as part of the initial 3DS Decisioning Request and as part of + the 3DS Challenge Event in the + [ThreeDSAuthentication](#/components/schemas/ThreeDSAuthentication) object + """ + + challenge_response: Required[ChallengeResult] + """Whether the Cardholder has Approved or Declined the issued Challenge""" diff --git a/src/lithic/types/three_ds/decisioning_retrieve_secret_response.py b/src/lithic/types/three_ds/decisioning_retrieve_secret_response.py index a14c545f..28f2569c 100644 --- a/src/lithic/types/three_ds/decisioning_retrieve_secret_response.py +++ b/src/lithic/types/three_ds/decisioning_retrieve_secret_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/tokenization.py b/src/lithic/types/tokenization.py index f2511e31..e00dc3f0 100644 --- a/src/lithic/types/tokenization.py +++ b/src/lithic/types/tokenization.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime @@ -58,6 +58,9 @@ class Tokenization(BaseModel): created_at: datetime """Date and time when the tokenization first occurred. UTC time zone.""" + dpan: Optional[str] = None + """The dynamic pan assigned to the token by the network.""" + status: Literal["ACTIVE", "DEACTIVATED", "INACTIVE", "PAUSED", "PENDING_2FA", "PENDING_ACTIVATION", "UNKNOWN"] """The status of the tokenization request""" @@ -65,20 +68,39 @@ class Tokenization(BaseModel): "AMAZON_ONE", "ANDROID_PAY", "APPLE_PAY", + "FACEBOOK", "FITBIT_PAY", "GARMIN_PAY", "MICROSOFT_PAY", + "NETFLIX", "SAMSUNG_PAY", "UNKNOWN", "VISA_CHECKOUT", ] - """The entity that is requested the tokenization. Represents a Digital Wallet.""" + """The entity that requested the tokenization. + + Represents a Digital Wallet or merchant. + """ token_unique_reference: str """The network's unique reference for the tokenization.""" + tokenization_channel: Literal["DIGITAL_WALLET", "MERCHANT"] + """The channel through which the tokenization was made.""" + updated_at: datetime """Latest date and time when the tokenization was updated. UTC time zone.""" + digital_card_art_token: Optional[str] = None + """ + Specifies the digital card art displayed in the user’s digital wallet after + tokenization. This will be null if the tokenization was created without an + associated digital card art. See + [Flexible Card Art Guide](https://docs.lithic.com/docs/about-digital-wallets#flexible-card-art). + """ + events: Optional[List[Event]] = None """A list of events related to the tokenization.""" + + payment_account_reference_id: Optional[str] = None + """The network's unique reference for the card that is tokenized.""" diff --git a/src/lithic/types/tokenization_decisioning_rotate_secret_response.py b/src/lithic/types/tokenization_decisioning_rotate_secret_response.py index 21182641..e1428626 100644 --- a/src/lithic/types/tokenization_decisioning_rotate_secret_response.py +++ b/src/lithic/types/tokenization_decisioning_rotate_secret_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/tokenization_list_params.py b/src/lithic/types/tokenization_list_params.py index 41901c2b..082ae2c1 100644 --- a/src/lithic/types/tokenization_list_params.py +++ b/src/lithic/types/tokenization_list_params.py @@ -1,10 +1,10 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from datetime import date -from typing_extensions import Annotated, TypedDict +from typing_extensions import Literal, Annotated, TypedDict from .._utils import PropertyInfo @@ -39,3 +39,9 @@ class TokenizationListParams(TypedDict, total=False): Used to retrieve the next page of results after this item. """ + + tokenization_channel: Literal["DIGITAL_WALLET", "MERCHANT", "ALL"] + """Filter for tokenizations by tokenization channel. + + If this is not specified, only DIGITAL_WALLET tokenizations will be returned. + """ diff --git a/src/lithic/types/tokenization_resend_activation_code_params.py b/src/lithic/types/tokenization_resend_activation_code_params.py new file mode 100644 index 00000000..a128d015 --- /dev/null +++ b/src/lithic/types/tokenization_resend_activation_code_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 Literal, TypedDict + +__all__ = ["TokenizationResendActivationCodeParams"] + + +class TokenizationResendActivationCodeParams(TypedDict, total=False): + activation_method_type: Literal["EMAIL_TO_CARDHOLDER_ADDRESS", "TEXT_TO_CARDHOLDER_NUMBER"] + """ + The communication method that the user has selected to use to receive the + authentication code. Supported Values: Sms = "TEXT_TO_CARDHOLDER_NUMBER". Email + = "EMAIL_TO_CARDHOLDER_ADDRESS" + """ diff --git a/src/lithic/types/tokenization_retrieve_response.py b/src/lithic/types/tokenization_retrieve_response.py index e0bc3a55..95f133a1 100644 --- a/src/lithic/types/tokenization_retrieve_response.py +++ b/src/lithic/types/tokenization_retrieve_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/tokenization_secret.py b/src/lithic/types/tokenization_secret.py index d91507b2..d699d84b 100644 --- a/src/lithic/types/tokenization_secret.py +++ b/src/lithic/types/tokenization_secret.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/tokenization_simulate_params.py b/src/lithic/types/tokenization_simulate_params.py index f5b20604..1b04cf6e 100644 --- a/src/lithic/types/tokenization_simulate_params.py +++ b/src/lithic/types/tokenization_simulate_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -17,7 +17,7 @@ class TokenizationSimulateParams(TypedDict, total=False): pan: Required[str] """The sixteen digit card number.""" - tokenization_source: Required[Literal["APPLE_PAY", "GOOGLE", "SAMSUNG_PAY"]] + tokenization_source: Required[Literal["APPLE_PAY", "GOOGLE", "SAMSUNG_PAY", "MERCHANT"]] """The source of the tokenization request.""" account_score: int @@ -32,5 +32,11 @@ class TokenizationSimulateParams(TypedDict, total=False): reputable an end user's device is. """ + entity: str + """ + Optional field to specify the token requestor name for a merchant token + simulation. Ignored when tokenization_source is not MERCHANT. + """ + wallet_recommended_decision: Literal["APPROVED", "DECLINED", "REQUIRE_ADDITIONAL_AUTHENTICATION"] """The decision that the Digital Wallet's recommend""" diff --git a/src/lithic/types/tokenization_simulate_response.py b/src/lithic/types/tokenization_simulate_response.py index e5fdd689..a991e422 100644 --- a/src/lithic/types/tokenization_simulate_response.py +++ b/src/lithic/types/tokenization_simulate_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional diff --git a/src/lithic/types/tokenization_update_digital_card_art_params.py b/src/lithic/types/tokenization_update_digital_card_art_params.py new file mode 100644 index 00000000..2d17c61d --- /dev/null +++ b/src/lithic/types/tokenization_update_digital_card_art_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_extensions import TypedDict + +__all__ = ["TokenizationUpdateDigitalCardArtParams"] + + +class TokenizationUpdateDigitalCardArtParams(TypedDict, total=False): + digital_card_art_token: str + """ + Specifies the digital card art to be displayed in the user’s digital wallet for + a tokenization. This artwork must be approved by the network and configured by + Lithic to use. See + [Flexible Card Art Guide](https://docs.lithic.com/docs/about-digital-wallets#flexible-card-art). + """ diff --git a/src/lithic/types/tokenization_update_digital_card_art_response.py b/src/lithic/types/tokenization_update_digital_card_art_response.py new file mode 100644 index 00000000..9bcb5616 --- /dev/null +++ b/src/lithic/types/tokenization_update_digital_card_art_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .tokenization import Tokenization + +__all__ = ["TokenizationUpdateDigitalCardArtResponse"] + + +class TokenizationUpdateDigitalCardArtResponse(BaseModel): + data: Optional[Tokenization] = None diff --git a/src/lithic/types/transaction.py b/src/lithic/types/transaction.py index f5b01ed6..0080ef14 100644 --- a/src/lithic/types/transaction.py +++ b/src/lithic/types/transaction.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime @@ -7,204 +7,191 @@ from pydantic import Field as FieldInfo from .._models import BaseModel +from .shared.currency import Currency __all__ = [ "Transaction", + "Amounts", + "AmountsCardholder", + "AmountsHold", + "AmountsMerchant", + "AmountsSettlement", "Avs", - "Event", + "CardholderAuthentication", "Merchant", "Pos", "PosEntryMode", "PosTerminal", "TokenInfo", - "CardholderAuthentication", + "Event", + "EventAmounts", + "EventAmountsCardholder", + "EventAmountsMerchant", + "EventAmountsSettlement", + "EventNetworkInfo", + "EventNetworkInfoAcquirer", + "EventNetworkInfoMastercard", + "EventNetworkInfoVisa", + "EventRuleResult", ] +class AmountsCardholder(BaseModel): + amount: int + """ + The estimated settled amount of the transaction in the cardholder billing + currency. + """ + + conversion_rate: str + """ + The exchange rate used to convert the merchant amount to the cardholder billing + amount. + """ + + currency: Currency + """3-character alphabetic ISO 4217 currency""" + + +class AmountsHold(BaseModel): + amount: int + """The pending amount of the transaction in the anticipated settlement currency.""" + + currency: Currency + """3-character alphabetic ISO 4217 currency""" + + +class AmountsMerchant(BaseModel): + amount: int + """The settled amount of the transaction in the merchant currency.""" + + currency: Currency + """3-character alphabetic ISO 4217 currency""" + + +class AmountsSettlement(BaseModel): + amount: int + """The settled amount of the transaction in the settlement currency.""" + + currency: Currency + """3-character alphabetic ISO 4217 currency""" + + +class Amounts(BaseModel): + cardholder: AmountsCardholder + + hold: AmountsHold + + merchant: AmountsMerchant + + settlement: AmountsSettlement + + class Avs(BaseModel): - address: Optional[str] = None + address: str """Cardholder address""" - zipcode: Optional[str] = None + zipcode: str """Cardholder ZIP code""" -class Event(BaseModel): - token: str - """Globally unique identifier.""" +class CardholderAuthentication(BaseModel): + three_ds_version: Optional[str] = FieldInfo(alias="3ds_version", default=None) + """The 3DS version used for the authentication""" - amount: int - """Amount of the transaction event (in cents), including any acquirer fees.""" + acquirer_exemption: Literal[ + "AUTHENTICATION_OUTAGE_EXCEPTION", + "LOW_VALUE", + "MERCHANT_INITIATED_TRANSACTION", + "NONE", + "RECURRING_PAYMENT", + "SECURE_CORPORATE_PAYMENT", + "STRONG_CUSTOMER_AUTHENTICATION_DELEGATION", + "TRANSACTION_RISK_ANALYSIS", + ] + """Whether an acquirer exemption applied to the transaction.""" - created: datetime - """RFC 3339 date and time this event entered the system. UTC time zone.""" + authentication_result: Literal["ATTEMPTS", "DECLINE", "NONE", "SUCCESS"] + """Indicates what the outcome of the 3DS authentication process is.""" - detailed_results: List[ - Literal[ - "ACCOUNT_DAILY_SPEND_LIMIT_EXCEEDED", - "ACCOUNT_INACTIVE", - "ACCOUNT_LIFETIME_SPEND_LIMIT_EXCEEDED", - "ACCOUNT_MONTHLY_SPEND_LIMIT_EXCEEDED", - "ACCOUNT_UNDER_REVIEW", - "ADDRESS_INCORRECT", - "APPROVED", - "AUTH_RULE_ALLOWED_COUNTRY", - "AUTH_RULE_ALLOWED_MCC", - "AUTH_RULE_BLOCKED_COUNTRY", - "AUTH_RULE_BLOCKED_MCC", - "CARD_CLOSED", - "CARD_CRYPTOGRAM_VALIDATION_FAILURE", - "CARD_EXPIRED", - "CARD_EXPIRY_DATE_INCORRECT", - "CARD_INVALID", - "CARD_PAUSED", - "CARD_PIN_INCORRECT", - "CARD_RESTRICTED", - "CARD_SECURITY_CODE_INCORRECT", - "CARD_SPEND_LIMIT_EXCEEDED", - "CONTACT_CARD_ISSUER", - "CUSTOMER_ASA_TIMEOUT", - "CUSTOM_ASA_RESULT", - "DECLINED", - "DO_NOT_HONOR", - "FORMAT_ERROR", - "INSUFFICIENT_FUNDING_SOURCE_BALANCE", - "INSUFFICIENT_FUNDS", - "LITHIC_SYSTEM_ERROR", - "LITHIC_SYSTEM_RATE_LIMIT", - "MALFORMED_ASA_RESPONSE", - "MERCHANT_INVALID", - "MERCHANT_LOCKED_CARD_ATTEMPTED_ELSEWHERE", - "MERCHANT_NOT_PERMITTED", - "OVER_REVERSAL_ATTEMPTED", - "PROGRAM_CARD_SPEND_LIMIT_EXCEEDED", - "PROGRAM_SUSPENDED", - "PROGRAM_USAGE_RESTRICTION", - "REVERSAL_UNMATCHED", - "SECURITY_VIOLATION", - "SINGLE_USE_CARD_REATTEMPTED", - "TRANSACTION_INVALID", - "TRANSACTION_NOT_PERMITTED_TO_ACQUIRER_OR_TERMINAL", - "TRANSACTION_NOT_PERMITTED_TO_ISSUER_OR_CARDHOLDER", - "TRANSACTION_PREVIOUSLY_COMPLETED", - "UNAUTHORIZED_MERCHANT", - ] - ] + decision_made_by: Literal["CUSTOMER_ENDPOINT", "LITHIC_DEFAULT", "LITHIC_RULES", "NETWORK", "UNKNOWN"] + """Indicates which party made the 3DS authentication decision.""" - result: Literal[ - "APPROVED", - "BANK_CONNECTION_ERROR", - "BANK_NOT_VERIFIED", - "CARD_CLOSED", - "CARD_PAUSED", - "DECLINED", - "FRAUD_ADVICE", - "INACTIVE_ACCOUNT", - "INCORRECT_PIN", - "INSUFFICIENT_FUNDS", - "INVALID_CARD_DETAILS", - "MERCHANT_BLACKLIST", - "SINGLE_USE_RECHARGED", - "SWITCH_INOPERATIVE_ADVICE", - "UNAUTHORIZED_MERCHANT", - "UNKNOWN_HOST_TIMEOUT", - "USER_TRANSACTION_LIMIT", - ] - """`APPROVED` or decline reason. - - Result types: - - - `ACCOUNT_STATE_TRANSACTION_FAIL` - Contact - [support@lithic.com](mailto:support@lithic.com). - - `APPROVED` - Transaction is approved. - - `BANK_CONNECTION_ERROR` - Please reconnect a funding source. - - `BANK_NOT_VERIFIED` - Please confirm the funding source. - - `CARD_CLOSED` - Card state was closed at the time of authorization. - - `CARD_PAUSED` - Card state was paused at the time of authorization. - - `FRAUD_ADVICE` - Transaction declined due to risk. - - `INACTIVE_ACCOUNT` - Account is inactive. Contact - [support@lithic.com](mailto:support@lithic.com). - - `INCORRECT_PIN` - PIN verification failed. - - `INVALID_CARD_DETAILS` - Incorrect CVV or expiry date. - - `INSUFFICIENT_FUNDS` - Please ensure the funding source is connected and up to - date. - - `MERCHANT_BLACKLIST` - This merchant is disallowed on the platform. - - `SINGLE_USE_RECHARGED` - Single use card attempted multiple times. - - `SWITCH_INOPERATIVE_ADVICE` - Network error, re-attempt the transaction. - - `UNAUTHORIZED_MERCHANT` - Merchant locked card attempted at different - merchant. - - `UNKNOWN_HOST_TIMEOUT` - Network error, re-attempt the transaction. - - `USER_TRANSACTION_LIMIT` - User-set spend limit exceeded. + liability_shift: Literal["3DS_AUTHENTICATED", "ACQUIRER_EXEMPTION", "NONE", "TOKEN_AUTHENTICATED"] + """Indicates whether chargeback liability shift applies to the transaction. + + Possible enum values: + + * `3DS_AUTHENTICATED`: The transaction was fully authenticated through a 3-D Secure flow, chargeback liability shift applies. + + * `ACQUIRER_EXEMPTION`: The acquirer utilised an exemption to bypass Strong Customer Authentication (`transStatus = N`, or `transStatus = I`). Liability remains with the acquirer and in this case the `acquirer_exemption` field is expected to be not `NONE`. + + * `NONE`: Chargeback liability shift has not shifted to the issuer, i.e. the merchant is liable. + + - `TOKEN_AUTHENTICATED`: The transaction was a tokenized payment with validated + cryptography, possibly recurring. Chargeback liability shift to the issuer + applies. """ - type: Literal[ - "AUTHORIZATION", - "AUTHORIZATION_ADVICE", - "AUTHORIZATION_EXPIRY", - "AUTHORIZATION_REVERSAL", - "BALANCE_INQUIRY", - "CLEARING", - "CORRECTION_CREDIT", - "CORRECTION_DEBIT", - "CREDIT_AUTHORIZATION", - "CREDIT_AUTHORIZATION_ADVICE", - "FINANCIAL_AUTHORIZATION", - "FINANCIAL_CREDIT_AUTHORIZATION", - "RETURN", - "RETURN_REVERSAL", - "VOID", - ] - """Event types: - - - `AUTHORIZATION` - Authorize a transaction. - - `AUTHORIZATION_ADVICE` - Advice on a transaction. - - `AUTHORIZATION_EXPIRY` - Authorization has expired and reversed by Lithic. - - `AUTHORIZATION_REVERSAL` - Authorization was reversed by the merchant. - - `BALANCE_INQUIRY` - A balance inquiry (typically a $0 authorization) has - occurred on a card. - - `CLEARING` - Transaction is settled. - - `CORRECTION_DEBIT` - Manual transaction correction (Debit). - - `CORRECTION_CREDIT` - Manual transaction correction (Credit). - - `CREDIT_AUTHORIZATION` - A refund or credit authorization from a merchant. - - `CREDIT_AUTHORIZATION_ADVICE` - A credit authorization was approved on your - behalf by the network. - - `FINANCIAL_AUTHORIZATION` - A request from a merchant to debit funds without - additional clearing. - - `FINANCIAL_CREDIT_AUTHORIZATION` - A request from a merchant to refund or - credit funds without additional clearing. - - `RETURN` - A refund has been processed on the transaction. - - `RETURN_REVERSAL` - A refund has been reversed (e.g., when a merchant reverses - an incorrect refund). + three_ds_authentication_token: Optional[str] = None + """ + Unique identifier you can use to match a given 3DS authentication (available via + the three_ds_authentication.created event webhook) and the transaction. Note + that in cases where liability shift does not occur, this token is matched to the + transaction on a best-effort basis. + """ + + verification_attempted: Literal["NONE", "OTHER"] + """ + Indicates whether a 3DS challenge flow was used, and if so, what the + verification method was. (deprecated, use `authentication_result`) + """ + + verification_result: Literal["CANCELLED", "FAILED", "FRICTIONLESS", "NOT_ATTEMPTED", "REJECTED", "SUCCESS"] + """Indicates whether a transaction is considered 3DS authenticated. + + (deprecated, use `authentication_result`) """ class Merchant(BaseModel): - acceptor_id: Optional[str] = None - """Unique identifier to identify the payment card acceptor.""" + acceptor_id: str + """Unique alphanumeric identifier for the payment card acceptor (merchant).""" + + acquiring_institution_id: str + """Unique numeric identifier of the acquiring institution.""" - city: Optional[str] = None - """City of card acceptor.""" + city: str + """City of card acceptor. - country: Optional[str] = None - """Uppercase country of card acceptor (see ISO 8583 specs).""" + Note that in many cases, particularly in card-not-present transactions, + merchants may send through a phone number or URL in this field. + """ + + country: str + """Country or entity of card acceptor. + + Possible values are: (1) all ISO 3166-1 alpha-3 country codes, (2) QZZ for + Kosovo, and (3) ANT for Netherlands Antilles. + """ - descriptor: Optional[str] = None + descriptor: str """Short description of card acceptor.""" - mcc: Optional[str] = None + mcc: str """Merchant category code (MCC). A four-digit number listed in ISO 18245. An MCC is used to classify a business by the types of goods or services it provides. """ - state: Optional[str] = None - """Geographic state of card acceptor (see ISO 8583 specs).""" + state: str + """Geographic state of card acceptor.""" class PosEntryMode(BaseModel): card: Literal["NOT_PRESENT", "PREAUTHORIZED", "PRESENT", "UNKNOWN"] - """Card status""" + """Card presence indicator""" cardholder: Literal[ "DEFERRED_BILLING", @@ -218,7 +205,7 @@ class PosEntryMode(BaseModel): "TELEPHONE_ORDER", "UNKNOWN", ] - """Cardholder Presence status""" + """Cardholder presence indicator""" pan: Literal[ "AUTO_ENTRY", @@ -240,7 +227,7 @@ class PosEntryMode(BaseModel): """Method of entry for the PAN""" pin_entered: bool - """True if the PIN was entered""" + """Indicates whether the cardholder entered the PIN. True if the PIN was entered.""" class PosTerminal(BaseModel): @@ -248,6 +235,15 @@ class PosTerminal(BaseModel): """True if a clerk is present at the sale.""" card_retention_capable: bool + """True if the terminal is capable of retaining the card.""" + + on_premise: bool + """True if the sale was made at the place of business (vs. mobile).""" + + operator: Literal["ADMINISTRATIVE", "CARDHOLDER", "CARD_ACCEPTOR", "UNKNOWN"] + """The person that is designated to swipe the card""" + + partial_approval_capable: bool """True if the terminal is capable of partial approval. Partial approval is when part of a transaction is approved and another payment @@ -257,12 +253,6 @@ class PosTerminal(BaseModel): payment of $15. """ - on_premise: bool - """True if the sale was made at the place of business (vs. mobile).""" - - operator: Literal["ADMINISTRATIVE", "CARDHOLDER", "CARD_ACCEPTOR", "UNKNOWN"] - """The person that is designed to swipe the card""" - pin_capability: Literal["CAPABLE", "INOPERATIVE", "NOT_CAPABLE", "UNSPECIFIED"] """Status of whether the POS is able to accept PINs""" @@ -288,9 +278,9 @@ class PosTerminal(BaseModel): "TELEVISION", "TELLER", "TRAVELERS_CHECK_MACHINE", - "UNKNOWN", "VENDING", "VOICE", + "UNKNOWN", ] """POS Type""" @@ -302,158 +292,348 @@ class Pos(BaseModel): class TokenInfo(BaseModel): - wallet_type: Optional[Literal["APPLE_PAY", "GOOGLE_PAY", "MASTERPASS", "MERCHANT", "OTHER", "SAMSUNG_PAY"]] = None - """Source of the token""" + wallet_type: Literal["APPLE_PAY", "GOOGLE_PAY", "MASTERPASS", "MERCHANT", "OTHER", "SAMSUNG_PAY"] + """The wallet_type field will indicate the source of the token. + Possible token sources include digital wallets (Apple, Google, or Samsung Pay), + merchant tokenization, and “other” sources like in-flight commerce. Masterpass + is not currently supported and is included for future use. + """ -class CardholderAuthentication(BaseModel): - three_ds_version: Optional[str] = FieldInfo(alias="3ds_version", default=None) - """3-D Secure Protocol version. Possible enum values: - - `1`: 3-D Secure Protocol version 1.x applied to the transaction. - - `2`: 3-D Secure Protocol version 2.x applied to the transaction. - - `null`: 3-D Secure was not used for the transaction - """ +class EventAmountsCardholder(BaseModel): + amount: int + """Amount of the event in the cardholder billing currency.""" - acquirer_exemption: Literal[ - "AUTHENTICATION_OUTAGE_EXCEPTION", - "LOW_VALUE", - "MERCHANT_INITIATED_TRANSACTION", - "NONE", - "RECURRING_PAYMENT", - "SECURE_CORPORATE_PAYMENT", - "STRONG_CUSTOMER_AUTHENTICATION_DELEGATION", - "TRANSACTION_RISK_ANALYSIS", - ] + conversion_rate: str + """ + Exchange rate used to convert the merchant amount to the cardholder billing + amount. """ - Exemption applied by the ACS to authenticate the transaction without requesting - a challenge. Possible enum values: - - `AUTHENTICATION_OUTAGE_EXCEPTION`: Authentication Outage Exception exemption. - - `LOW_VALUE`: Low Value Payment exemption. - - `MERCHANT_INITIATED_TRANSACTION`: Merchant Initiated Transaction (3RI). - - `NONE`: No exemption applied. - - `RECURRING_PAYMENT`: Recurring Payment exemption. - - `SECURE_CORPORATE_PAYMENT`: Secure Corporate Payment exemption. - - `STRONG_CUSTOMER_AUTHENTICATION_DELEGATION`: Strong Customer Authentication - Delegation exemption. - - `TRANSACTION_RISK_ANALYSIS`: Acquirer Low-Fraud and Transaction Risk Analysis - exemption. + currency: Currency + """3-character alphabetic ISO 4217 currency""" + - Maps to the 3-D Secure `transChallengeExemption` field. +class EventAmountsMerchant(BaseModel): + amount: int + """Amount of the event in the merchant currency.""" + + currency: Currency + """3-character alphabetic ISO 4217 currency""" + + +class EventAmountsSettlement(BaseModel): + amount: int + """Amount of the event, if it is financial, in the settlement currency. + + Non-financial events do not contain this amount because they do not move funds. """ - authentication_result: Literal["ATTEMPTS", "DECLINE", "NONE", "SUCCESS"] - """Outcome of the 3DS authentication process. Possible enum values: + conversion_rate: str + """Exchange rate used to convert the merchant amount to the settlement amount.""" + + currency: Currency + """3-character alphabetic ISO 4217 currency""" + + +class EventAmounts(BaseModel): + cardholder: EventAmountsCardholder + + merchant: EventAmountsMerchant - - `SUCCESS`: 3DS authentication was successful and the transaction is considered - authenticated. - - `DECLINE`: 3DS authentication was attempted but was unsuccessful — i.e., the - issuer declined to authenticate the cardholder; note that Lithic populates - this value on a best-effort basis based on common data across the 3DS - authentication and ASA data elements. - - `ATTEMPTS`: 3DS authentication was attempted but full authentication did not - occur. A proof of attempted authenticated is provided by the merchant. - - `NONE`: 3DS authentication was not performed on the transaction. + settlement: Optional[EventAmountsSettlement] = None + + +class EventNetworkInfoAcquirer(BaseModel): + acquirer_reference_number: Optional[str] = None + """ + Identifier assigned by the acquirer, applicable to dual-message transactions + only. The acquirer reference number (ARN) is only populated once a transaction + has been cleared, and it is not available in all transactions (such as automated + fuel dispenser transactions). A single transaction can contain multiple ARNs if + the merchant sends multiple clearings. """ - decision_made_by: Literal["CUSTOMER_ENDPOINT", "LITHIC_DEFAULT", "LITHIC_RULES", "NETWORK", "UNKNOWN"] - """Indicator for which party made the 3DS authentication decision. + retrieval_reference_number: Optional[str] = None + """Identifier assigned by the acquirer.""" - Possible enum values: - - `NETWORK`: A networks tand-in service decided on the outcome; for token - authentications (as indicated in the `liability_shift` attribute), this is the - default value - - `LITHIC_DEFAULT`: A default decision was made by Lithic, without running a - rules-based authentication; this value will be set on card programs that do - not participate in one of our two 3DS product tiers - - `LITHIC_RULES`: A rules-based authentication was conducted by Lithic and - Lithic decided on the outcome - - `CUSTOMER_ENDPOINT`: Lithic customer decided on the outcome based on a - real-time request sent to a configured endpoint - - `UNKNOWN`: Data on which party decided is unavailable +class EventNetworkInfoMastercard(BaseModel): + banknet_reference_number: Optional[str] = None + """Identifier assigned by Mastercard. + + Guaranteed by Mastercard to be unique for any transaction within a specific + financial network on any processing day. """ - liability_shift: Literal["3DS_AUTHENTICATED", "ACQUIRER_EXEMPTION", "NONE", "TOKEN_AUTHENTICATED"] - """Indicates whether chargeback liability shift applies to the transaction. + original_banknet_reference_number: Optional[str] = None + """Identifier assigned by Mastercard. - Possible enum values: + Matches the `banknet_reference_number` of a prior related event. May be + populated in authorization reversals, incremental authorizations (authorization + requests that augment a previously authorized amount), automated fuel dispenser + authorization advices and clearings, and financial authorizations. If the + original banknet reference number contains all zeroes, then no actual reference + number could be found by the network or acquirer. If Mastercard converts a + transaction from dual-message to single-message, such as for certain ATM + transactions, it will populate the original banknet reference number in the + resulting financial authorization with the banknet reference number of the + initial authorization, which Lithic does not receive. + """ - - `3DS_AUTHENTICATED`: The transaction was fully authenticated through a 3-D - Secure flow, chargeback liability shift applies. - - `ACQUIRER_EXEMPTION`: The acquirer utilised an exemption to bypass Strong - Customer Authentication (`transStatus = N`, or `transStatus = I`). Liability - remains with the acquirer and in this case the `acquirer_exemption` field is - expected to be not `NONE`. - - `NONE`: Chargeback liability shift has not shifted to the issuer, i.e. the - merchant is liable. - - `TOKEN_AUTHENTICATED`: The transaction was a tokenized payment with validated - cryptography, possibly recurring. Chargeback liability shift to the issuer - applies. + original_switch_serial_number: Optional[str] = None + """Identifier assigned by Mastercard. + + Matches the `switch_serial_number` of a prior related event. May be populated in + returns and return reversals. Applicable to single-message transactions only. """ - three_ds_authentication_token: str + switch_serial_number: Optional[str] = None """ - Unique identifier you can use to match a given 3DS authentication and the - transaction. Note that in cases where liability shift does not occur, this token - is matched to the transaction on a best-effort basis. + Identifier assigned by Mastercard, applicable to single-message transactions + only. """ - verification_attempted: Literal["APP_LOGIN", "BIOMETRIC", "NONE", "OTHER", "OTP"] - """Verification attempted values: - - `APP_LOGIN`: Out-of-band login verification was attempted by the ACS. - - `BIOMETRIC`: Out-of-band biometric verification was attempted by the ACS. - - `NONE`: No cardholder verification was attempted by the Access Control Server - (e.g. frictionless 3-D Secure flow, no 3-D Secure, or stand-in Risk Based - Analysis). - - `OTHER`: Other method was used by the ACS to verify the cardholder (e.g. - Mastercard Identity Check Express, recurring transactions, etc.) - - `OTP`: One-time password verification was attempted by the ACS. +class EventNetworkInfoVisa(BaseModel): + original_transaction_id: Optional[str] = None + """Identifier assigned by Visa. + + Matches the `transaction_id` of a prior related event. May be populated in + incremental authorizations (authorization requests that augment a previously + authorized amount), authorization advices, financial authorizations, and + clearings. """ - verification_result: Literal["CANCELLED", "FAILED", "FRICTIONLESS", "NOT_ATTEMPTED", "REJECTED", "SUCCESS"] + transaction_id: Optional[str] = None + """Identifier assigned by Visa to link original messages to subsequent messages. + + Guaranteed by Visa to be unique for each original authorization and financial + authorization. """ - This field partially maps to the `transStatus` field in the - [EMVCo 3-D Secure specification](https://www.emvco.com/emv-technologies/3d-secure/) - and Mastercard SPA2 AAV leading indicators. - Verification result values: - - `CANCELLED`: Authentication/Account verification could not be performed, - `transStatus = U`. - - `FAILED`: Transaction was not authenticated. `transStatus = N`, note: the - utilization of exemptions could also result in `transStatus = N`, inspect the - `acquirer_exemption` field for more information. - - `FRICTIONLESS`: Attempts processing performed, the transaction was not - authenticated, but a proof of attempted authentication/verification is - provided. `transStatus = A` and the leading AAV indicator was one of {`kE`, - `kF`, `kQ`}. - - `NOT_ATTEMPTED`: A 3-D Secure flow was not applied to this transaction. - Leading AAV indicator was one of {`kN`, `kX`} or no AAV was provided for the - transaction. - - `REJECTED`: Authentication/Account Verification rejected; `transStatus = R`. - Issuer is rejecting authentication/verification and requests that - authorization not be attempted. - - `SUCCESS`: Authentication verification successful. `transStatus = Y` and - leading AAV indicator for the transaction was one of {`kA`, `kB`, `kC`, `kD`, - `kO`, `kP`, `kR`, `kS`}. +class EventNetworkInfo(BaseModel): + acquirer: Optional[EventNetworkInfoAcquirer] = None + + mastercard: Optional[EventNetworkInfoMastercard] = None + + visa: Optional[EventNetworkInfoVisa] = None - Note that the following `transStatus` values are not represented by this field: - - `C`: Challenge Required - - `D`: Challenge Required; decoupled authentication confirmed - - `I`: Informational only - - `S`: Challenge using Secure Payment Confirmation (SPC) +class EventRuleResult(BaseModel): + auth_rule_token: Optional[str] = None + """The Auth Rule Token associated with the rule from which the decline originated. + + If this is set to null, then the decline was not associated with a + customer-configured Auth Rule. This may happen in cases where a transaction is + declined due to a Lithic-configured security or compliance rule, for example. """ + explanation: Optional[str] = None + """A human-readable explanation outlining the motivation for the rule's decline.""" + + name: Optional[str] = None + """The name for the rule, if any was configured.""" + + result: Literal[ + "ACCOUNT_DAILY_SPEND_LIMIT_EXCEEDED", + "ACCOUNT_DELINQUENT", + "ACCOUNT_INACTIVE", + "ACCOUNT_LIFETIME_SPEND_LIMIT_EXCEEDED", + "ACCOUNT_MONTHLY_SPEND_LIMIT_EXCEEDED", + "ACCOUNT_UNDER_REVIEW", + "ADDRESS_INCORRECT", + "APPROVED", + "AUTH_RULE_ALLOWED_COUNTRY", + "AUTH_RULE_ALLOWED_MCC", + "AUTH_RULE_BLOCKED_COUNTRY", + "AUTH_RULE_BLOCKED_MCC", + "CARD_CLOSED", + "CARD_CRYPTOGRAM_VALIDATION_FAILURE", + "CARD_EXPIRED", + "CARD_EXPIRY_DATE_INCORRECT", + "CARD_INVALID", + "CARD_NOT_ACTIVATED", + "CARD_PAUSED", + "CARD_PIN_INCORRECT", + "CARD_RESTRICTED", + "CARD_SECURITY_CODE_INCORRECT", + "CARD_SPEND_LIMIT_EXCEEDED", + "CONTACT_CARD_ISSUER", + "CUSTOMER_ASA_TIMEOUT", + "CUSTOM_ASA_RESULT", + "DECLINED", + "DO_NOT_HONOR", + "DRIVER_NUMBER_INVALID", + "FORMAT_ERROR", + "INSUFFICIENT_FUNDING_SOURCE_BALANCE", + "INSUFFICIENT_FUNDS", + "LITHIC_SYSTEM_ERROR", + "LITHIC_SYSTEM_RATE_LIMIT", + "MALFORMED_ASA_RESPONSE", + "MERCHANT_INVALID", + "MERCHANT_LOCKED_CARD_ATTEMPTED_ELSEWHERE", + "MERCHANT_NOT_PERMITTED", + "OVER_REVERSAL_ATTEMPTED", + "PIN_BLOCKED", + "PROGRAM_CARD_SPEND_LIMIT_EXCEEDED", + "PROGRAM_SUSPENDED", + "PROGRAM_USAGE_RESTRICTION", + "REVERSAL_UNMATCHED", + "SECURITY_VIOLATION", + "SINGLE_USE_CARD_REATTEMPTED", + "TRANSACTION_INVALID", + "TRANSACTION_NOT_PERMITTED_TO_ACQUIRER_OR_TERMINAL", + "TRANSACTION_NOT_PERMITTED_TO_ISSUER_OR_CARDHOLDER", + "TRANSACTION_PREVIOUSLY_COMPLETED", + "UNAUTHORIZED_MERCHANT", + "VEHICLE_NUMBER_INVALID", + ] + """The detailed_result associated with this rule's decline.""" + + +class Event(BaseModel): + token: str + """Transaction event identifier.""" + + amount: int + """Amount of the event in the settlement currency.""" + + amounts: EventAmounts + + created: datetime + """RFC 3339 date and time this event entered the system. UTC time zone.""" + + detailed_results: List[ + Literal[ + "ACCOUNT_DAILY_SPEND_LIMIT_EXCEEDED", + "ACCOUNT_DELINQUENT", + "ACCOUNT_INACTIVE", + "ACCOUNT_LIFETIME_SPEND_LIMIT_EXCEEDED", + "ACCOUNT_MONTHLY_SPEND_LIMIT_EXCEEDED", + "ACCOUNT_UNDER_REVIEW", + "ADDRESS_INCORRECT", + "APPROVED", + "AUTH_RULE_ALLOWED_COUNTRY", + "AUTH_RULE_ALLOWED_MCC", + "AUTH_RULE_BLOCKED_COUNTRY", + "AUTH_RULE_BLOCKED_MCC", + "CARD_CLOSED", + "CARD_CRYPTOGRAM_VALIDATION_FAILURE", + "CARD_EXPIRED", + "CARD_EXPIRY_DATE_INCORRECT", + "CARD_INVALID", + "CARD_NOT_ACTIVATED", + "CARD_PAUSED", + "CARD_PIN_INCORRECT", + "CARD_RESTRICTED", + "CARD_SECURITY_CODE_INCORRECT", + "CARD_SPEND_LIMIT_EXCEEDED", + "CONTACT_CARD_ISSUER", + "CUSTOMER_ASA_TIMEOUT", + "CUSTOM_ASA_RESULT", + "DECLINED", + "DO_NOT_HONOR", + "DRIVER_NUMBER_INVALID", + "FORMAT_ERROR", + "INSUFFICIENT_FUNDING_SOURCE_BALANCE", + "INSUFFICIENT_FUNDS", + "LITHIC_SYSTEM_ERROR", + "LITHIC_SYSTEM_RATE_LIMIT", + "MALFORMED_ASA_RESPONSE", + "MERCHANT_INVALID", + "MERCHANT_LOCKED_CARD_ATTEMPTED_ELSEWHERE", + "MERCHANT_NOT_PERMITTED", + "OVER_REVERSAL_ATTEMPTED", + "PIN_BLOCKED", + "PROGRAM_CARD_SPEND_LIMIT_EXCEEDED", + "PROGRAM_SUSPENDED", + "PROGRAM_USAGE_RESTRICTION", + "REVERSAL_UNMATCHED", + "SECURITY_VIOLATION", + "SINGLE_USE_CARD_REATTEMPTED", + "TRANSACTION_INVALID", + "TRANSACTION_NOT_PERMITTED_TO_ACQUIRER_OR_TERMINAL", + "TRANSACTION_NOT_PERMITTED_TO_ISSUER_OR_CARDHOLDER", + "TRANSACTION_PREVIOUSLY_COMPLETED", + "UNAUTHORIZED_MERCHANT", + "VEHICLE_NUMBER_INVALID", + ] + ] + + effective_polarity: Literal["CREDIT", "DEBIT"] + """Indicates whether the transaction event is a credit or debit to the account.""" + + network_info: Optional[EventNetworkInfo] = None + """Information provided by the card network in each event. + + This includes common identifiers shared between you, Lithic, the card network + and in some cases the acquirer. These identifiers often link together events + within the same transaction lifecycle and can be used to locate a particular + transaction, such as during processing of disputes. Not all fields are available + in all events, and the presence of these fields is dependent on the card network + and the event type. If the field is populated by the network, we will pass it + through as is unless otherwise specified. Please consult the official network + documentation for more details about these fields and how to use them. + """ + + result: Literal[ + "ACCOUNT_STATE_TRANSACTION_FAIL", + "APPROVED", + "BANK_CONNECTION_ERROR", + "BANK_NOT_VERIFIED", + "CARD_CLOSED", + "CARD_PAUSED", + "DECLINED", + "FRAUD_ADVICE", + "IGNORED_TTL_EXPIRY", + "INACTIVE_ACCOUNT", + "INCORRECT_PIN", + "INVALID_CARD_DETAILS", + "INSUFFICIENT_FUNDS", + "INSUFFICIENT_FUNDS_PRELOAD", + "INVALID_TRANSACTION", + "MERCHANT_BLACKLIST", + "ORIGINAL_NOT_FOUND", + "PREVIOUSLY_COMPLETED", + "SINGLE_USE_RECHARGED", + "SWITCH_INOPERATIVE_ADVICE", + "UNAUTHORIZED_MERCHANT", + "UNKNOWN_HOST_TIMEOUT", + "USER_TRANSACTION_LIMIT", + ] + + rule_results: List[EventRuleResult] + + type: Literal[ + "AUTHORIZATION", + "AUTHORIZATION_ADVICE", + "AUTHORIZATION_EXPIRY", + "AUTHORIZATION_REVERSAL", + "BALANCE_INQUIRY", + "CLEARING", + "CORRECTION_CREDIT", + "CORRECTION_DEBIT", + "CREDIT_AUTHORIZATION", + "CREDIT_AUTHORIZATION_ADVICE", + "FINANCIAL_AUTHORIZATION", + "FINANCIAL_CREDIT_AUTHORIZATION", + "RETURN", + "RETURN_REVERSAL", + ] + """Type of transaction event""" + class Transaction(BaseModel): token: str """Globally unique identifier.""" - acquirer_fee: int + account_token: str + """The token for the account associated with this transaction.""" + + acquirer_fee: Optional[int] = None """ Fee assessed by the merchant and paid for by the cardholder in the smallest unit of the currency. Will be zero if no fee is assessed. Rebates may be transmitted @@ -467,53 +647,46 @@ class Transaction(BaseModel): """ amount: int - """Authorization amount of the transaction (in cents), including any acquirer fees. - - This may change over time, and will represent the settled amount once the - transaction is settled. + """ + When the transaction is pending, this represents the authorization amount of the + transaction in the anticipated settlement currency. Once the transaction has + settled, this field represents the settled amount in the settlement currency. """ - authorization_amount: int - """Authorization amount (in cents) of the transaction, including any acquirer fees. + amounts: Amounts - This amount always represents the amount authorized for the transaction, - unaffected by settlement. + authorization_amount: Optional[int] = None + """ + The authorization amount of the transaction in the anticipated settlement + currency. """ - authorization_code: str + authorization_code: Optional[str] = None """ A fixed-width 6-digit numeric identifier that can be used to identify a transaction with networks. """ - avs: Avs + avs: Optional[Avs] = None card_token: str """Token for the card used in this transaction.""" + cardholder_authentication: Optional[CardholderAuthentication] = None + created: datetime """Date and time when the transaction first occurred. UTC time zone.""" - events: List[Event] - """A list of all events that have modified this transaction.""" - merchant: Merchant - merchant_amount: int - """ - Analogous to the "amount" property, but will represent the amount in the - transaction's local currency (smallest unit), including any acquirer fees. - """ + merchant_amount: Optional[int] = None + """Analogous to the 'amount', but in the merchant currency.""" - merchant_authorization_amount: int - """ - Analogous to the "authorization_amount" property, but will represent the amount - in the transaction's local currency (smallest unit), including any acquirer - fees. - """ + merchant_authorization_amount: Optional[int] = None + """Analogous to the 'authorization_amount', but in the merchant currency.""" merchant_currency: str - """3-digit alphabetic ISO 4217 code for the local currency of the transaction.""" + """3-character alphabetic ISO 4217 code for the local currency of the transaction.""" network: Optional[Literal["INTERLINK", "MAESTRO", "MASTERCARD", "UNKNOWN", "VISA"]] = None """Card network of the authorization. @@ -523,21 +696,19 @@ class Transaction(BaseModel): provider. """ - network_risk_score: float + network_risk_score: Optional[int] = None """ Network-provided score assessing risk level associated with a given authorization. Scores are on a range of 0-999, with 0 representing the lowest risk and 999 representing the highest risk. For Visa transactions, where the raw score has a range of 0-99, Lithic will normalize the score by multiplying the raw score by 10x. - - A score may not be available for all authorizations, and where it is not, this - field will be set to null. """ pos: Pos result: Literal[ + "ACCOUNT_STATE_TRANSACTION_FAIL", "APPROVED", "BANK_CONNECTION_ERROR", "BANK_NOT_VERIFIED", @@ -545,36 +716,32 @@ class Transaction(BaseModel): "CARD_PAUSED", "DECLINED", "FRAUD_ADVICE", + "IGNORED_TTL_EXPIRY", "INACTIVE_ACCOUNT", "INCORRECT_PIN", - "INSUFFICIENT_FUNDS", "INVALID_CARD_DETAILS", + "INSUFFICIENT_FUNDS", + "INSUFFICIENT_FUNDS_PRELOAD", + "INVALID_TRANSACTION", "MERCHANT_BLACKLIST", + "ORIGINAL_NOT_FOUND", + "PREVIOUSLY_COMPLETED", "SINGLE_USE_RECHARGED", "SWITCH_INOPERATIVE_ADVICE", "UNAUTHORIZED_MERCHANT", "UNKNOWN_HOST_TIMEOUT", "USER_TRANSACTION_LIMIT", ] - """`APPROVED` or decline reason. See Event result types""" settled_amount: int - """ - Amount of the transaction that has been settled (in cents), including any - acquirer fees. This may change over time. - """ + """The settled amount of the transaction in the settlement currency.""" status: Literal["DECLINED", "EXPIRED", "PENDING", "SETTLED", "VOIDED"] - """Status types: + """Status of the transaction.""" - - `DECLINED` - The transaction was declined. - - `EXPIRED` - Lithic reversed the authorization as it has passed its expiration - time. - - `PENDING` - Authorization is pending completion from the merchant. - - `SETTLED` - The transaction is complete. - - `VOIDED` - The merchant has voided the previously pending authorization. - """ + token_info: Optional[TokenInfo] = None - token_info: TokenInfo + updated: datetime + """Date and time when the transaction last updated. UTC time zone.""" - cardholder_authentication: Optional[CardholderAuthentication] = None + events: Optional[List[Event]] = None diff --git a/src/lithic/types/transaction_list_params.py b/src/lithic/types/transaction_list_params.py index f9d88f22..d6e98242 100644 --- a/src/lithic/types/transaction_list_params.py +++ b/src/lithic/types/transaction_list_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -51,3 +51,6 @@ class TransactionListParams(TypedDict, total=False): Used to retrieve the next page of results after this item. """ + + status: Literal["PENDING", "VOIDED", "SETTLED", "DECLINED", "EXPIRED"] + """Filters for transactions using transaction status field.""" diff --git a/src/lithic/types/transaction_simulate_authorization_advice_params.py b/src/lithic/types/transaction_simulate_authorization_advice_params.py index da7f5307..5e973292 100644 --- a/src/lithic/types/transaction_simulate_authorization_advice_params.py +++ b/src/lithic/types/transaction_simulate_authorization_advice_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,7 +9,7 @@ class TransactionSimulateAuthorizationAdviceParams(TypedDict, total=False): token: Required[str] - """The transaction token returned from the /v1/simulate/authorize response.""" + """The transaction token returned from the /v1/simulate/authorize. response.""" amount: Required[int] """Amount (in cents) to authorize. diff --git a/src/lithic/types/transaction_simulate_authorization_advice_response.py b/src/lithic/types/transaction_simulate_authorization_advice_response.py index 7c48d3e9..4e8016cb 100644 --- a/src/lithic/types/transaction_simulate_authorization_advice_response.py +++ b/src/lithic/types/transaction_simulate_authorization_advice_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/transaction_simulate_authorization_params.py b/src/lithic/types/transaction_simulate_authorization_params.py index 8d0c9104..d4088968 100644 --- a/src/lithic/types/transaction_simulate_authorization_params.py +++ b/src/lithic/types/transaction_simulate_authorization_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -13,7 +13,7 @@ class TransactionSimulateAuthorizationParams(TypedDict, total=False): For credit authorizations and financial credit authorizations, any value entered will be converted into a negative amount in the simulated transaction. For - example, entering 100 in this field will appear as a -100 amount in the + example, entering 100 in this field will result in a -100 amount in the transaction. For balance inquiries, this field must be set to 0. """ @@ -41,7 +41,11 @@ class TransactionSimulateAuthorizationParams(TypedDict, total=False): """ merchant_currency: str - """3-digit alphabetic ISO 4217 currency code.""" + """3-character alphabetic ISO 4217 currency code. + + Note: Simulator only accepts USD, GBP, EUR and defaults to GBP if another ISO + 4217 code is provided + """ partial_approval_capable: bool """ @@ -50,6 +54,9 @@ class TransactionSimulateAuthorizationParams(TypedDict, total=False): must be used for the remainder. """ + pin: str + """Simulate entering a PIN. If omitted, PIN check will not be performed.""" + status: Literal[ "AUTHORIZATION", "BALANCE_INQUIRY", @@ -61,12 +68,12 @@ class TransactionSimulateAuthorizationParams(TypedDict, total=False): - `AUTHORIZATION` is a dual message purchase authorization, meaning a subsequent clearing step is required to settle the transaction. - - `BALANCE_INQUIRY` is a $0 authorization that includes a request for the - balance held on the card, and is most typically seen when a cardholder - requests to view a card's balance at an ATM. + - `BALANCE_INQUIRY` is a $0 authorization requesting the balance held on the + card, and is most often observed when a cardholder requests to view a card's + balance at an ATM. - `CREDIT_AUTHORIZATION` is a dual message request from a merchant to authorize - a refund or credit, meaning a subsequent clearing step is required to settle - the transaction. + a refund, meaning a subsequent clearing step is required to settle the + transaction. - `FINANCIAL_AUTHORIZATION` is a single message request from a merchant to debit funds immediately (such as an ATM withdrawal), and no subsequent clearing is required to settle the transaction. diff --git a/src/lithic/types/transaction_simulate_authorization_response.py b/src/lithic/types/transaction_simulate_authorization_response.py index 0766f63d..859424ac 100644 --- a/src/lithic/types/transaction_simulate_authorization_response.py +++ b/src/lithic/types/transaction_simulate_authorization_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/transaction_simulate_clearing_params.py b/src/lithic/types/transaction_simulate_clearing_params.py index dcf7a984..ab45523f 100644 --- a/src/lithic/types/transaction_simulate_clearing_params.py +++ b/src/lithic/types/transaction_simulate_clearing_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -12,11 +12,15 @@ class TransactionSimulateClearingParams(TypedDict, total=False): """The transaction token returned from the /v1/simulate/authorize response.""" amount: int - """Amount (in cents) to complete. + """Amount (in cents) to clear. - Typically this will match the original authorization, but may be more or less. + Typically this will match the amount in the original authorization, but can be + higher or lower. The sign of this amount will automatically match the sign of + the original authorization's amount. For example, entering 100 in this field + will result in a -100 amount in the transaction, if the original authorization + is a credit authorization. - If no amount is supplied to this endpoint, the amount of the transaction will be - captured. Any transaction that has any amount completed at all do not have - access to this behavior. + If `amount` is not set, the full amount of the transaction will be cleared. + Transactions that have already cleared, either partially or fully, cannot be + cleared again using this endpoint. """ diff --git a/src/lithic/types/transaction_simulate_clearing_response.py b/src/lithic/types/transaction_simulate_clearing_response.py index cd92cf8a..91af533f 100644 --- a/src/lithic/types/transaction_simulate_clearing_response.py +++ b/src/lithic/types/transaction_simulate_clearing_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/transaction_simulate_credit_authorization_params.py b/src/lithic/types/transaction_simulate_credit_authorization_params.py index 6f165543..a25b00f0 100644 --- a/src/lithic/types/transaction_simulate_credit_authorization_params.py +++ b/src/lithic/types/transaction_simulate_credit_authorization_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/transaction_simulate_credit_authorization_response.py b/src/lithic/types/transaction_simulate_credit_authorization_response.py index 36d65d35..e50b3889 100644 --- a/src/lithic/types/transaction_simulate_credit_authorization_response.py +++ b/src/lithic/types/transaction_simulate_credit_authorization_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/transaction_simulate_return_params.py b/src/lithic/types/transaction_simulate_return_params.py index 46684e01..7487fe82 100644 --- a/src/lithic/types/transaction_simulate_return_params.py +++ b/src/lithic/types/transaction_simulate_return_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/transaction_simulate_return_response.py b/src/lithic/types/transaction_simulate_return_response.py index c898f734..6f67e632 100644 --- a/src/lithic/types/transaction_simulate_return_response.py +++ b/src/lithic/types/transaction_simulate_return_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/transaction_simulate_return_reversal_params.py b/src/lithic/types/transaction_simulate_return_reversal_params.py index 7b8ee082..f67a31d4 100644 --- a/src/lithic/types/transaction_simulate_return_reversal_params.py +++ b/src/lithic/types/transaction_simulate_return_reversal_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/src/lithic/types/transaction_simulate_return_reversal_response.py b/src/lithic/types/transaction_simulate_return_reversal_response.py index 30b4a9e0..abc9edd2 100644 --- a/src/lithic/types/transaction_simulate_return_reversal_response.py +++ b/src/lithic/types/transaction_simulate_return_reversal_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/transaction_simulate_void_params.py b/src/lithic/types/transaction_simulate_void_params.py index 95dd6e2f..e5b10ea9 100644 --- a/src/lithic/types/transaction_simulate_void_params.py +++ b/src/lithic/types/transaction_simulate_void_params.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -14,7 +14,8 @@ class TransactionSimulateVoidParams(TypedDict, total=False): amount: int """Amount (in cents) to void. - Typically this will match the original authorization, but may be less. + Typically this will match the amount in the original authorization, but can be + less. """ type: Literal["AUTHORIZATION_EXPIRY", "AUTHORIZATION_REVERSAL"] diff --git a/src/lithic/types/transaction_simulate_void_response.py b/src/lithic/types/transaction_simulate_void_response.py index a110202f..df321bf9 100644 --- a/src/lithic/types/transaction_simulate_void_response.py +++ b/src/lithic/types/transaction_simulate_void_response.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional diff --git a/src/lithic/types/transactions/__init__.py b/src/lithic/types/transactions/__init__.py new file mode 100644 index 00000000..7a0111fd --- /dev/null +++ b/src/lithic/types/transactions/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .enhanced_commercial_data_retrieve_response import ( + EnhancedCommercialDataRetrieveResponse as EnhancedCommercialDataRetrieveResponse, +) diff --git a/src/lithic/types/transactions/enhanced_commercial_data_retrieve_response.py b/src/lithic/types/transactions/enhanced_commercial_data_retrieve_response.py new file mode 100644 index 00000000..9702760d --- /dev/null +++ b/src/lithic/types/transactions/enhanced_commercial_data_retrieve_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel +from .events.enhanced_data import EnhancedData + +__all__ = ["EnhancedCommercialDataRetrieveResponse"] + + +class EnhancedCommercialDataRetrieveResponse(BaseModel): + data: List[EnhancedData] diff --git a/src/lithic/types/transactions/events/__init__.py b/src/lithic/types/transactions/events/__init__.py new file mode 100644 index 00000000..e7119fc4 --- /dev/null +++ b/src/lithic/types/transactions/events/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .enhanced_data import EnhancedData as EnhancedData diff --git a/src/lithic/types/transactions/events/enhanced_data.py b/src/lithic/types/transactions/events/enhanced_data.py new file mode 100644 index 00000000..27d7b68e --- /dev/null +++ b/src/lithic/types/transactions/events/enhanced_data.py @@ -0,0 +1,235 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import date +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["EnhancedData", "Common", "CommonLineItem", "CommonTax", "Fleet", "FleetAmountTotals", "FleetFuel"] + + +class CommonLineItem(BaseModel): + amount: Optional[float] = None + """The price of the item purchased in merchant currency.""" + + description: Optional[str] = None + """A human-readable description of the item.""" + + product_code: Optional[str] = None + """An identifier for the item purchased.""" + + quantity: Optional[float] = None + """The quantity of the item purchased.""" + + +class CommonTax(BaseModel): + amount: Optional[int] = None + """The amount of tax collected.""" + + exempt: Optional[Literal["TAX_INCLUDED", "TAX_NOT_INCLUDED", "NOT_SUPPORTED"]] = None + """A flag indicating whether the transaction is tax exempt or not.""" + + merchant_tax_id: Optional[str] = None + """The tax ID of the merchant.""" + + +class Common(BaseModel): + line_items: List[CommonLineItem] + + tax: CommonTax + + customer_reference_number: Optional[str] = None + """A customer identifier.""" + + merchant_reference_number: Optional[str] = None + """A merchant identifier.""" + + order_date: Optional[date] = None + """The date of the order.""" + + +class FleetAmountTotals(BaseModel): + discount: Optional[int] = None + """The discount applied to the gross sale amount.""" + + gross_sale: Optional[int] = None + """The gross sale amount.""" + + net_sale: Optional[int] = None + """The amount after discount.""" + + +class FleetFuel(BaseModel): + quantity: Optional[float] = None + """The quantity of fuel purchased.""" + + type: Optional[ + Literal[ + "UNKNOWN", + "REGULAR", + "MID_PLUS", + "PREMIUM_SUPER", + "MID_PLUS_2", + "PREMIUM_SUPER_2", + "ETHANOL_5_7_BLEND", + "MID_PLUS_ETHANOL_5_7_PERCENT_BLEND", + "PREMIUM_SUPER_ETHANOL_5_7_PERCENT_BLEND", + "ETHANOL_7_7_PERCENT_BLEND", + "MID_PLUS_ETHANOL_7_7_PERCENT_BLEND", + "GREEN_GASOLINE_REGULAR", + "GREEN_GASOLINE_MID_PLUS", + "GREEN_GASOLINE_PREMIUM_SUPER", + "REGULAR_DIESEL_2", + "PREMIUM_DIESEL_2", + "REGULAR_DIESEL_1", + "COMPRESSED_NATURAL_GAS", + "LIQUID_PROPANE_GAS", + "LIQUID_NATURAL_GAS", + "E_85", + "REFORMULATED_1", + "REFORMULATED_2", + "REFORMULATED_3", + "REFORMULATED_4", + "REFORMULATED_5", + "DIESEL_OFF_ROAD_1_AND_2_NON_TAXABLE", + "DIESEL_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_OFF_ROAD_NON_TAXABLE", + "UNDEFINED_FUEL", + "RACING_FUEL", + "MID_PLUS_2_10_PERCENT_BLEND", + "PREMIUM_SUPER_2_10_PERCENT_BLEND", + "MID_PLUS_ETHANOL_2_15_PERCENT_BLEND", + "PREMIUM_SUPER_ETHANOL_2_15_PERCENT_BLEND", + "PREMIUM_SUPER_ETHANOL_7_7_PERCENT_BLEND", + "REGULAR_ETHANOL_10_PERCENT_BLEND", + "MID_PLUS_ETHANOL_10_PERCENT_BLEND", + "PREMIUM_SUPER_ETHANOL_10_PERCENT_BLEND", + "B2_DIESEL_BLEND_2_PERCENT_BIODIESEL", + "B5_DIESEL_BLEND_5_PERCENT_BIODIESEL", + "B10_DIESEL_BLEND_10_PERCENT_BIODIESEL", + "B11_DIESEL_BLEND_11_PERCENT_BIODIESEL", + "B15_DIESEL_BLEND_15_PERCENT_BIODIESEL", + "B20_DIESEL_BLEND_20_PERCENT_BIODIESEL", + "B100_DIESEL_BLEND_100_PERCENT_BIODIESEL", + "B1_DIESEL_BLEND_1_PERCENT_BIODIESEL", + "ADDITIZED_DIESEL_2", + "ADDITIZED_DIESEL_3", + "RENEWABLE_DIESEL_R95", + "RENEWABLE_DIESEL_BIODIESEL_6_20_PERCENT", + "DIESEL_EXHAUST_FLUID", + "PREMIUM_DIESEL_1", + "REGULAR_ETHANOL_15_PERCENT_BLEND", + "MID_PLUS_ETHANOL_15_PERCENT_BLEND", + "PREMIUM_SUPER_ETHANOL_15_PERCENT_BLEND", + "PREMIUM_DIESEL_BLEND_LESS_THAN_20_PERCENT_BIODIESEL", + "PREMIUM_DIESEL_BLEND_GREATER_THAN_20_PERCENT_BIODIESEL", + "B75_DIESEL_BLEND_75_PERCENT_BIODIESEL", + "B99_DIESEL_BLEND_99_PERCENT_BIODIESEL", + "MISCELLANEOUS_FUEL", + "JET_FUEL", + "AVIATION_FUEL_REGULAR", + "AVIATION_FUEL_PREMIUM", + "AVIATION_FUEL_JP8", + "AVIATION_FUEL_4", + "AVIATION_FUEL_5", + "BIOJET_DIESEL", + "AVIATION_BIOFUEL_GASOLINE", + "MISCELLANEOUS_AVIATION_FUEL", + "MARINE_FUEL_1", + "MARINE_FUEL_2", + "MARINE_FUEL_3", + "MARINE_FUEL_4", + "MARINE_FUEL_5", + "MARINE_OTHER", + "MARINE_DIESEL", + "MISCELLANEOUS_MARINE_FUEL", + "KEROSENE_LOW_SULFUR", + "WHITE_GAS", + "HEATING_OIL", + "OTHER_FUEL_NON_TAXABLE", + "KEROSENE_ULTRA_LOW_SULFUR", + "KEROSENE_LOW_SULFUR_NON_TAXABLE", + "KEROSENE_ULTRA_LOW_SULFUR_NON_TAXABLE", + "EVC_1_LEVEL_1_CHARGE_110V_15_AMP", + "EVC_2_LEVEL_2_CHARGE_240V_15_40_AMP", + "EVC_3_LEVEL_3_CHARGE_480V_3_PHASE_CHARGE", + "BIODIESEL_BLEND_2_PERCENT_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_5_PERCENT_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_10_PERCENT_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_11_PERCENT_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_15_PERCENT_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_20_PERCENT_OFF_ROAD_NON_TAXABLE", + "DIESEL_1_OFF_ROAD_NON_TAXABLE", + "DIESEL_2_OFF_ROAD_NON_TAXABLE", + "DIESEL_1_PREMIUM_OFF_ROAD_NON_TAXABLE", + "DIESEL_2_PREMIUM_OFF_ROAD_NON_TAXABLE", + "ADDITIVE_DOSAGE", + "ETHANOL_BLENDS_E16_E84", + "LOW_OCTANE_UNL", + "BLENDED_DIESEL_1_AND_2", + "OFF_ROAD_REGULAR_NON_TAXABLE", + "OFF_ROAD_MID_PLUS_NON_TAXABLE", + "OFF_ROAD_PREMIUM_SUPER_NON_TAXABLE", + "OFF_ROAD_MID_PLUS_2_NON_TAXABLE", + "OFF_ROAD_PREMIUM_SUPER_2_NON_TAXABLE", + "RECREATIONAL_FUEL_90_OCTANE", + "HYDROGEN_H35", + "HYDROGEN_H70", + "RENEWABLE_DIESEL_R95_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_1_PERCENT_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_75_PERCENT_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_99_PERCENT_OFF_ROAD_NON_TAXABLE", + "BIODIESEL_BLEND_100_PERCENT_OFF_ROAD_NON_TAXABLE", + "RENEWABLE_DIESEL_BIODIESEL_6_20_PERCENT_OFF_ROAD_NON_TAXABLE", + "MISCELLANEOUS_OTHER_FUEL", + ] + ] = None + """The type of fuel purchased.""" + + unit_of_measure: Optional[ + Literal["GALLONS", "LITERS", "POUNDS", "KILOGRAMS", "IMPERIAL_GALLONS", "NOT_APPLICABLE", "UNKNOWN"] + ] = None + """Unit of measure for fuel disbursement.""" + + unit_price: Optional[int] = None + """The price per unit of fuel.""" + + +class Fleet(BaseModel): + amount_totals: FleetAmountTotals + + fuel: FleetFuel + + driver_number: Optional[str] = None + """ + The driver number entered into the terminal at the time of sale, with leading + zeros stripped. + """ + + odometer: Optional[int] = None + """The odometer reading entered into the terminal at the time of sale.""" + + service_type: Optional[Literal["UNKNOWN", "UNDEFINED", "SELF_SERVICE", "FULL_SERVICE", "NON_FUEL_ONLY"]] = None + """The type of fuel service.""" + + vehicle_number: Optional[str] = None + """ + The vehicle number entered into the terminal at the time of sale, with leading + zeros stripped. + """ + + +class EnhancedData(BaseModel): + token: str + """A unique identifier for the enhanced commercial data.""" + + common: Common + + event_token: str + """The token of the event that the enhanced data is associated with.""" + + fleet: List[Fleet] + + transaction_token: str + """The token of the transaction that the enhanced data is associated with.""" diff --git a/src/lithic/types/verification_method.py b/src/lithic/types/verification_method.py index 9972e037..72eae972 100644 --- a/src/lithic/types/verification_method.py +++ b/src/lithic/types/verification_method.py @@ -1,7 +1,7 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["VerificationMethod"] -VerificationMethod = Literal["MANUAL", "MICRO_DEPOSIT", "PLAID", "PRENOTE"] +VerificationMethod: TypeAlias = Literal["MANUAL", "MICRO_DEPOSIT", "PLAID", "PRENOTE", "EXTERNALLY_VERIFIED"] diff --git a/tests/__init__.py b/tests/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/__init__.py b/tests/api_resources/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/api_resources/__init__.py +++ b/tests/api_resources/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/accounts/__init__.py b/tests/api_resources/accounts/__init__.py deleted file mode 100644 index 1016754e..00000000 --- a/tests/api_resources/accounts/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. diff --git a/tests/api_resources/accounts/test_credit_configurations.py b/tests/api_resources/accounts/test_credit_configurations.py deleted file mode 100644 index e6e1c3f9..00000000 --- a/tests/api_resources/accounts/test_credit_configurations.py +++ /dev/null @@ -1,196 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from lithic import Lithic, AsyncLithic -from tests.utils import assert_matches_type -from lithic.types import BusinessAccount - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestCreditConfigurations: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: Lithic) -> None: - credit_configuration = client.accounts.credit_configurations.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Lithic) -> None: - response = client.accounts.credit_configurations.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - credit_configuration = response.parse() - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Lithic) -> None: - with client.accounts.credit_configurations.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - credit_configuration = response.parse() - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_retrieve(self, client: Lithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_token` but received ''"): - client.accounts.credit_configurations.with_raw_response.retrieve( - "", - ) - - @parametrize - def test_method_update(self, client: Lithic) -> None: - credit_configuration = client.accounts.credit_configurations.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - def test_method_update_with_all_params(self, client: Lithic) -> None: - credit_configuration = client.accounts.credit_configurations.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - billing_period=0, - credit_limit=0, - external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - payment_period=0, - ) - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - def test_raw_response_update(self, client: Lithic) -> None: - response = client.accounts.credit_configurations.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - credit_configuration = response.parse() - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - def test_streaming_response_update(self, client: Lithic) -> None: - with client.accounts.credit_configurations.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - credit_configuration = response.parse() - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_update(self, client: Lithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_token` but received ''"): - client.accounts.credit_configurations.with_raw_response.update( - "", - ) - - -class TestAsyncCreditConfigurations: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncLithic) -> None: - credit_configuration = await async_client.accounts.credit_configurations.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: - response = await async_client.accounts.credit_configurations.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - credit_configuration = response.parse() - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: - async with async_client.accounts.credit_configurations.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - credit_configuration = await response.parse() - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_token` but received ''"): - await async_client.accounts.credit_configurations.with_raw_response.retrieve( - "", - ) - - @parametrize - async def test_method_update(self, async_client: AsyncLithic) -> None: - credit_configuration = await async_client.accounts.credit_configurations.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: - credit_configuration = await async_client.accounts.credit_configurations.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - billing_period=0, - credit_limit=0, - external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - payment_period=0, - ) - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - async def test_raw_response_update(self, async_client: AsyncLithic) -> None: - response = await async_client.accounts.credit_configurations.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - credit_configuration = response.parse() - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - @parametrize - async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: - async with async_client.accounts.credit_configurations.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - credit_configuration = await response.parse() - assert_matches_type(BusinessAccount, credit_configuration, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_update(self, async_client: AsyncLithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_token` but received ''"): - await async_client.accounts.credit_configurations.with_raw_response.update( - "", - ) diff --git a/tests/api_resources/auth_rules/__init__.py b/tests/api_resources/auth_rules/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/auth_rules/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/auth_rules/test_v2.py b/tests/api_resources/auth_rules/test_v2.py new file mode 100644 index 00000000..0f7e7bb6 --- /dev/null +++ b/tests/api_resources/auth_rules/test_v2.py @@ -0,0 +1,1372 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.pagination import SyncCursorPage, AsyncCursorPage +from lithic.types.auth_rules import ( + V2ListResponse, + V2ApplyResponse, + V2DraftResponse, + V2CreateResponse, + V2ReportResponse, + V2UpdateResponse, + V2PromoteResponse, + V2RetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestV2: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create_overload_1(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.create( + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_1(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.create( + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + parameters={ + "conditions": [ + { + "attribute": "MCC", + "operation": "IS_ONE_OF", + "value": "string", + } + ] + }, + type="CONDITIONAL_BLOCK", + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_create_overload_1(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.create( + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_create_overload_1(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.create( + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_create_overload_2(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.create( + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_2(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.create( + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + parameters={ + "conditions": [ + { + "attribute": "MCC", + "operation": "IS_ONE_OF", + "value": "string", + } + ] + }, + type="CONDITIONAL_BLOCK", + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_create_overload_2(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.create( + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_create_overload_2(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.create( + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_create_overload_3(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.create( + program_level=True, + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_3(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.create( + program_level=True, + excluded_card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + parameters={ + "conditions": [ + { + "attribute": "MCC", + "operation": "IS_ONE_OF", + "value": "string", + } + ] + }, + type="CONDITIONAL_BLOCK", + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_create_overload_3(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.create( + program_level=True, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_create_overload_3(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.create( + program_level=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2RetrieveResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2RetrieveResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2RetrieveResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update_overload_1(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_method_update_with_all_params_overload_1(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + state="INACTIVE", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_update_overload_1(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_update_overload_1(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update_overload_1(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.update( + auth_rule_token="", + ) + + @parametrize + def test_method_update_overload_2(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_method_update_with_all_params_overload_2(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + state="INACTIVE", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_update_overload_2(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_update_overload_2(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update_overload_2(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.update( + auth_rule_token="", + ) + + @parametrize + def test_method_update_overload_3(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_method_update_with_all_params_overload_3(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + excluded_card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + program_level=True, + state="INACTIVE", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_update_overload_3(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_update_overload_3(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update_overload_3(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.update( + auth_rule_token="", + ) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.list() + assert_matches_type(SyncCursorPage[V2ListResponse], v2, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.list( + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ending_before="ending_before", + page_size=1, + starting_after="starting_after", + ) + assert_matches_type(SyncCursorPage[V2ListResponse], v2, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(SyncCursorPage[V2ListResponse], v2, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(SyncCursorPage[V2ListResponse], v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert v2 is None + + @parametrize + def test_raw_response_delete(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert v2 is None + + @parametrize + def test_streaming_response_delete(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert v2 is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.delete( + "", + ) + + @parametrize + def test_method_apply_overload_1(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_apply_overload_1(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_apply_overload_1(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_apply_overload_1(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + @parametrize + def test_method_apply_overload_2(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_apply_overload_2(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_apply_overload_2(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_apply_overload_2(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + @parametrize + def test_method_apply_overload_3(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + program_level=True, + ) + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + def test_method_apply_with_all_params_overload_3(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + program_level=True, + excluded_card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_apply_overload_3(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + program_level=True, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_apply_overload_3(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + program_level=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_apply_overload_3(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="", + program_level=True, + ) + + @parametrize + def test_method_draft(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.draft( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2DraftResponse, v2, path=["response"]) + + @parametrize + def test_method_draft_with_all_params(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.draft( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + parameters={ + "conditions": [ + { + "attribute": "MCC", + "operation": "IS_ONE_OF", + "value": "string", + } + ] + }, + ) + assert_matches_type(V2DraftResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_draft(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.draft( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2DraftResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_draft(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.draft( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2DraftResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_draft(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.draft( + auth_rule_token="", + ) + + @parametrize + def test_method_promote(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.promote( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2PromoteResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_promote(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.promote( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2PromoteResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_promote(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.promote( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2PromoteResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_promote(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.promote( + "", + ) + + @parametrize + def test_method_report(self, client: Lithic) -> None: + v2 = client.auth_rules.v2.report( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2ReportResponse, v2, path=["response"]) + + @parametrize + def test_raw_response_report(self, client: Lithic) -> None: + response = client.auth_rules.v2.with_raw_response.report( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2ReportResponse, v2, path=["response"]) + + @parametrize + def test_streaming_response_report(self, client: Lithic) -> None: + with client.auth_rules.v2.with_streaming_response.report( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = response.parse() + assert_matches_type(V2ReportResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_report(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.with_raw_response.report( + "", + ) + + +class TestAsyncV2: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.create( + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.create( + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + parameters={ + "conditions": [ + { + "attribute": "MCC", + "operation": "IS_ONE_OF", + "value": "string", + } + ] + }, + type="CONDITIONAL_BLOCK", + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.create( + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.create( + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.create( + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.create( + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + parameters={ + "conditions": [ + { + "attribute": "MCC", + "operation": "IS_ONE_OF", + "value": "string", + } + ] + }, + type="CONDITIONAL_BLOCK", + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.create( + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.create( + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_create_overload_3(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.create( + program_level=True, + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.create( + program_level=True, + excluded_card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + parameters={ + "conditions": [ + { + "attribute": "MCC", + "operation": "IS_ONE_OF", + "value": "string", + } + ] + }, + type="CONDITIONAL_BLOCK", + ) + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_3(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.create( + program_level=True, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_create_overload_3(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.create( + program_level=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2CreateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2RetrieveResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2RetrieveResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2RetrieveResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update_overload_1(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_method_update_with_all_params_overload_1(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + state="INACTIVE", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_update_overload_1(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_update_overload_1(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update_overload_1(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.update( + auth_rule_token="", + ) + + @parametrize + async def test_method_update_overload_2(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_method_update_with_all_params_overload_2(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + state="INACTIVE", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_update_overload_2(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_update_overload_2(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update_overload_2(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.update( + auth_rule_token="", + ) + + @parametrize + async def test_method_update_overload_3(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_method_update_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + excluded_card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + name="name", + program_level=True, + state="INACTIVE", + ) + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_update_overload_3(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_update_overload_3(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.update( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2UpdateResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update_overload_3(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.update( + auth_rule_token="", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.list() + assert_matches_type(AsyncCursorPage[V2ListResponse], v2, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.list( + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ending_before="ending_before", + page_size=1, + starting_after="starting_after", + ) + assert_matches_type(AsyncCursorPage[V2ListResponse], v2, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(AsyncCursorPage[V2ListResponse], v2, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(AsyncCursorPage[V2ListResponse], v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert v2 is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert v2 is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert v2 is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.delete( + "", + ) + + @parametrize + async def test_method_apply_overload_1(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_apply_overload_1(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_apply_overload_1(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_apply_overload_1(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="", + account_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + @parametrize + async def test_method_apply_overload_2(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_apply_overload_2(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_apply_overload_2(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_apply_overload_2(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="", + card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + + @parametrize + async def test_method_apply_overload_3(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + program_level=True, + ) + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + async def test_method_apply_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + program_level=True, + excluded_card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], + ) + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_apply_overload_3(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + program_level=True, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_apply_overload_3(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.apply( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + program_level=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2ApplyResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_apply_overload_3(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.apply( + auth_rule_token="", + program_level=True, + ) + + @parametrize + async def test_method_draft(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.draft( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2DraftResponse, v2, path=["response"]) + + @parametrize + async def test_method_draft_with_all_params(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.draft( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + parameters={ + "conditions": [ + { + "attribute": "MCC", + "operation": "IS_ONE_OF", + "value": "string", + } + ] + }, + ) + assert_matches_type(V2DraftResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_draft(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.draft( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2DraftResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_draft(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.draft( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2DraftResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_draft(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.draft( + auth_rule_token="", + ) + + @parametrize + async def test_method_promote(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.promote( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2PromoteResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_promote(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.promote( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2PromoteResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_promote(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.promote( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2PromoteResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_promote(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.promote( + "", + ) + + @parametrize + async def test_method_report(self, async_client: AsyncLithic) -> None: + v2 = await async_client.auth_rules.v2.report( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(V2ReportResponse, v2, path=["response"]) + + @parametrize + async def test_raw_response_report(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.with_raw_response.report( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + v2 = response.parse() + assert_matches_type(V2ReportResponse, v2, path=["response"]) + + @parametrize + async def test_streaming_response_report(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.with_streaming_response.report( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + v2 = await response.parse() + assert_matches_type(V2ReportResponse, v2, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_report(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.with_raw_response.report( + "", + ) diff --git a/tests/api_resources/auth_rules/v2/__init__.py b/tests/api_resources/auth_rules/v2/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/auth_rules/v2/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/auth_rules/v2/test_backtests.py b/tests/api_resources/auth_rules/v2/test_backtests.py new file mode 100644 index 00000000..12a05cec --- /dev/null +++ b/tests/api_resources/auth_rules/v2/test_backtests.py @@ -0,0 +1,217 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic._utils import parse_datetime +from lithic.types.auth_rules.v2 import BacktestResults, BacktestCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestBacktests: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + backtest = client.auth_rules.v2.backtests.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Lithic) -> None: + backtest = client.auth_rules.v2.backtests.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + start=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Lithic) -> None: + response = client.auth_rules.v2.backtests.with_raw_response.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backtest = response.parse() + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Lithic) -> None: + with client.auth_rules.v2.backtests.with_streaming_response.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backtest = response.parse() + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.backtests.with_raw_response.create( + auth_rule_token="", + ) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + backtest = client.auth_rules.v2.backtests.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BacktestResults, backtest, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backtest = response.parse() + assert_matches_type(BacktestResults, backtest, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.auth_rules.v2.backtests.with_streaming_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backtest = response.parse() + assert_matches_type(BacktestResults, backtest, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="", + ) + + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `auth_rule_backtest_token` but received ''" + ): + client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + +class TestAsyncBacktests: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncLithic) -> None: + backtest = await async_client.auth_rules.v2.backtests.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: + backtest = await async_client.auth_rules.v2.backtests.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + start=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.backtests.with_raw_response.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backtest = response.parse() + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.backtests.with_streaming_response.create( + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backtest = await response.parse() + assert_matches_type(BacktestCreateResponse, backtest, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.backtests.with_raw_response.create( + auth_rule_token="", + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + backtest = await async_client.auth_rules.v2.backtests.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BacktestResults, backtest, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + backtest = response.parse() + assert_matches_type(BacktestResults, backtest, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.auth_rules.v2.backtests.with_streaming_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + backtest = await response.parse() + assert_matches_type(BacktestResults, backtest, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): + await async_client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + auth_rule_token="", + ) + + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `auth_rule_backtest_token` but received ''" + ): + await async_client.auth_rules.v2.backtests.with_raw_response.retrieve( + auth_rule_backtest_token="", + auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) diff --git a/tests/api_resources/cards/__init__.py b/tests/api_resources/cards/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/api_resources/cards/__init__.py +++ b/tests/api_resources/cards/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/cards/test_aggregate_balances.py b/tests/api_resources/cards/test_aggregate_balances.py index 57e508e7..79c01585 100644 --- a/tests/api_resources/cards/test_aggregate_balances.py +++ b/tests/api_resources/cards/test_aggregate_balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -26,8 +26,8 @@ def test_method_list(self, client: Lithic) -> None: @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: aggregate_balance = client.cards.aggregate_balances.list( - account_token="string", - business_account_token="string", + account_token="account_token", + business_account_token="business_account_token", ) assert_matches_type(SyncSinglePage[AggregateBalanceListResponse], aggregate_balance, path=["response"]) @@ -63,8 +63,8 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: aggregate_balance = await async_client.cards.aggregate_balances.list( - account_token="string", - business_account_token="string", + account_token="account_token", + business_account_token="business_account_token", ) assert_matches_type(AsyncSinglePage[AggregateBalanceListResponse], aggregate_balance, path=["response"]) diff --git a/tests/api_resources/cards/test_balances.py b/tests/api_resources/cards/test_balances.py index 2fab472e..726066ea 100644 --- a/tests/api_resources/cards/test_balances.py +++ b/tests/api_resources/cards/test_balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,9 +9,9 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type -from lithic.types import Balance from lithic._utils import parse_datetime from lithic.pagination import SyncSinglePage, AsyncSinglePage +from lithic.types.cards import BalanceListResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -22,40 +22,40 @@ class TestBalances: @parametrize def test_method_list(self, client: Lithic) -> None: balance = client.cards.balances.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: balance = client.cards.balances.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), last_transaction_event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: response = client.cards.balances.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize def test_streaming_response_list(self, client: Lithic) -> None: with client.cards.balances.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) assert cast(Any, response.is_closed) is True @@ -63,7 +63,7 @@ def test_streaming_response_list(self, client: Lithic) -> None: def test_path_params_list(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): client.cards.balances.with_raw_response.list( - "", + card_token="", ) @@ -73,40 +73,40 @@ class TestAsyncBalances: @parametrize async def test_method_list(self, async_client: AsyncLithic) -> None: balance = await async_client.cards.balances.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: balance = await async_client.cards.balances.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), last_transaction_event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: response = await async_client.cards.balances.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: async with async_client.cards.balances.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = await response.parse() - assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) assert cast(Any, response.is_closed) is True @@ -114,5 +114,5 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: async def test_path_params_list(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): await async_client.cards.balances.with_raw_response.list( - "", + card_token="", ) diff --git a/tests/api_resources/cards/test_financial_transactions.py b/tests/api_resources/cards/test_financial_transactions.py index 3d580cee..7d3f5c06 100644 --- a/tests/api_resources/cards/test_financial_transactions.py +++ b/tests/api_resources/cards/test_financial_transactions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -22,7 +22,7 @@ class TestFinancialTransactions: @parametrize def test_method_retrieve(self, client: Lithic) -> None: financial_transaction = client.cards.financial_transactions.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(FinancialTransaction, financial_transaction, path=["response"]) @@ -30,7 +30,7 @@ def test_method_retrieve(self, client: Lithic) -> None: @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: response = client.cards.financial_transactions.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -42,7 +42,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: with client.cards.financial_transactions.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -57,7 +57,7 @@ def test_streaming_response_retrieve(self, client: Lithic) -> None: def test_path_params_retrieve(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): client.cards.financial_transactions.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", card_token="", ) @@ -65,27 +65,27 @@ def test_path_params_retrieve(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_transaction_token` but received ''" ): client.cards.financial_transactions.with_raw_response.retrieve( - "", + financial_transaction_token="", card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize def test_method_list(self, client: Lithic) -> None: financial_transaction = client.cards.financial_transactions.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(SyncSinglePage[FinancialTransaction], financial_transaction, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: financial_transaction = client.cards.financial_transactions.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), category="CARD", end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", result="APPROVED", - starting_after="string", + starting_after="starting_after", status="DECLINED", ) assert_matches_type(SyncSinglePage[FinancialTransaction], financial_transaction, path=["response"]) @@ -93,7 +93,7 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: @parametrize def test_raw_response_list(self, client: Lithic) -> None: response = client.cards.financial_transactions.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -104,7 +104,7 @@ def test_raw_response_list(self, client: Lithic) -> None: @parametrize def test_streaming_response_list(self, client: Lithic) -> None: with client.cards.financial_transactions.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -118,7 +118,7 @@ def test_streaming_response_list(self, client: Lithic) -> None: def test_path_params_list(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): client.cards.financial_transactions.with_raw_response.list( - "", + card_token="", ) @@ -128,7 +128,7 @@ class TestAsyncFinancialTransactions: @parametrize async def test_method_retrieve(self, async_client: AsyncLithic) -> None: financial_transaction = await async_client.cards.financial_transactions.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(FinancialTransaction, financial_transaction, path=["response"]) @@ -136,7 +136,7 @@ async def test_method_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: response = await async_client.cards.financial_transactions.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -148,7 +148,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: async with async_client.cards.financial_transactions.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -163,7 +163,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> N async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): await async_client.cards.financial_transactions.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", card_token="", ) @@ -171,27 +171,27 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_transaction_token` but received ''" ): await async_client.cards.financial_transactions.with_raw_response.retrieve( - "", + financial_transaction_token="", card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize async def test_method_list(self, async_client: AsyncLithic) -> None: financial_transaction = await async_client.cards.financial_transactions.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(AsyncSinglePage[FinancialTransaction], financial_transaction, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: financial_transaction = await async_client.cards.financial_transactions.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), category="CARD", end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", result="APPROVED", - starting_after="string", + starting_after="starting_after", status="DECLINED", ) assert_matches_type(AsyncSinglePage[FinancialTransaction], financial_transaction, path=["response"]) @@ -199,7 +199,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: response = await async_client.cards.financial_transactions.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -210,7 +210,7 @@ async def test_raw_response_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: async with async_client.cards.financial_transactions.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -224,5 +224,5 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: async def test_path_params_list(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): await async_client.cards.financial_transactions.with_raw_response.list( - "", + card_token="", ) diff --git a/tests/api_resources/credit_products/__init__.py b/tests/api_resources/credit_products/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/credit_products/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/credit_products/test_extended_credit.py b/tests/api_resources/credit_products/test_extended_credit.py new file mode 100644 index 00000000..46b755db --- /dev/null +++ b/tests/api_resources/credit_products/test_extended_credit.py @@ -0,0 +1,98 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types.credit_products import ExtendedCredit + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestExtendedCredit: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + extended_credit = client.credit_products.extended_credit.retrieve( + "credit_product_token", + ) + assert_matches_type(ExtendedCredit, extended_credit, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.credit_products.extended_credit.with_raw_response.retrieve( + "credit_product_token", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extended_credit = response.parse() + assert_matches_type(ExtendedCredit, extended_credit, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.credit_products.extended_credit.with_streaming_response.retrieve( + "credit_product_token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extended_credit = response.parse() + assert_matches_type(ExtendedCredit, extended_credit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `credit_product_token` but received ''"): + client.credit_products.extended_credit.with_raw_response.retrieve( + "", + ) + + +class TestAsyncExtendedCredit: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + extended_credit = await async_client.credit_products.extended_credit.retrieve( + "credit_product_token", + ) + assert_matches_type(ExtendedCredit, extended_credit, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.credit_products.extended_credit.with_raw_response.retrieve( + "credit_product_token", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + extended_credit = response.parse() + assert_matches_type(ExtendedCredit, extended_credit, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.credit_products.extended_credit.with_streaming_response.retrieve( + "credit_product_token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + extended_credit = await response.parse() + assert_matches_type(ExtendedCredit, extended_credit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `credit_product_token` but received ''"): + await async_client.credit_products.extended_credit.with_raw_response.retrieve( + "", + ) diff --git a/tests/api_resources/credit_products/test_prime_rates.py b/tests/api_resources/credit_products/test_prime_rates.py new file mode 100644 index 00000000..3534a8de --- /dev/null +++ b/tests/api_resources/credit_products/test_prime_rates.py @@ -0,0 +1,209 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic._utils import parse_date +from lithic.types.credit_products import PrimeRateRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestPrimeRates: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + prime_rate = client.credit_products.prime_rates.create( + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + rate="rate", + ) + assert prime_rate is None + + @parametrize + def test_raw_response_create(self, client: Lithic) -> None: + response = client.credit_products.prime_rates.with_raw_response.create( + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + rate="rate", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + prime_rate = response.parse() + assert prime_rate is None + + @parametrize + def test_streaming_response_create(self, client: Lithic) -> None: + with client.credit_products.prime_rates.with_streaming_response.create( + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + rate="rate", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + prime_rate = response.parse() + assert prime_rate is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `credit_product_token` but received ''"): + client.credit_products.prime_rates.with_raw_response.create( + credit_product_token="", + effective_date=parse_date("2019-12-27"), + rate="rate", + ) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + prime_rate = client.credit_products.prime_rates.retrieve( + credit_product_token="credit_product_token", + ) + assert_matches_type(PrimeRateRetrieveResponse, prime_rate, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: Lithic) -> None: + prime_rate = client.credit_products.prime_rates.retrieve( + credit_product_token="credit_product_token", + ending_before=parse_date("2019-12-27"), + starting_after=parse_date("2019-12-27"), + ) + assert_matches_type(PrimeRateRetrieveResponse, prime_rate, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.credit_products.prime_rates.with_raw_response.retrieve( + credit_product_token="credit_product_token", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + prime_rate = response.parse() + assert_matches_type(PrimeRateRetrieveResponse, prime_rate, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.credit_products.prime_rates.with_streaming_response.retrieve( + credit_product_token="credit_product_token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + prime_rate = response.parse() + assert_matches_type(PrimeRateRetrieveResponse, prime_rate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `credit_product_token` but received ''"): + client.credit_products.prime_rates.with_raw_response.retrieve( + credit_product_token="", + ) + + +class TestAsyncPrimeRates: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncLithic) -> None: + prime_rate = await async_client.credit_products.prime_rates.create( + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + rate="rate", + ) + assert prime_rate is None + + @parametrize + async def test_raw_response_create(self, async_client: AsyncLithic) -> None: + response = await async_client.credit_products.prime_rates.with_raw_response.create( + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + rate="rate", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + prime_rate = response.parse() + assert prime_rate is None + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: + async with async_client.credit_products.prime_rates.with_streaming_response.create( + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + rate="rate", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + prime_rate = await response.parse() + assert prime_rate is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `credit_product_token` but received ''"): + await async_client.credit_products.prime_rates.with_raw_response.create( + credit_product_token="", + effective_date=parse_date("2019-12-27"), + rate="rate", + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + prime_rate = await async_client.credit_products.prime_rates.retrieve( + credit_product_token="credit_product_token", + ) + assert_matches_type(PrimeRateRetrieveResponse, prime_rate, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncLithic) -> None: + prime_rate = await async_client.credit_products.prime_rates.retrieve( + credit_product_token="credit_product_token", + ending_before=parse_date("2019-12-27"), + starting_after=parse_date("2019-12-27"), + ) + assert_matches_type(PrimeRateRetrieveResponse, prime_rate, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.credit_products.prime_rates.with_raw_response.retrieve( + credit_product_token="credit_product_token", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + prime_rate = response.parse() + assert_matches_type(PrimeRateRetrieveResponse, prime_rate, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.credit_products.prime_rates.with_streaming_response.retrieve( + credit_product_token="credit_product_token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + prime_rate = await response.parse() + assert_matches_type(PrimeRateRetrieveResponse, prime_rate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `credit_product_token` but received ''"): + await async_client.credit_products.prime_rates.with_raw_response.retrieve( + credit_product_token="", + ) diff --git a/tests/api_resources/events/__init__.py b/tests/api_resources/events/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/api_resources/events/__init__.py +++ b/tests/api_resources/events/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/events/test_event_subscriptions.py b/tests/api_resources/events/test_event_subscriptions.py new file mode 100644 index 00000000..a8d7f749 --- /dev/null +++ b/tests/api_resources/events/test_event_subscriptions.py @@ -0,0 +1,120 @@ +# 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 lithic import Lithic, AsyncLithic + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEventSubscriptions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_resend(self, client: Lithic) -> None: + event_subscription = client.events.event_subscriptions.resend( + event_subscription_token="event_subscription_token", + event_token="event_token", + ) + assert event_subscription is None + + @parametrize + def test_raw_response_resend(self, client: Lithic) -> None: + response = client.events.event_subscriptions.with_raw_response.resend( + event_subscription_token="event_subscription_token", + event_token="event_token", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + event_subscription = response.parse() + assert event_subscription is None + + @parametrize + def test_streaming_response_resend(self, client: Lithic) -> None: + with client.events.event_subscriptions.with_streaming_response.resend( + event_subscription_token="event_subscription_token", + event_token="event_token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + event_subscription = response.parse() + assert event_subscription is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_resend(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `event_token` but received ''"): + client.events.event_subscriptions.with_raw_response.resend( + event_subscription_token="event_subscription_token", + event_token="", + ) + + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" + ): + client.events.event_subscriptions.with_raw_response.resend( + event_subscription_token="", + event_token="event_token", + ) + + +class TestAsyncEventSubscriptions: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_resend(self, async_client: AsyncLithic) -> None: + event_subscription = await async_client.events.event_subscriptions.resend( + event_subscription_token="event_subscription_token", + event_token="event_token", + ) + assert event_subscription is None + + @parametrize + async def test_raw_response_resend(self, async_client: AsyncLithic) -> None: + response = await async_client.events.event_subscriptions.with_raw_response.resend( + event_subscription_token="event_subscription_token", + event_token="event_token", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + event_subscription = response.parse() + assert event_subscription is None + + @parametrize + async def test_streaming_response_resend(self, async_client: AsyncLithic) -> None: + async with async_client.events.event_subscriptions.with_streaming_response.resend( + event_subscription_token="event_subscription_token", + event_token="event_token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + event_subscription = await response.parse() + assert event_subscription is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_resend(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `event_token` but received ''"): + await async_client.events.event_subscriptions.with_raw_response.resend( + event_subscription_token="event_subscription_token", + event_token="", + ) + + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" + ): + await async_client.events.event_subscriptions.with_raw_response.resend( + event_subscription_token="", + event_token="event_token", + ) diff --git a/tests/api_resources/events/test_subscriptions.py b/tests/api_resources/events/test_subscriptions.py index adab0a0d..307bc14d 100644 --- a/tests/api_resources/events/test_subscriptions.py +++ b/tests/api_resources/events/test_subscriptions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -33,9 +33,9 @@ def test_method_create(self, client: Lithic) -> None: def test_method_create_with_all_params(self, client: Lithic) -> None: subscription = client.events.subscriptions.create( url="https://example.com", - description="string", + description="description", disabled=True, - event_types=["account_holder.created", "account_holder.updated", "account_holder.verification"], + event_types=["account_holder.created"], ) assert_matches_type(EventSubscription, subscription, path=["response"]) @@ -66,14 +66,14 @@ def test_streaming_response_create(self, client: Lithic) -> None: @parametrize def test_method_retrieve(self, client: Lithic) -> None: subscription = client.events.subscriptions.retrieve( - "string", + "event_subscription_token", ) assert_matches_type(EventSubscription, subscription, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.retrieve( - "string", + "event_subscription_token", ) assert response.is_closed is True @@ -84,7 +84,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.retrieve( - "string", + "event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -106,7 +106,7 @@ def test_path_params_retrieve(self, client: Lithic) -> None: @parametrize def test_method_update(self, client: Lithic) -> None: subscription = client.events.subscriptions.update( - "string", + event_subscription_token="event_subscription_token", url="https://example.com", ) assert_matches_type(EventSubscription, subscription, path=["response"]) @@ -114,18 +114,18 @@ def test_method_update(self, client: Lithic) -> None: @parametrize def test_method_update_with_all_params(self, client: Lithic) -> None: subscription = client.events.subscriptions.update( - "string", + event_subscription_token="event_subscription_token", url="https://example.com", - description="string", + description="description", disabled=True, - event_types=["account_holder.created", "account_holder.updated", "account_holder.verification"], + event_types=["account_holder.created"], ) assert_matches_type(EventSubscription, subscription, path=["response"]) @parametrize def test_raw_response_update(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.update( - "string", + event_subscription_token="event_subscription_token", url="https://example.com", ) @@ -137,7 +137,7 @@ def test_raw_response_update(self, client: Lithic) -> None: @parametrize def test_streaming_response_update(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.update( - "string", + event_subscription_token="event_subscription_token", url="https://example.com", ) as response: assert not response.is_closed @@ -154,7 +154,7 @@ def test_path_params_update(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): client.events.subscriptions.with_raw_response.update( - "", + event_subscription_token="", url="https://example.com", ) @@ -166,9 +166,9 @@ def test_method_list(self, client: Lithic) -> None: @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: subscription = client.events.subscriptions.list( - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(SyncCursorPage[EventSubscription], subscription, path=["response"]) @@ -192,19 +192,19 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_method_delete(self, client: Lithic) -> None: subscription = client.events.subscriptions.delete( - "string", + "event_subscription_token", ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_raw_response_delete(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.delete( - "string", + "event_subscription_token", ) assert response.is_closed is True @@ -212,11 +212,11 @@ def test_raw_response_delete(self, client: Lithic) -> None: subscription = response.parse() assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_streaming_response_delete(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.delete( - "string", + "event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -226,7 +226,7 @@ def test_streaming_response_delete(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_path_params_delete(self, client: Lithic) -> None: with pytest.raises( @@ -239,19 +239,19 @@ def test_path_params_delete(self, client: Lithic) -> None: @parametrize def test_method_list_attempts(self, client: Lithic) -> None: subscription = client.events.subscriptions.list_attempts( - "string", + event_subscription_token="event_subscription_token", ) assert_matches_type(SyncCursorPage[MessageAttempt], subscription, path=["response"]) @parametrize def test_method_list_attempts_with_all_params(self, client: Lithic) -> None: subscription = client.events.subscriptions.list_attempts( - "string", + event_subscription_token="event_subscription_token", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", status="FAILED", ) assert_matches_type(SyncCursorPage[MessageAttempt], subscription, path=["response"]) @@ -259,7 +259,7 @@ def test_method_list_attempts_with_all_params(self, client: Lithic) -> None: @parametrize def test_raw_response_list_attempts(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.list_attempts( - "string", + event_subscription_token="event_subscription_token", ) assert response.is_closed is True @@ -270,7 +270,7 @@ def test_raw_response_list_attempts(self, client: Lithic) -> None: @parametrize def test_streaming_response_list_attempts(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.list_attempts( - "string", + event_subscription_token="event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -286,32 +286,32 @@ def test_path_params_list_attempts(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): client.events.subscriptions.with_raw_response.list_attempts( - "", + event_subscription_token="", ) - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_method_recover(self, client: Lithic) -> None: subscription = client.events.subscriptions.recover( - "string", + event_subscription_token="event_subscription_token", ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_method_recover_with_all_params(self, client: Lithic) -> None: subscription = client.events.subscriptions.recover( - "string", + event_subscription_token="event_subscription_token", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_raw_response_recover(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.recover( - "string", + event_subscription_token="event_subscription_token", ) assert response.is_closed is True @@ -319,11 +319,11 @@ def test_raw_response_recover(self, client: Lithic) -> None: subscription = response.parse() assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_streaming_response_recover(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.recover( - "string", + event_subscription_token="event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -333,39 +333,39 @@ def test_streaming_response_recover(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_path_params_recover(self, client: Lithic) -> None: with pytest.raises( ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): client.events.subscriptions.with_raw_response.recover( - "", + event_subscription_token="", ) - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_method_replay_missing(self, client: Lithic) -> None: subscription = client.events.subscriptions.replay_missing( - "string", + event_subscription_token="event_subscription_token", ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_method_replay_missing_with_all_params(self, client: Lithic) -> None: subscription = client.events.subscriptions.replay_missing( - "string", + event_subscription_token="event_subscription_token", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_raw_response_replay_missing(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.replay_missing( - "string", + event_subscription_token="event_subscription_token", ) assert response.is_closed is True @@ -373,11 +373,11 @@ def test_raw_response_replay_missing(self, client: Lithic) -> None: subscription = response.parse() assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_streaming_response_replay_missing(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.replay_missing( - "string", + event_subscription_token="event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -387,27 +387,27 @@ def test_streaming_response_replay_missing(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_path_params_replay_missing(self, client: Lithic) -> None: with pytest.raises( ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): client.events.subscriptions.with_raw_response.replay_missing( - "", + event_subscription_token="", ) @parametrize def test_method_retrieve_secret(self, client: Lithic) -> None: subscription = client.events.subscriptions.retrieve_secret( - "string", + "event_subscription_token", ) assert_matches_type(SubscriptionRetrieveSecretResponse, subscription, path=["response"]) @parametrize def test_raw_response_retrieve_secret(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.retrieve_secret( - "string", + "event_subscription_token", ) assert response.is_closed is True @@ -418,7 +418,7 @@ def test_raw_response_retrieve_secret(self, client: Lithic) -> None: @parametrize def test_streaming_response_retrieve_secret(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.retrieve_secret( - "string", + "event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -437,19 +437,19 @@ def test_path_params_retrieve_secret(self, client: Lithic) -> None: "", ) - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_method_rotate_secret(self, client: Lithic) -> None: subscription = client.events.subscriptions.rotate_secret( - "string", + "event_subscription_token", ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_raw_response_rotate_secret(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.rotate_secret( - "string", + "event_subscription_token", ) assert response.is_closed is True @@ -457,11 +457,11 @@ def test_raw_response_rotate_secret(self, client: Lithic) -> None: subscription = response.parse() assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_streaming_response_rotate_secret(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.rotate_secret( - "string", + "event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -471,7 +471,7 @@ def test_streaming_response_rotate_secret(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize def test_path_params_rotate_secret(self, client: Lithic) -> None: with pytest.raises( @@ -484,14 +484,14 @@ def test_path_params_rotate_secret(self, client: Lithic) -> None: @parametrize def test_method_send_simulated_example(self, client: Lithic) -> None: subscription = client.events.subscriptions.send_simulated_example( - "string", + event_subscription_token="event_subscription_token", ) assert subscription is None @parametrize def test_method_send_simulated_example_with_all_params(self, client: Lithic) -> None: subscription = client.events.subscriptions.send_simulated_example( - "string", + event_subscription_token="event_subscription_token", event_type="account_holder.created", ) assert subscription is None @@ -499,7 +499,7 @@ def test_method_send_simulated_example_with_all_params(self, client: Lithic) -> @parametrize def test_raw_response_send_simulated_example(self, client: Lithic) -> None: response = client.events.subscriptions.with_raw_response.send_simulated_example( - "string", + event_subscription_token="event_subscription_token", ) assert response.is_closed is True @@ -510,7 +510,7 @@ def test_raw_response_send_simulated_example(self, client: Lithic) -> None: @parametrize def test_streaming_response_send_simulated_example(self, client: Lithic) -> None: with client.events.subscriptions.with_streaming_response.send_simulated_example( - "string", + event_subscription_token="event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -526,7 +526,7 @@ def test_path_params_send_simulated_example(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): client.events.subscriptions.with_raw_response.send_simulated_example( - "", + event_subscription_token="", ) @@ -544,9 +544,9 @@ async def test_method_create(self, async_client: AsyncLithic) -> None: async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.create( url="https://example.com", - description="string", + description="description", disabled=True, - event_types=["account_holder.created", "account_holder.updated", "account_holder.verification"], + event_types=["account_holder.created"], ) assert_matches_type(EventSubscription, subscription, path=["response"]) @@ -577,14 +577,14 @@ async def test_streaming_response_create(self, async_client: AsyncLithic) -> Non @parametrize async def test_method_retrieve(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.retrieve( - "string", + "event_subscription_token", ) assert_matches_type(EventSubscription, subscription, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.retrieve( - "string", + "event_subscription_token", ) assert response.is_closed is True @@ -595,7 +595,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.retrieve( - "string", + "event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -617,7 +617,7 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_update(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.update( - "string", + event_subscription_token="event_subscription_token", url="https://example.com", ) assert_matches_type(EventSubscription, subscription, path=["response"]) @@ -625,18 +625,18 @@ async def test_method_update(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.update( - "string", + event_subscription_token="event_subscription_token", url="https://example.com", - description="string", + description="description", disabled=True, - event_types=["account_holder.created", "account_holder.updated", "account_holder.verification"], + event_types=["account_holder.created"], ) assert_matches_type(EventSubscription, subscription, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.update( - "string", + event_subscription_token="event_subscription_token", url="https://example.com", ) @@ -648,7 +648,7 @@ async def test_raw_response_update(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.update( - "string", + event_subscription_token="event_subscription_token", url="https://example.com", ) as response: assert not response.is_closed @@ -665,7 +665,7 @@ async def test_path_params_update(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): await async_client.events.subscriptions.with_raw_response.update( - "", + event_subscription_token="", url="https://example.com", ) @@ -677,9 +677,9 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.list( - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(AsyncCursorPage[EventSubscription], subscription, path=["response"]) @@ -703,19 +703,19 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_method_delete(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.delete( - "string", + "event_subscription_token", ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_raw_response_delete(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.delete( - "string", + "event_subscription_token", ) assert response.is_closed is True @@ -723,11 +723,11 @@ async def test_raw_response_delete(self, async_client: AsyncLithic) -> None: subscription = response.parse() assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_streaming_response_delete(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.delete( - "string", + "event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -737,7 +737,7 @@ async def test_streaming_response_delete(self, async_client: AsyncLithic) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_path_params_delete(self, async_client: AsyncLithic) -> None: with pytest.raises( @@ -750,19 +750,19 @@ async def test_path_params_delete(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list_attempts(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.list_attempts( - "string", + event_subscription_token="event_subscription_token", ) assert_matches_type(AsyncCursorPage[MessageAttempt], subscription, path=["response"]) @parametrize async def test_method_list_attempts_with_all_params(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.list_attempts( - "string", + event_subscription_token="event_subscription_token", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", status="FAILED", ) assert_matches_type(AsyncCursorPage[MessageAttempt], subscription, path=["response"]) @@ -770,7 +770,7 @@ async def test_method_list_attempts_with_all_params(self, async_client: AsyncLit @parametrize async def test_raw_response_list_attempts(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.list_attempts( - "string", + event_subscription_token="event_subscription_token", ) assert response.is_closed is True @@ -781,7 +781,7 @@ async def test_raw_response_list_attempts(self, async_client: AsyncLithic) -> No @parametrize async def test_streaming_response_list_attempts(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.list_attempts( - "string", + event_subscription_token="event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -797,32 +797,32 @@ async def test_path_params_list_attempts(self, async_client: AsyncLithic) -> Non ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): await async_client.events.subscriptions.with_raw_response.list_attempts( - "", + event_subscription_token="", ) - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_method_recover(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.recover( - "string", + event_subscription_token="event_subscription_token", ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_method_recover_with_all_params(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.recover( - "string", + event_subscription_token="event_subscription_token", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_raw_response_recover(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.recover( - "string", + event_subscription_token="event_subscription_token", ) assert response.is_closed is True @@ -830,11 +830,11 @@ async def test_raw_response_recover(self, async_client: AsyncLithic) -> None: subscription = response.parse() assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_streaming_response_recover(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.recover( - "string", + event_subscription_token="event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -844,39 +844,39 @@ async def test_streaming_response_recover(self, async_client: AsyncLithic) -> No assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_path_params_recover(self, async_client: AsyncLithic) -> None: with pytest.raises( ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): await async_client.events.subscriptions.with_raw_response.recover( - "", + event_subscription_token="", ) - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_method_replay_missing(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.replay_missing( - "string", + event_subscription_token="event_subscription_token", ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_method_replay_missing_with_all_params(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.replay_missing( - "string", + event_subscription_token="event_subscription_token", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_raw_response_replay_missing(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.replay_missing( - "string", + event_subscription_token="event_subscription_token", ) assert response.is_closed is True @@ -884,11 +884,11 @@ async def test_raw_response_replay_missing(self, async_client: AsyncLithic) -> N subscription = response.parse() assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_streaming_response_replay_missing(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.replay_missing( - "string", + event_subscription_token="event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -898,27 +898,27 @@ async def test_streaming_response_replay_missing(self, async_client: AsyncLithic assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_path_params_replay_missing(self, async_client: AsyncLithic) -> None: with pytest.raises( ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): await async_client.events.subscriptions.with_raw_response.replay_missing( - "", + event_subscription_token="", ) @parametrize async def test_method_retrieve_secret(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.retrieve_secret( - "string", + "event_subscription_token", ) assert_matches_type(SubscriptionRetrieveSecretResponse, subscription, path=["response"]) @parametrize async def test_raw_response_retrieve_secret(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.retrieve_secret( - "string", + "event_subscription_token", ) assert response.is_closed is True @@ -929,7 +929,7 @@ async def test_raw_response_retrieve_secret(self, async_client: AsyncLithic) -> @parametrize async def test_streaming_response_retrieve_secret(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.retrieve_secret( - "string", + "event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -948,19 +948,19 @@ async def test_path_params_retrieve_secret(self, async_client: AsyncLithic) -> N "", ) - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_method_rotate_secret(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.rotate_secret( - "string", + "event_subscription_token", ) assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_raw_response_rotate_secret(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.rotate_secret( - "string", + "event_subscription_token", ) assert response.is_closed is True @@ -968,11 +968,11 @@ async def test_raw_response_rotate_secret(self, async_client: AsyncLithic) -> No subscription = response.parse() assert subscription is None - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_streaming_response_rotate_secret(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.rotate_secret( - "string", + "event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -982,7 +982,7 @@ async def test_streaming_response_rotate_secret(self, async_client: AsyncLithic) assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") + @pytest.mark.skip(reason="Prism Mock server doesn't want Accept header, but server requires it.") @parametrize async def test_path_params_rotate_secret(self, async_client: AsyncLithic) -> None: with pytest.raises( @@ -995,14 +995,14 @@ async def test_path_params_rotate_secret(self, async_client: AsyncLithic) -> Non @parametrize async def test_method_send_simulated_example(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.send_simulated_example( - "string", + event_subscription_token="event_subscription_token", ) assert subscription is None @parametrize async def test_method_send_simulated_example_with_all_params(self, async_client: AsyncLithic) -> None: subscription = await async_client.events.subscriptions.send_simulated_example( - "string", + event_subscription_token="event_subscription_token", event_type="account_holder.created", ) assert subscription is None @@ -1010,7 +1010,7 @@ async def test_method_send_simulated_example_with_all_params(self, async_client: @parametrize async def test_raw_response_send_simulated_example(self, async_client: AsyncLithic) -> None: response = await async_client.events.subscriptions.with_raw_response.send_simulated_example( - "string", + event_subscription_token="event_subscription_token", ) assert response.is_closed is True @@ -1021,7 +1021,7 @@ async def test_raw_response_send_simulated_example(self, async_client: AsyncLith @parametrize async def test_streaming_response_send_simulated_example(self, async_client: AsyncLithic) -> None: async with async_client.events.subscriptions.with_streaming_response.send_simulated_example( - "string", + event_subscription_token="event_subscription_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1037,5 +1037,5 @@ async def test_path_params_send_simulated_example(self, async_client: AsyncLithi ValueError, match=r"Expected a non-empty value for `event_subscription_token` but received ''" ): await async_client.events.subscriptions.with_raw_response.send_simulated_example( - "", + event_subscription_token="", ) diff --git a/tests/api_resources/external_bank_accounts/__init__.py b/tests/api_resources/external_bank_accounts/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/api_resources/external_bank_accounts/__init__.py +++ b/tests/api_resources/external_bank_accounts/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/external_bank_accounts/test_micro_deposits.py b/tests/api_resources/external_bank_accounts/test_micro_deposits.py index 98d2ef7b..679c9865 100644 --- a/tests/api_resources/external_bank_accounts/test_micro_deposits.py +++ b/tests/api_resources/external_bank_accounts/test_micro_deposits.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -20,16 +20,16 @@ class TestMicroDeposits: @parametrize def test_method_create(self, client: Lithic) -> None: micro_deposit = client.external_bank_accounts.micro_deposits.create( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - micro_deposits=[0, 0, 0], + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + micro_deposits=[0, 0], ) assert_matches_type(MicroDepositCreateResponse, micro_deposit, path=["response"]) @parametrize def test_raw_response_create(self, client: Lithic) -> None: response = client.external_bank_accounts.micro_deposits.with_raw_response.create( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - micro_deposits=[0, 0, 0], + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + micro_deposits=[0, 0], ) assert response.is_closed is True @@ -40,8 +40,8 @@ def test_raw_response_create(self, client: Lithic) -> None: @parametrize def test_streaming_response_create(self, client: Lithic) -> None: with client.external_bank_accounts.micro_deposits.with_streaming_response.create( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - micro_deposits=[0, 0, 0], + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + micro_deposits=[0, 0], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -57,8 +57,8 @@ def test_path_params_create(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `external_bank_account_token` but received ''" ): client.external_bank_accounts.micro_deposits.with_raw_response.create( - "", - micro_deposits=[0, 0, 0], + external_bank_account_token="", + micro_deposits=[0, 0], ) @@ -68,16 +68,16 @@ class TestAsyncMicroDeposits: @parametrize async def test_method_create(self, async_client: AsyncLithic) -> None: micro_deposit = await async_client.external_bank_accounts.micro_deposits.create( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - micro_deposits=[0, 0, 0], + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + micro_deposits=[0, 0], ) assert_matches_type(MicroDepositCreateResponse, micro_deposit, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncLithic) -> None: response = await async_client.external_bank_accounts.micro_deposits.with_raw_response.create( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - micro_deposits=[0, 0, 0], + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + micro_deposits=[0, 0], ) assert response.is_closed is True @@ -88,8 +88,8 @@ async def test_raw_response_create(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: async with async_client.external_bank_accounts.micro_deposits.with_streaming_response.create( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - micro_deposits=[0, 0, 0], + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + micro_deposits=[0, 0], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -105,6 +105,6 @@ async def test_path_params_create(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `external_bank_account_token` but received ''" ): await async_client.external_bank_accounts.micro_deposits.with_raw_response.create( - "", - micro_deposits=[0, 0, 0], + external_bank_account_token="", + micro_deposits=[0, 0], ) diff --git a/tests/api_resources/financial_accounts/__init__.py b/tests/api_resources/financial_accounts/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/api_resources/financial_accounts/__init__.py +++ b/tests/api_resources/financial_accounts/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/financial_accounts/statements/__init__.py b/tests/api_resources/financial_accounts/statements/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/api_resources/financial_accounts/statements/__init__.py +++ b/tests/api_resources/financial_accounts/statements/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/financial_accounts/statements/test_line_items.py b/tests/api_resources/financial_accounts/statements/test_line_items.py index b6be34e2..842282fd 100644 --- a/tests/api_resources/financial_accounts/statements/test_line_items.py +++ b/tests/api_resources/financial_accounts/statements/test_line_items.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -10,7 +10,7 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type from lithic.pagination import SyncCursorPage, AsyncCursorPage -from lithic.types.financial_accounts.statements import LineItemListResponse +from lithic.types.financial_accounts.statements.statement_line_items import Data base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -21,45 +21,45 @@ class TestLineItems: @parametrize def test_method_list(self, client: Lithic) -> None: line_item = client.financial_accounts.statements.line_items.list( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncCursorPage[LineItemListResponse], line_item, path=["response"]) + assert_matches_type(SyncCursorPage[Data], line_item, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: line_item = client.financial_accounts.statements.line_items.list( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) - assert_matches_type(SyncCursorPage[LineItemListResponse], line_item, path=["response"]) + assert_matches_type(SyncCursorPage[Data], line_item, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: response = client.financial_accounts.statements.line_items.with_raw_response.list( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" line_item = response.parse() - assert_matches_type(SyncCursorPage[LineItemListResponse], line_item, path=["response"]) + assert_matches_type(SyncCursorPage[Data], line_item, path=["response"]) @parametrize def test_streaming_response_list(self, client: Lithic) -> None: with client.financial_accounts.statements.line_items.with_streaming_response.list( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" line_item = response.parse() - assert_matches_type(SyncCursorPage[LineItemListResponse], line_item, path=["response"]) + assert_matches_type(SyncCursorPage[Data], line_item, path=["response"]) assert cast(Any, response.is_closed) is True @@ -69,13 +69,13 @@ def test_path_params_list(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): client.financial_accounts.statements.line_items.with_raw_response.list( - "string", + statement_token="statement_token", financial_account_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `statement_token` but received ''"): client.financial_accounts.statements.line_items.with_raw_response.list( - "", + statement_token="", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -86,45 +86,45 @@ class TestAsyncLineItems: @parametrize async def test_method_list(self, async_client: AsyncLithic) -> None: line_item = await async_client.financial_accounts.statements.line_items.list( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncCursorPage[LineItemListResponse], line_item, path=["response"]) + assert_matches_type(AsyncCursorPage[Data], line_item, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: line_item = await async_client.financial_accounts.statements.line_items.list( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) - assert_matches_type(AsyncCursorPage[LineItemListResponse], line_item, path=["response"]) + assert_matches_type(AsyncCursorPage[Data], line_item, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: response = await async_client.financial_accounts.statements.line_items.with_raw_response.list( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" line_item = response.parse() - assert_matches_type(AsyncCursorPage[LineItemListResponse], line_item, path=["response"]) + assert_matches_type(AsyncCursorPage[Data], line_item, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: async with async_client.financial_accounts.statements.line_items.with_streaming_response.list( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" line_item = await response.parse() - assert_matches_type(AsyncCursorPage[LineItemListResponse], line_item, path=["response"]) + assert_matches_type(AsyncCursorPage[Data], line_item, path=["response"]) assert cast(Any, response.is_closed) is True @@ -134,12 +134,12 @@ async def test_path_params_list(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): await async_client.financial_accounts.statements.line_items.with_raw_response.list( - "string", + statement_token="statement_token", financial_account_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `statement_token` but received ''"): await async_client.financial_accounts.statements.line_items.with_raw_response.list( - "", + statement_token="", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) diff --git a/tests/api_resources/financial_accounts/test_balances.py b/tests/api_resources/financial_accounts/test_balances.py index f5b2f846..6f5ac9f2 100644 --- a/tests/api_resources/financial_accounts/test_balances.py +++ b/tests/api_resources/financial_accounts/test_balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,9 +9,9 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type -from lithic.types import Balance from lithic._utils import parse_datetime from lithic.pagination import SyncSinglePage, AsyncSinglePage +from lithic.types.financial_accounts import BalanceListResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -22,40 +22,40 @@ class TestBalances: @parametrize def test_method_list(self, client: Lithic) -> None: balance = client.financial_accounts.balances.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: balance = client.financial_accounts.balances.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), last_transaction_event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: response = client.financial_accounts.balances.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize def test_streaming_response_list(self, client: Lithic) -> None: with client.financial_accounts.balances.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) assert cast(Any, response.is_closed) is True @@ -65,7 +65,7 @@ def test_path_params_list(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): client.financial_accounts.balances.with_raw_response.list( - "", + financial_account_token="", ) @@ -75,40 +75,40 @@ class TestAsyncBalances: @parametrize async def test_method_list(self, async_client: AsyncLithic) -> None: balance = await async_client.financial_accounts.balances.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: balance = await async_client.financial_accounts.balances.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), last_transaction_event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: response = await async_client.financial_accounts.balances.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: async with async_client.financial_accounts.balances.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = await response.parse() - assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) assert cast(Any, response.is_closed) is True @@ -118,5 +118,5 @@ async def test_path_params_list(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): await async_client.financial_accounts.balances.with_raw_response.list( - "", + financial_account_token="", ) diff --git a/tests/api_resources/financial_accounts/test_credit_configuration.py b/tests/api_resources/financial_accounts/test_credit_configuration.py new file mode 100644 index 00000000..98e8d545 --- /dev/null +++ b/tests/api_resources/financial_accounts/test_credit_configuration.py @@ -0,0 +1,204 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types.financial_accounts import FinancialAccountCreditConfig + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCreditConfiguration: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + credit_configuration = client.financial_accounts.credit_configuration.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.financial_accounts.credit_configuration.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + credit_configuration = response.parse() + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.financial_accounts.credit_configuration.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + credit_configuration = response.parse() + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.credit_configuration.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: Lithic) -> None: + credit_configuration = client.financial_accounts.credit_configuration.update( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Lithic) -> None: + credit_configuration = client.financial_accounts.credit_configuration.update( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_limit=0, + credit_product_token="credit_product_token", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + tier="x", + ) + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Lithic) -> None: + response = client.financial_accounts.credit_configuration.with_raw_response.update( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + credit_configuration = response.parse() + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Lithic) -> None: + with client.financial_accounts.credit_configuration.with_streaming_response.update( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + credit_configuration = response.parse() + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.credit_configuration.with_raw_response.update( + financial_account_token="", + ) + + +class TestAsyncCreditConfiguration: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + credit_configuration = await async_client.financial_accounts.credit_configuration.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.credit_configuration.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + credit_configuration = response.parse() + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.credit_configuration.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + credit_configuration = await response.parse() + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.credit_configuration.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncLithic) -> None: + credit_configuration = await async_client.financial_accounts.credit_configuration.update( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: + credit_configuration = await async_client.financial_accounts.credit_configuration.update( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_limit=0, + credit_product_token="credit_product_token", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + tier="x", + ) + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.credit_configuration.with_raw_response.update( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + credit_configuration = response.parse() + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.credit_configuration.with_streaming_response.update( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + credit_configuration = await response.parse() + assert_matches_type(FinancialAccountCreditConfig, credit_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.credit_configuration.with_raw_response.update( + financial_account_token="", + ) diff --git a/tests/api_resources/financial_accounts/test_financial_transactions.py b/tests/api_resources/financial_accounts/test_financial_transactions.py index 4d97ef36..efa82133 100644 --- a/tests/api_resources/financial_accounts/test_financial_transactions.py +++ b/tests/api_resources/financial_accounts/test_financial_transactions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -22,7 +22,7 @@ class TestFinancialTransactions: @parametrize def test_method_retrieve(self, client: Lithic) -> None: financial_transaction = client.financial_accounts.financial_transactions.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(FinancialTransaction, financial_transaction, path=["response"]) @@ -30,7 +30,7 @@ def test_method_retrieve(self, client: Lithic) -> None: @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: response = client.financial_accounts.financial_transactions.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -42,7 +42,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: with client.financial_accounts.financial_transactions.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -59,7 +59,7 @@ def test_path_params_retrieve(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): client.financial_accounts.financial_transactions.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="", ) @@ -67,27 +67,27 @@ def test_path_params_retrieve(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_transaction_token` but received ''" ): client.financial_accounts.financial_transactions.with_raw_response.retrieve( - "", + financial_transaction_token="", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize def test_method_list(self, client: Lithic) -> None: financial_transaction = client.financial_accounts.financial_transactions.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(SyncSinglePage[FinancialTransaction], financial_transaction, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: financial_transaction = client.financial_accounts.financial_transactions.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), category="ACH", end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", result="APPROVED", - starting_after="string", + starting_after="starting_after", status="DECLINED", ) assert_matches_type(SyncSinglePage[FinancialTransaction], financial_transaction, path=["response"]) @@ -95,7 +95,7 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: @parametrize def test_raw_response_list(self, client: Lithic) -> None: response = client.financial_accounts.financial_transactions.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -106,7 +106,7 @@ def test_raw_response_list(self, client: Lithic) -> None: @parametrize def test_streaming_response_list(self, client: Lithic) -> None: with client.financial_accounts.financial_transactions.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -122,7 +122,7 @@ def test_path_params_list(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): client.financial_accounts.financial_transactions.with_raw_response.list( - "", + financial_account_token="", ) @@ -132,7 +132,7 @@ class TestAsyncFinancialTransactions: @parametrize async def test_method_retrieve(self, async_client: AsyncLithic) -> None: financial_transaction = await async_client.financial_accounts.financial_transactions.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(FinancialTransaction, financial_transaction, path=["response"]) @@ -140,7 +140,7 @@ async def test_method_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: response = await async_client.financial_accounts.financial_transactions.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -152,7 +152,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: async with async_client.financial_accounts.financial_transactions.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -169,7 +169,7 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): await async_client.financial_accounts.financial_transactions.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_transaction_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="", ) @@ -177,27 +177,27 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_transaction_token` but received ''" ): await async_client.financial_accounts.financial_transactions.with_raw_response.retrieve( - "", + financial_transaction_token="", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize async def test_method_list(self, async_client: AsyncLithic) -> None: financial_transaction = await async_client.financial_accounts.financial_transactions.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(AsyncSinglePage[FinancialTransaction], financial_transaction, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: financial_transaction = await async_client.financial_accounts.financial_transactions.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), category="ACH", end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", result="APPROVED", - starting_after="string", + starting_after="starting_after", status="DECLINED", ) assert_matches_type(AsyncSinglePage[FinancialTransaction], financial_transaction, path=["response"]) @@ -205,7 +205,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: response = await async_client.financial_accounts.financial_transactions.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -216,7 +216,7 @@ async def test_raw_response_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: async with async_client.financial_accounts.financial_transactions.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -232,5 +232,5 @@ async def test_path_params_list(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): await async_client.financial_accounts.financial_transactions.with_raw_response.list( - "", + financial_account_token="", ) diff --git a/tests/api_resources/financial_accounts/test_loan_tapes.py b/tests/api_resources/financial_accounts/test_loan_tapes.py new file mode 100644 index 00000000..c64e0e66 --- /dev/null +++ b/tests/api_resources/financial_accounts/test_loan_tapes.py @@ -0,0 +1,228 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic._utils import parse_date +from lithic.pagination import SyncCursorPage, AsyncCursorPage +from lithic.types.financial_accounts import LoanTape + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLoanTapes: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + loan_tape = client.financial_accounts.loan_tapes.retrieve( + loan_tape_token="loan_tape_token", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(LoanTape, loan_tape, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.financial_accounts.loan_tapes.with_raw_response.retrieve( + loan_tape_token="loan_tape_token", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + loan_tape = response.parse() + assert_matches_type(LoanTape, loan_tape, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.financial_accounts.loan_tapes.with_streaming_response.retrieve( + loan_tape_token="loan_tape_token", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + loan_tape = response.parse() + assert_matches_type(LoanTape, loan_tape, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.loan_tapes.with_raw_response.retrieve( + loan_tape_token="loan_tape_token", + financial_account_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `loan_tape_token` but received ''"): + client.financial_accounts.loan_tapes.with_raw_response.retrieve( + loan_tape_token="", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + loan_tape = client.financial_accounts.loan_tapes.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(SyncCursorPage[LoanTape], loan_tape, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + loan_tape = client.financial_accounts.loan_tapes.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + begin=parse_date("2019-12-27"), + end=parse_date("2019-12-27"), + ending_before="ending_before", + page_size=1, + starting_after="starting_after", + ) + assert_matches_type(SyncCursorPage[LoanTape], loan_tape, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.financial_accounts.loan_tapes.with_raw_response.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + loan_tape = response.parse() + assert_matches_type(SyncCursorPage[LoanTape], loan_tape, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.financial_accounts.loan_tapes.with_streaming_response.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + loan_tape = response.parse() + assert_matches_type(SyncCursorPage[LoanTape], loan_tape, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.loan_tapes.with_raw_response.list( + financial_account_token="", + ) + + +class TestAsyncLoanTapes: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + loan_tape = await async_client.financial_accounts.loan_tapes.retrieve( + loan_tape_token="loan_tape_token", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(LoanTape, loan_tape, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.loan_tapes.with_raw_response.retrieve( + loan_tape_token="loan_tape_token", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + loan_tape = response.parse() + assert_matches_type(LoanTape, loan_tape, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.loan_tapes.with_streaming_response.retrieve( + loan_tape_token="loan_tape_token", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + loan_tape = await response.parse() + assert_matches_type(LoanTape, loan_tape, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.loan_tapes.with_raw_response.retrieve( + loan_tape_token="loan_tape_token", + financial_account_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `loan_tape_token` but received ''"): + await async_client.financial_accounts.loan_tapes.with_raw_response.retrieve( + loan_tape_token="", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncLithic) -> None: + loan_tape = await async_client.financial_accounts.loan_tapes.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(AsyncCursorPage[LoanTape], loan_tape, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: + loan_tape = await async_client.financial_accounts.loan_tapes.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + begin=parse_date("2019-12-27"), + end=parse_date("2019-12-27"), + ending_before="ending_before", + page_size=1, + starting_after="starting_after", + ) + assert_matches_type(AsyncCursorPage[LoanTape], loan_tape, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.loan_tapes.with_raw_response.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + loan_tape = response.parse() + assert_matches_type(AsyncCursorPage[LoanTape], loan_tape, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.loan_tapes.with_streaming_response.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + loan_tape = await response.parse() + assert_matches_type(AsyncCursorPage[LoanTape], loan_tape, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.loan_tapes.with_raw_response.list( + financial_account_token="", + ) diff --git a/tests/api_resources/financial_accounts/test_statements.py b/tests/api_resources/financial_accounts/test_statements.py index 7af808f3..20752a11 100644 --- a/tests/api_resources/financial_accounts/test_statements.py +++ b/tests/api_resources/financial_accounts/test_statements.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -22,7 +22,7 @@ class TestStatements: @parametrize def test_method_retrieve(self, client: Lithic) -> None: statement = client.financial_accounts.statements.retrieve( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Statement, statement, path=["response"]) @@ -30,7 +30,7 @@ def test_method_retrieve(self, client: Lithic) -> None: @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: response = client.financial_accounts.statements.with_raw_response.retrieve( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -42,7 +42,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: with client.financial_accounts.statements.with_streaming_response.retrieve( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -59,39 +59,40 @@ def test_path_params_retrieve(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): client.financial_accounts.statements.with_raw_response.retrieve( - "string", + statement_token="statement_token", financial_account_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `statement_token` but received ''"): client.financial_accounts.statements.with_raw_response.retrieve( - "", + statement_token="", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize def test_method_list(self, client: Lithic) -> None: statement = client.financial_accounts.statements.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(SyncCursorPage[Statement], statement, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: statement = client.financial_accounts.statements.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_date("2019-12-27"), end=parse_date("2019-12-27"), - ending_before="string", + ending_before="ending_before", + include_initial_statements=True, page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(SyncCursorPage[Statement], statement, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: response = client.financial_accounts.statements.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -102,7 +103,7 @@ def test_raw_response_list(self, client: Lithic) -> None: @parametrize def test_streaming_response_list(self, client: Lithic) -> None: with client.financial_accounts.statements.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -118,7 +119,7 @@ def test_path_params_list(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): client.financial_accounts.statements.with_raw_response.list( - "", + financial_account_token="", ) @@ -128,7 +129,7 @@ class TestAsyncStatements: @parametrize async def test_method_retrieve(self, async_client: AsyncLithic) -> None: statement = await async_client.financial_accounts.statements.retrieve( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Statement, statement, path=["response"]) @@ -136,7 +137,7 @@ async def test_method_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: response = await async_client.financial_accounts.statements.with_raw_response.retrieve( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -148,7 +149,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: async with async_client.financial_accounts.statements.with_streaming_response.retrieve( - "string", + statement_token="statement_token", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -165,39 +166,40 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): await async_client.financial_accounts.statements.with_raw_response.retrieve( - "string", + statement_token="statement_token", financial_account_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `statement_token` but received ''"): await async_client.financial_accounts.statements.with_raw_response.retrieve( - "", + statement_token="", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize async def test_method_list(self, async_client: AsyncLithic) -> None: statement = await async_client.financial_accounts.statements.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(AsyncCursorPage[Statement], statement, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: statement = await async_client.financial_accounts.statements.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_date("2019-12-27"), end=parse_date("2019-12-27"), - ending_before="string", + ending_before="ending_before", + include_initial_statements=True, page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(AsyncCursorPage[Statement], statement, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: response = await async_client.financial_accounts.statements.with_raw_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -208,7 +210,7 @@ async def test_raw_response_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: async with async_client.financial_accounts.statements.with_streaming_response.list( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -224,5 +226,5 @@ async def test_path_params_list(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): await async_client.financial_accounts.statements.with_raw_response.list( - "", + financial_account_token="", ) diff --git a/tests/api_resources/reports/__init__.py b/tests/api_resources/reports/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/api_resources/reports/__init__.py +++ b/tests/api_resources/reports/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/reports/settlement/__init__.py b/tests/api_resources/reports/settlement/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/reports/settlement/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/reports/settlement/test_network_totals.py b/tests/api_resources/reports/settlement/test_network_totals.py new file mode 100644 index 00000000..7a247d61 --- /dev/null +++ b/tests/api_resources/reports/settlement/test_network_totals.py @@ -0,0 +1,187 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic._utils import parse_date, parse_datetime +from lithic.pagination import SyncCursorPage, AsyncCursorPage +from lithic.types.reports.settlement import ( + NetworkTotalListResponse, + NetworkTotalRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestNetworkTotals: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + network_total = client.reports.settlement.network_totals.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(NetworkTotalRetrieveResponse, network_total, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.reports.settlement.network_totals.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + network_total = response.parse() + assert_matches_type(NetworkTotalRetrieveResponse, network_total, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.reports.settlement.network_totals.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + network_total = response.parse() + assert_matches_type(NetworkTotalRetrieveResponse, network_total, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `token` but received ''"): + client.reports.settlement.network_totals.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + network_total = client.reports.settlement.network_totals.list() + assert_matches_type(SyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + network_total = client.reports.settlement.network_totals.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + institution_id="institution_id", + network="VISA", + page_size=1, + report_date=parse_date("2019-12-27"), + report_date_begin=parse_date("2019-12-27"), + report_date_end=parse_date("2019-12-27"), + settlement_institution_id="settlement_institution_id", + starting_after="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(SyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.reports.settlement.network_totals.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + network_total = response.parse() + assert_matches_type(SyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.reports.settlement.network_totals.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + network_total = response.parse() + assert_matches_type(SyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncNetworkTotals: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + network_total = await async_client.reports.settlement.network_totals.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(NetworkTotalRetrieveResponse, network_total, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.reports.settlement.network_totals.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + network_total = response.parse() + assert_matches_type(NetworkTotalRetrieveResponse, network_total, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.reports.settlement.network_totals.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + network_total = await response.parse() + assert_matches_type(NetworkTotalRetrieveResponse, network_total, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `token` but received ''"): + await async_client.reports.settlement.network_totals.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncLithic) -> None: + network_total = await async_client.reports.settlement.network_totals.list() + assert_matches_type(AsyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: + network_total = await async_client.reports.settlement.network_totals.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + institution_id="institution_id", + network="VISA", + page_size=1, + report_date=parse_date("2019-12-27"), + report_date_begin=parse_date("2019-12-27"), + report_date_end=parse_date("2019-12-27"), + settlement_institution_id="settlement_institution_id", + starting_after="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(AsyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncLithic) -> None: + response = await async_client.reports.settlement.network_totals.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + network_total = response.parse() + assert_matches_type(AsyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: + async with async_client.reports.settlement.network_totals.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + network_total = await response.parse() + assert_matches_type(AsyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/reports/test_settlement.py b/tests/api_resources/reports/test_settlement.py index 5ee7842b..57e78cde 100644 --- a/tests/api_resources/reports/test_settlement.py +++ b/tests/api_resources/reports/test_settlement.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -22,24 +22,24 @@ class TestSettlement: @parametrize def test_method_list_details(self, client: Lithic) -> None: settlement = client.reports.settlement.list_details( - parse_date("2019-12-27"), + report_date=parse_date("2023-09-01"), ) assert_matches_type(SyncCursorPage[SettlementDetail], settlement, path=["response"]) @parametrize def test_method_list_details_with_all_params(self, client: Lithic) -> None: settlement = client.reports.settlement.list_details( - parse_date("2019-12-27"), - ending_before="string", + report_date=parse_date("2023-09-01"), + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(SyncCursorPage[SettlementDetail], settlement, path=["response"]) @parametrize def test_raw_response_list_details(self, client: Lithic) -> None: response = client.reports.settlement.with_raw_response.list_details( - parse_date("2019-12-27"), + report_date=parse_date("2023-09-01"), ) assert response.is_closed is True @@ -50,7 +50,7 @@ def test_raw_response_list_details(self, client: Lithic) -> None: @parametrize def test_streaming_response_list_details(self, client: Lithic) -> None: with client.reports.settlement.with_streaming_response.list_details( - parse_date("2019-12-27"), + report_date=parse_date("2023-09-01"), ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -64,7 +64,7 @@ def test_streaming_response_list_details(self, client: Lithic) -> None: def test_path_params_list_details(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_date` but received ''"): client.reports.settlement.with_raw_response.list_details( - "", + report_date="", ) @parametrize @@ -112,24 +112,24 @@ class TestAsyncSettlement: @parametrize async def test_method_list_details(self, async_client: AsyncLithic) -> None: settlement = await async_client.reports.settlement.list_details( - parse_date("2019-12-27"), + report_date=parse_date("2023-09-01"), ) assert_matches_type(AsyncCursorPage[SettlementDetail], settlement, path=["response"]) @parametrize async def test_method_list_details_with_all_params(self, async_client: AsyncLithic) -> None: settlement = await async_client.reports.settlement.list_details( - parse_date("2019-12-27"), - ending_before="string", + report_date=parse_date("2023-09-01"), + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(AsyncCursorPage[SettlementDetail], settlement, path=["response"]) @parametrize async def test_raw_response_list_details(self, async_client: AsyncLithic) -> None: response = await async_client.reports.settlement.with_raw_response.list_details( - parse_date("2019-12-27"), + report_date=parse_date("2023-09-01"), ) assert response.is_closed is True @@ -140,7 +140,7 @@ async def test_raw_response_list_details(self, async_client: AsyncLithic) -> Non @parametrize async def test_streaming_response_list_details(self, async_client: AsyncLithic) -> None: async with async_client.reports.settlement.with_streaming_response.list_details( - parse_date("2019-12-27"), + report_date=parse_date("2023-09-01"), ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -154,7 +154,7 @@ async def test_streaming_response_list_details(self, async_client: AsyncLithic) async def test_path_params_list_details(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_date` but received ''"): await async_client.reports.settlement.with_raw_response.list_details( - "", + report_date="", ) @parametrize diff --git a/tests/api_resources/test_account_holders.py b/tests/api_resources/test_account_holders.py index 76c01a0c..e813c3fd 100644 --- a/tests/api_resources/test_account_holders.py +++ b/tests/api_resources/test_account_holders.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -11,12 +11,14 @@ from tests.utils import assert_matches_type from lithic.types import ( AccountHolder, - AccountHolderDocument, AccountHolderCreateResponse, AccountHolderUpdateResponse, AccountHolderListDocumentsResponse, + AccountHolderSimulateEnrollmentReviewResponse, ) +from lithic._utils import parse_datetime from lithic.pagination import SyncSinglePage, AsyncSinglePage +from lithic.types.shared import Document base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -27,44 +29,6 @@ class TestAccountHolders: @parametrize def test_method_create_overload_1(self, client: Lithic) -> None: account_holder = client.account_holders.create( - beneficial_owner_entities=[ - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - ], beneficial_owner_individuals=[ { "address": { @@ -79,35 +43,7 @@ def test_method_create_overload_1(self, client: Lithic) -> None: "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, + } ], business_entity={ "address": { @@ -119,7 +55,7 @@ def test_method_create_overload_1(self, client: Lithic) -> None: }, "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], + "phone_numbers": ["+15555555555"], }, control_person={ "address": { @@ -144,146 +80,58 @@ def test_method_create_overload_1(self, client: Lithic) -> None: @parametrize def test_method_create_with_all_params_overload_1(self, client: Lithic) -> None: account_holder = client.account_holders.create( - beneficial_owner_entities=[ - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dba_business_name": "string", - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "parent_company": "string", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dba_business_name": "string", - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "parent_company": "string", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dba_business_name": "string", - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "parent_company": "string", - "phone_numbers": ["+12124007676"], - }, - ], beneficial_owner_individuals=[ { "address": { "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, "dob": "1991-03-08 08:00:00", "email": "tom@middle-earth.com", "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", - }, + "phone_number": "+15555555555", + } ], business_entity={ "address": { "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, - "dba_business_name": "string", "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "parent_company": "string", - "phone_numbers": ["+12124007676"], + "phone_numbers": ["+15555555555"], + "dba_business_name": "dba_business_name", + "parent_company": "parent_company", }, control_person={ "address": { "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, "dob": "1991-03-08 08:00:00", "email": "tom@middle-earth.com", "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, nature_of_business="Software company selling solutions to the restaurant industry", tos_timestamp="2018-05-29T21:16:05Z", workflow="KYB_BASIC", - external_id="string", - kyb_passed_timestamp="2018-05-29T21:16:05Z", - website_url="www.mybusiness.com", - ) - assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) - - @parametrize - def test_raw_response_create_overload_1(self, client: Lithic) -> None: - response = client.account_holders.with_raw_response.create( beneficial_owner_entities=[ { "address": { @@ -292,36 +140,24 @@ def test_raw_response_create_overload_1(self, client: Lithic) -> None: "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, + "phone_numbers": ["+15555555555"], + "dba_business_name": "dba_business_name", + "parent_company": "parent_company", + } ], + external_id="external_id", + kyb_passed_timestamp="2018-05-29T21:16:05Z", + website_url="www.mybusiness.com", + ) + assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + + @parametrize + def test_raw_response_create_overload_1(self, client: Lithic) -> None: + response = client.account_holders.with_raw_response.create( beneficial_owner_individuals=[ { "address": { @@ -336,35 +172,7 @@ def test_raw_response_create_overload_1(self, client: Lithic) -> None: "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, + } ], business_entity={ "address": { @@ -376,7 +184,7 @@ def test_raw_response_create_overload_1(self, client: Lithic) -> None: }, "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], + "phone_numbers": ["+15555555555"], }, control_person={ "address": { @@ -405,44 +213,6 @@ def test_raw_response_create_overload_1(self, client: Lithic) -> None: @parametrize def test_streaming_response_create_overload_1(self, client: Lithic) -> None: with client.account_holders.with_streaming_response.create( - beneficial_owner_entities=[ - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - ], beneficial_owner_individuals=[ { "address": { @@ -457,47 +227,19 @@ def test_streaming_response_create_overload_1(self, client: Lithic) -> None: "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, - ], - business_entity={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", + } + ], + business_entity={ + "address": { + "address1": "123 Old Forest Way", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", }, "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], + "phone_numbers": ["+15555555555"], }, control_person={ "address": { @@ -541,10 +283,10 @@ def test_method_create_overload_2(self, client: Lithic) -> None: "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, - tos_timestamp="string", - workflow="KYC_ADVANCED", + tos_timestamp="tos_timestamp", + workflow="KYC_BASIC", ) assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) @@ -554,23 +296,23 @@ def test_method_create_with_all_params_overload_2(self, client: Lithic) -> None: individual={ "address": { "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, "dob": "1991-03-08 08:00:00", "email": "tom@middle-earth.com", "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, - tos_timestamp="string", - workflow="KYC_ADVANCED", - external_id="string", - kyc_passed_timestamp="string", + tos_timestamp="tos_timestamp", + workflow="KYC_BASIC", + external_id="external_id", + kyc_passed_timestamp="kyc_passed_timestamp", ) assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) @@ -590,10 +332,10 @@ def test_raw_response_create_overload_2(self, client: Lithic) -> None: "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, - tos_timestamp="string", - workflow="KYC_ADVANCED", + tos_timestamp="tos_timestamp", + workflow="KYC_BASIC", ) assert response.is_closed is True @@ -617,10 +359,10 @@ def test_streaming_response_create_overload_2(self, client: Lithic) -> None: "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, - tos_timestamp="string", - workflow="KYC_ADVANCED", + tos_timestamp="tos_timestamp", + workflow="KYC_BASIC", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -633,11 +375,18 @@ def test_streaming_response_create_overload_2(self, client: Lithic) -> None: @parametrize def test_method_create_overload_3(self, client: Lithic) -> None: account_holder = client.account_holders.create( - email="string", - first_name="string", + address={ + "address1": "123 Old Forest Way", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + email="email", + first_name="first_name", kyc_exemption_type="AUTHORIZED_USER", - last_name="string", - phone_number="string", + last_name="last_name", + phone_number="phone_number", workflow="KYC_EXEMPT", ) assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) @@ -645,33 +394,40 @@ def test_method_create_overload_3(self, client: Lithic) -> None: @parametrize def test_method_create_with_all_params_overload_3(self, client: Lithic) -> None: account_holder = client.account_holders.create( - email="string", - first_name="string", - kyc_exemption_type="AUTHORIZED_USER", - last_name="string", - phone_number="string", - workflow="KYC_EXEMPT", address={ "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, - business_account_token="string", - external_id="string", + email="email", + first_name="first_name", + kyc_exemption_type="AUTHORIZED_USER", + last_name="last_name", + phone_number="phone_number", + workflow="KYC_EXEMPT", + business_account_token="business_account_token", + external_id="external_id", ) assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) @parametrize def test_raw_response_create_overload_3(self, client: Lithic) -> None: response = client.account_holders.with_raw_response.create( - email="string", - first_name="string", + address={ + "address1": "123 Old Forest Way", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + email="email", + first_name="first_name", kyc_exemption_type="AUTHORIZED_USER", - last_name="string", - phone_number="string", + last_name="last_name", + phone_number="phone_number", workflow="KYC_EXEMPT", ) @@ -683,11 +439,18 @@ def test_raw_response_create_overload_3(self, client: Lithic) -> None: @parametrize def test_streaming_response_create_overload_3(self, client: Lithic) -> None: with client.account_holders.with_streaming_response.create( - email="string", - first_name="string", + address={ + "address1": "123 Old Forest Way", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + email="email", + first_name="first_name", kyc_exemption_type="AUTHORIZED_USER", - last_name="string", - phone_number="string", + last_name="last_name", + phone_number="phone_number", workflow="KYC_EXEMPT", ) as response: assert not response.is_closed @@ -737,26 +500,96 @@ def test_path_params_retrieve(self, client: Lithic) -> None: ) @parametrize - def test_method_update(self, client: Lithic) -> None: + def test_method_update_overload_1(self, client: Lithic) -> None: account_holder = client.account_holders.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - def test_method_update_with_all_params(self, client: Lithic) -> None: + def test_method_update_with_all_params_overload_1(self, client: Lithic) -> None: account_holder = client.account_holders.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - business_account_token="string", - email="string", - phone_number="string", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + beneficial_owner_entities=[ + { + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dba_business_name": "dba_business_name", + "government_id": "114-123-1513", + "legal_business_name": "Acme, Inc.", + "parent_company": "parent_company", + "phone_numbers": ["+15555555555"], + } + ], + beneficial_owner_individuals=[ + { + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dob": "1991-03-08 08:00:00", + "email": "tom@middle-earth.com", + "first_name": "Tom", + "government_id": "111-23-1412", + "last_name": "Bombadil", + "phone_number": "+15555555555", + } + ], + business_entity={ + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dba_business_name": "dba_business_name", + "government_id": "114-123-1513", + "legal_business_name": "Acme, Inc.", + "parent_company": "parent_company", + "phone_numbers": ["+15555555555"], + }, + control_person={ + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dob": "1991-03-08 08:00:00", + "email": "tom@middle-earth.com", + "first_name": "Tom", + "government_id": "111-23-1412", + "last_name": "Bombadil", + "phone_number": "+15555555555", + }, + external_id="external_id", + nature_of_business="Software company selling solutions to the restaurant industry", + website_url="www.mybusiness.com", ) assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - def test_raw_response_update(self, client: Lithic) -> None: + def test_raw_response_update_overload_1(self, client: Lithic) -> None: response = client.account_holders.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -765,9 +598,9 @@ def test_raw_response_update(self, client: Lithic) -> None: assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - def test_streaming_response_update(self, client: Lithic) -> None: + def test_streaming_response_update_overload_1(self, client: Lithic) -> None: with client.account_holders.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -778,222 +611,245 @@ def test_streaming_response_update(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_update(self, client: Lithic) -> None: + def test_path_params_update_overload_1(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): client.account_holders.with_raw_response.update( - "", + account_holder_token="", ) @parametrize - def test_method_list(self, client: Lithic) -> None: - account_holder = client.account_holders.list() - assert_matches_type(SyncSinglePage[AccountHolder], account_holder, path=["response"]) + def test_method_update_overload_2(self, client: Lithic) -> None: + account_holder = client.account_holders.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - def test_method_list_with_all_params(self, client: Lithic) -> None: - account_holder = client.account_holders.list( - ending_before="string", - external_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - limit=0, - starting_after="string", + def test_method_update_with_all_params_overload_2(self, client: Lithic) -> None: + account_holder = client.account_holders.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_id="external_id", + individual={ + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dob": "1991-03-08 08:00:00", + "email": "tom@middle-earth.com", + "first_name": "Tom", + "government_id": "111-23-1412", + "last_name": "Bombadil", + "phone_number": "+15555555555", + }, ) - assert_matches_type(SyncSinglePage[AccountHolder], account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - def test_raw_response_list(self, client: Lithic) -> None: - response = client.account_holders.with_raw_response.list() + def test_raw_response_update_overload_2(self, client: Lithic) -> None: + response = client.account_holders.with_raw_response.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(SyncSinglePage[AccountHolder], account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - def test_streaming_response_list(self, client: Lithic) -> None: - with client.account_holders.with_streaming_response.list() as response: + def test_streaming_response_update_overload_2(self, client: Lithic) -> None: + with client.account_holders.with_streaming_response.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(SyncSinglePage[AccountHolder], account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - def test_method_list_documents(self, client: Lithic) -> None: - account_holder = client.account_holders.list_documents( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + def test_path_params_update_overload_2(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): + client.account_holders.with_raw_response.update( + account_holder_token="", + ) + + @parametrize + def test_method_update_overload_3(self, client: Lithic) -> None: + account_holder = client.account_holders.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AccountHolderListDocumentsResponse, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - def test_raw_response_list_documents(self, client: Lithic) -> None: - response = client.account_holders.with_raw_response.list_documents( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + def test_method_update_with_all_params_overload_3(self, client: Lithic) -> None: + account_holder = client.account_holders.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + address={ + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + business_account_token="business_account_token", + email="email", + first_name="first_name", + last_name="last_name", + legal_business_name="legal_business_name", + phone_number="phone_number", + ) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) + + @parametrize + def test_raw_response_update_overload_3(self, client: Lithic) -> None: + response = client.account_holders.with_raw_response.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderListDocumentsResponse, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - def test_streaming_response_list_documents(self, client: Lithic) -> None: - with client.account_holders.with_streaming_response.list_documents( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + def test_streaming_response_update_overload_3(self, client: Lithic) -> None: + with client.account_holders.with_streaming_response.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderListDocumentsResponse, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_list_documents(self, client: Lithic) -> None: + def test_path_params_update_overload_3(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): - client.account_holders.with_raw_response.list_documents( - "", + client.account_holders.with_raw_response.update( + account_holder_token="", ) @parametrize - def test_method_resubmit(self, client: Lithic) -> None: - account_holder = client.account_holders.resubmit( + def test_method_list(self, client: Lithic) -> None: + account_holder = client.account_holders.list() + assert_matches_type(SyncSinglePage[AccountHolder], account_holder, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + account_holder = client.account_holders.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + email="email", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + external_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + first_name="first_name", + last_name="last_name", + legal_business_name="legal_business_name", + limit=0, + phone_number="phone_number", + starting_after="starting_after", + ) + assert_matches_type(SyncSinglePage[AccountHolder], account_holder, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.account_holders.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account_holder = response.parse() + assert_matches_type(SyncSinglePage[AccountHolder], account_holder, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.account_holders.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account_holder = response.parse() + assert_matches_type(SyncSinglePage[AccountHolder], account_holder, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_list_documents(self, client: Lithic) -> None: + account_holder = client.account_holders.list_documents( "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - individual={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYC_ADVANCED", ) - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(AccountHolderListDocumentsResponse, account_holder, path=["response"]) @parametrize - def test_raw_response_resubmit(self, client: Lithic) -> None: - response = client.account_holders.with_raw_response.resubmit( + def test_raw_response_list_documents(self, client: Lithic) -> None: + response = client.account_holders.with_raw_response.list_documents( "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - individual={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYC_ADVANCED", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(AccountHolderListDocumentsResponse, account_holder, path=["response"]) @parametrize - def test_streaming_response_resubmit(self, client: Lithic) -> None: - with client.account_holders.with_streaming_response.resubmit( + def test_streaming_response_list_documents(self, client: Lithic) -> None: + with client.account_holders.with_streaming_response.list_documents( "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - individual={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYC_ADVANCED", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(AccountHolderListDocumentsResponse, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_resubmit(self, client: Lithic) -> None: + def test_path_params_list_documents(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): - client.account_holders.with_raw_response.resubmit( + client.account_holders.with_raw_response.list_documents( "", - individual={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYC_ADVANCED", ) @parametrize def test_method_retrieve_document(self, client: Lithic) -> None: account_holder = client.account_holders.retrieve_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize def test_raw_response_retrieve_document(self, client: Lithic) -> None: response = client.account_holders.with_raw_response.retrieve_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize def test_streaming_response_retrieve_document(self, client: Lithic) -> None: with client.account_holders.with_streaming_response.retrieve_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1001,47 +857,128 @@ def test_streaming_response_retrieve_document(self, client: Lithic) -> None: def test_path_params_retrieve_document(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): client.account_holders.with_raw_response.retrieve_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", account_holder_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `document_token` but received ''"): client.account_holders.with_raw_response.retrieve_document( - "", + document_token="", account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) + @parametrize + def test_method_simulate_enrollment_document_review(self, client: Lithic) -> None: + account_holder = client.account_holders.simulate_enrollment_document_review( + document_upload_token="document_upload_token", + status="UPLOADED", + ) + assert_matches_type(Document, account_holder, path=["response"]) + + @parametrize + def test_method_simulate_enrollment_document_review_with_all_params(self, client: Lithic) -> None: + account_holder = client.account_holders.simulate_enrollment_document_review( + document_upload_token="document_upload_token", + status="UPLOADED", + accepted_entity_status_reasons=["string"], + status_reason="DOCUMENT_MISSING_REQUIRED_DATA", + ) + assert_matches_type(Document, account_holder, path=["response"]) + + @parametrize + def test_raw_response_simulate_enrollment_document_review(self, client: Lithic) -> None: + response = client.account_holders.with_raw_response.simulate_enrollment_document_review( + document_upload_token="document_upload_token", + status="UPLOADED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account_holder = response.parse() + assert_matches_type(Document, account_holder, path=["response"]) + + @parametrize + def test_streaming_response_simulate_enrollment_document_review(self, client: Lithic) -> None: + with client.account_holders.with_streaming_response.simulate_enrollment_document_review( + document_upload_token="document_upload_token", + status="UPLOADED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account_holder = response.parse() + assert_matches_type(Document, account_holder, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_simulate_enrollment_review(self, client: Lithic) -> None: + account_holder = client.account_holders.simulate_enrollment_review() + assert_matches_type(AccountHolderSimulateEnrollmentReviewResponse, account_holder, path=["response"]) + + @parametrize + def test_method_simulate_enrollment_review_with_all_params(self, client: Lithic) -> None: + account_holder = client.account_holders.simulate_enrollment_review( + account_holder_token="1415964d-4400-4d79-9fb3-eee0faaee4e4", + status="ACCEPTED", + status_reasons=["PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE"], + ) + assert_matches_type(AccountHolderSimulateEnrollmentReviewResponse, account_holder, path=["response"]) + + @parametrize + def test_raw_response_simulate_enrollment_review(self, client: Lithic) -> None: + response = client.account_holders.with_raw_response.simulate_enrollment_review() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account_holder = response.parse() + assert_matches_type(AccountHolderSimulateEnrollmentReviewResponse, account_holder, path=["response"]) + + @parametrize + def test_streaming_response_simulate_enrollment_review(self, client: Lithic) -> None: + with client.account_holders.with_streaming_response.simulate_enrollment_review() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account_holder = response.parse() + assert_matches_type(AccountHolderSimulateEnrollmentReviewResponse, account_holder, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_upload_document(self, client: Lithic) -> None: account_holder = client.account_holders.upload_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - document_type="drivers_license", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_type="EIN_LETTER", + entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize def test_raw_response_upload_document(self, client: Lithic) -> None: response = client.account_holders.with_raw_response.upload_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - document_type="drivers_license", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_type="EIN_LETTER", + entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize def test_streaming_response_upload_document(self, client: Lithic) -> None: with client.account_holders.with_streaming_response.upload_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - document_type="drivers_license", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_type="EIN_LETTER", + entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1049,8 +986,9 @@ def test_streaming_response_upload_document(self, client: Lithic) -> None: def test_path_params_upload_document(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): client.account_holders.with_raw_response.upload_document( - "", - document_type="drivers_license", + account_holder_token="", + document_type="EIN_LETTER", + entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -1060,44 +998,6 @@ class TestAsyncAccountHolders: @parametrize async def test_method_create_overload_1(self, async_client: AsyncLithic) -> None: account_holder = await async_client.account_holders.create( - beneficial_owner_entities=[ - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - ], beneficial_owner_individuals=[ { "address": { @@ -1112,35 +1012,7 @@ async def test_method_create_overload_1(self, async_client: AsyncLithic) -> None "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, + } ], business_entity={ "address": { @@ -1152,7 +1024,7 @@ async def test_method_create_overload_1(self, async_client: AsyncLithic) -> None }, "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], + "phone_numbers": ["+15555555555"], }, control_person={ "address": { @@ -1177,199 +1049,59 @@ async def test_method_create_overload_1(self, async_client: AsyncLithic) -> None @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncLithic) -> None: account_holder = await async_client.account_holders.create( - beneficial_owner_entities=[ - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dba_business_name": "string", - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "parent_company": "string", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dba_business_name": "string", - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "parent_company": "string", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dba_business_name": "string", - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "parent_company": "string", - "phone_numbers": ["+12124007676"], - }, - ], beneficial_owner_individuals=[ { "address": { "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - { - "address": { - "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, "dob": "1991-03-08 08:00:00", "email": "tom@middle-earth.com", "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", - }, + "phone_number": "+15555555555", + } ], business_entity={ "address": { "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, - "dba_business_name": "string", "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "parent_company": "string", - "phone_numbers": ["+12124007676"], + "phone_numbers": ["+15555555555"], + "dba_business_name": "dba_business_name", + "parent_company": "parent_company", }, control_person={ "address": { "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, "dob": "1991-03-08 08:00:00", "email": "tom@middle-earth.com", "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, nature_of_business="Software company selling solutions to the restaurant industry", tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYB_BASIC", - external_id="string", - kyb_passed_timestamp="2018-05-29T21:16:05Z", - website_url="www.mybusiness.com", - ) - assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) - - @parametrize - async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) -> None: - response = await async_client.account_holders.with_raw_response.create( - beneficial_owner_entities=[ - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - ], - beneficial_owner_individuals=[ - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, + workflow="KYB_BASIC", + beneficial_owner_entities=[ { "address": { "address1": "123 Old Forest Way", @@ -1377,13 +1109,25 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) - "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, + "government_id": "114-123-1513", + "legal_business_name": "Acme, Inc.", + "phone_numbers": ["+15555555555"], + "dba_business_name": "dba_business_name", + "parent_company": "parent_company", + } + ], + external_id="external_id", + kyb_passed_timestamp="2018-05-29T21:16:05Z", + website_url="www.mybusiness.com", + ) + assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) -> None: + response = await async_client.account_holders.with_raw_response.create( + beneficial_owner_individuals=[ { "address": { "address1": "123 Old Forest Way", @@ -1397,7 +1141,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) - "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - }, + } ], business_entity={ "address": { @@ -1409,7 +1153,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) - }, "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], + "phone_numbers": ["+15555555555"], }, control_person={ "address": { @@ -1438,44 +1182,6 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) - @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncLithic) -> None: async with async_client.account_holders.with_streaming_response.create( - beneficial_owner_entities=[ - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "government_id": "114-123-1513", - "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], - }, - ], beneficial_owner_individuals=[ { "address": { @@ -1490,35 +1196,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncLit "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, - { - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - }, + } ], business_entity={ "address": { @@ -1530,7 +1208,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncLit }, "government_id": "114-123-1513", "legal_business_name": "Acme, Inc.", - "phone_numbers": ["+12124007676"], + "phone_numbers": ["+15555555555"], }, control_person={ "address": { @@ -1574,10 +1252,10 @@ async def test_method_create_overload_2(self, async_client: AsyncLithic) -> None "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, - tos_timestamp="string", - workflow="KYC_ADVANCED", + tos_timestamp="tos_timestamp", + workflow="KYC_BASIC", ) assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) @@ -1587,23 +1265,23 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn individual={ "address": { "address1": "123 Old Forest Way", - "address2": "string", "city": "Omaha", "country": "USA", "postal_code": "68022", "state": "NE", + "address2": "address2", }, "dob": "1991-03-08 08:00:00", "email": "tom@middle-earth.com", "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, - tos_timestamp="string", - workflow="KYC_ADVANCED", - external_id="string", - kyc_passed_timestamp="string", + tos_timestamp="tos_timestamp", + workflow="KYC_BASIC", + external_id="external_id", + kyc_passed_timestamp="kyc_passed_timestamp", ) assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) @@ -1623,10 +1301,10 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncLithic) - "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", + "phone_number": "+15555555555", }, - tos_timestamp="string", - workflow="KYC_ADVANCED", + tos_timestamp="tos_timestamp", + workflow="KYC_BASIC", ) assert response.is_closed is True @@ -1650,146 +1328,359 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncLit "first_name": "Tom", "government_id": "111-23-1412", "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="string", - workflow="KYC_ADVANCED", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - account_holder = await response.parse() - assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_create_overload_3(self, async_client: AsyncLithic) -> None: - account_holder = await async_client.account_holders.create( - email="string", - first_name="string", - kyc_exemption_type="AUTHORIZED_USER", - last_name="string", - phone_number="string", - workflow="KYC_EXEMPT", - ) - assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) - - @parametrize - async def test_method_create_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: - account_holder = await async_client.account_holders.create( - email="string", - first_name="string", - kyc_exemption_type="AUTHORIZED_USER", - last_name="string", - phone_number="string", - workflow="KYC_EXEMPT", - address={ - "address1": "123 Old Forest Way", - "address2": "string", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", + "phone_number": "+15555555555", + }, + tos_timestamp="tos_timestamp", + workflow="KYC_BASIC", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account_holder = await response.parse() + assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_create_overload_3(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.create( + address={ + "address1": "123 Old Forest Way", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + email="email", + first_name="first_name", + kyc_exemption_type="AUTHORIZED_USER", + last_name="last_name", + phone_number="phone_number", + workflow="KYC_EXEMPT", + ) + assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.create( + address={ + "address1": "123 Old Forest Way", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + "address2": "address2", + }, + email="email", + first_name="first_name", + kyc_exemption_type="AUTHORIZED_USER", + last_name="last_name", + phone_number="phone_number", + workflow="KYC_EXEMPT", + business_account_token="business_account_token", + external_id="external_id", + ) + assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_3(self, async_client: AsyncLithic) -> None: + response = await async_client.account_holders.with_raw_response.create( + address={ + "address1": "123 Old Forest Way", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + email="email", + first_name="first_name", + kyc_exemption_type="AUTHORIZED_USER", + last_name="last_name", + phone_number="phone_number", + workflow="KYC_EXEMPT", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account_holder = response.parse() + assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + + @parametrize + async def test_streaming_response_create_overload_3(self, async_client: AsyncLithic) -> None: + async with async_client.account_holders.with_streaming_response.create( + address={ + "address1": "123 Old Forest Way", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + email="email", + first_name="first_name", + kyc_exemption_type="AUTHORIZED_USER", + last_name="last_name", + phone_number="phone_number", + workflow="KYC_EXEMPT", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account_holder = await response.parse() + assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(AccountHolder, account_holder, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.account_holders.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account_holder = response.parse() + assert_matches_type(AccountHolder, account_holder, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.account_holders.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account_holder = await response.parse() + assert_matches_type(AccountHolder, account_holder, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): + await async_client.account_holders.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update_overload_1(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) + + @parametrize + async def test_method_update_with_all_params_overload_1(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + beneficial_owner_entities=[ + { + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dba_business_name": "dba_business_name", + "government_id": "114-123-1513", + "legal_business_name": "Acme, Inc.", + "parent_company": "parent_company", + "phone_numbers": ["+15555555555"], + } + ], + beneficial_owner_individuals=[ + { + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dob": "1991-03-08 08:00:00", + "email": "tom@middle-earth.com", + "first_name": "Tom", + "government_id": "111-23-1412", + "last_name": "Bombadil", + "phone_number": "+15555555555", + } + ], + business_entity={ + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dba_business_name": "dba_business_name", + "government_id": "114-123-1513", + "legal_business_name": "Acme, Inc.", + "parent_company": "parent_company", + "phone_numbers": ["+15555555555"], + }, + control_person={ + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dob": "1991-03-08 08:00:00", + "email": "tom@middle-earth.com", + "first_name": "Tom", + "government_id": "111-23-1412", + "last_name": "Bombadil", + "phone_number": "+15555555555", }, - business_account_token="string", - external_id="string", + external_id="external_id", + nature_of_business="Software company selling solutions to the restaurant industry", + website_url="www.mybusiness.com", ) - assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - async def test_raw_response_create_overload_3(self, async_client: AsyncLithic) -> None: - response = await async_client.account_holders.with_raw_response.create( - email="string", - first_name="string", - kyc_exemption_type="AUTHORIZED_USER", - last_name="string", - phone_number="string", - workflow="KYC_EXEMPT", + async def test_raw_response_update_overload_1(self, async_client: AsyncLithic) -> None: + response = await async_client.account_holders.with_raw_response.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - async def test_streaming_response_create_overload_3(self, async_client: AsyncLithic) -> None: - async with async_client.account_holders.with_streaming_response.create( - email="string", - first_name="string", - kyc_exemption_type="AUTHORIZED_USER", - last_name="string", - phone_number="string", - workflow="KYC_EXEMPT", + async def test_streaming_response_update_overload_1(self, async_client: AsyncLithic) -> None: + async with async_client.account_holders.with_streaming_response.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = await response.parse() - assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - async def test_method_retrieve(self, async_client: AsyncLithic) -> None: - account_holder = await async_client.account_holders.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + async def test_path_params_update_overload_1(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): + await async_client.account_holders.with_raw_response.update( + account_holder_token="", + ) + + @parametrize + async def test_method_update_overload_2(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: - response = await async_client.account_holders.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + async def test_method_update_with_all_params_overload_2(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_id="external_id", + individual={ + "entity_token": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "address": { + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + "dob": "1991-03-08 08:00:00", + "email": "tom@middle-earth.com", + "first_name": "Tom", + "government_id": "111-23-1412", + "last_name": "Bombadil", + "phone_number": "+15555555555", + }, + ) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) + + @parametrize + async def test_raw_response_update_overload_2(self, async_client: AsyncLithic) -> None: + response = await async_client.account_holders.with_raw_response.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: - async with async_client.account_holders.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + async def test_streaming_response_update_overload_2(self, async_client: AsyncLithic) -> None: + async with async_client.account_holders.with_streaming_response.update( + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = await response.parse() - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + async def test_path_params_update_overload_2(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): - await async_client.account_holders.with_raw_response.retrieve( - "", + await async_client.account_holders.with_raw_response.update( + account_holder_token="", ) @parametrize - async def test_method_update(self, async_client: AsyncLithic) -> None: + async def test_method_update_overload_3(self, async_client: AsyncLithic) -> None: account_holder = await async_client.account_holders.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: + async def test_method_update_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: account_holder = await async_client.account_holders.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - business_account_token="string", - email="string", - phone_number="string", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + address={ + "address1": "123 Old Forest Way", + "address2": "address2", + "city": "Omaha", + "country": "USA", + "postal_code": "68022", + "state": "NE", + }, + business_account_token="business_account_token", + email="email", + first_name="first_name", + last_name="last_name", + legal_business_name="legal_business_name", + phone_number="phone_number", ) assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - async def test_raw_response_update(self, async_client: AsyncLithic) -> None: + async def test_raw_response_update_overload_3(self, async_client: AsyncLithic) -> None: response = await async_client.account_holders.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -1798,9 +1689,9 @@ async def test_raw_response_update(self, async_client: AsyncLithic) -> None: assert_matches_type(AccountHolderUpdateResponse, account_holder, path=["response"]) @parametrize - async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: + async def test_streaming_response_update_overload_3(self, async_client: AsyncLithic) -> None: async with async_client.account_holders.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1811,10 +1702,10 @@ async def test_streaming_response_update(self, async_client: AsyncLithic) -> Non assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_update(self, async_client: AsyncLithic) -> None: + async def test_path_params_update_overload_3(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): await async_client.account_holders.with_raw_response.update( - "", + account_holder_token="", ) @parametrize @@ -1825,10 +1716,17 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: account_holder = await async_client.account_holders.list( - ending_before="string", + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + email="email", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", external_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + first_name="first_name", + last_name="last_name", + legal_business_name="legal_business_name", limit=0, - starting_after="string", + phone_number="phone_number", + starting_after="starting_after", ) assert_matches_type(AsyncSinglePage[AccountHolder], account_holder, path=["response"]) @@ -1891,190 +1789,165 @@ async def test_path_params_list_documents(self, async_client: AsyncLithic) -> No ) @parametrize - async def test_method_resubmit(self, async_client: AsyncLithic) -> None: - account_holder = await async_client.account_holders.resubmit( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - individual={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYC_ADVANCED", + async def test_method_retrieve_document(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.retrieve_document( + document_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize - async def test_raw_response_resubmit(self, async_client: AsyncLithic) -> None: - response = await async_client.account_holders.with_raw_response.resubmit( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - individual={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYC_ADVANCED", + async def test_raw_response_retrieve_document(self, async_client: AsyncLithic) -> None: + response = await async_client.account_holders.with_raw_response.retrieve_document( + document_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize - async def test_streaming_response_resubmit(self, async_client: AsyncLithic) -> None: - async with async_client.account_holders.with_streaming_response.resubmit( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - individual={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYC_ADVANCED", + async def test_streaming_response_retrieve_document(self, async_client: AsyncLithic) -> None: + async with async_client.account_holders.with_streaming_response.retrieve_document( + document_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = await response.parse() - assert_matches_type(AccountHolder, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_resubmit(self, async_client: AsyncLithic) -> None: + async def test_path_params_retrieve_document(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): - await async_client.account_holders.with_raw_response.resubmit( - "", - individual={ - "address": { - "address1": "123 Old Forest Way", - "city": "Omaha", - "country": "USA", - "postal_code": "68022", - "state": "NE", - }, - "dob": "1991-03-08 08:00:00", - "email": "tom@middle-earth.com", - "first_name": "Tom", - "government_id": "111-23-1412", - "last_name": "Bombadil", - "phone_number": "+12124007676", - }, - tos_timestamp="2018-05-29T21:16:05Z", - workflow="KYC_ADVANCED", + await async_client.account_holders.with_raw_response.retrieve_document( + document_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_holder_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `document_token` but received ''"): + await async_client.account_holders.with_raw_response.retrieve_document( + document_token="", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize - async def test_method_retrieve_document(self, async_client: AsyncLithic) -> None: - account_holder = await async_client.account_holders.retrieve_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + async def test_method_simulate_enrollment_document_review(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.simulate_enrollment_document_review( + document_upload_token="document_upload_token", + status="UPLOADED", ) - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize - async def test_raw_response_retrieve_document(self, async_client: AsyncLithic) -> None: - response = await async_client.account_holders.with_raw_response.retrieve_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + async def test_method_simulate_enrollment_document_review_with_all_params(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.simulate_enrollment_document_review( + document_upload_token="document_upload_token", + status="UPLOADED", + accepted_entity_status_reasons=["string"], + status_reason="DOCUMENT_MISSING_REQUIRED_DATA", + ) + assert_matches_type(Document, account_holder, path=["response"]) + + @parametrize + async def test_raw_response_simulate_enrollment_document_review(self, async_client: AsyncLithic) -> None: + response = await async_client.account_holders.with_raw_response.simulate_enrollment_document_review( + document_upload_token="document_upload_token", + status="UPLOADED", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize - async def test_streaming_response_retrieve_document(self, async_client: AsyncLithic) -> None: - async with async_client.account_holders.with_streaming_response.retrieve_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + async def test_streaming_response_simulate_enrollment_document_review(self, async_client: AsyncLithic) -> None: + async with async_client.account_holders.with_streaming_response.simulate_enrollment_document_review( + document_upload_token="document_upload_token", + status="UPLOADED", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = await response.parse() - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve_document(self, async_client: AsyncLithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): - await async_client.account_holders.with_raw_response.retrieve_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - account_holder_token="", - ) + async def test_method_simulate_enrollment_review(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.simulate_enrollment_review() + assert_matches_type(AccountHolderSimulateEnrollmentReviewResponse, account_holder, path=["response"]) - with pytest.raises(ValueError, match=r"Expected a non-empty value for `document_token` but received ''"): - await async_client.account_holders.with_raw_response.retrieve_document( - "", - account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) + @parametrize + async def test_method_simulate_enrollment_review_with_all_params(self, async_client: AsyncLithic) -> None: + account_holder = await async_client.account_holders.simulate_enrollment_review( + account_holder_token="1415964d-4400-4d79-9fb3-eee0faaee4e4", + status="ACCEPTED", + status_reasons=["PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE"], + ) + assert_matches_type(AccountHolderSimulateEnrollmentReviewResponse, account_holder, path=["response"]) + + @parametrize + async def test_raw_response_simulate_enrollment_review(self, async_client: AsyncLithic) -> None: + response = await async_client.account_holders.with_raw_response.simulate_enrollment_review() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account_holder = response.parse() + assert_matches_type(AccountHolderSimulateEnrollmentReviewResponse, account_holder, path=["response"]) + + @parametrize + async def test_streaming_response_simulate_enrollment_review(self, async_client: AsyncLithic) -> None: + async with async_client.account_holders.with_streaming_response.simulate_enrollment_review() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account_holder = await response.parse() + assert_matches_type(AccountHolderSimulateEnrollmentReviewResponse, account_holder, path=["response"]) + + assert cast(Any, response.is_closed) is True @parametrize async def test_method_upload_document(self, async_client: AsyncLithic) -> None: account_holder = await async_client.account_holders.upload_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - document_type="drivers_license", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_type="EIN_LETTER", + entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize async def test_raw_response_upload_document(self, async_client: AsyncLithic) -> None: response = await async_client.account_holders.with_raw_response.upload_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - document_type="drivers_license", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_type="EIN_LETTER", + entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = response.parse() - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) @parametrize async def test_streaming_response_upload_document(self, async_client: AsyncLithic) -> None: async with async_client.account_holders.with_streaming_response.upload_document( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - document_type="drivers_license", + account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + document_type="EIN_LETTER", + entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" account_holder = await response.parse() - assert_matches_type(AccountHolderDocument, account_holder, path=["response"]) + assert_matches_type(Document, account_holder, path=["response"]) assert cast(Any, response.is_closed) is True @@ -2082,6 +1955,7 @@ async def test_streaming_response_upload_document(self, async_client: AsyncLithi async def test_path_params_upload_document(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_holder_token` but received ''"): await async_client.account_holders.with_raw_response.upload_document( - "", - document_type="drivers_license", + account_holder_token="", + document_type="EIN_LETTER", + entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) diff --git a/tests/api_resources/test_accounts.py b/tests/api_resources/test_accounts.py index be99c07d..601d8d31 100644 --- a/tests/api_resources/test_accounts.py +++ b/tests/api_resources/test_accounts.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -61,7 +61,7 @@ def test_path_params_retrieve(self, client: Lithic) -> None: @parametrize def test_method_update(self, client: Lithic) -> None: account = client.accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Account, account, path=["response"]) @@ -69,18 +69,18 @@ def test_method_update(self, client: Lithic) -> None: @parametrize def test_method_update_with_all_params(self, client: Lithic) -> None: account = client.accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", daily_spend_limit=1000, lifetime_spend_limit=0, monthly_spend_limit=0, state="ACTIVE", verification_address={ - "address1": "string", - "address2": "string", - "city": "string", - "country": "string", - "postal_code": "string", - "state": "string", + "address1": "address1", + "address2": "address2", + "city": "city", + "country": "country", + "postal_code": "postal_code", + "state": "state", }, ) assert_matches_type(Account, account, path=["response"]) @@ -89,7 +89,7 @@ def test_method_update_with_all_params(self, client: Lithic) -> None: @parametrize def test_raw_response_update(self, client: Lithic) -> None: response = client.accounts.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -101,7 +101,7 @@ def test_raw_response_update(self, client: Lithic) -> None: @parametrize def test_streaming_response_update(self, client: Lithic) -> None: with client.accounts.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -116,7 +116,7 @@ def test_streaming_response_update(self, client: Lithic) -> None: def test_path_params_update(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_token` but received ''"): client.accounts.with_raw_response.update( - "", + account_token="", ) @parametrize @@ -129,9 +129,9 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: account = client.accounts.list( begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(SyncCursorPage[Account], account, path=["response"]) @@ -239,7 +239,7 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_update(self, async_client: AsyncLithic) -> None: account = await async_client.accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Account, account, path=["response"]) @@ -247,18 +247,18 @@ async def test_method_update(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: account = await async_client.accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", daily_spend_limit=1000, lifetime_spend_limit=0, monthly_spend_limit=0, state="ACTIVE", verification_address={ - "address1": "string", - "address2": "string", - "city": "string", - "country": "string", - "postal_code": "string", - "state": "string", + "address1": "address1", + "address2": "address2", + "city": "city", + "country": "country", + "postal_code": "postal_code", + "state": "state", }, ) assert_matches_type(Account, account, path=["response"]) @@ -267,7 +267,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> @parametrize async def test_raw_response_update(self, async_client: AsyncLithic) -> None: response = await async_client.accounts.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -279,7 +279,7 @@ async def test_raw_response_update(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: async with async_client.accounts.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -294,7 +294,7 @@ async def test_streaming_response_update(self, async_client: AsyncLithic) -> Non async def test_path_params_update(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_token` but received ''"): await async_client.accounts.with_raw_response.update( - "", + account_token="", ) @parametrize @@ -307,9 +307,9 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N account = await async_client.accounts.list( begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(AsyncCursorPage[Account], account, path=["response"]) diff --git a/tests/api_resources/test_aggregate_balances.py b/tests/api_resources/test_aggregate_balances.py index 5065d562..ab692037 100644 --- a/tests/api_resources/test_aggregate_balances.py +++ b/tests/api_resources/test_aggregate_balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_auth_rules.py b/tests/api_resources/test_auth_rules.py deleted file mode 100644 index ddbcdcb1..00000000 --- a/tests/api_resources/test_auth_rules.py +++ /dev/null @@ -1,509 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from lithic import Lithic, AsyncLithic -from tests.utils import assert_matches_type -from lithic.types import ( - AuthRule, - AuthRuleRemoveResponse, - AuthRuleRetrieveResponse, -) -from lithic.pagination import SyncCursorPage, AsyncCursorPage - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestAuthRules: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: Lithic) -> None: - auth_rule = client.auth_rules.create() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: Lithic) -> None: - auth_rule = client.auth_rules.create( - account_tokens=["3fa85f64-5717-4562-b3fc-2c963f66afa6"], - allowed_countries=["MEX"], - allowed_mcc=["3000"], - blocked_countries=["CAN", "USA"], - blocked_mcc=["5811", "5812"], - card_tokens=["3fa85f64-5717-4562-b3fc-2c963f66afa6"], - program_level=False, - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Lithic) -> None: - response = client.auth_rules.with_raw_response.create() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Lithic) -> None: - with client.auth_rules.with_streaming_response.create() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_retrieve(self, client: Lithic) -> None: - auth_rule = client.auth_rules.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(AuthRuleRetrieveResponse, auth_rule, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Lithic) -> None: - response = client.auth_rules.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRuleRetrieveResponse, auth_rule, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Lithic) -> None: - with client.auth_rules.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = response.parse() - assert_matches_type(AuthRuleRetrieveResponse, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_retrieve(self, client: Lithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): - client.auth_rules.with_raw_response.retrieve( - "", - ) - - @parametrize - def test_method_update(self, client: Lithic) -> None: - auth_rule = client.auth_rules.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_method_update_with_all_params(self, client: Lithic) -> None: - auth_rule = client.auth_rules.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - allowed_countries=["USA"], - allowed_mcc=["3000", "3001"], - blocked_countries=["string", "string", "string"], - blocked_mcc=["string", "string", "string"], - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_raw_response_update(self, client: Lithic) -> None: - response = client.auth_rules.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_streaming_response_update(self, client: Lithic) -> None: - with client.auth_rules.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_update(self, client: Lithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): - client.auth_rules.with_raw_response.update( - "", - ) - - @parametrize - def test_method_list(self, client: Lithic) -> None: - auth_rule = client.auth_rules.list() - assert_matches_type(SyncCursorPage[AuthRule], auth_rule, path=["response"]) - - @parametrize - def test_method_list_with_all_params(self, client: Lithic) -> None: - auth_rule = client.auth_rules.list( - ending_before="string", - page_size=1, - starting_after="string", - ) - assert_matches_type(SyncCursorPage[AuthRule], auth_rule, path=["response"]) - - @parametrize - def test_raw_response_list(self, client: Lithic) -> None: - response = client.auth_rules.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(SyncCursorPage[AuthRule], auth_rule, path=["response"]) - - @parametrize - def test_streaming_response_list(self, client: Lithic) -> None: - with client.auth_rules.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = response.parse() - assert_matches_type(SyncCursorPage[AuthRule], auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_apply(self, client: Lithic) -> None: - auth_rule = client.auth_rules.apply( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_method_apply_with_all_params(self, client: Lithic) -> None: - auth_rule = client.auth_rules.apply( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - account_tokens=["string", "string", "string"], - card_tokens=["1336a403-2447-4b36-a009-6fbb852ee675", "df942c4e-9130-4ab5-b067-778a2c55b357"], - program_level=True, - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_raw_response_apply(self, client: Lithic) -> None: - response = client.auth_rules.with_raw_response.apply( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - def test_streaming_response_apply(self, client: Lithic) -> None: - with client.auth_rules.with_streaming_response.apply( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_apply(self, client: Lithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): - client.auth_rules.with_raw_response.apply( - "", - ) - - @parametrize - def test_method_remove(self, client: Lithic) -> None: - auth_rule = client.auth_rules.remove() - assert_matches_type(AuthRuleRemoveResponse, auth_rule, path=["response"]) - - @parametrize - def test_method_remove_with_all_params(self, client: Lithic) -> None: - auth_rule = client.auth_rules.remove( - account_tokens=["string", "string", "string"], - card_tokens=["string", "string", "string"], - program_level=False, - ) - assert_matches_type(AuthRuleRemoveResponse, auth_rule, path=["response"]) - - @parametrize - def test_raw_response_remove(self, client: Lithic) -> None: - response = client.auth_rules.with_raw_response.remove() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRuleRemoveResponse, auth_rule, path=["response"]) - - @parametrize - def test_streaming_response_remove(self, client: Lithic) -> None: - with client.auth_rules.with_streaming_response.remove() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = response.parse() - assert_matches_type(AuthRuleRemoveResponse, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncAuthRules: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - async def test_method_create(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.create() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.create( - account_tokens=["3fa85f64-5717-4562-b3fc-2c963f66afa6"], - allowed_countries=["MEX"], - allowed_mcc=["3000"], - blocked_countries=["CAN", "USA"], - blocked_mcc=["5811", "5812"], - card_tokens=["3fa85f64-5717-4562-b3fc-2c963f66afa6"], - program_level=False, - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncLithic) -> None: - response = await async_client.auth_rules.with_raw_response.create() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: - async with async_client.auth_rules.with_streaming_response.create() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = await response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_retrieve(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(AuthRuleRetrieveResponse, auth_rule, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: - response = await async_client.auth_rules.with_raw_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRuleRetrieveResponse, auth_rule, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: - async with async_client.auth_rules.with_streaming_response.retrieve( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = await response.parse() - assert_matches_type(AuthRuleRetrieveResponse, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): - await async_client.auth_rules.with_raw_response.retrieve( - "", - ) - - @parametrize - async def test_method_update(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - allowed_countries=["USA"], - allowed_mcc=["3000", "3001"], - blocked_countries=["string", "string", "string"], - blocked_mcc=["string", "string", "string"], - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_raw_response_update(self, async_client: AsyncLithic) -> None: - response = await async_client.auth_rules.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: - async with async_client.auth_rules.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = await response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_update(self, async_client: AsyncLithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): - await async_client.auth_rules.with_raw_response.update( - "", - ) - - @parametrize - async def test_method_list(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.list() - assert_matches_type(AsyncCursorPage[AuthRule], auth_rule, path=["response"]) - - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.list( - ending_before="string", - page_size=1, - starting_after="string", - ) - assert_matches_type(AsyncCursorPage[AuthRule], auth_rule, path=["response"]) - - @parametrize - async def test_raw_response_list(self, async_client: AsyncLithic) -> None: - response = await async_client.auth_rules.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AsyncCursorPage[AuthRule], auth_rule, path=["response"]) - - @parametrize - async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: - async with async_client.auth_rules.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = await response.parse() - assert_matches_type(AsyncCursorPage[AuthRule], auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_apply(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.apply( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_method_apply_with_all_params(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.apply( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - account_tokens=["string", "string", "string"], - card_tokens=["1336a403-2447-4b36-a009-6fbb852ee675", "df942c4e-9130-4ab5-b067-778a2c55b357"], - program_level=True, - ) - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_raw_response_apply(self, async_client: AsyncLithic) -> None: - response = await async_client.auth_rules.with_raw_response.apply( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - @parametrize - async def test_streaming_response_apply(self, async_client: AsyncLithic) -> None: - async with async_client.auth_rules.with_streaming_response.apply( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = await response.parse() - assert_matches_type(AuthRule, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_apply(self, async_client: AsyncLithic) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `auth_rule_token` but received ''"): - await async_client.auth_rules.with_raw_response.apply( - "", - ) - - @parametrize - async def test_method_remove(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.remove() - assert_matches_type(AuthRuleRemoveResponse, auth_rule, path=["response"]) - - @parametrize - async def test_method_remove_with_all_params(self, async_client: AsyncLithic) -> None: - auth_rule = await async_client.auth_rules.remove( - account_tokens=["string", "string", "string"], - card_tokens=["string", "string", "string"], - program_level=False, - ) - assert_matches_type(AuthRuleRemoveResponse, auth_rule, path=["response"]) - - @parametrize - async def test_raw_response_remove(self, async_client: AsyncLithic) -> None: - response = await async_client.auth_rules.with_raw_response.remove() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - auth_rule = response.parse() - assert_matches_type(AuthRuleRemoveResponse, auth_rule, path=["response"]) - - @parametrize - async def test_streaming_response_remove(self, async_client: AsyncLithic) -> None: - async with async_client.auth_rules.with_streaming_response.remove() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - auth_rule = await response.parse() - assert_matches_type(AuthRuleRemoveResponse, auth_rule, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_auth_stream_enrollment.py b/tests/api_resources/test_auth_stream_enrollment.py index ac3763d5..6405a48a 100644 --- a/tests/api_resources/test_auth_stream_enrollment.py +++ b/tests/api_resources/test_auth_stream_enrollment.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_balances.py b/tests/api_resources/test_balances.py index b8a6b28a..57b39a42 100644 --- a/tests/api_resources/test_balances.py +++ b/tests/api_resources/test_balances.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -29,6 +29,7 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: balance = client.balances.list( account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_type="ISSUING", ) assert_matches_type(SyncSinglePage[Balance], balance, path=["response"]) @@ -67,6 +68,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N balance = await async_client.balances.list( account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_type="ISSUING", ) assert_matches_type(AsyncSinglePage[Balance], balance, path=["response"]) diff --git a/tests/api_resources/test_book_transfers.py b/tests/api_resources/test_book_transfers.py new file mode 100644 index 00000000..5880bdc9 --- /dev/null +++ b/tests/api_resources/test_book_transfers.py @@ -0,0 +1,398 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types import ( + BookTransferResponse, +) +from lithic._utils import parse_datetime +from lithic.pagination import SyncCursorPage, AsyncCursorPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestBookTransfers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + book_transfer = client.book_transfers.create( + amount=1, + category="ADJUSTMENT", + from_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + subtype="subtype", + to_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + type="ATM_WITHDRAWAL", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Lithic) -> None: + book_transfer = client.book_transfers.create( + amount=1, + category="ADJUSTMENT", + from_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + subtype="subtype", + to_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + type="ATM_WITHDRAWAL", + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + memo="memo", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Lithic) -> None: + response = client.book_transfers.with_raw_response.create( + amount=1, + category="ADJUSTMENT", + from_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + subtype="subtype", + to_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + type="ATM_WITHDRAWAL", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Lithic) -> None: + with client.book_transfers.with_streaming_response.create( + amount=1, + category="ADJUSTMENT", + from_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + subtype="subtype", + to_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + type="ATM_WITHDRAWAL", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + book_transfer = client.book_transfers.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.book_transfers.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.book_transfers.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `book_transfer_token` but received ''"): + client.book_transfers.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + book_transfer = client.book_transfers.list() + assert_matches_type(SyncCursorPage[BookTransferResponse], book_transfer, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + book_transfer = client.book_transfers.list( + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + category="BALANCE_OR_FUNDING", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + page_size=1, + result="APPROVED", + starting_after="starting_after", + status="DECLINED", + ) + assert_matches_type(SyncCursorPage[BookTransferResponse], book_transfer, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.book_transfers.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + book_transfer = response.parse() + assert_matches_type(SyncCursorPage[BookTransferResponse], book_transfer, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.book_transfers.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + book_transfer = response.parse() + assert_matches_type(SyncCursorPage[BookTransferResponse], book_transfer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_reverse(self, client: Lithic) -> None: + book_transfer = client.book_transfers.reverse( + book_transfer_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + def test_method_reverse_with_all_params(self, client: Lithic) -> None: + book_transfer = client.book_transfers.reverse( + book_transfer_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + memo="memo", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + def test_raw_response_reverse(self, client: Lithic) -> None: + response = client.book_transfers.with_raw_response.reverse( + book_transfer_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + def test_streaming_response_reverse(self, client: Lithic) -> None: + with client.book_transfers.with_streaming_response.reverse( + book_transfer_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_reverse(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `book_transfer_token` but received ''"): + client.book_transfers.with_raw_response.reverse( + book_transfer_token="", + ) + + +class TestAsyncBookTransfers: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncLithic) -> None: + book_transfer = await async_client.book_transfers.create( + amount=1, + category="ADJUSTMENT", + from_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + subtype="subtype", + to_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + type="ATM_WITHDRAWAL", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: + book_transfer = await async_client.book_transfers.create( + amount=1, + category="ADJUSTMENT", + from_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + subtype="subtype", + to_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + type="ATM_WITHDRAWAL", + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + memo="memo", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncLithic) -> None: + response = await async_client.book_transfers.with_raw_response.create( + amount=1, + category="ADJUSTMENT", + from_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + subtype="subtype", + to_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + type="ATM_WITHDRAWAL", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: + async with async_client.book_transfers.with_streaming_response.create( + amount=1, + category="ADJUSTMENT", + from_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + subtype="subtype", + to_financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + type="ATM_WITHDRAWAL", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + book_transfer = await response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + book_transfer = await async_client.book_transfers.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.book_transfers.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.book_transfers.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + book_transfer = await response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `book_transfer_token` but received ''"): + await async_client.book_transfers.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncLithic) -> None: + book_transfer = await async_client.book_transfers.list() + assert_matches_type(AsyncCursorPage[BookTransferResponse], book_transfer, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: + book_transfer = await async_client.book_transfers.list( + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + category="BALANCE_OR_FUNDING", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + page_size=1, + result="APPROVED", + starting_after="starting_after", + status="DECLINED", + ) + assert_matches_type(AsyncCursorPage[BookTransferResponse], book_transfer, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncLithic) -> None: + response = await async_client.book_transfers.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + book_transfer = response.parse() + assert_matches_type(AsyncCursorPage[BookTransferResponse], book_transfer, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: + async with async_client.book_transfers.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + book_transfer = await response.parse() + assert_matches_type(AsyncCursorPage[BookTransferResponse], book_transfer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_reverse(self, async_client: AsyncLithic) -> None: + book_transfer = await async_client.book_transfers.reverse( + book_transfer_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + async def test_method_reverse_with_all_params(self, async_client: AsyncLithic) -> None: + book_transfer = await async_client.book_transfers.reverse( + book_transfer_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + memo="memo", + ) + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + async def test_raw_response_reverse(self, async_client: AsyncLithic) -> None: + response = await async_client.book_transfers.with_raw_response.reverse( + book_transfer_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + book_transfer = response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + @parametrize + async def test_streaming_response_reverse(self, async_client: AsyncLithic) -> None: + async with async_client.book_transfers.with_streaming_response.reverse( + book_transfer_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + book_transfer = await response.parse() + assert_matches_type(BookTransferResponse, book_transfer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_reverse(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `book_transfer_token` but received ''"): + await async_client.book_transfers.with_raw_response.reverse( + book_transfer_token="", + ) diff --git a/tests/api_resources/test_card_product.py b/tests/api_resources/test_card_product.py deleted file mode 100644 index 12bceacc..00000000 --- a/tests/api_resources/test_card_product.py +++ /dev/null @@ -1,72 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from lithic import Lithic, AsyncLithic -from tests.utils import assert_matches_type -from lithic.types import CardProductCreditDetailResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestCardProduct: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_credit_detail(self, client: Lithic) -> None: - card_product = client.card_product.credit_detail() - assert_matches_type(CardProductCreditDetailResponse, card_product, path=["response"]) - - @parametrize - def test_raw_response_credit_detail(self, client: Lithic) -> None: - response = client.card_product.with_raw_response.credit_detail() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - card_product = response.parse() - assert_matches_type(CardProductCreditDetailResponse, card_product, path=["response"]) - - @parametrize - def test_streaming_response_credit_detail(self, client: Lithic) -> None: - with client.card_product.with_streaming_response.credit_detail() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - card_product = response.parse() - assert_matches_type(CardProductCreditDetailResponse, card_product, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncCardProduct: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - async def test_method_credit_detail(self, async_client: AsyncLithic) -> None: - card_product = await async_client.card_product.credit_detail() - assert_matches_type(CardProductCreditDetailResponse, card_product, path=["response"]) - - @parametrize - async def test_raw_response_credit_detail(self, async_client: AsyncLithic) -> None: - response = await async_client.card_product.with_raw_response.credit_detail() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - card_product = response.parse() - assert_matches_type(CardProductCreditDetailResponse, card_product, path=["response"]) - - @parametrize - async def test_streaming_response_credit_detail(self, async_client: AsyncLithic) -> None: - async with async_client.card_product.with_streaming_response.credit_detail() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - card_product = await response.parse() - assert_matches_type(CardProductCreditDetailResponse, card_product, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_card_programs.py b/tests/api_resources/test_card_programs.py index 72cb866b..362db573 100644 --- a/tests/api_resources/test_card_programs.py +++ b/tests/api_resources/test_card_programs.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -64,9 +64,9 @@ def test_method_list(self, client: Lithic) -> None: @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: card_program = client.card_programs.list( - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(SyncCursorPage[CardProgram], card_program, path=["response"]) @@ -140,9 +140,9 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: card_program = await async_client.card_programs.list( - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(AsyncCursorPage[CardProgram], card_program, path=["response"]) diff --git a/tests/api_resources/test_cards.py b/tests/api_resources/test_cards.py index a01331e0..10f19644 100644 --- a/tests/api_resources/test_cards.py +++ b/tests/api_resources/test_cards.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -26,40 +26,41 @@ class TestCards: @parametrize def test_method_create(self, client: Lithic) -> None: card = client.cards.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", ) assert_matches_type(Card, card, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Lithic) -> None: card = client.cards.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - card_program_token="00000000-0000-0000-1000-000000000000", - carrier={"qr_code_url": "string"}, - digital_card_art_token="00000000-0000-0000-1000-000000000000", + card_program_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + carrier={"qr_code_url": "qr_code_url"}, + digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", exp_month="06", exp_year="2027", memo="New Card", - pin="string", + pin="pin", product_id="1", - replacement_for="00000000-0000-0000-1000-000000000000", + replacement_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + replacement_for="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", - "address2": "Unit 25A", "city": "NEW YORK", "country": "USA", - "email": "johnny@appleseed.com", "first_name": "Michael", "last_name": "Bluth", - "line2_text": "The Bluth Company", - "phone_number": "+12124007676", "postal_code": "10001-1809", "state": "NY", + "address2": "Unit 25A", + "email": "johnny@appleseed.com", + "line2_text": "The Bluth Company", + "phone_number": "+15555555555", }, shipping_method="2_DAY", spend_limit=1000, - spend_limit_duration="TRANSACTION", + spend_limit_duration="ANNUALLY", state="OPEN", ) assert_matches_type(Card, card, path=["response"]) @@ -67,7 +68,7 @@ def test_method_create_with_all_params(self, client: Lithic) -> None: @parametrize def test_raw_response_create(self, client: Lithic) -> None: response = client.cards.with_raw_response.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", ) assert response.is_closed is True @@ -78,7 +79,7 @@ def test_raw_response_create(self, client: Lithic) -> None: @parametrize def test_streaming_response_create(self, client: Lithic) -> None: with client.cards.with_streaming_response.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -129,28 +130,28 @@ def test_path_params_retrieve(self, client: Lithic) -> None: @parametrize def test_method_update(self, client: Lithic) -> None: card = client.cards.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Card, card, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Lithic) -> None: card = client.cards.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - auth_rule_token="string", - digital_card_art_token="00000000-0000-0000-1000-000000000000", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", memo="Updated Name", - pin="string", + pin="pin", + pin_status="OK", spend_limit=100, - spend_limit_duration="FOREVER", - state="OPEN", + spend_limit_duration="ANNUALLY", + state="CLOSED", ) assert_matches_type(Card, card, path=["response"]) @parametrize def test_raw_response_update(self, client: Lithic) -> None: response = client.cards.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -161,7 +162,7 @@ def test_raw_response_update(self, client: Lithic) -> None: @parametrize def test_streaming_response_update(self, client: Lithic) -> None: with client.cards.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -175,7 +176,7 @@ def test_streaming_response_update(self, client: Lithic) -> None: def test_path_params_update(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): client.cards.with_raw_response.update( - "", + card_token="", ) @parametrize @@ -189,9 +190,9 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", state="CLOSED", ) assert_matches_type(SyncCursorPage[Card], card, path=["response"]) @@ -216,19 +217,116 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_convert_physical(self, client: Lithic) -> None: + card = client.cards.convert_physical( + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + }, + ) + assert_matches_type(Card, card, path=["response"]) + + @parametrize + def test_method_convert_physical_with_all_params(self, client: Lithic) -> None: + card = client.cards.convert_physical( + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + "address2": "Unit 5A", + "email": "johnny@appleseed.com", + "line2_text": "The Bluth Company", + "phone_number": "+15555555555", + }, + carrier={"qr_code_url": "https://lithic.com/activate-card/1"}, + product_id="100", + shipping_method="2_DAY", + ) + assert_matches_type(Card, card, path=["response"]) + + @parametrize + def test_raw_response_convert_physical(self, client: Lithic) -> None: + response = client.cards.with_raw_response.convert_physical( + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + card = response.parse() + assert_matches_type(Card, card, path=["response"]) + + @parametrize + def test_streaming_response_convert_physical(self, client: Lithic) -> None: + with client.cards.with_streaming_response.convert_physical( + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + card = response.parse() + assert_matches_type(Card, card, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_convert_physical(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): + client.cards.with_raw_response.convert_physical( + card_token="", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + }, + ) + @parametrize def test_method_embed(self, client: Lithic) -> None: card = client.cards.embed( - embed_request="string", - hmac="string", + embed_request="embed_request", + hmac="hmac", ) assert_matches_type(str, card, path=["response"]) @parametrize def test_raw_response_embed(self, client: Lithic) -> None: response = client.cards.with_raw_response.embed( - embed_request="string", - hmac="string", + embed_request="embed_request", + hmac="hmac", ) assert response.is_closed is True @@ -239,8 +337,8 @@ def test_raw_response_embed(self, client: Lithic) -> None: @parametrize def test_streaming_response_embed(self, client: Lithic) -> None: with client.cards.with_streaming_response.embed( - embed_request="string", - hmac="string", + embed_request="embed_request", + hmac="hmac", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -250,31 +348,21 @@ def test_streaming_response_embed(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True - def test_get_embed_html(self, client: Lithic) -> None: - html = client.cards.get_embed_html(token="foo") - assert "html" in html - - def test_get_embed_url(self, client: Lithic) -> None: - url = client.cards.get_embed_url(token="foo") - params = set( # pyright: ignore[reportUnknownVariableType] - url.params.keys() # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType] - ) - assert "hmac" in params - assert "embed_request" in params - @parametrize def test_method_provision(self, client: Lithic) -> None: card = client.cards.provision( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(CardProvisionResponse, card, path=["response"]) @parametrize def test_method_provision_with_all_params(self, client: Lithic) -> None: card = client.cards.provision( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", certificate="U3RhaW5sZXNzIHJvY2tz", - digital_wallet="GOOGLE_PAY", + client_device_id="client_device_id", + client_wallet_account_id="client_wallet_account_id", + digital_wallet="APPLE_PAY", nonce="U3RhaW5sZXNzIHJvY2tz", nonce_signature="U3RhaW5sZXNzIHJvY2tz", ) @@ -283,7 +371,7 @@ def test_method_provision_with_all_params(self, client: Lithic) -> None: @parametrize def test_raw_response_provision(self, client: Lithic) -> None: response = client.cards.with_raw_response.provision( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -294,7 +382,7 @@ def test_raw_response_provision(self, client: Lithic) -> None: @parametrize def test_streaming_response_provision(self, client: Lithic) -> None: with client.cards.with_streaming_response.provision( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -308,43 +396,43 @@ def test_streaming_response_provision(self, client: Lithic) -> None: def test_path_params_provision(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): client.cards.with_raw_response.provision( - "", + card_token="", ) @parametrize def test_method_reissue(self, client: Lithic) -> None: card = client.cards.reissue( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Card, card, path=["response"]) @parametrize def test_method_reissue_with_all_params(self, client: Lithic) -> None: card = client.cards.reissue( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", carrier={"qr_code_url": "https://lithic.com/activate-card/1"}, product_id="100", shipping_address={ "address1": "5 Broad Street", - "address2": "Unit 5A", "city": "NEW YORK", "country": "USA", - "email": "johnny@appleseed.com", "first_name": "Janet", "last_name": "Yellen", - "line2_text": "The Bluth Company", - "phone_number": "+12124007676", "postal_code": "10001", "state": "NY", + "address2": "Unit 5A", + "email": "johnny@appleseed.com", + "line2_text": "The Bluth Company", + "phone_number": "+15555555555", }, - shipping_method="STANDARD", + shipping_method="2_DAY", ) assert_matches_type(Card, card, path=["response"]) @parametrize def test_raw_response_reissue(self, client: Lithic) -> None: response = client.cards.with_raw_response.reissue( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -355,7 +443,7 @@ def test_raw_response_reissue(self, client: Lithic) -> None: @parametrize def test_streaming_response_reissue(self, client: Lithic) -> None: with client.cards.with_streaming_response.reissue( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -369,13 +457,13 @@ def test_streaming_response_reissue(self, client: Lithic) -> None: def test_path_params_reissue(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): client.cards.with_raw_response.reissue( - "", + card_token="", ) @parametrize def test_method_renew(self, client: Lithic) -> None: card = client.cards.renew( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", "city": "NEW YORK", @@ -391,32 +479,32 @@ def test_method_renew(self, client: Lithic) -> None: @parametrize def test_method_renew_with_all_params(self, client: Lithic) -> None: card = client.cards.renew( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", - "address2": "Unit 5A", "city": "NEW YORK", "country": "USA", - "email": "johnny@appleseed.com", "first_name": "Janet", "last_name": "Yellen", - "line2_text": "The Bluth Company", - "phone_number": "+12124007676", "postal_code": "10001", "state": "NY", + "address2": "Unit 5A", + "email": "johnny@appleseed.com", + "line2_text": "The Bluth Company", + "phone_number": "+15555555555", }, carrier={"qr_code_url": "https://lithic.com/activate-card/1"}, exp_month="06", exp_year="2027", product_id="100", - shipping_method="STANDARD", + shipping_method="2_DAY", ) assert_matches_type(Card, card, path=["response"]) @parametrize def test_raw_response_renew(self, client: Lithic) -> None: response = client.cards.with_raw_response.renew( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", "city": "NEW YORK", @@ -436,7 +524,7 @@ def test_raw_response_renew(self, client: Lithic) -> None: @parametrize def test_streaming_response_renew(self, client: Lithic) -> None: with client.cards.with_streaming_response.renew( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", "city": "NEW YORK", @@ -459,7 +547,7 @@ def test_streaming_response_renew(self, client: Lithic) -> None: def test_path_params_renew(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): client.cards.with_raw_response.renew( - "", + card_token="", shipping_address={ "address1": "5 Broad Street", "city": "NEW YORK", @@ -547,40 +635,41 @@ class TestAsyncCards: @parametrize async def test_method_create(self, async_client: AsyncLithic) -> None: card = await async_client.cards.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", ) assert_matches_type(Card, card, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: card = await async_client.cards.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - card_program_token="00000000-0000-0000-1000-000000000000", - carrier={"qr_code_url": "string"}, - digital_card_art_token="00000000-0000-0000-1000-000000000000", + card_program_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + carrier={"qr_code_url": "qr_code_url"}, + digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", exp_month="06", exp_year="2027", memo="New Card", - pin="string", + pin="pin", product_id="1", - replacement_for="00000000-0000-0000-1000-000000000000", + replacement_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + replacement_for="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", - "address2": "Unit 25A", "city": "NEW YORK", "country": "USA", - "email": "johnny@appleseed.com", "first_name": "Michael", "last_name": "Bluth", - "line2_text": "The Bluth Company", - "phone_number": "+12124007676", "postal_code": "10001-1809", "state": "NY", + "address2": "Unit 25A", + "email": "johnny@appleseed.com", + "line2_text": "The Bluth Company", + "phone_number": "+15555555555", }, shipping_method="2_DAY", spend_limit=1000, - spend_limit_duration="TRANSACTION", + spend_limit_duration="ANNUALLY", state="OPEN", ) assert_matches_type(Card, card, path=["response"]) @@ -588,7 +677,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> @parametrize async def test_raw_response_create(self, async_client: AsyncLithic) -> None: response = await async_client.cards.with_raw_response.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", ) assert response.is_closed is True @@ -599,7 +688,7 @@ async def test_raw_response_create(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: async with async_client.cards.with_streaming_response.create( - type="VIRTUAL", + type="MERCHANT_LOCKED", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -650,28 +739,28 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_update(self, async_client: AsyncLithic) -> None: card = await async_client.cards.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Card, card, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: card = await async_client.cards.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - auth_rule_token="string", - digital_card_art_token="00000000-0000-0000-1000-000000000000", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", memo="Updated Name", - pin="string", + pin="pin", + pin_status="OK", spend_limit=100, - spend_limit_duration="FOREVER", - state="OPEN", + spend_limit_duration="ANNUALLY", + state="CLOSED", ) assert_matches_type(Card, card, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncLithic) -> None: response = await async_client.cards.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -682,7 +771,7 @@ async def test_raw_response_update(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: async with async_client.cards.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -696,7 +785,7 @@ async def test_streaming_response_update(self, async_client: AsyncLithic) -> Non async def test_path_params_update(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): await async_client.cards.with_raw_response.update( - "", + card_token="", ) @parametrize @@ -710,9 +799,9 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", state="CLOSED", ) assert_matches_type(AsyncCursorPage[Card], card, path=["response"]) @@ -737,19 +826,116 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_convert_physical(self, async_client: AsyncLithic) -> None: + card = await async_client.cards.convert_physical( + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + }, + ) + assert_matches_type(Card, card, path=["response"]) + + @parametrize + async def test_method_convert_physical_with_all_params(self, async_client: AsyncLithic) -> None: + card = await async_client.cards.convert_physical( + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + "address2": "Unit 5A", + "email": "johnny@appleseed.com", + "line2_text": "The Bluth Company", + "phone_number": "+15555555555", + }, + carrier={"qr_code_url": "https://lithic.com/activate-card/1"}, + product_id="100", + shipping_method="2_DAY", + ) + assert_matches_type(Card, card, path=["response"]) + + @parametrize + async def test_raw_response_convert_physical(self, async_client: AsyncLithic) -> None: + response = await async_client.cards.with_raw_response.convert_physical( + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + card = response.parse() + assert_matches_type(Card, card, path=["response"]) + + @parametrize + async def test_streaming_response_convert_physical(self, async_client: AsyncLithic) -> None: + async with async_client.cards.with_streaming_response.convert_physical( + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + card = await response.parse() + assert_matches_type(Card, card, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_convert_physical(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): + await async_client.cards.with_raw_response.convert_physical( + card_token="", + shipping_address={ + "address1": "5 Broad Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Janet", + "last_name": "Yellen", + "postal_code": "10001", + "state": "NY", + }, + ) + @parametrize async def test_method_embed(self, async_client: AsyncLithic) -> None: card = await async_client.cards.embed( - embed_request="string", - hmac="string", + embed_request="embed_request", + hmac="hmac", ) assert_matches_type(str, card, path=["response"]) @parametrize async def test_raw_response_embed(self, async_client: AsyncLithic) -> None: response = await async_client.cards.with_raw_response.embed( - embed_request="string", - hmac="string", + embed_request="embed_request", + hmac="hmac", ) assert response.is_closed is True @@ -760,8 +946,8 @@ async def test_raw_response_embed(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_embed(self, async_client: AsyncLithic) -> None: async with async_client.cards.with_streaming_response.embed( - embed_request="string", - hmac="string", + embed_request="embed_request", + hmac="hmac", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -771,31 +957,21 @@ async def test_streaming_response_embed(self, async_client: AsyncLithic) -> None assert cast(Any, response.is_closed) is True - async def test_get_embed_html(self, async_client: AsyncLithic) -> None: - html = await async_client.cards.get_embed_html(token="foo") - assert "html" in html - - def test_get_embed_url(self, async_client: Lithic) -> None: - url = async_client.cards.get_embed_url(token="foo") - params = set( # pyright: ignore[reportUnknownVariableType] - url.params.keys() # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType] - ) - assert "hmac" in params - assert "embed_request" in params - @parametrize async def test_method_provision(self, async_client: AsyncLithic) -> None: card = await async_client.cards.provision( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(CardProvisionResponse, card, path=["response"]) @parametrize async def test_method_provision_with_all_params(self, async_client: AsyncLithic) -> None: card = await async_client.cards.provision( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", certificate="U3RhaW5sZXNzIHJvY2tz", - digital_wallet="GOOGLE_PAY", + client_device_id="client_device_id", + client_wallet_account_id="client_wallet_account_id", + digital_wallet="APPLE_PAY", nonce="U3RhaW5sZXNzIHJvY2tz", nonce_signature="U3RhaW5sZXNzIHJvY2tz", ) @@ -804,7 +980,7 @@ async def test_method_provision_with_all_params(self, async_client: AsyncLithic) @parametrize async def test_raw_response_provision(self, async_client: AsyncLithic) -> None: response = await async_client.cards.with_raw_response.provision( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -815,7 +991,7 @@ async def test_raw_response_provision(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_provision(self, async_client: AsyncLithic) -> None: async with async_client.cards.with_streaming_response.provision( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -829,43 +1005,43 @@ async def test_streaming_response_provision(self, async_client: AsyncLithic) -> async def test_path_params_provision(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): await async_client.cards.with_raw_response.provision( - "", + card_token="", ) @parametrize async def test_method_reissue(self, async_client: AsyncLithic) -> None: card = await async_client.cards.reissue( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Card, card, path=["response"]) @parametrize async def test_method_reissue_with_all_params(self, async_client: AsyncLithic) -> None: card = await async_client.cards.reissue( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", carrier={"qr_code_url": "https://lithic.com/activate-card/1"}, product_id="100", shipping_address={ "address1": "5 Broad Street", - "address2": "Unit 5A", "city": "NEW YORK", "country": "USA", - "email": "johnny@appleseed.com", "first_name": "Janet", "last_name": "Yellen", - "line2_text": "The Bluth Company", - "phone_number": "+12124007676", "postal_code": "10001", "state": "NY", + "address2": "Unit 5A", + "email": "johnny@appleseed.com", + "line2_text": "The Bluth Company", + "phone_number": "+15555555555", }, - shipping_method="STANDARD", + shipping_method="2_DAY", ) assert_matches_type(Card, card, path=["response"]) @parametrize async def test_raw_response_reissue(self, async_client: AsyncLithic) -> None: response = await async_client.cards.with_raw_response.reissue( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -876,7 +1052,7 @@ async def test_raw_response_reissue(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_reissue(self, async_client: AsyncLithic) -> None: async with async_client.cards.with_streaming_response.reissue( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -890,13 +1066,13 @@ async def test_streaming_response_reissue(self, async_client: AsyncLithic) -> No async def test_path_params_reissue(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): await async_client.cards.with_raw_response.reissue( - "", + card_token="", ) @parametrize async def test_method_renew(self, async_client: AsyncLithic) -> None: card = await async_client.cards.renew( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", "city": "NEW YORK", @@ -912,32 +1088,32 @@ async def test_method_renew(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_renew_with_all_params(self, async_client: AsyncLithic) -> None: card = await async_client.cards.renew( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", - "address2": "Unit 5A", "city": "NEW YORK", "country": "USA", - "email": "johnny@appleseed.com", "first_name": "Janet", "last_name": "Yellen", - "line2_text": "The Bluth Company", - "phone_number": "+12124007676", "postal_code": "10001", "state": "NY", + "address2": "Unit 5A", + "email": "johnny@appleseed.com", + "line2_text": "The Bluth Company", + "phone_number": "+15555555555", }, carrier={"qr_code_url": "https://lithic.com/activate-card/1"}, exp_month="06", exp_year="2027", product_id="100", - shipping_method="STANDARD", + shipping_method="2_DAY", ) assert_matches_type(Card, card, path=["response"]) @parametrize async def test_raw_response_renew(self, async_client: AsyncLithic) -> None: response = await async_client.cards.with_raw_response.renew( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", "city": "NEW YORK", @@ -957,7 +1133,7 @@ async def test_raw_response_renew(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_renew(self, async_client: AsyncLithic) -> None: async with async_client.cards.with_streaming_response.renew( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", shipping_address={ "address1": "5 Broad Street", "city": "NEW YORK", @@ -980,7 +1156,7 @@ async def test_streaming_response_renew(self, async_client: AsyncLithic) -> None async def test_path_params_renew(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `card_token` but received ''"): await async_client.cards.with_raw_response.renew( - "", + card_token="", shipping_address={ "address1": "5 Broad Street", "city": "NEW YORK", diff --git a/tests/api_resources/test_top_level.py b/tests/api_resources/test_client.py similarity index 71% rename from tests/api_resources/test_top_level.py rename to tests/api_resources/test_client.py index 6f5c8ba6..339a1212 100644 --- a/tests/api_resources/test_top_level.py +++ b/tests/api_resources/test_client.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -14,13 +14,13 @@ base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -class TestTopLevel: +class TestClient: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_api_status(self, client: Lithic) -> None: - top_level = client.api_status() - assert_matches_type(APIStatus, top_level, path=["response"]) + client_ = client.api_status() + assert_matches_type(APIStatus, client_, path=["response"]) @parametrize def test_raw_response_api_status(self, client: Lithic) -> None: @@ -28,8 +28,8 @@ def test_raw_response_api_status(self, client: Lithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - top_level = response.parse() - assert_matches_type(APIStatus, top_level, path=["response"]) + client_ = response.parse() + assert_matches_type(APIStatus, client_, path=["response"]) @parametrize def test_streaming_response_api_status(self, client: Lithic) -> None: @@ -37,19 +37,19 @@ def test_streaming_response_api_status(self, client: Lithic) -> None: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - top_level = response.parse() - assert_matches_type(APIStatus, top_level, path=["response"]) + client_ = response.parse() + assert_matches_type(APIStatus, client_, path=["response"]) assert cast(Any, response.is_closed) is True -class TestAsyncTopLevel: +class TestAsyncClient: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize async def test_method_api_status(self, async_client: AsyncLithic) -> None: - top_level = await async_client.api_status() - assert_matches_type(APIStatus, top_level, path=["response"]) + client = await async_client.api_status() + assert_matches_type(APIStatus, client, path=["response"]) @parametrize async def test_raw_response_api_status(self, async_client: AsyncLithic) -> None: @@ -57,8 +57,8 @@ async def test_raw_response_api_status(self, async_client: AsyncLithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - top_level = response.parse() - assert_matches_type(APIStatus, top_level, path=["response"]) + client = response.parse() + assert_matches_type(APIStatus, client, path=["response"]) @parametrize async def test_streaming_response_api_status(self, async_client: AsyncLithic) -> None: @@ -66,7 +66,7 @@ async def test_streaming_response_api_status(self, async_client: AsyncLithic) -> assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - top_level = await response.parse() - assert_matches_type(APIStatus, top_level, path=["response"]) + client = await response.parse() + assert_matches_type(APIStatus, client, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_digital_card_art.py b/tests/api_resources/test_digital_card_art.py index 9d002777..b46cd04f 100644 --- a/tests/api_resources/test_digital_card_art.py +++ b/tests/api_resources/test_digital_card_art.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -66,9 +66,9 @@ def test_method_list(self, client: Lithic) -> None: @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: digital_card_art = client.digital_card_art.list( - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(SyncCursorPage[DigitalCardArt], digital_card_art, path=["response"]) @@ -144,9 +144,9 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: digital_card_art = await async_client.digital_card_art.list( - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(AsyncCursorPage[DigitalCardArt], digital_card_art, path=["response"]) diff --git a/tests/api_resources/test_disputes.py b/tests/api_resources/test_disputes.py index c1db18b7..2104bf81 100644 --- a/tests/api_resources/test_disputes.py +++ b/tests/api_resources/test_disputes.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -26,7 +26,7 @@ class TestDisputes: def test_method_create(self, client: Lithic) -> None: dispute = client.disputes.create( amount=10000, - reason="FRAUD_CARD_PRESENT", + reason="ATM_CASH_MISDISPENSE", transaction_token="12345624-aa69-4cbc-a946-30d90181b621", ) assert_matches_type(Dispute, dispute, path=["response"]) @@ -35,10 +35,10 @@ def test_method_create(self, client: Lithic) -> None: def test_method_create_with_all_params(self, client: Lithic) -> None: dispute = client.disputes.create( amount=10000, - reason="FRAUD_CARD_PRESENT", + reason="ATM_CASH_MISDISPENSE", transaction_token="12345624-aa69-4cbc-a946-30d90181b621", customer_filed_date=parse_datetime("2021-06-28T22:53:15Z"), - customer_note="string", + customer_note="customer_note", ) assert_matches_type(Dispute, dispute, path=["response"]) @@ -46,7 +46,7 @@ def test_method_create_with_all_params(self, client: Lithic) -> None: def test_raw_response_create(self, client: Lithic) -> None: response = client.disputes.with_raw_response.create( amount=10000, - reason="FRAUD_CARD_PRESENT", + reason="ATM_CASH_MISDISPENSE", transaction_token="12345624-aa69-4cbc-a946-30d90181b621", ) @@ -59,7 +59,7 @@ def test_raw_response_create(self, client: Lithic) -> None: def test_streaming_response_create(self, client: Lithic) -> None: with client.disputes.with_streaming_response.create( amount=10000, - reason="FRAUD_CARD_PRESENT", + reason="ATM_CASH_MISDISPENSE", transaction_token="12345624-aa69-4cbc-a946-30d90181b621", ) as response: assert not response.is_closed @@ -111,17 +111,17 @@ def test_path_params_retrieve(self, client: Lithic) -> None: @parametrize def test_method_update(self, client: Lithic) -> None: dispute = client.disputes.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Dispute, dispute, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Lithic) -> None: dispute = client.disputes.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", amount=0, customer_filed_date=parse_datetime("2019-12-27T18:11:19.117Z"), - customer_note="string", + customer_note="customer_note", reason="ATM_CASH_MISDISPENSE", ) assert_matches_type(Dispute, dispute, path=["response"]) @@ -129,7 +129,7 @@ def test_method_update_with_all_params(self, client: Lithic) -> None: @parametrize def test_raw_response_update(self, client: Lithic) -> None: response = client.disputes.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -140,7 +140,7 @@ def test_raw_response_update(self, client: Lithic) -> None: @parametrize def test_streaming_response_update(self, client: Lithic) -> None: with client.disputes.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -154,7 +154,7 @@ def test_streaming_response_update(self, client: Lithic) -> None: def test_path_params_update(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): client.disputes.with_raw_response.update( - "", + dispute_token="", ) @parametrize @@ -167,15 +167,11 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: dispute = client.disputes.list( begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", status="ARBITRATION", - transaction_tokens=[ - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ], + transaction_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], ) assert_matches_type(SyncCursorPage[Dispute], dispute, path=["response"]) @@ -240,7 +236,7 @@ def test_path_params_delete(self, client: Lithic) -> None: @parametrize def test_method_delete_evidence(self, client: Lithic) -> None: dispute = client.disputes.delete_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(DisputeEvidence, dispute, path=["response"]) @@ -248,7 +244,7 @@ def test_method_delete_evidence(self, client: Lithic) -> None: @parametrize def test_raw_response_delete_evidence(self, client: Lithic) -> None: response = client.disputes.with_raw_response.delete_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -260,7 +256,7 @@ def test_raw_response_delete_evidence(self, client: Lithic) -> None: @parametrize def test_streaming_response_delete_evidence(self, client: Lithic) -> None: with client.disputes.with_streaming_response.delete_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -275,35 +271,35 @@ def test_streaming_response_delete_evidence(self, client: Lithic) -> None: def test_path_params_delete_evidence(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): client.disputes.with_raw_response.delete_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `evidence_token` but received ''"): client.disputes.with_raw_response.delete_evidence( - "", + evidence_token="", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize def test_method_initiate_evidence_upload(self, client: Lithic) -> None: dispute = client.disputes.initiate_evidence_upload( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(DisputeEvidence, dispute, path=["response"]) @parametrize def test_method_initiate_evidence_upload_with_all_params(self, client: Lithic) -> None: dispute = client.disputes.initiate_evidence_upload( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - filename="string", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + filename="filename", ) assert_matches_type(DisputeEvidence, dispute, path=["response"]) @parametrize def test_raw_response_initiate_evidence_upload(self, client: Lithic) -> None: response = client.disputes.with_raw_response.initiate_evidence_upload( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -314,7 +310,7 @@ def test_raw_response_initiate_evidence_upload(self, client: Lithic) -> None: @parametrize def test_streaming_response_initiate_evidence_upload(self, client: Lithic) -> None: with client.disputes.with_streaming_response.initiate_evidence_upload( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -328,32 +324,32 @@ def test_streaming_response_initiate_evidence_upload(self, client: Lithic) -> No def test_path_params_initiate_evidence_upload(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): client.disputes.with_raw_response.initiate_evidence_upload( - "", + dispute_token="", ) @parametrize def test_method_list_evidences(self, client: Lithic) -> None: dispute = client.disputes.list_evidences( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(SyncCursorPage[DisputeEvidence], dispute, path=["response"]) @parametrize def test_method_list_evidences_with_all_params(self, client: Lithic) -> None: dispute = client.disputes.list_evidences( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(SyncCursorPage[DisputeEvidence], dispute, path=["response"]) @parametrize def test_raw_response_list_evidences(self, client: Lithic) -> None: response = client.disputes.with_raw_response.list_evidences( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -364,7 +360,7 @@ def test_raw_response_list_evidences(self, client: Lithic) -> None: @parametrize def test_streaming_response_list_evidences(self, client: Lithic) -> None: with client.disputes.with_streaming_response.list_evidences( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -378,13 +374,13 @@ def test_streaming_response_list_evidences(self, client: Lithic) -> None: def test_path_params_list_evidences(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): client.disputes.with_raw_response.list_evidences( - "", + dispute_token="", ) @parametrize def test_method_retrieve_evidence(self, client: Lithic) -> None: dispute = client.disputes.retrieve_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(DisputeEvidence, dispute, path=["response"]) @@ -392,7 +388,7 @@ def test_method_retrieve_evidence(self, client: Lithic) -> None: @parametrize def test_raw_response_retrieve_evidence(self, client: Lithic) -> None: response = client.disputes.with_raw_response.retrieve_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -404,7 +400,7 @@ def test_raw_response_retrieve_evidence(self, client: Lithic) -> None: @parametrize def test_streaming_response_retrieve_evidence(self, client: Lithic) -> None: with client.disputes.with_streaming_response.retrieve_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -419,13 +415,13 @@ def test_streaming_response_retrieve_evidence(self, client: Lithic) -> None: def test_path_params_retrieve_evidence(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): client.disputes.with_raw_response.retrieve_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `evidence_token` but received ''"): client.disputes.with_raw_response.retrieve_evidence( - "", + evidence_token="", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -437,7 +433,7 @@ class TestAsyncDisputes: async def test_method_create(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.create( amount=10000, - reason="FRAUD_CARD_PRESENT", + reason="ATM_CASH_MISDISPENSE", transaction_token="12345624-aa69-4cbc-a946-30d90181b621", ) assert_matches_type(Dispute, dispute, path=["response"]) @@ -446,10 +442,10 @@ async def test_method_create(self, async_client: AsyncLithic) -> None: async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.create( amount=10000, - reason="FRAUD_CARD_PRESENT", + reason="ATM_CASH_MISDISPENSE", transaction_token="12345624-aa69-4cbc-a946-30d90181b621", customer_filed_date=parse_datetime("2021-06-28T22:53:15Z"), - customer_note="string", + customer_note="customer_note", ) assert_matches_type(Dispute, dispute, path=["response"]) @@ -457,7 +453,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> async def test_raw_response_create(self, async_client: AsyncLithic) -> None: response = await async_client.disputes.with_raw_response.create( amount=10000, - reason="FRAUD_CARD_PRESENT", + reason="ATM_CASH_MISDISPENSE", transaction_token="12345624-aa69-4cbc-a946-30d90181b621", ) @@ -470,7 +466,7 @@ async def test_raw_response_create(self, async_client: AsyncLithic) -> None: async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: async with async_client.disputes.with_streaming_response.create( amount=10000, - reason="FRAUD_CARD_PRESENT", + reason="ATM_CASH_MISDISPENSE", transaction_token="12345624-aa69-4cbc-a946-30d90181b621", ) as response: assert not response.is_closed @@ -522,17 +518,17 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_update(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(Dispute, dispute, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", amount=0, customer_filed_date=parse_datetime("2019-12-27T18:11:19.117Z"), - customer_note="string", + customer_note="customer_note", reason="ATM_CASH_MISDISPENSE", ) assert_matches_type(Dispute, dispute, path=["response"]) @@ -540,7 +536,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> @parametrize async def test_raw_response_update(self, async_client: AsyncLithic) -> None: response = await async_client.disputes.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -551,7 +547,7 @@ async def test_raw_response_update(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: async with async_client.disputes.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -565,7 +561,7 @@ async def test_streaming_response_update(self, async_client: AsyncLithic) -> Non async def test_path_params_update(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): await async_client.disputes.with_raw_response.update( - "", + dispute_token="", ) @parametrize @@ -578,15 +574,11 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N dispute = await async_client.disputes.list( begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", status="ARBITRATION", - transaction_tokens=[ - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - ], + transaction_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], ) assert_matches_type(AsyncCursorPage[Dispute], dispute, path=["response"]) @@ -651,7 +643,7 @@ async def test_path_params_delete(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_delete_evidence(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.delete_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(DisputeEvidence, dispute, path=["response"]) @@ -659,7 +651,7 @@ async def test_method_delete_evidence(self, async_client: AsyncLithic) -> None: @parametrize async def test_raw_response_delete_evidence(self, async_client: AsyncLithic) -> None: response = await async_client.disputes.with_raw_response.delete_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -671,7 +663,7 @@ async def test_raw_response_delete_evidence(self, async_client: AsyncLithic) -> @parametrize async def test_streaming_response_delete_evidence(self, async_client: AsyncLithic) -> None: async with async_client.disputes.with_streaming_response.delete_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -686,35 +678,35 @@ async def test_streaming_response_delete_evidence(self, async_client: AsyncLithi async def test_path_params_delete_evidence(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): await async_client.disputes.with_raw_response.delete_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `evidence_token` but received ''"): await async_client.disputes.with_raw_response.delete_evidence( - "", + evidence_token="", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @parametrize async def test_method_initiate_evidence_upload(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.initiate_evidence_upload( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(DisputeEvidence, dispute, path=["response"]) @parametrize async def test_method_initiate_evidence_upload_with_all_params(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.initiate_evidence_upload( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - filename="string", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + filename="filename", ) assert_matches_type(DisputeEvidence, dispute, path=["response"]) @parametrize async def test_raw_response_initiate_evidence_upload(self, async_client: AsyncLithic) -> None: response = await async_client.disputes.with_raw_response.initiate_evidence_upload( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -725,7 +717,7 @@ async def test_raw_response_initiate_evidence_upload(self, async_client: AsyncLi @parametrize async def test_streaming_response_initiate_evidence_upload(self, async_client: AsyncLithic) -> None: async with async_client.disputes.with_streaming_response.initiate_evidence_upload( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -739,32 +731,32 @@ async def test_streaming_response_initiate_evidence_upload(self, async_client: A async def test_path_params_initiate_evidence_upload(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): await async_client.disputes.with_raw_response.initiate_evidence_upload( - "", + dispute_token="", ) @parametrize async def test_method_list_evidences(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.list_evidences( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(AsyncCursorPage[DisputeEvidence], dispute, path=["response"]) @parametrize async def test_method_list_evidences_with_all_params(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.list_evidences( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", ) assert_matches_type(AsyncCursorPage[DisputeEvidence], dispute, path=["response"]) @parametrize async def test_raw_response_list_evidences(self, async_client: AsyncLithic) -> None: response = await async_client.disputes.with_raw_response.list_evidences( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -775,7 +767,7 @@ async def test_raw_response_list_evidences(self, async_client: AsyncLithic) -> N @parametrize async def test_streaming_response_list_evidences(self, async_client: AsyncLithic) -> None: async with async_client.disputes.with_streaming_response.list_evidences( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -789,13 +781,13 @@ async def test_streaming_response_list_evidences(self, async_client: AsyncLithic async def test_path_params_list_evidences(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): await async_client.disputes.with_raw_response.list_evidences( - "", + dispute_token="", ) @parametrize async def test_method_retrieve_evidence(self, async_client: AsyncLithic) -> None: dispute = await async_client.disputes.retrieve_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(DisputeEvidence, dispute, path=["response"]) @@ -803,7 +795,7 @@ async def test_method_retrieve_evidence(self, async_client: AsyncLithic) -> None @parametrize async def test_raw_response_retrieve_evidence(self, async_client: AsyncLithic) -> None: response = await async_client.disputes.with_raw_response.retrieve_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -815,7 +807,7 @@ async def test_raw_response_retrieve_evidence(self, async_client: AsyncLithic) - @parametrize async def test_streaming_response_retrieve_evidence(self, async_client: AsyncLithic) -> None: async with async_client.disputes.with_streaming_response.retrieve_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed @@ -830,12 +822,12 @@ async def test_streaming_response_retrieve_evidence(self, async_client: AsyncLit async def test_path_params_retrieve_evidence(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dispute_token` but received ''"): await async_client.disputes.with_raw_response.retrieve_evidence( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + evidence_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", dispute_token="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `evidence_token` but received ''"): await async_client.disputes.with_raw_response.retrieve_evidence( - "", + evidence_token="", dispute_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) diff --git a/tests/api_resources/test_events.py b/tests/api_resources/test_events.py index 893dc631..d4d0e703 100644 --- a/tests/api_resources/test_events.py +++ b/tests/api_resources/test_events.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -22,14 +22,14 @@ class TestEvents: @parametrize def test_method_retrieve(self, client: Lithic) -> None: event = client.events.retrieve( - "string", + "event_token", ) assert_matches_type(Event, event, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: response = client.events.with_raw_response.retrieve( - "string", + "event_token", ) assert response.is_closed is True @@ -40,7 +40,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: with client.events.with_streaming_response.retrieve( - "string", + "event_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -67,10 +67,10 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: event = client.events.list( begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", - event_types=["account_holder.created", "account_holder.updated", "account_holder.verification"], + ending_before="ending_before", + event_types=["account_holder.created"], page_size=1, - starting_after="string", + starting_after="starting_after", with_content=True, ) assert_matches_type(SyncCursorPage[Event], event, path=["response"]) @@ -98,19 +98,19 @@ def test_streaming_response_list(self, client: Lithic) -> None: @parametrize def test_method_list_attempts(self, client: Lithic) -> None: event = client.events.list_attempts( - "string", + event_token="event_token", ) assert_matches_type(SyncCursorPage[MessageAttempt], event, path=["response"]) @parametrize def test_method_list_attempts_with_all_params(self, client: Lithic) -> None: event = client.events.list_attempts( - "string", + event_token="event_token", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", status="FAILED", ) assert_matches_type(SyncCursorPage[MessageAttempt], event, path=["response"]) @@ -118,7 +118,7 @@ def test_method_list_attempts_with_all_params(self, client: Lithic) -> None: @parametrize def test_raw_response_list_attempts(self, client: Lithic) -> None: response = client.events.with_raw_response.list_attempts( - "string", + event_token="event_token", ) assert response.is_closed is True @@ -129,7 +129,7 @@ def test_raw_response_list_attempts(self, client: Lithic) -> None: @parametrize def test_streaming_response_list_attempts(self, client: Lithic) -> None: with client.events.with_streaming_response.list_attempts( - "string", + event_token="event_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -143,16 +143,9 @@ def test_streaming_response_list_attempts(self, client: Lithic) -> None: def test_path_params_list_attempts(self, client: Lithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `event_token` but received ''"): client.events.with_raw_response.list_attempts( - "", + event_token="", ) - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") - def test_method_resend(self, client: Lithic) -> None: - client.events.resend( - "string", - event_subscription_token="string", - ) - class TestAsyncEvents: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -160,14 +153,14 @@ class TestAsyncEvents: @parametrize async def test_method_retrieve(self, async_client: AsyncLithic) -> None: event = await async_client.events.retrieve( - "string", + "event_token", ) assert_matches_type(Event, event, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: response = await async_client.events.with_raw_response.retrieve( - "string", + "event_token", ) assert response.is_closed is True @@ -178,7 +171,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: async with async_client.events.with_streaming_response.retrieve( - "string", + "event_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -205,10 +198,10 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N event = await async_client.events.list( begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", - event_types=["account_holder.created", "account_holder.updated", "account_holder.verification"], + ending_before="ending_before", + event_types=["account_holder.created"], page_size=1, - starting_after="string", + starting_after="starting_after", with_content=True, ) assert_matches_type(AsyncCursorPage[Event], event, path=["response"]) @@ -236,19 +229,19 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list_attempts(self, async_client: AsyncLithic) -> None: event = await async_client.events.list_attempts( - "string", + event_token="event_token", ) assert_matches_type(AsyncCursorPage[MessageAttempt], event, path=["response"]) @parametrize async def test_method_list_attempts_with_all_params(self, async_client: AsyncLithic) -> None: event = await async_client.events.list_attempts( - "string", + event_token="event_token", begin=parse_datetime("2019-12-27T18:11:19.117Z"), end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", status="FAILED", ) assert_matches_type(AsyncCursorPage[MessageAttempt], event, path=["response"]) @@ -256,7 +249,7 @@ async def test_method_list_attempts_with_all_params(self, async_client: AsyncLit @parametrize async def test_raw_response_list_attempts(self, async_client: AsyncLithic) -> None: response = await async_client.events.with_raw_response.list_attempts( - "string", + event_token="event_token", ) assert response.is_closed is True @@ -267,7 +260,7 @@ async def test_raw_response_list_attempts(self, async_client: AsyncLithic) -> No @parametrize async def test_streaming_response_list_attempts(self, async_client: AsyncLithic) -> None: async with async_client.events.with_streaming_response.list_attempts( - "string", + event_token="event_token", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -281,12 +274,5 @@ async def test_streaming_response_list_attempts(self, async_client: AsyncLithic) async def test_path_params_list_attempts(self, async_client: AsyncLithic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `event_token` but received ''"): await async_client.events.with_raw_response.list_attempts( - "", + event_token="", ) - - @pytest.mark.skip(reason="Prism Mock server doesnt want Accept header, but server requires it.") - async def test_method_resend(self, async_client: AsyncLithic) -> None: - await async_client.events.resend( - "string", - event_subscription_token="string", - ) diff --git a/tests/api_resources/test_external_bank_accounts.py b/tests/api_resources/test_external_bank_accounts.py index 45f0d235..bdb409d2 100644 --- a/tests/api_resources/test_external_bank_accounts.py +++ b/tests/api_resources/test_external_bank_accounts.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -14,6 +14,7 @@ ExternalBankAccountCreateResponse, ExternalBankAccountUpdateResponse, ExternalBankAccountRetrieveResponse, + ExternalBankAccountRetryPrenoteResponse, ExternalBankAccountRetryMicroDepositsResponse, ) from lithic._utils import parse_date @@ -28,11 +29,12 @@ class TestExternalBankAccounts: @parametrize def test_method_create_overload_1(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.create( - account_number="string", + account_number="12345678901234567", country="USD", currency="USD", - owner="x", - owner_type="BUSINESS", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + owner="owner", + owner_type="INDIVIDUAL", routing_number="123456789", type="CHECKING", verification_method="MANUAL", @@ -42,28 +44,29 @@ def test_method_create_overload_1(self, client: Lithic) -> None: @parametrize def test_method_create_with_all_params_overload_1(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.create( - account_number="string", + account_number="12345678901234567", country="USD", currency="USD", - owner="x", - owner_type="BUSINESS", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + owner="owner", + owner_type="INDIVIDUAL", routing_number="123456789", type="CHECKING", verification_method="MANUAL", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", address={ "address1": "x", - "address2": "x", "city": "x", "country": "USD", "postal_code": "11201", "state": "xx", + "address2": "x", }, - company_id="x", + company_id="sq", dob=parse_date("2019-12-27"), - doing_business_as="string", - name="x", - user_defined_id="string", + doing_business_as="x", + name="name", + user_defined_id="x", verification_enforcement=True, ) assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) @@ -71,11 +74,12 @@ def test_method_create_with_all_params_overload_1(self, client: Lithic) -> None: @parametrize def test_raw_response_create_overload_1(self, client: Lithic) -> None: response = client.external_bank_accounts.with_raw_response.create( - account_number="string", + account_number="12345678901234567", country="USD", currency="USD", - owner="x", - owner_type="BUSINESS", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + owner="owner", + owner_type="INDIVIDUAL", routing_number="123456789", type="CHECKING", verification_method="MANUAL", @@ -89,11 +93,12 @@ def test_raw_response_create_overload_1(self, client: Lithic) -> None: @parametrize def test_streaming_response_create_overload_1(self, client: Lithic) -> None: with client.external_bank_accounts.with_streaming_response.create( - account_number="string", + account_number="12345678901234567", country="USD", currency="USD", - owner="x", - owner_type="BUSINESS", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + owner="owner", + owner_type="INDIVIDUAL", routing_number="123456789", type="CHECKING", verification_method="MANUAL", @@ -109,8 +114,8 @@ def test_streaming_response_create_overload_1(self, client: Lithic) -> None: @parametrize def test_method_create_overload_2(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.create( - owner="x", - owner_type="BUSINESS", + owner="owner", + owner_type="INDIVIDUAL", processor_token="x", verification_method="MANUAL", ) @@ -119,23 +124,23 @@ def test_method_create_overload_2(self, client: Lithic) -> None: @parametrize def test_method_create_with_all_params_overload_2(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.create( - owner="x", - owner_type="BUSINESS", + owner="owner", + owner_type="INDIVIDUAL", processor_token="x", verification_method="MANUAL", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - company_id="x", + company_id="sq", dob=parse_date("2019-12-27"), - doing_business_as="string", - user_defined_id="string", + doing_business_as="x", + user_defined_id="x", ) assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) @parametrize def test_raw_response_create_overload_2(self, client: Lithic) -> None: response = client.external_bank_accounts.with_raw_response.create( - owner="x", - owner_type="BUSINESS", + owner="owner", + owner_type="INDIVIDUAL", processor_token="x", verification_method="MANUAL", ) @@ -148,8 +153,8 @@ def test_raw_response_create_overload_2(self, client: Lithic) -> None: @parametrize def test_streaming_response_create_overload_2(self, client: Lithic) -> None: with client.external_bank_accounts.with_streaming_response.create( - owner="x", - owner_type="BUSINESS", + owner="owner", + owner_type="INDIVIDUAL", processor_token="x", verification_method="MANUAL", ) as response: @@ -161,6 +166,86 @@ def test_streaming_response_create_overload_2(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_create_overload_3(self, client: Lithic) -> None: + external_bank_account = client.external_bank_accounts.create( + account_number="12345678901234567", + country="USD", + currency="USD", + owner="owner", + owner_type="INDIVIDUAL", + routing_number="123456789", + type="CHECKING", + verification_method="EXTERNALLY_VERIFIED", + ) + assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_3(self, client: Lithic) -> None: + external_bank_account = client.external_bank_accounts.create( + account_number="12345678901234567", + country="USD", + currency="USD", + owner="owner", + owner_type="INDIVIDUAL", + routing_number="123456789", + type="CHECKING", + verification_method="EXTERNALLY_VERIFIED", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + address={ + "address1": "x", + "city": "x", + "country": "USD", + "postal_code": "11201", + "state": "xx", + "address2": "x", + }, + company_id="sq", + dob=parse_date("2019-12-27"), + doing_business_as="x", + name="name", + user_defined_id="x", + ) + assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) + + @parametrize + def test_raw_response_create_overload_3(self, client: Lithic) -> None: + response = client.external_bank_accounts.with_raw_response.create( + account_number="12345678901234567", + country="USD", + currency="USD", + owner="owner", + owner_type="INDIVIDUAL", + routing_number="123456789", + type="CHECKING", + verification_method="EXTERNALLY_VERIFIED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_bank_account = response.parse() + assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) + + @parametrize + def test_streaming_response_create_overload_3(self, client: Lithic) -> None: + with client.external_bank_accounts.with_streaming_response.create( + account_number="12345678901234567", + country="USD", + currency="USD", + owner="owner", + owner_type="INDIVIDUAL", + routing_number="123456789", + type="CHECKING", + verification_method="EXTERNALLY_VERIFIED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_bank_account = response.parse() + assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.retrieve( @@ -204,36 +289,37 @@ def test_path_params_retrieve(self, client: Lithic) -> None: @parametrize def test_method_update(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(ExternalBankAccountUpdateResponse, external_bank_account, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", address={ "address1": "x", - "address2": "x", "city": "x", "country": "USD", "postal_code": "11201", "state": "xx", + "address2": "x", }, - company_id="x", + company_id="sq", dob=parse_date("2019-12-27"), - doing_business_as="string", - name="x", - owner="x", - owner_type="BUSINESS", - user_defined_id="string", + doing_business_as="x", + name="name", + owner="owner", + owner_type="INDIVIDUAL", + type="CHECKING", + user_defined_id="x", ) assert_matches_type(ExternalBankAccountUpdateResponse, external_bank_account, path=["response"]) @parametrize def test_raw_response_update(self, client: Lithic) -> None: response = client.external_bank_accounts.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -244,7 +330,7 @@ def test_raw_response_update(self, client: Lithic) -> None: @parametrize def test_streaming_response_update(self, client: Lithic) -> None: with client.external_bank_accounts.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -260,7 +346,7 @@ def test_path_params_update(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `external_bank_account_token` but received ''" ): client.external_bank_accounts.with_raw_response.update( - "", + external_bank_account_token="", ) @parametrize @@ -272,14 +358,14 @@ def test_method_list(self, client: Lithic) -> None: def test_method_list_with_all_params(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.list( account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - account_types=["CHECKING", "SAVINGS"], - countries=["string", "string", "string"], - ending_before="string", - owner_types=["BUSINESS", "INDIVIDUAL"], + account_types=["CHECKING"], + countries=["string"], + ending_before="ending_before", + owner_types=["INDIVIDUAL"], page_size=1, - starting_after="string", - states=["CLOSED", "ENABLED", "PAUSED"], - verification_states=["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"], + starting_after="starting_after", + states=["ENABLED"], + verification_states=["PENDING"], ) assert_matches_type(SyncCursorPage[ExternalBankAccountListResponse], external_bank_account, path=["response"]) @@ -308,14 +394,22 @@ def test_streaming_response_list(self, client: Lithic) -> None: @parametrize def test_method_retry_micro_deposits(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.retry_micro_deposits( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ExternalBankAccountRetryMicroDepositsResponse, external_bank_account, path=["response"]) + + @parametrize + def test_method_retry_micro_deposits_with_all_params(self, client: Lithic) -> None: + external_bank_account = client.external_bank_accounts.retry_micro_deposits( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(ExternalBankAccountRetryMicroDepositsResponse, external_bank_account, path=["response"]) @parametrize def test_raw_response_retry_micro_deposits(self, client: Lithic) -> None: response = client.external_bank_accounts.with_raw_response.retry_micro_deposits( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -326,7 +420,7 @@ def test_raw_response_retry_micro_deposits(self, client: Lithic) -> None: @parametrize def test_streaming_response_retry_micro_deposits(self, client: Lithic) -> None: with client.external_bank_accounts.with_streaming_response.retry_micro_deposits( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -342,7 +436,55 @@ def test_path_params_retry_micro_deposits(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `external_bank_account_token` but received ''" ): client.external_bank_accounts.with_raw_response.retry_micro_deposits( - "", + external_bank_account_token="", + ) + + @parametrize + def test_method_retry_prenote(self, client: Lithic) -> None: + external_bank_account = client.external_bank_accounts.retry_prenote( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ExternalBankAccountRetryPrenoteResponse, external_bank_account, path=["response"]) + + @parametrize + def test_method_retry_prenote_with_all_params(self, client: Lithic) -> None: + external_bank_account = client.external_bank_accounts.retry_prenote( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ExternalBankAccountRetryPrenoteResponse, external_bank_account, path=["response"]) + + @parametrize + def test_raw_response_retry_prenote(self, client: Lithic) -> None: + response = client.external_bank_accounts.with_raw_response.retry_prenote( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_bank_account = response.parse() + assert_matches_type(ExternalBankAccountRetryPrenoteResponse, external_bank_account, path=["response"]) + + @parametrize + def test_streaming_response_retry_prenote(self, client: Lithic) -> None: + with client.external_bank_accounts.with_streaming_response.retry_prenote( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_bank_account = response.parse() + assert_matches_type(ExternalBankAccountRetryPrenoteResponse, external_bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retry_prenote(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_bank_account_token` but received ''" + ): + client.external_bank_accounts.with_raw_response.retry_prenote( + external_bank_account_token="", ) @@ -352,11 +494,12 @@ class TestAsyncExternalBankAccounts: @parametrize async def test_method_create_overload_1(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.create( - account_number="string", + account_number="12345678901234567", country="USD", currency="USD", - owner="x", - owner_type="BUSINESS", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + owner="owner", + owner_type="INDIVIDUAL", routing_number="123456789", type="CHECKING", verification_method="MANUAL", @@ -366,28 +509,29 @@ async def test_method_create_overload_1(self, async_client: AsyncLithic) -> None @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.create( - account_number="string", + account_number="12345678901234567", country="USD", currency="USD", - owner="x", - owner_type="BUSINESS", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + owner="owner", + owner_type="INDIVIDUAL", routing_number="123456789", type="CHECKING", verification_method="MANUAL", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", address={ "address1": "x", - "address2": "x", "city": "x", "country": "USD", "postal_code": "11201", "state": "xx", + "address2": "x", }, - company_id="x", + company_id="sq", dob=parse_date("2019-12-27"), - doing_business_as="string", - name="x", - user_defined_id="string", + doing_business_as="x", + name="name", + user_defined_id="x", verification_enforcement=True, ) assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) @@ -395,11 +539,12 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) -> None: response = await async_client.external_bank_accounts.with_raw_response.create( - account_number="string", + account_number="12345678901234567", country="USD", currency="USD", - owner="x", - owner_type="BUSINESS", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + owner="owner", + owner_type="INDIVIDUAL", routing_number="123456789", type="CHECKING", verification_method="MANUAL", @@ -413,11 +558,12 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) - @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncLithic) -> None: async with async_client.external_bank_accounts.with_streaming_response.create( - account_number="string", + account_number="12345678901234567", country="USD", currency="USD", - owner="x", - owner_type="BUSINESS", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + owner="owner", + owner_type="INDIVIDUAL", routing_number="123456789", type="CHECKING", verification_method="MANUAL", @@ -433,8 +579,8 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncLit @parametrize async def test_method_create_overload_2(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.create( - owner="x", - owner_type="BUSINESS", + owner="owner", + owner_type="INDIVIDUAL", processor_token="x", verification_method="MANUAL", ) @@ -443,23 +589,23 @@ async def test_method_create_overload_2(self, async_client: AsyncLithic) -> None @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.create( - owner="x", - owner_type="BUSINESS", + owner="owner", + owner_type="INDIVIDUAL", processor_token="x", verification_method="MANUAL", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - company_id="x", + company_id="sq", dob=parse_date("2019-12-27"), - doing_business_as="string", - user_defined_id="string", + doing_business_as="x", + user_defined_id="x", ) assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncLithic) -> None: response = await async_client.external_bank_accounts.with_raw_response.create( - owner="x", - owner_type="BUSINESS", + owner="owner", + owner_type="INDIVIDUAL", processor_token="x", verification_method="MANUAL", ) @@ -472,8 +618,8 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncLithic) - @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncLithic) -> None: async with async_client.external_bank_accounts.with_streaming_response.create( - owner="x", - owner_type="BUSINESS", + owner="owner", + owner_type="INDIVIDUAL", processor_token="x", verification_method="MANUAL", ) as response: @@ -485,6 +631,86 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncLit assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_create_overload_3(self, async_client: AsyncLithic) -> None: + external_bank_account = await async_client.external_bank_accounts.create( + account_number="12345678901234567", + country="USD", + currency="USD", + owner="owner", + owner_type="INDIVIDUAL", + routing_number="123456789", + type="CHECKING", + verification_method="EXTERNALLY_VERIFIED", + ) + assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: + external_bank_account = await async_client.external_bank_accounts.create( + account_number="12345678901234567", + country="USD", + currency="USD", + owner="owner", + owner_type="INDIVIDUAL", + routing_number="123456789", + type="CHECKING", + verification_method="EXTERNALLY_VERIFIED", + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + address={ + "address1": "x", + "city": "x", + "country": "USD", + "postal_code": "11201", + "state": "xx", + "address2": "x", + }, + company_id="sq", + dob=parse_date("2019-12-27"), + doing_business_as="x", + name="name", + user_defined_id="x", + ) + assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_3(self, async_client: AsyncLithic) -> None: + response = await async_client.external_bank_accounts.with_raw_response.create( + account_number="12345678901234567", + country="USD", + currency="USD", + owner="owner", + owner_type="INDIVIDUAL", + routing_number="123456789", + type="CHECKING", + verification_method="EXTERNALLY_VERIFIED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_bank_account = response.parse() + assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) + + @parametrize + async def test_streaming_response_create_overload_3(self, async_client: AsyncLithic) -> None: + async with async_client.external_bank_accounts.with_streaming_response.create( + account_number="12345678901234567", + country="USD", + currency="USD", + owner="owner", + owner_type="INDIVIDUAL", + routing_number="123456789", + type="CHECKING", + verification_method="EXTERNALLY_VERIFIED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_bank_account = await response.parse() + assert_matches_type(ExternalBankAccountCreateResponse, external_bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.retrieve( @@ -528,36 +754,37 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_update(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(ExternalBankAccountUpdateResponse, external_bank_account, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", address={ "address1": "x", - "address2": "x", "city": "x", "country": "USD", "postal_code": "11201", "state": "xx", + "address2": "x", }, - company_id="x", + company_id="sq", dob=parse_date("2019-12-27"), - doing_business_as="string", - name="x", - owner="x", - owner_type="BUSINESS", - user_defined_id="string", + doing_business_as="x", + name="name", + owner="owner", + owner_type="INDIVIDUAL", + type="CHECKING", + user_defined_id="x", ) assert_matches_type(ExternalBankAccountUpdateResponse, external_bank_account, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncLithic) -> None: response = await async_client.external_bank_accounts.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -568,7 +795,7 @@ async def test_raw_response_update(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: async with async_client.external_bank_accounts.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -584,7 +811,7 @@ async def test_path_params_update(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `external_bank_account_token` but received ''" ): await async_client.external_bank_accounts.with_raw_response.update( - "", + external_bank_account_token="", ) @parametrize @@ -596,14 +823,14 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.list( account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - account_types=["CHECKING", "SAVINGS"], - countries=["string", "string", "string"], - ending_before="string", - owner_types=["BUSINESS", "INDIVIDUAL"], + account_types=["CHECKING"], + countries=["string"], + ending_before="ending_before", + owner_types=["INDIVIDUAL"], page_size=1, - starting_after="string", - states=["CLOSED", "ENABLED", "PAUSED"], - verification_states=["ENABLED", "FAILED_VERIFICATION", "INSUFFICIENT_FUNDS"], + starting_after="starting_after", + states=["ENABLED"], + verification_states=["PENDING"], ) assert_matches_type(AsyncCursorPage[ExternalBankAccountListResponse], external_bank_account, path=["response"]) @@ -632,14 +859,22 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_retry_micro_deposits(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.retry_micro_deposits( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ExternalBankAccountRetryMicroDepositsResponse, external_bank_account, path=["response"]) + + @parametrize + async def test_method_retry_micro_deposits_with_all_params(self, async_client: AsyncLithic) -> None: + external_bank_account = await async_client.external_bank_accounts.retry_micro_deposits( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(ExternalBankAccountRetryMicroDepositsResponse, external_bank_account, path=["response"]) @parametrize async def test_raw_response_retry_micro_deposits(self, async_client: AsyncLithic) -> None: response = await async_client.external_bank_accounts.with_raw_response.retry_micro_deposits( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -650,7 +885,7 @@ async def test_raw_response_retry_micro_deposits(self, async_client: AsyncLithic @parametrize async def test_streaming_response_retry_micro_deposits(self, async_client: AsyncLithic) -> None: async with async_client.external_bank_accounts.with_streaming_response.retry_micro_deposits( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -666,5 +901,53 @@ async def test_path_params_retry_micro_deposits(self, async_client: AsyncLithic) ValueError, match=r"Expected a non-empty value for `external_bank_account_token` but received ''" ): await async_client.external_bank_accounts.with_raw_response.retry_micro_deposits( - "", + external_bank_account_token="", + ) + + @parametrize + async def test_method_retry_prenote(self, async_client: AsyncLithic) -> None: + external_bank_account = await async_client.external_bank_accounts.retry_prenote( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ExternalBankAccountRetryPrenoteResponse, external_bank_account, path=["response"]) + + @parametrize + async def test_method_retry_prenote_with_all_params(self, async_client: AsyncLithic) -> None: + external_bank_account = await async_client.external_bank_accounts.retry_prenote( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ExternalBankAccountRetryPrenoteResponse, external_bank_account, path=["response"]) + + @parametrize + async def test_raw_response_retry_prenote(self, async_client: AsyncLithic) -> None: + response = await async_client.external_bank_accounts.with_raw_response.retry_prenote( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_bank_account = response.parse() + assert_matches_type(ExternalBankAccountRetryPrenoteResponse, external_bank_account, path=["response"]) + + @parametrize + async def test_streaming_response_retry_prenote(self, async_client: AsyncLithic) -> None: + async with async_client.external_bank_accounts.with_streaming_response.retry_prenote( + external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_bank_account = await response.parse() + assert_matches_type(ExternalBankAccountRetryPrenoteResponse, external_bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retry_prenote(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_bank_account_token` but received ''" + ): + await async_client.external_bank_accounts.with_raw_response.retry_prenote( + external_bank_account_token="", ) diff --git a/tests/api_resources/test_external_payments.py b/tests/api_resources/test_external_payments.py new file mode 100644 index 00000000..2d2e1e49 --- /dev/null +++ b/tests/api_resources/test_external_payments.py @@ -0,0 +1,730 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types import ( + ExternalPayment, +) +from lithic._utils import parse_date, parse_datetime +from lithic.pagination import SyncCursorPage, AsyncCursorPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestExternalPayments: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + external_payment = client.external_payments.create( + amount=0, + category="EXTERNAL_WIRE", + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + payment_type="DEPOSIT", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Lithic) -> None: + external_payment = client.external_payments.create( + amount=0, + category="EXTERNAL_WIRE", + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + payment_type="DEPOSIT", + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + memo="memo", + progress_to="SETTLED", + user_defined_id="user_defined_id", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Lithic) -> None: + response = client.external_payments.with_raw_response.create( + amount=0, + category="EXTERNAL_WIRE", + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + payment_type="DEPOSIT", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Lithic) -> None: + with client.external_payments.with_streaming_response.create( + amount=0, + category="EXTERNAL_WIRE", + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + payment_type="DEPOSIT", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + external_payment = client.external_payments.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.external_payments.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.external_payments.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + client.external_payments.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + external_payment = client.external_payments.list() + assert_matches_type(SyncCursorPage[ExternalPayment], external_payment, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + external_payment = client.external_payments.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + category="EXTERNAL_WIRE", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + page_size=1, + result="APPROVED", + starting_after="starting_after", + status="PENDING", + ) + assert_matches_type(SyncCursorPage[ExternalPayment], external_payment, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.external_payments.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(SyncCursorPage[ExternalPayment], external_payment, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.external_payments.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = response.parse() + assert_matches_type(SyncCursorPage[ExternalPayment], external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_cancel(self, client: Lithic) -> None: + external_payment = client.external_payments.cancel( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_method_cancel_with_all_params(self, client: Lithic) -> None: + external_payment = client.external_payments.cancel( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: Lithic) -> None: + response = client.external_payments.with_raw_response.cancel( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: Lithic) -> None: + with client.external_payments.with_streaming_response.cancel( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + client.external_payments.with_raw_response.cancel( + external_payment_token="", + effective_date=parse_date("2019-12-27"), + ) + + @parametrize + def test_method_release(self, client: Lithic) -> None: + external_payment = client.external_payments.release( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_method_release_with_all_params(self, client: Lithic) -> None: + external_payment = client.external_payments.release( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_raw_response_release(self, client: Lithic) -> None: + response = client.external_payments.with_raw_response.release( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_streaming_response_release(self, client: Lithic) -> None: + with client.external_payments.with_streaming_response.release( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_release(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + client.external_payments.with_raw_response.release( + external_payment_token="", + effective_date=parse_date("2019-12-27"), + ) + + @parametrize + def test_method_reverse(self, client: Lithic) -> None: + external_payment = client.external_payments.reverse( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_method_reverse_with_all_params(self, client: Lithic) -> None: + external_payment = client.external_payments.reverse( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_raw_response_reverse(self, client: Lithic) -> None: + response = client.external_payments.with_raw_response.reverse( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_streaming_response_reverse(self, client: Lithic) -> None: + with client.external_payments.with_streaming_response.reverse( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_reverse(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + client.external_payments.with_raw_response.reverse( + external_payment_token="", + effective_date=parse_date("2019-12-27"), + ) + + @parametrize + def test_method_settle(self, client: Lithic) -> None: + external_payment = client.external_payments.settle( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_method_settle_with_all_params(self, client: Lithic) -> None: + external_payment = client.external_payments.settle( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + progress_to="SETTLED", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_raw_response_settle(self, client: Lithic) -> None: + response = client.external_payments.with_raw_response.settle( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + def test_streaming_response_settle(self, client: Lithic) -> None: + with client.external_payments.with_streaming_response.settle( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_settle(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + client.external_payments.with_raw_response.settle( + external_payment_token="", + effective_date=parse_date("2019-12-27"), + ) + + +class TestAsyncExternalPayments: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.create( + amount=0, + category="EXTERNAL_WIRE", + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + payment_type="DEPOSIT", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.create( + amount=0, + category="EXTERNAL_WIRE", + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + payment_type="DEPOSIT", + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + memo="memo", + progress_to="SETTLED", + user_defined_id="user_defined_id", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncLithic) -> None: + response = await async_client.external_payments.with_raw_response.create( + amount=0, + category="EXTERNAL_WIRE", + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + payment_type="DEPOSIT", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: + async with async_client.external_payments.with_streaming_response.create( + amount=0, + category="EXTERNAL_WIRE", + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + payment_type="DEPOSIT", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = await response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.external_payments.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.external_payments.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = await response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + await async_client.external_payments.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.list() + assert_matches_type(AsyncCursorPage[ExternalPayment], external_payment, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + category="EXTERNAL_WIRE", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + page_size=1, + result="APPROVED", + starting_after="starting_after", + status="PENDING", + ) + assert_matches_type(AsyncCursorPage[ExternalPayment], external_payment, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncLithic) -> None: + response = await async_client.external_payments.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(AsyncCursorPage[ExternalPayment], external_payment, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: + async with async_client.external_payments.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = await response.parse() + assert_matches_type(AsyncCursorPage[ExternalPayment], external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_cancel(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.cancel( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_method_cancel_with_all_params(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.cancel( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncLithic) -> None: + response = await async_client.external_payments.with_raw_response.cancel( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncLithic) -> None: + async with async_client.external_payments.with_streaming_response.cancel( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = await response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + await async_client.external_payments.with_raw_response.cancel( + external_payment_token="", + effective_date=parse_date("2019-12-27"), + ) + + @parametrize + async def test_method_release(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.release( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_method_release_with_all_params(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.release( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_raw_response_release(self, async_client: AsyncLithic) -> None: + response = await async_client.external_payments.with_raw_response.release( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_streaming_response_release(self, async_client: AsyncLithic) -> None: + async with async_client.external_payments.with_streaming_response.release( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = await response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_release(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + await async_client.external_payments.with_raw_response.release( + external_payment_token="", + effective_date=parse_date("2019-12-27"), + ) + + @parametrize + async def test_method_reverse(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.reverse( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_method_reverse_with_all_params(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.reverse( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_raw_response_reverse(self, async_client: AsyncLithic) -> None: + response = await async_client.external_payments.with_raw_response.reverse( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_streaming_response_reverse(self, async_client: AsyncLithic) -> None: + async with async_client.external_payments.with_streaming_response.reverse( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = await response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_reverse(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + await async_client.external_payments.with_raw_response.reverse( + external_payment_token="", + effective_date=parse_date("2019-12-27"), + ) + + @parametrize + async def test_method_settle(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.settle( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_method_settle_with_all_params(self, async_client: AsyncLithic) -> None: + external_payment = await async_client.external_payments.settle( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + progress_to="SETTLED", + ) + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_raw_response_settle(self, async_client: AsyncLithic) -> None: + response = await async_client.external_payments.with_raw_response.settle( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + external_payment = response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + @parametrize + async def test_streaming_response_settle(self, async_client: AsyncLithic) -> None: + async with async_client.external_payments.with_streaming_response.settle( + external_payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + external_payment = await response.parse() + assert_matches_type(ExternalPayment, external_payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_settle(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `external_payment_token` but received ''" + ): + await async_client.external_payments.with_raw_response.settle( + external_payment_token="", + effective_date=parse_date("2019-12-27"), + ) diff --git a/tests/api_resources/test_financial_accounts.py b/tests/api_resources/test_financial_accounts.py index 32d7e033..dc79f4dc 100644 --- a/tests/api_resources/test_financial_accounts.py +++ b/tests/api_resources/test_financial_accounts.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -23,7 +23,7 @@ class TestFinancialAccounts: @parametrize def test_method_create(self, client: Lithic) -> None: financial_account = client.financial_accounts.create( - nickname="string", + nickname="nickname", type="OPERATING", ) assert_matches_type(FinancialAccount, financial_account, path=["response"]) @@ -31,16 +31,17 @@ def test_method_create(self, client: Lithic) -> None: @parametrize def test_method_create_with_all_params(self, client: Lithic) -> None: financial_account = client.financial_accounts.create( - nickname="string", + nickname="nickname", type="OPERATING", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + is_for_benefit_of=True, ) assert_matches_type(FinancialAccount, financial_account, path=["response"]) @parametrize def test_raw_response_create(self, client: Lithic) -> None: response = client.financial_accounts.with_raw_response.create( - nickname="string", + nickname="nickname", type="OPERATING", ) @@ -52,7 +53,7 @@ def test_raw_response_create(self, client: Lithic) -> None: @parametrize def test_streaming_response_create(self, client: Lithic) -> None: with client.financial_accounts.with_streaming_response.create( - nickname="string", + nickname="nickname", type="OPERATING", ) as response: assert not response.is_closed @@ -106,22 +107,22 @@ def test_path_params_retrieve(self, client: Lithic) -> None: @parametrize def test_method_update(self, client: Lithic) -> None: financial_account = client.financial_accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(FinancialAccount, financial_account, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Lithic) -> None: financial_account = client.financial_accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - nickname="string", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + nickname="nickname", ) assert_matches_type(FinancialAccount, financial_account, path=["response"]) @parametrize def test_raw_response_update(self, client: Lithic) -> None: response = client.financial_accounts.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -132,7 +133,7 @@ def test_raw_response_update(self, client: Lithic) -> None: @parametrize def test_streaming_response_update(self, client: Lithic) -> None: with client.financial_accounts.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -148,7 +149,7 @@ def test_path_params_update(self, client: Lithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): client.financial_accounts.with_raw_response.update( - "", + financial_account_token="", ) @parametrize @@ -185,6 +186,54 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_update_status(self, client: Lithic) -> None: + financial_account = client.financial_accounts.update_status( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="OPEN", + substatus="CHARGED_OFF_FRAUD", + ) + assert_matches_type(FinancialAccount, financial_account, path=["response"]) + + @parametrize + def test_raw_response_update_status(self, client: Lithic) -> None: + response = client.financial_accounts.with_raw_response.update_status( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="OPEN", + substatus="CHARGED_OFF_FRAUD", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + financial_account = response.parse() + assert_matches_type(FinancialAccount, financial_account, path=["response"]) + + @parametrize + def test_streaming_response_update_status(self, client: Lithic) -> None: + with client.financial_accounts.with_streaming_response.update_status( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="OPEN", + substatus="CHARGED_OFF_FRAUD", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + financial_account = response.parse() + assert_matches_type(FinancialAccount, financial_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update_status(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.with_raw_response.update_status( + financial_account_token="", + status="OPEN", + substatus="CHARGED_OFF_FRAUD", + ) + class TestAsyncFinancialAccounts: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -192,7 +241,7 @@ class TestAsyncFinancialAccounts: @parametrize async def test_method_create(self, async_client: AsyncLithic) -> None: financial_account = await async_client.financial_accounts.create( - nickname="string", + nickname="nickname", type="OPERATING", ) assert_matches_type(FinancialAccount, financial_account, path=["response"]) @@ -200,16 +249,17 @@ async def test_method_create(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: financial_account = await async_client.financial_accounts.create( - nickname="string", + nickname="nickname", type="OPERATING", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + is_for_benefit_of=True, ) assert_matches_type(FinancialAccount, financial_account, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncLithic) -> None: response = await async_client.financial_accounts.with_raw_response.create( - nickname="string", + nickname="nickname", type="OPERATING", ) @@ -221,7 +271,7 @@ async def test_raw_response_create(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: async with async_client.financial_accounts.with_streaming_response.create( - nickname="string", + nickname="nickname", type="OPERATING", ) as response: assert not response.is_closed @@ -275,22 +325,22 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_update(self, async_client: AsyncLithic) -> None: financial_account = await async_client.financial_accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(FinancialAccount, financial_account, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: financial_account = await async_client.financial_accounts.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - nickname="string", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + nickname="nickname", ) assert_matches_type(FinancialAccount, financial_account, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncLithic) -> None: response = await async_client.financial_accounts.with_raw_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert response.is_closed is True @@ -301,7 +351,7 @@ async def test_raw_response_update(self, async_client: AsyncLithic) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: async with async_client.financial_accounts.with_streaming_response.update( - "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -317,7 +367,7 @@ async def test_path_params_update(self, async_client: AsyncLithic) -> None: ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" ): await async_client.financial_accounts.with_raw_response.update( - "", + financial_account_token="", ) @parametrize @@ -353,3 +403,51 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert_matches_type(AsyncSinglePage[FinancialAccount], financial_account, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update_status(self, async_client: AsyncLithic) -> None: + financial_account = await async_client.financial_accounts.update_status( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="OPEN", + substatus="CHARGED_OFF_FRAUD", + ) + assert_matches_type(FinancialAccount, financial_account, path=["response"]) + + @parametrize + async def test_raw_response_update_status(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.with_raw_response.update_status( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="OPEN", + substatus="CHARGED_OFF_FRAUD", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + financial_account = response.parse() + assert_matches_type(FinancialAccount, financial_account, path=["response"]) + + @parametrize + async def test_streaming_response_update_status(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.with_streaming_response.update_status( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="OPEN", + substatus="CHARGED_OFF_FRAUD", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + financial_account = await response.parse() + assert_matches_type(FinancialAccount, financial_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update_status(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.with_raw_response.update_status( + financial_account_token="", + status="OPEN", + substatus="CHARGED_OFF_FRAUD", + ) diff --git a/tests/api_resources/test_management_operations.py b/tests/api_resources/test_management_operations.py new file mode 100644 index 00000000..f299847b --- /dev/null +++ b/tests/api_resources/test_management_operations.py @@ -0,0 +1,418 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types import ( + ManagementOperationTransaction, +) +from lithic._utils import parse_date, parse_datetime +from lithic.pagination import SyncCursorPage, AsyncCursorPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestManagementOperations: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + management_operation = client.management_operations.create( + amount=0, + category="MANAGEMENT_FEE", + direction="CREDIT", + effective_date=parse_date("2019-12-27"), + event_type="CASH_BACK", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Lithic) -> None: + management_operation = client.management_operations.create( + amount=0, + category="MANAGEMENT_FEE", + direction="CREDIT", + effective_date=parse_date("2019-12-27"), + event_type="CASH_BACK", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + memo="memo", + subtype="subtype", + user_defined_id="user_defined_id", + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Lithic) -> None: + response = client.management_operations.with_raw_response.create( + amount=0, + category="MANAGEMENT_FEE", + direction="CREDIT", + effective_date=parse_date("2019-12-27"), + event_type="CASH_BACK", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Lithic) -> None: + with client.management_operations.with_streaming_response.create( + amount=0, + category="MANAGEMENT_FEE", + direction="CREDIT", + effective_date=parse_date("2019-12-27"), + event_type="CASH_BACK", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + management_operation = client.management_operations.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.management_operations.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.management_operations.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `management_operation_token` but received ''" + ): + client.management_operations.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + management_operation = client.management_operations.list() + assert_matches_type(SyncCursorPage[ManagementOperationTransaction], management_operation, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + management_operation = client.management_operations.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + category="MANAGEMENT_FEE", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + page_size=1, + starting_after="starting_after", + status="PENDING", + ) + assert_matches_type(SyncCursorPage[ManagementOperationTransaction], management_operation, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.management_operations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + management_operation = response.parse() + assert_matches_type(SyncCursorPage[ManagementOperationTransaction], management_operation, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.management_operations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + management_operation = response.parse() + assert_matches_type(SyncCursorPage[ManagementOperationTransaction], management_operation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_reverse(self, client: Lithic) -> None: + management_operation = client.management_operations.reverse( + management_operation_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + def test_method_reverse_with_all_params(self, client: Lithic) -> None: + management_operation = client.management_operations.reverse( + management_operation_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + def test_raw_response_reverse(self, client: Lithic) -> None: + response = client.management_operations.with_raw_response.reverse( + management_operation_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + def test_streaming_response_reverse(self, client: Lithic) -> None: + with client.management_operations.with_streaming_response.reverse( + management_operation_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_reverse(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `management_operation_token` but received ''" + ): + client.management_operations.with_raw_response.reverse( + management_operation_token="", + effective_date=parse_date("2019-12-27"), + ) + + +class TestAsyncManagementOperations: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncLithic) -> None: + management_operation = await async_client.management_operations.create( + amount=0, + category="MANAGEMENT_FEE", + direction="CREDIT", + effective_date=parse_date("2019-12-27"), + event_type="CASH_BACK", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: + management_operation = await async_client.management_operations.create( + amount=0, + category="MANAGEMENT_FEE", + direction="CREDIT", + effective_date=parse_date("2019-12-27"), + event_type="CASH_BACK", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + memo="memo", + subtype="subtype", + user_defined_id="user_defined_id", + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncLithic) -> None: + response = await async_client.management_operations.with_raw_response.create( + amount=0, + category="MANAGEMENT_FEE", + direction="CREDIT", + effective_date=parse_date("2019-12-27"), + event_type="CASH_BACK", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: + async with async_client.management_operations.with_streaming_response.create( + amount=0, + category="MANAGEMENT_FEE", + direction="CREDIT", + effective_date=parse_date("2019-12-27"), + event_type="CASH_BACK", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + management_operation = await response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + management_operation = await async_client.management_operations.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.management_operations.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.management_operations.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + management_operation = await response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `management_operation_token` but received ''" + ): + await async_client.management_operations.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncLithic) -> None: + management_operation = await async_client.management_operations.list() + assert_matches_type(AsyncCursorPage[ManagementOperationTransaction], management_operation, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: + management_operation = await async_client.management_operations.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + category="MANAGEMENT_FEE", + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + page_size=1, + starting_after="starting_after", + status="PENDING", + ) + assert_matches_type(AsyncCursorPage[ManagementOperationTransaction], management_operation, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncLithic) -> None: + response = await async_client.management_operations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + management_operation = response.parse() + assert_matches_type(AsyncCursorPage[ManagementOperationTransaction], management_operation, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: + async with async_client.management_operations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + management_operation = await response.parse() + assert_matches_type( + AsyncCursorPage[ManagementOperationTransaction], management_operation, path=["response"] + ) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_reverse(self, async_client: AsyncLithic) -> None: + management_operation = await async_client.management_operations.reverse( + management_operation_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + async def test_method_reverse_with_all_params(self, async_client: AsyncLithic) -> None: + management_operation = await async_client.management_operations.reverse( + management_operation_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + memo="memo", + ) + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + async def test_raw_response_reverse(self, async_client: AsyncLithic) -> None: + response = await async_client.management_operations.with_raw_response.reverse( + management_operation_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + management_operation = response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + @parametrize + async def test_streaming_response_reverse(self, async_client: AsyncLithic) -> None: + async with async_client.management_operations.with_streaming_response.reverse( + management_operation_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + management_operation = await response.parse() + assert_matches_type(ManagementOperationTransaction, management_operation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_reverse(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `management_operation_token` but received ''" + ): + await async_client.management_operations.with_raw_response.reverse( + management_operation_token="", + effective_date=parse_date("2019-12-27"), + ) diff --git a/tests/api_resources/test_payments.py b/tests/api_resources/test_payments.py index 72f04978..e556f11a 100644 --- a/tests/api_resources/test_payments.py +++ b/tests/api_resources/test_payments.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -13,7 +13,9 @@ Payment, PaymentRetryResponse, PaymentCreateResponse, + PaymentSimulateActionResponse, PaymentSimulateReturnResponse, + PaymentSimulateReceiptResponse, PaymentSimulateReleaseResponse, ) from lithic._utils import parse_datetime @@ -44,17 +46,11 @@ def test_method_create_with_all_params(self, client: Lithic) -> None: external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", method="ACH_NEXT_DAY", - method_attributes={ - "company_id": "string", - "receipt_routing_number": "string", - "retries": 0, - "return_reason_code": "string", - "sec_code": "CCD", - }, + method_attributes={"sec_code": "CCD"}, type="COLLECTION", token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - memo="string", - user_defined_id="string", + memo="memo", + user_defined_id="user_defined_id", ) assert_matches_type(PaymentCreateResponse, payment, path=["response"]) @@ -138,13 +134,16 @@ def test_method_list(self, client: Lithic) -> None: @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: payment = client.payments.list( + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + category="ACH", end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", page_size=1, result="APPROVED", - starting_after="string", + starting_after="starting_after", status="DECLINED", ) assert_matches_type(SyncCursorPage[Payment], payment, path=["response"]) @@ -207,6 +206,109 @@ def test_path_params_retry(self, client: Lithic) -> None: "", ) + @parametrize + def test_method_simulate_action(self, client: Lithic) -> None: + payment = client.payments.simulate_action( + payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + event_type="ACH_ORIGINATION_REVIEWED", + ) + assert_matches_type(PaymentSimulateActionResponse, payment, path=["response"]) + + @parametrize + def test_method_simulate_action_with_all_params(self, client: Lithic) -> None: + payment = client.payments.simulate_action( + payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + event_type="ACH_ORIGINATION_REVIEWED", + decline_reason="PROGRAM_TRANSACTION_LIMIT_EXCEEDED", + return_reason_code="return_reason_code", + ) + assert_matches_type(PaymentSimulateActionResponse, payment, path=["response"]) + + @parametrize + def test_raw_response_simulate_action(self, client: Lithic) -> None: + response = client.payments.with_raw_response.simulate_action( + payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + event_type="ACH_ORIGINATION_REVIEWED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + payment = response.parse() + assert_matches_type(PaymentSimulateActionResponse, payment, path=["response"]) + + @parametrize + def test_streaming_response_simulate_action(self, client: Lithic) -> None: + with client.payments.with_streaming_response.simulate_action( + payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + event_type="ACH_ORIGINATION_REVIEWED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + payment = response.parse() + assert_matches_type(PaymentSimulateActionResponse, payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_simulate_action(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `payment_token` but received ''"): + client.payments.with_raw_response.simulate_action( + payment_token="", + event_type="ACH_ORIGINATION_REVIEWED", + ) + + @parametrize + def test_method_simulate_receipt(self, client: Lithic) -> None: + payment = client.payments.simulate_receipt( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + amount=0, + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + receipt_type="RECEIPT_CREDIT", + ) + assert_matches_type(PaymentSimulateReceiptResponse, payment, path=["response"]) + + @parametrize + def test_method_simulate_receipt_with_all_params(self, client: Lithic) -> None: + payment = client.payments.simulate_receipt( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + amount=0, + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + receipt_type="RECEIPT_CREDIT", + memo="memo", + ) + assert_matches_type(PaymentSimulateReceiptResponse, payment, path=["response"]) + + @parametrize + def test_raw_response_simulate_receipt(self, client: Lithic) -> None: + response = client.payments.with_raw_response.simulate_receipt( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + amount=0, + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + receipt_type="RECEIPT_CREDIT", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + payment = response.parse() + assert_matches_type(PaymentSimulateReceiptResponse, payment, path=["response"]) + + @parametrize + def test_streaming_response_simulate_receipt(self, client: Lithic) -> None: + with client.payments.with_streaming_response.simulate_receipt( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + amount=0, + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + receipt_type="RECEIPT_CREDIT", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + payment = response.parse() + assert_matches_type(PaymentSimulateReceiptResponse, payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_simulate_release(self, client: Lithic) -> None: payment = client.payments.simulate_release( @@ -249,7 +351,7 @@ def test_method_simulate_return(self, client: Lithic) -> None: def test_method_simulate_return_with_all_params(self, client: Lithic) -> None: payment = client.payments.simulate_return( payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - return_reason_code="string", + return_reason_code="R12", ) assert_matches_type(PaymentSimulateReturnResponse, payment, path=["response"]) @@ -300,17 +402,11 @@ async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> external_bank_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", method="ACH_NEXT_DAY", - method_attributes={ - "company_id": "string", - "receipt_routing_number": "string", - "retries": 0, - "return_reason_code": "string", - "sec_code": "CCD", - }, + method_attributes={"sec_code": "CCD"}, type="COLLECTION", token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - memo="string", - user_defined_id="string", + memo="memo", + user_defined_id="user_defined_id", ) assert_matches_type(PaymentCreateResponse, payment, path=["response"]) @@ -394,13 +490,16 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: payment = await async_client.payments.list( + account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", begin=parse_datetime("2019-12-27T18:11:19.117Z"), + business_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + category="ACH", end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", page_size=1, result="APPROVED", - starting_after="string", + starting_after="starting_after", status="DECLINED", ) assert_matches_type(AsyncCursorPage[Payment], payment, path=["response"]) @@ -463,6 +562,109 @@ async def test_path_params_retry(self, async_client: AsyncLithic) -> None: "", ) + @parametrize + async def test_method_simulate_action(self, async_client: AsyncLithic) -> None: + payment = await async_client.payments.simulate_action( + payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + event_type="ACH_ORIGINATION_REVIEWED", + ) + assert_matches_type(PaymentSimulateActionResponse, payment, path=["response"]) + + @parametrize + async def test_method_simulate_action_with_all_params(self, async_client: AsyncLithic) -> None: + payment = await async_client.payments.simulate_action( + payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + event_type="ACH_ORIGINATION_REVIEWED", + decline_reason="PROGRAM_TRANSACTION_LIMIT_EXCEEDED", + return_reason_code="return_reason_code", + ) + assert_matches_type(PaymentSimulateActionResponse, payment, path=["response"]) + + @parametrize + async def test_raw_response_simulate_action(self, async_client: AsyncLithic) -> None: + response = await async_client.payments.with_raw_response.simulate_action( + payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + event_type="ACH_ORIGINATION_REVIEWED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + payment = response.parse() + assert_matches_type(PaymentSimulateActionResponse, payment, path=["response"]) + + @parametrize + async def test_streaming_response_simulate_action(self, async_client: AsyncLithic) -> None: + async with async_client.payments.with_streaming_response.simulate_action( + payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + event_type="ACH_ORIGINATION_REVIEWED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + payment = await response.parse() + assert_matches_type(PaymentSimulateActionResponse, payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_simulate_action(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `payment_token` but received ''"): + await async_client.payments.with_raw_response.simulate_action( + payment_token="", + event_type="ACH_ORIGINATION_REVIEWED", + ) + + @parametrize + async def test_method_simulate_receipt(self, async_client: AsyncLithic) -> None: + payment = await async_client.payments.simulate_receipt( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + amount=0, + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + receipt_type="RECEIPT_CREDIT", + ) + assert_matches_type(PaymentSimulateReceiptResponse, payment, path=["response"]) + + @parametrize + async def test_method_simulate_receipt_with_all_params(self, async_client: AsyncLithic) -> None: + payment = await async_client.payments.simulate_receipt( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + amount=0, + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + receipt_type="RECEIPT_CREDIT", + memo="memo", + ) + assert_matches_type(PaymentSimulateReceiptResponse, payment, path=["response"]) + + @parametrize + async def test_raw_response_simulate_receipt(self, async_client: AsyncLithic) -> None: + response = await async_client.payments.with_raw_response.simulate_receipt( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + amount=0, + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + receipt_type="RECEIPT_CREDIT", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + payment = response.parse() + assert_matches_type(PaymentSimulateReceiptResponse, payment, path=["response"]) + + @parametrize + async def test_streaming_response_simulate_receipt(self, async_client: AsyncLithic) -> None: + async with async_client.payments.with_streaming_response.simulate_receipt( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + amount=0, + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + receipt_type="RECEIPT_CREDIT", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + payment = await response.parse() + assert_matches_type(PaymentSimulateReceiptResponse, payment, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_simulate_release(self, async_client: AsyncLithic) -> None: payment = await async_client.payments.simulate_release( @@ -505,7 +707,7 @@ async def test_method_simulate_return(self, async_client: AsyncLithic) -> None: async def test_method_simulate_return_with_all_params(self, async_client: AsyncLithic) -> None: payment = await async_client.payments.simulate_return( payment_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - return_reason_code="string", + return_reason_code="R12", ) assert_matches_type(PaymentSimulateReturnResponse, payment, path=["response"]) diff --git a/tests/api_resources/test_responder_endpoints.py b/tests/api_resources/test_responder_endpoints.py index e5d9e506..53555059 100644 --- a/tests/api_resources/test_responder_endpoints.py +++ b/tests/api_resources/test_responder_endpoints.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_tokenization_decisioning.py b/tests/api_resources/test_tokenization_decisioning.py index 7ae71bbc..b20f6cfa 100644 --- a/tests/api_resources/test_tokenization_decisioning.py +++ b/tests/api_resources/test_tokenization_decisioning.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations diff --git a/tests/api_resources/test_tokenizations.py b/tests/api_resources/test_tokenizations.py index 1d48cc9a..3fecca6a 100644 --- a/tests/api_resources/test_tokenizations.py +++ b/tests/api_resources/test_tokenizations.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -13,6 +13,7 @@ Tokenization, TokenizationRetrieveResponse, TokenizationSimulateResponse, + TokenizationUpdateDigitalCardArtResponse, ) from lithic._utils import parse_date from lithic.pagination import SyncCursorPage, AsyncCursorPage @@ -73,9 +74,10 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: begin=parse_date("2019-12-27"), card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", end=parse_date("2019-12-27"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", + tokenization_channel="DIGITAL_WALLET", ) assert_matches_type(SyncCursorPage[Tokenization], tokenization, path=["response"]) @@ -99,6 +101,166 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_activate(self, client: Lithic) -> None: + tokenization = client.tokenizations.activate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + def test_raw_response_activate(self, client: Lithic) -> None: + response = client.tokenizations.with_raw_response.activate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + def test_streaming_response_activate(self, client: Lithic) -> None: + with client.tokenizations.with_streaming_response.activate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_activate(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + client.tokenizations.with_raw_response.activate( + "", + ) + + @parametrize + def test_method_deactivate(self, client: Lithic) -> None: + tokenization = client.tokenizations.deactivate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + def test_raw_response_deactivate(self, client: Lithic) -> None: + response = client.tokenizations.with_raw_response.deactivate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + def test_streaming_response_deactivate(self, client: Lithic) -> None: + with client.tokenizations.with_streaming_response.deactivate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_deactivate(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + client.tokenizations.with_raw_response.deactivate( + "", + ) + + @parametrize + def test_method_pause(self, client: Lithic) -> None: + tokenization = client.tokenizations.pause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + def test_raw_response_pause(self, client: Lithic) -> None: + response = client.tokenizations.with_raw_response.pause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + def test_streaming_response_pause(self, client: Lithic) -> None: + with client.tokenizations.with_streaming_response.pause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_pause(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + client.tokenizations.with_raw_response.pause( + "", + ) + + @parametrize + def test_method_resend_activation_code(self, client: Lithic) -> None: + tokenization = client.tokenizations.resend_activation_code( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + def test_method_resend_activation_code_with_all_params(self, client: Lithic) -> None: + tokenization = client.tokenizations.resend_activation_code( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + activation_method_type="EMAIL_TO_CARDHOLDER_ADDRESS", + ) + assert tokenization is None + + @parametrize + def test_raw_response_resend_activation_code(self, client: Lithic) -> None: + response = client.tokenizations.with_raw_response.resend_activation_code( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + def test_streaming_response_resend_activation_code(self, client: Lithic) -> None: + with client.tokenizations.with_streaming_response.resend_activation_code( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_resend_activation_code(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + client.tokenizations.with_raw_response.resend_activation_code( + tokenization_token="", + ) + @parametrize def test_method_simulate(self, client: Lithic) -> None: tokenization = client.tokenizations.simulate( @@ -118,6 +280,7 @@ def test_method_simulate_with_all_params(self, client: Lithic) -> None: tokenization_source="APPLE_PAY", account_score=5, device_score=5, + entity="entity", wallet_recommended_decision="APPROVED", ) assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) @@ -152,6 +315,90 @@ def test_streaming_response_simulate(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_unpause(self, client: Lithic) -> None: + tokenization = client.tokenizations.unpause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + def test_raw_response_unpause(self, client: Lithic) -> None: + response = client.tokenizations.with_raw_response.unpause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + def test_streaming_response_unpause(self, client: Lithic) -> None: + with client.tokenizations.with_streaming_response.unpause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_unpause(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + client.tokenizations.with_raw_response.unpause( + "", + ) + + @parametrize + def test_method_update_digital_card_art(self, client: Lithic) -> None: + tokenization = client.tokenizations.update_digital_card_art( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + + @parametrize + def test_method_update_digital_card_art_with_all_params(self, client: Lithic) -> None: + tokenization = client.tokenizations.update_digital_card_art( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + + @parametrize + def test_raw_response_update_digital_card_art(self, client: Lithic) -> None: + response = client.tokenizations.with_raw_response.update_digital_card_art( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + + @parametrize + def test_streaming_response_update_digital_card_art(self, client: Lithic) -> None: + with client.tokenizations.with_streaming_response.update_digital_card_art( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = response.parse() + assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update_digital_card_art(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + client.tokenizations.with_raw_response.update_digital_card_art( + tokenization_token="", + ) + class TestAsyncTokenizations: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -206,9 +453,10 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N begin=parse_date("2019-12-27"), card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", end=parse_date("2019-12-27"), - ending_before="string", + ending_before="ending_before", page_size=1, - starting_after="string", + starting_after="starting_after", + tokenization_channel="DIGITAL_WALLET", ) assert_matches_type(AsyncCursorPage[Tokenization], tokenization, path=["response"]) @@ -232,6 +480,166 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_activate(self, async_client: AsyncLithic) -> None: + tokenization = await async_client.tokenizations.activate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + async def test_raw_response_activate(self, async_client: AsyncLithic) -> None: + response = await async_client.tokenizations.with_raw_response.activate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + async def test_streaming_response_activate(self, async_client: AsyncLithic) -> None: + async with async_client.tokenizations.with_streaming_response.activate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = await response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_activate(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + await async_client.tokenizations.with_raw_response.activate( + "", + ) + + @parametrize + async def test_method_deactivate(self, async_client: AsyncLithic) -> None: + tokenization = await async_client.tokenizations.deactivate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + async def test_raw_response_deactivate(self, async_client: AsyncLithic) -> None: + response = await async_client.tokenizations.with_raw_response.deactivate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + async def test_streaming_response_deactivate(self, async_client: AsyncLithic) -> None: + async with async_client.tokenizations.with_streaming_response.deactivate( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = await response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_deactivate(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + await async_client.tokenizations.with_raw_response.deactivate( + "", + ) + + @parametrize + async def test_method_pause(self, async_client: AsyncLithic) -> None: + tokenization = await async_client.tokenizations.pause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + async def test_raw_response_pause(self, async_client: AsyncLithic) -> None: + response = await async_client.tokenizations.with_raw_response.pause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + async def test_streaming_response_pause(self, async_client: AsyncLithic) -> None: + async with async_client.tokenizations.with_streaming_response.pause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = await response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_pause(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + await async_client.tokenizations.with_raw_response.pause( + "", + ) + + @parametrize + async def test_method_resend_activation_code(self, async_client: AsyncLithic) -> None: + tokenization = await async_client.tokenizations.resend_activation_code( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + async def test_method_resend_activation_code_with_all_params(self, async_client: AsyncLithic) -> None: + tokenization = await async_client.tokenizations.resend_activation_code( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + activation_method_type="EMAIL_TO_CARDHOLDER_ADDRESS", + ) + assert tokenization is None + + @parametrize + async def test_raw_response_resend_activation_code(self, async_client: AsyncLithic) -> None: + response = await async_client.tokenizations.with_raw_response.resend_activation_code( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + async def test_streaming_response_resend_activation_code(self, async_client: AsyncLithic) -> None: + async with async_client.tokenizations.with_streaming_response.resend_activation_code( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = await response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_resend_activation_code(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + await async_client.tokenizations.with_raw_response.resend_activation_code( + tokenization_token="", + ) + @parametrize async def test_method_simulate(self, async_client: AsyncLithic) -> None: tokenization = await async_client.tokenizations.simulate( @@ -251,6 +659,7 @@ async def test_method_simulate_with_all_params(self, async_client: AsyncLithic) tokenization_source="APPLE_PAY", account_score=5, device_score=5, + entity="entity", wallet_recommended_decision="APPROVED", ) assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) @@ -284,3 +693,87 @@ async def test_streaming_response_simulate(self, async_client: AsyncLithic) -> N assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_unpause(self, async_client: AsyncLithic) -> None: + tokenization = await async_client.tokenizations.unpause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert tokenization is None + + @parametrize + async def test_raw_response_unpause(self, async_client: AsyncLithic) -> None: + response = await async_client.tokenizations.with_raw_response.unpause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert tokenization is None + + @parametrize + async def test_streaming_response_unpause(self, async_client: AsyncLithic) -> None: + async with async_client.tokenizations.with_streaming_response.unpause( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = await response.parse() + assert tokenization is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_unpause(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + await async_client.tokenizations.with_raw_response.unpause( + "", + ) + + @parametrize + async def test_method_update_digital_card_art(self, async_client: AsyncLithic) -> None: + tokenization = await async_client.tokenizations.update_digital_card_art( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + + @parametrize + async def test_method_update_digital_card_art_with_all_params(self, async_client: AsyncLithic) -> None: + tokenization = await async_client.tokenizations.update_digital_card_art( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + + @parametrize + async def test_raw_response_update_digital_card_art(self, async_client: AsyncLithic) -> None: + response = await async_client.tokenizations.with_raw_response.update_digital_card_art( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tokenization = response.parse() + assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + + @parametrize + async def test_streaming_response_update_digital_card_art(self, async_client: AsyncLithic) -> None: + async with async_client.tokenizations.with_streaming_response.update_digital_card_art( + tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tokenization = await response.parse() + assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update_digital_card_art(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `tokenization_token` but received ''"): + await async_client.tokenizations.with_raw_response.update_digital_card_art( + tokenization_token="", + ) diff --git a/tests/api_resources/test_transactions.py b/tests/api_resources/test_transactions.py index 4e7b9003..b0f32521 100644 --- a/tests/api_resources/test_transactions.py +++ b/tests/api_resources/test_transactions.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -78,10 +78,11 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: begin=parse_datetime("2019-12-27T18:11:19.117Z"), card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, result="APPROVED", - starting_after="string", + starting_after="starting_after", + status="PENDING", ) assert_matches_type(SyncCursorPage[Transaction], transaction, path=["response"]) @@ -105,6 +106,44 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_expire_authorization(self, client: Lithic) -> None: + transaction = client.transactions.expire_authorization( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert transaction is None + + @parametrize + def test_raw_response_expire_authorization(self, client: Lithic) -> None: + response = client.transactions.with_raw_response.expire_authorization( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + transaction = response.parse() + assert transaction is None + + @parametrize + def test_streaming_response_expire_authorization(self, client: Lithic) -> None: + with client.transactions.with_streaming_response.expire_authorization( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + transaction = response.parse() + assert transaction is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_expire_authorization(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `transaction_token` but received ''"): + client.transactions.with_raw_response.expire_authorization( + "", + ) + @parametrize def test_method_simulate_authorization(self, client: Lithic) -> None: transaction = client.transactions.simulate_authorization( @@ -125,6 +164,7 @@ def test_method_simulate_authorization_with_all_params(self, client: Lithic) -> merchant_amount=0, merchant_currency="GBP", partial_approval_capable=True, + pin="1234", status="AUTHORIZATION", ) assert_matches_type(TransactionSimulateAuthorizationResponse, transaction, path=["response"]) @@ -440,10 +480,11 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N begin=parse_datetime("2019-12-27T18:11:19.117Z"), card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", end=parse_datetime("2019-12-27T18:11:19.117Z"), - ending_before="string", + ending_before="ending_before", page_size=1, result="APPROVED", - starting_after="string", + starting_after="starting_after", + status="PENDING", ) assert_matches_type(AsyncCursorPage[Transaction], transaction, path=["response"]) @@ -467,6 +508,44 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_expire_authorization(self, async_client: AsyncLithic) -> None: + transaction = await async_client.transactions.expire_authorization( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert transaction is None + + @parametrize + async def test_raw_response_expire_authorization(self, async_client: AsyncLithic) -> None: + response = await async_client.transactions.with_raw_response.expire_authorization( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + transaction = response.parse() + assert transaction is None + + @parametrize + async def test_streaming_response_expire_authorization(self, async_client: AsyncLithic) -> None: + async with async_client.transactions.with_streaming_response.expire_authorization( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + transaction = await response.parse() + assert transaction is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_expire_authorization(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `transaction_token` but received ''"): + await async_client.transactions.with_raw_response.expire_authorization( + "", + ) + @parametrize async def test_method_simulate_authorization(self, async_client: AsyncLithic) -> None: transaction = await async_client.transactions.simulate_authorization( @@ -487,6 +566,7 @@ async def test_method_simulate_authorization_with_all_params(self, async_client: merchant_amount=0, merchant_currency="GBP", partial_approval_capable=True, + pin="1234", status="AUTHORIZATION", ) assert_matches_type(TransactionSimulateAuthorizationResponse, transaction, path=["response"]) diff --git a/tests/api_resources/test_webhooks.py b/tests/api_resources/test_webhooks.py deleted file mode 100644 index c3fc25b5..00000000 --- a/tests/api_resources/test_webhooks.py +++ /dev/null @@ -1,211 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. - -from __future__ import annotations - -import os -import base64 -from typing import Any, cast -from datetime import datetime, timezone, timedelta - -import pytest -import time_machine - -from lithic import Lithic, AsyncLithic - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestWebhooks: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - timestamp = "1676312382" - fake_now = datetime.fromtimestamp(float(timestamp), tz=timezone.utc) - - payload = """{"card_token":"sit Lorem ipsum, accusantium repellendus possimus","created_at":"elit. placeat libero architecto molestias, sit","account_token":"elit.","issuer_decision":"magnam, libero esse Lorem ipsum magnam, magnam,","tokenization_attempt_id":"illum dolor repellendus libero esse accusantium","wallet_decisioning_info":{"device_score":"placeat architecto"},"digital_wallet_token_metadata":{"status":"reprehenderit dolor","token_requestor_id":"possimus","payment_account_info":{"account_holder_data":{"phone_number":"libero","email_address":"nobis molestias, veniam culpa! quas elit. quas libero esse architecto placeat"},"pan_unique_reference":"adipisicing odit magnam, odit"}}}""" - signature = "Dwa0AHInLL3XFo2sxcHamOQDrJNi7F654S3L6skMAOI=" - headers = { - "webhook-id": "msg_2Lh9KRb0pzN4LePd3XiA4v12Axj", - "webhook-timestamp": timestamp, - "webhook-signature": f"v1,{signature}", - } - secret = "whsec_zlFsbBZ8Xcodlpcu6NDTdSzZRLSdhkst" - - @time_machine.travel(fake_now) - def test_unwrap(self, client: Lithic) -> None: - payload = self.payload - headers = self.headers - secret = self.secret - - client.webhooks.unwrap(payload, headers, secret=secret) - - @time_machine.travel(fake_now) - def test_verify_signature(self, client: Lithic) -> None: - payload = self.payload - headers = self.headers - secret = self.secret - signature = self.signature - verify = client.webhooks.verify_signature - - assert verify(payload=payload, headers=headers, secret=secret) is None - - with pytest.raises(ValueError, match="Webhook timestamp is too old"): - with time_machine.travel(self.fake_now + timedelta(minutes=6)): - assert verify(payload=payload, headers=headers, secret=secret) is False - - with pytest.raises(ValueError, match="Webhook timestamp is too new"): - with time_machine.travel(self.fake_now - timedelta(minutes=6)): - assert verify(payload=payload, headers=headers, secret=secret) is False - - # wrong secret - with pytest.raises(ValueError, match=r"Bad secret"): - verify(payload=payload, headers=headers, secret="invalid secret") - - invalid_signature_message = "None of the given webhook signatures match the expected signature" - with pytest.raises(ValueError, match=invalid_signature_message): - verify( - payload=payload, - headers=headers, - secret=f"whsec_{base64.b64encode(b'foo').decode('utf-8')}", - ) - - # multiple signatures - invalid_signature = base64.b64encode(b"my_sig").decode("utf-8") - assert ( - verify( - payload=payload, - headers={**headers, "webhook-signature": f"v1,{invalid_signature} v1,{signature}"}, - secret=secret, - ) - is None - ) - - # different signature version - with pytest.raises(ValueError, match=invalid_signature_message): - verify( - payload=payload, - headers={**headers, "webhook-signature": f"v2,{signature}"}, - secret=secret, - ) - - assert ( - verify( - payload=payload, - headers={**headers, "webhook-signature": f"v2,{signature} v1,{signature}"}, - secret=secret, - ) - is None - ) - - # missing version - with pytest.raises(ValueError, match=invalid_signature_message): - verify( - payload=payload, - headers={**headers, "webhook-signature": signature}, - secret=secret, - ) - - # non-string payload - with pytest.raises(ValueError, match=r"Webhook body should be a string"): - verify( - payload=cast(Any, {"payload": payload}), - headers=headers, - secret=secret, - ) - - -class TestAsyncWebhooks: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - timestamp = "1676312382" - fake_now = datetime.fromtimestamp(float(timestamp), tz=timezone.utc) - - payload = """{"card_token":"sit Lorem ipsum, accusantium repellendus possimus","created_at":"elit. placeat libero architecto molestias, sit","account_token":"elit.","issuer_decision":"magnam, libero esse Lorem ipsum magnam, magnam,","tokenization_attempt_id":"illum dolor repellendus libero esse accusantium","wallet_decisioning_info":{"device_score":"placeat architecto"},"digital_wallet_token_metadata":{"status":"reprehenderit dolor","token_requestor_id":"possimus","payment_account_info":{"account_holder_data":{"phone_number":"libero","email_address":"nobis molestias, veniam culpa! quas elit. quas libero esse architecto placeat"},"pan_unique_reference":"adipisicing odit magnam, odit"}}}""" - signature = "Dwa0AHInLL3XFo2sxcHamOQDrJNi7F654S3L6skMAOI=" - headers = { - "webhook-id": "msg_2Lh9KRb0pzN4LePd3XiA4v12Axj", - "webhook-timestamp": timestamp, - "webhook-signature": f"v1,{signature}", - } - secret = "whsec_zlFsbBZ8Xcodlpcu6NDTdSzZRLSdhkst" - - @time_machine.travel(fake_now) - def test_unwrap(self, async_client: AsyncLithic) -> None: - payload = self.payload - headers = self.headers - secret = self.secret - - async_client.webhooks.unwrap(payload, headers, secret=secret) - - @time_machine.travel(fake_now) - def test_verify_signature(self, async_client: Lithic) -> None: - payload = self.payload - headers = self.headers - secret = self.secret - signature = self.signature - verify = async_client.webhooks.verify_signature - - assert verify(payload=payload, headers=headers, secret=secret) is None - - with pytest.raises(ValueError, match="Webhook timestamp is too old"): - with time_machine.travel(self.fake_now + timedelta(minutes=6)): - assert verify(payload=payload, headers=headers, secret=secret) is False - - with pytest.raises(ValueError, match="Webhook timestamp is too new"): - with time_machine.travel(self.fake_now - timedelta(minutes=6)): - assert verify(payload=payload, headers=headers, secret=secret) is False - - # wrong secret - with pytest.raises(ValueError, match=r"Bad secret"): - verify(payload=payload, headers=headers, secret="invalid secret") - - invalid_signature_message = "None of the given webhook signatures match the expected signature" - with pytest.raises(ValueError, match=invalid_signature_message): - verify( - payload=payload, - headers=headers, - secret=f"whsec_{base64.b64encode(b'foo').decode('utf-8')}", - ) - - # multiple signatures - invalid_signature = base64.b64encode(b"my_sig").decode("utf-8") - assert ( - verify( - payload=payload, - headers={**headers, "webhook-signature": f"v1,{invalid_signature} v1,{signature}"}, - secret=secret, - ) - is None - ) - - # different signature version - with pytest.raises(ValueError, match=invalid_signature_message): - verify( - payload=payload, - headers={**headers, "webhook-signature": f"v2,{signature}"}, - secret=secret, - ) - - assert ( - verify( - payload=payload, - headers={**headers, "webhook-signature": f"v2,{signature} v1,{signature}"}, - secret=secret, - ) - is None - ) - - # missing version - with pytest.raises(ValueError, match=invalid_signature_message): - verify( - payload=payload, - headers={**headers, "webhook-signature": signature}, - secret=secret, - ) - - # non-string payload - with pytest.raises(ValueError, match=r"Webhook body should be a string"): - verify( - payload=cast(Any, {"payload": payload}), - headers=headers, - secret=secret, - ) diff --git a/tests/api_resources/three_ds/__init__.py b/tests/api_resources/three_ds/__init__.py index 1016754e..fd8019a9 100644 --- a/tests/api_resources/three_ds/__init__.py +++ b/tests/api_resources/three_ds/__init__.py @@ -1 +1 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/three_ds/test_authentication.py b/tests/api_resources/three_ds/test_authentication.py index f4a9d774..466818f5 100644 --- a/tests/api_resources/three_ds/test_authentication.py +++ b/tests/api_resources/three_ds/test_authentication.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -64,8 +64,25 @@ def test_path_params_retrieve(self, client: Lithic) -> None: def test_method_simulate(self, client: Lithic) -> None: authentication = client.three_ds.authentication.simulate( merchant={ + "id": "OODKZAPJVN4YS7O", "country": "USA", + "mcc": "5812", + "name": "COFFEE SHOP", + }, + pan="4111111289144142", + transaction={ + "amount": 100, + "currency": "USD", + }, + ) + assert_matches_type(AuthenticationSimulateResponse, authentication, path=["response"]) + + @parametrize + def test_method_simulate_with_all_params(self, client: Lithic) -> None: + authentication = client.three_ds.authentication.simulate( + merchant={ "id": "OODKZAPJVN4YS7O", + "country": "USA", "mcc": "5812", "name": "COFFEE SHOP", }, @@ -74,6 +91,7 @@ def test_method_simulate(self, client: Lithic) -> None: "amount": 100, "currency": "USD", }, + card_expiry_check="MATCH", ) assert_matches_type(AuthenticationSimulateResponse, authentication, path=["response"]) @@ -81,8 +99,8 @@ def test_method_simulate(self, client: Lithic) -> None: def test_raw_response_simulate(self, client: Lithic) -> None: response = client.three_ds.authentication.with_raw_response.simulate( merchant={ - "country": "USA", "id": "OODKZAPJVN4YS7O", + "country": "USA", "mcc": "5812", "name": "COFFEE SHOP", }, @@ -102,8 +120,8 @@ def test_raw_response_simulate(self, client: Lithic) -> None: def test_streaming_response_simulate(self, client: Lithic) -> None: with client.three_ds.authentication.with_streaming_response.simulate( merchant={ - "country": "USA", "id": "OODKZAPJVN4YS7O", + "country": "USA", "mcc": "5812", "name": "COFFEE SHOP", }, @@ -121,6 +139,40 @@ def test_streaming_response_simulate(self, client: Lithic) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_simulate_otp_entry(self, client: Lithic) -> None: + authentication = client.three_ds.authentication.simulate_otp_entry( + token="fabd829d-7f7b-4432-a8f2-07ea4889aaac", + otp="123456", + ) + assert authentication is None + + @parametrize + def test_raw_response_simulate_otp_entry(self, client: Lithic) -> None: + response = client.three_ds.authentication.with_raw_response.simulate_otp_entry( + token="fabd829d-7f7b-4432-a8f2-07ea4889aaac", + otp="123456", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + authentication = response.parse() + assert authentication is None + + @parametrize + def test_streaming_response_simulate_otp_entry(self, client: Lithic) -> None: + with client.three_ds.authentication.with_streaming_response.simulate_otp_entry( + token="fabd829d-7f7b-4432-a8f2-07ea4889aaac", + otp="123456", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + authentication = response.parse() + assert authentication is None + + assert cast(Any, response.is_closed) is True + class TestAsyncAuthentication: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -169,8 +221,25 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: async def test_method_simulate(self, async_client: AsyncLithic) -> None: authentication = await async_client.three_ds.authentication.simulate( merchant={ + "id": "OODKZAPJVN4YS7O", "country": "USA", + "mcc": "5812", + "name": "COFFEE SHOP", + }, + pan="4111111289144142", + transaction={ + "amount": 100, + "currency": "USD", + }, + ) + assert_matches_type(AuthenticationSimulateResponse, authentication, path=["response"]) + + @parametrize + async def test_method_simulate_with_all_params(self, async_client: AsyncLithic) -> None: + authentication = await async_client.three_ds.authentication.simulate( + merchant={ "id": "OODKZAPJVN4YS7O", + "country": "USA", "mcc": "5812", "name": "COFFEE SHOP", }, @@ -179,6 +248,7 @@ async def test_method_simulate(self, async_client: AsyncLithic) -> None: "amount": 100, "currency": "USD", }, + card_expiry_check="MATCH", ) assert_matches_type(AuthenticationSimulateResponse, authentication, path=["response"]) @@ -186,8 +256,8 @@ async def test_method_simulate(self, async_client: AsyncLithic) -> None: async def test_raw_response_simulate(self, async_client: AsyncLithic) -> None: response = await async_client.three_ds.authentication.with_raw_response.simulate( merchant={ - "country": "USA", "id": "OODKZAPJVN4YS7O", + "country": "USA", "mcc": "5812", "name": "COFFEE SHOP", }, @@ -207,8 +277,8 @@ async def test_raw_response_simulate(self, async_client: AsyncLithic) -> None: async def test_streaming_response_simulate(self, async_client: AsyncLithic) -> None: async with async_client.three_ds.authentication.with_streaming_response.simulate( merchant={ - "country": "USA", "id": "OODKZAPJVN4YS7O", + "country": "USA", "mcc": "5812", "name": "COFFEE SHOP", }, @@ -225,3 +295,37 @@ async def test_streaming_response_simulate(self, async_client: AsyncLithic) -> N assert_matches_type(AuthenticationSimulateResponse, authentication, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_simulate_otp_entry(self, async_client: AsyncLithic) -> None: + authentication = await async_client.three_ds.authentication.simulate_otp_entry( + token="fabd829d-7f7b-4432-a8f2-07ea4889aaac", + otp="123456", + ) + assert authentication is None + + @parametrize + async def test_raw_response_simulate_otp_entry(self, async_client: AsyncLithic) -> None: + response = await async_client.three_ds.authentication.with_raw_response.simulate_otp_entry( + token="fabd829d-7f7b-4432-a8f2-07ea4889aaac", + otp="123456", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + authentication = response.parse() + assert authentication is None + + @parametrize + async def test_streaming_response_simulate_otp_entry(self, async_client: AsyncLithic) -> None: + async with async_client.three_ds.authentication.with_streaming_response.simulate_otp_entry( + token="fabd829d-7f7b-4432-a8f2-07ea4889aaac", + otp="123456", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + authentication = await response.parse() + assert authentication is None + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/three_ds/test_decisioning.py b/tests/api_resources/three_ds/test_decisioning.py index cc15ea98..6a0a392e 100644 --- a/tests/api_resources/three_ds/test_decisioning.py +++ b/tests/api_resources/three_ds/test_decisioning.py @@ -1,4 +1,4 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations @@ -9,7 +9,9 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type -from lithic.types.three_ds import DecisioningRetrieveSecretResponse +from lithic.types.three_ds import ( + DecisioningRetrieveSecretResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,6 +19,40 @@ class TestDecisioning: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @parametrize + def test_method_challenge_response(self, client: Lithic) -> None: + decisioning = client.three_ds.decisioning.challenge_response( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + challenge_response="APPROVE", + ) + assert decisioning is None + + @parametrize + def test_raw_response_challenge_response(self, client: Lithic) -> None: + response = client.three_ds.decisioning.with_raw_response.challenge_response( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + challenge_response="APPROVE", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + decisioning = response.parse() + assert decisioning is None + + @parametrize + def test_streaming_response_challenge_response(self, client: Lithic) -> None: + with client.three_ds.decisioning.with_streaming_response.challenge_response( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + challenge_response="APPROVE", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + decisioning = response.parse() + assert decisioning is None + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_retrieve_secret(self, client: Lithic) -> None: decisioning = client.three_ds.decisioning.retrieve_secret() @@ -71,6 +107,40 @@ def test_streaming_response_rotate_secret(self, client: Lithic) -> None: class TestAsyncDecisioning: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + @parametrize + async def test_method_challenge_response(self, async_client: AsyncLithic) -> None: + decisioning = await async_client.three_ds.decisioning.challenge_response( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + challenge_response="APPROVE", + ) + assert decisioning is None + + @parametrize + async def test_raw_response_challenge_response(self, async_client: AsyncLithic) -> None: + response = await async_client.three_ds.decisioning.with_raw_response.challenge_response( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + challenge_response="APPROVE", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + decisioning = response.parse() + assert decisioning is None + + @parametrize + async def test_streaming_response_challenge_response(self, async_client: AsyncLithic) -> None: + async with async_client.three_ds.decisioning.with_streaming_response.challenge_response( + token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + challenge_response="APPROVE", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + decisioning = await response.parse() + assert decisioning is None + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_retrieve_secret(self, async_client: AsyncLithic) -> None: decisioning = await async_client.three_ds.decisioning.retrieve_secret() diff --git a/tests/api_resources/transactions/__init__.py b/tests/api_resources/transactions/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/transactions/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/transactions/events/__init__.py b/tests/api_resources/transactions/events/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/transactions/events/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/transactions/events/test_enhanced_commercial_data.py b/tests/api_resources/transactions/events/test_enhanced_commercial_data.py new file mode 100644 index 00000000..a9eb3ce9 --- /dev/null +++ b/tests/api_resources/transactions/events/test_enhanced_commercial_data.py @@ -0,0 +1,98 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types.transactions.events import EnhancedData + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEnhancedCommercialData: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + enhanced_commercial_data = client.transactions.events.enhanced_commercial_data.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(EnhancedData, enhanced_commercial_data, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.transactions.events.enhanced_commercial_data.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + enhanced_commercial_data = response.parse() + assert_matches_type(EnhancedData, enhanced_commercial_data, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.transactions.events.enhanced_commercial_data.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + enhanced_commercial_data = response.parse() + assert_matches_type(EnhancedData, enhanced_commercial_data, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `event_token` but received ''"): + client.transactions.events.enhanced_commercial_data.with_raw_response.retrieve( + "", + ) + + +class TestAsyncEnhancedCommercialData: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + enhanced_commercial_data = await async_client.transactions.events.enhanced_commercial_data.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(EnhancedData, enhanced_commercial_data, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.transactions.events.enhanced_commercial_data.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + enhanced_commercial_data = response.parse() + assert_matches_type(EnhancedData, enhanced_commercial_data, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.transactions.events.enhanced_commercial_data.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + enhanced_commercial_data = await response.parse() + assert_matches_type(EnhancedData, enhanced_commercial_data, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `event_token` but received ''"): + await async_client.transactions.events.enhanced_commercial_data.with_raw_response.retrieve( + "", + ) diff --git a/tests/api_resources/transactions/test_enhanced_commercial_data.py b/tests/api_resources/transactions/test_enhanced_commercial_data.py new file mode 100644 index 00000000..7330e2e5 --- /dev/null +++ b/tests/api_resources/transactions/test_enhanced_commercial_data.py @@ -0,0 +1,98 @@ +# 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 lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types.transactions import EnhancedCommercialDataRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEnhancedCommercialData: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + enhanced_commercial_data = client.transactions.enhanced_commercial_data.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(EnhancedCommercialDataRetrieveResponse, enhanced_commercial_data, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.transactions.enhanced_commercial_data.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + enhanced_commercial_data = response.parse() + assert_matches_type(EnhancedCommercialDataRetrieveResponse, enhanced_commercial_data, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.transactions.enhanced_commercial_data.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + enhanced_commercial_data = response.parse() + assert_matches_type(EnhancedCommercialDataRetrieveResponse, enhanced_commercial_data, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `transaction_token` but received ''"): + client.transactions.enhanced_commercial_data.with_raw_response.retrieve( + "", + ) + + +class TestAsyncEnhancedCommercialData: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + enhanced_commercial_data = await async_client.transactions.enhanced_commercial_data.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(EnhancedCommercialDataRetrieveResponse, enhanced_commercial_data, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.transactions.enhanced_commercial_data.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + enhanced_commercial_data = response.parse() + assert_matches_type(EnhancedCommercialDataRetrieveResponse, enhanced_commercial_data, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.transactions.enhanced_commercial_data.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + enhanced_commercial_data = await response.parse() + assert_matches_type(EnhancedCommercialDataRetrieveResponse, enhanced_commercial_data, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `transaction_token` but received ''"): + await async_client.transactions.enhanced_commercial_data.with_raw_response.retrieve( + "", + ) diff --git a/tests/conftest.py b/tests/conftest.py index 029c962e..f38ded45 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,11 @@ from __future__ import annotations import os -import asyncio import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator import pytest +from pytest_asyncio import is_async_test from lithic import Lithic, AsyncLithic @@ -17,11 +17,13 @@ logging.getLogger("lithic").setLevel(logging.DEBUG) -@pytest.fixture(scope="session") -def event_loop() -> Iterator[asyncio.AbstractEventLoop]: - loop = asyncio.new_event_loop() - yield loop - loop.close() +# automatically add `pytest.mark.asyncio()` to all of our async tests +# so we don't have to add that boilerplate everywhere +def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: + pytest_asyncio_tests = (item for item in items if is_async_test(item)) + session_scope_marker = pytest.mark.asyncio(loop_scope="session") + for async_test in pytest_asyncio_tests: + async_test.add_marker(session_scope_marker, append=False) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") diff --git a/tests/sample_file.txt b/tests/sample_file.txt new file mode 100644 index 00000000..af5626b4 --- /dev/null +++ b/tests/sample_file.txt @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/test_client.py b/tests/test_client.py index 7c53d83b..db2c23c4 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,15 +1,20 @@ -# File generated from our OpenAPI spec by Stainless. +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import gc import os +import sys import json +import time import asyncio import inspect +import subprocess import tracemalloc from typing import Any, Union, cast +from textwrap import dedent from unittest import mock +from typing_extensions import Literal import httpx import pytest @@ -17,11 +22,13 @@ from pydantic import ValidationError from lithic import Lithic, AsyncLithic, APIResponseValidationError -from lithic._client import Lithic, AsyncLithic +from lithic._types import Omit +from lithic._utils import maybe_transform from lithic._models import BaseModel, FinalRequestOptions from lithic._constants import RAW_RESPONSE_HEADER from lithic._exceptions import LithicError, APIStatusError, APITimeoutError, APIResponseValidationError from lithic._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options +from lithic.types.card_create_params import CardCreateParams from .utils import update_env @@ -291,6 +298,16 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + async def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + async with httpx.AsyncClient() as http_client: + Lithic( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + def test_default_headers_option(self) -> None: client = Lithic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} @@ -318,7 +335,8 @@ def test_validate_headers(self) -> None: assert request.headers.get("Authorization") == api_key with pytest.raises(LithicError): - client2 = Lithic(base_url=base_url, api_key=None, _strict_response_validation=True) + with update_env(**{"LITHIC_API_KEY": Omit()}): + client2 = Lithic(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 def test_default_query_option(self) -> None: @@ -333,11 +351,11 @@ def test_default_query_option(self) -> None: FinalRequestOptions( method="get", url="/foo", - params={"foo": "baz", "query_param": "overriden"}, + params={"foo": "baz", "query_param": "overridden"}, ) ) url = httpx.URL(request.url) - assert dict(url.params) == {"foo": "baz", "query_param": "overriden"} + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} def test_request_extra_json(self) -> None: request = self.client._build_request( @@ -541,7 +559,7 @@ def test_base_url_env(self) -> None: Lithic(api_key=api_key, _strict_response_validation=True, environment="production") client = Lithic(base_url=None, api_key=api_key, _strict_response_validation=True, environment="production") - assert str(client.base_url).startswith("https://api.lithic.com/v1") + assert str(client.base_url).startswith("https://api.lithic.com") @pytest.mark.parametrize( "client", @@ -612,97 +630,6 @@ def test_absolute_request_url(self, client: Lithic) -> None: ) assert request.url == "https://myapi.com/foo" - def test_transport_option_is_deprecated(self) -> None: - with pytest.warns( - DeprecationWarning, - match="The `transport` argument is deprecated. The `http_client` argument should be passed instead", - ): - transport = httpx.MockTransport( - lambda: None, # type: ignore - ) - - client = Lithic(base_url=base_url, api_key=api_key, _strict_response_validation=True, transport=transport) - - assert client._client._transport is transport - - def test_transport_option_mutually_exclusive_with_http_client(self) -> None: - with httpx.Client() as http_client: - with pytest.raises(ValueError, match="The `http_client` argument is mutually exclusive with `transport`"): - with pytest.warns(DeprecationWarning): - Lithic( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - transport=httpx.MockTransport( - lambda: None, # type: ignore - ), - http_client=http_client, - ) - - def test_connection_pool_limits_option_is_deprecated(self) -> None: - with pytest.warns( - DeprecationWarning, - match="The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - ): - connection_pool_limits = httpx.Limits( - max_connections=101, max_keepalive_connections=76, keepalive_expiry=23 - ) - - client = Lithic( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - connection_pool_limits=connection_pool_limits, - ) - - assert isinstance(client._client._transport, httpx.HTTPTransport) - assert client._client._transport._pool._max_connections == 101 - assert client._client._transport._pool._max_keepalive_connections == 76 - assert client._client._transport._pool._keepalive_expiry == 23 - - def test_connection_pool_limits_option_mutually_exclusive_with_http_client(self) -> None: - with httpx.Client() as http_client: - with pytest.raises( - ValueError, match="The `http_client` argument is mutually exclusive with `connection_pool_limits`" - ): - with pytest.warns(DeprecationWarning): - Lithic( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - connection_pool_limits=httpx.Limits( - max_connections=101, max_keepalive_connections=76, keepalive_expiry=23 - ), - http_client=http_client, - ) - - def test_proxies_option_is_deprecated(self) -> None: - with pytest.warns( - DeprecationWarning, - match="The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - ): - proxies = "https://www.example.com/proxy" - - client = Lithic(base_url=base_url, api_key=api_key, _strict_response_validation=True, proxies=proxies) - - mounts = list(client._client._mounts.keys()) - assert len(mounts) == 1 - - pattern = mounts[0].pattern - assert pattern == "all://" - - def test_proxies_option_mutually_exclusive_with_http_client(self) -> None: - with httpx.Client() as http_client: - with pytest.raises(ValueError, match="The `http_client` argument is mutually exclusive with `proxies`"): - with pytest.warns(DeprecationWarning): - Lithic( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - proxies="https://www.example.com/proxy", - http_client=http_client, - ) - def test_copied_client_does_not_close_http(self) -> None: client = Lithic(base_url=base_url, api_key=api_key, _strict_response_validation=True) assert not client.is_closed() @@ -734,6 +661,10 @@ class Model(BaseModel): assert isinstance(exc.value.__cause__, ValidationError) + def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + Lithic(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) + @pytest.mark.respx(base_url=base_url) def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): @@ -769,6 +700,7 @@ class Model(BaseModel): [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], + [-1100, "", 8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -783,12 +715,12 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: - respx_mock.post("/cards").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/v1/cards").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): self.client.post( - "/cards", - body=cast(object, dict(type="SINGLE_USE")), + "/v1/cards", + body=cast(object, maybe_transform(dict(type="SINGLE_USE"), CardCreateParams)), cast_to=httpx.Response, options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, ) @@ -798,18 +730,122 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: - respx_mock.post("/cards").mock(return_value=httpx.Response(500)) + respx_mock.post("/v1/cards").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): self.client.post( - "/cards", - body=cast(object, dict(type="SINGLE_USE")), + "/v1/cards", + body=cast(object, maybe_transform(dict(type="SINGLE_USE"), CardCreateParams)), cast_to=httpx.Response, options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, ) assert _get_open_connections(self.client) == 0 + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + def test_retries_taken( + self, + client: Lithic, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/cards").mock(side_effect=retry_handler) + + response = client.cards.with_raw_response.create(type="MERCHANT_LOCKED") + + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_omit_retry_count_header( + self, client: Lithic, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/cards").mock(side_effect=retry_handler) + + response = client.cards.with_raw_response.create( + type="MERCHANT_LOCKED", extra_headers={"x-stainless-retry-count": Omit()} + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_overwrite_retry_count_header( + self, client: Lithic, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/cards").mock(side_effect=retry_handler) + + response = client.cards.with_raw_response.create( + type="MERCHANT_LOCKED", extra_headers={"x-stainless-retry-count": "42"} + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retries_taken_new_response_class( + self, client: Lithic, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/cards").mock(side_effect=retry_handler) + + with client.cards.with_streaming_response.create(type="MERCHANT_LOCKED") as response: + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + class TestAsyncLithic: client = AsyncLithic(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -1059,6 +1095,16 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + with httpx.Client() as http_client: + AsyncLithic( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + def test_default_headers_option(self) -> None: client = AsyncLithic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} @@ -1086,7 +1132,8 @@ def test_validate_headers(self) -> None: assert request.headers.get("Authorization") == api_key with pytest.raises(LithicError): - client2 = AsyncLithic(base_url=base_url, api_key=None, _strict_response_validation=True) + with update_env(**{"LITHIC_API_KEY": Omit()}): + client2 = AsyncLithic(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 def test_default_query_option(self) -> None: @@ -1101,11 +1148,11 @@ def test_default_query_option(self) -> None: FinalRequestOptions( method="get", url="/foo", - params={"foo": "baz", "query_param": "overriden"}, + params={"foo": "baz", "query_param": "overridden"}, ) ) url = httpx.URL(request.url) - assert dict(url.params) == {"foo": "baz", "query_param": "overriden"} + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} def test_request_extra_json(self) -> None: request = self.client._build_request( @@ -1313,7 +1360,7 @@ def test_base_url_env(self) -> None: client = AsyncLithic( base_url=None, api_key=api_key, _strict_response_validation=True, environment="production" ) - assert str(client.base_url).startswith("https://api.lithic.com/v1") + assert str(client.base_url).startswith("https://api.lithic.com") @pytest.mark.parametrize( "client", @@ -1390,99 +1437,6 @@ def test_absolute_request_url(self, client: AsyncLithic) -> None: ) assert request.url == "https://myapi.com/foo" - def test_transport_option_is_deprecated(self) -> None: - with pytest.warns( - DeprecationWarning, - match="The `transport` argument is deprecated. The `http_client` argument should be passed instead", - ): - transport = httpx.MockTransport( - lambda: None, # type: ignore - ) - - client = AsyncLithic( - base_url=base_url, api_key=api_key, _strict_response_validation=True, transport=transport - ) - - assert client._client._transport is transport - - async def test_transport_option_mutually_exclusive_with_http_client(self) -> None: - async with httpx.AsyncClient() as http_client: - with pytest.raises(ValueError, match="The `http_client` argument is mutually exclusive with `transport`"): - with pytest.warns(DeprecationWarning): - AsyncLithic( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - transport=httpx.MockTransport( - lambda: None, # type: ignore - ), - http_client=http_client, - ) - - def test_connection_pool_limits_option_is_deprecated(self) -> None: - with pytest.warns( - DeprecationWarning, - match="The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - ): - connection_pool_limits = httpx.Limits( - max_connections=101, max_keepalive_connections=76, keepalive_expiry=23 - ) - - client = AsyncLithic( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - connection_pool_limits=connection_pool_limits, - ) - - assert isinstance(client._client._transport, httpx.AsyncHTTPTransport) - assert client._client._transport._pool._max_connections == 101 - assert client._client._transport._pool._max_keepalive_connections == 76 - assert client._client._transport._pool._keepalive_expiry == 23 - - async def test_connection_pool_limits_option_mutually_exclusive_with_http_client(self) -> None: - async with httpx.AsyncClient() as http_client: - with pytest.raises( - ValueError, match="The `http_client` argument is mutually exclusive with `connection_pool_limits`" - ): - with pytest.warns(DeprecationWarning): - AsyncLithic( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - connection_pool_limits=httpx.Limits( - max_connections=101, max_keepalive_connections=76, keepalive_expiry=23 - ), - http_client=http_client, - ) - - def test_proxies_option_is_deprecated(self) -> None: - with pytest.warns( - DeprecationWarning, - match="The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - ): - proxies = "https://www.example.com/proxy" - - client = AsyncLithic(base_url=base_url, api_key=api_key, _strict_response_validation=True, proxies=proxies) - - mounts = list(client._client._mounts.keys()) - assert len(mounts) == 1 - - pattern = mounts[0].pattern - assert pattern == "all://" - - async def test_proxies_option_mutually_exclusive_with_http_client(self) -> None: - async with httpx.AsyncClient() as http_client: - with pytest.raises(ValueError, match="The `http_client` argument is mutually exclusive with `proxies`"): - with pytest.warns(DeprecationWarning): - AsyncLithic( - base_url=base_url, - api_key=api_key, - _strict_response_validation=True, - proxies="https://www.example.com/proxy", - http_client=http_client, - ) - async def test_copied_client_does_not_close_http(self) -> None: client = AsyncLithic(base_url=base_url, api_key=api_key, _strict_response_validation=True) assert not client.is_closed() @@ -1516,6 +1470,12 @@ class Model(BaseModel): assert isinstance(exc.value.__cause__, ValidationError) + async def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + AsyncLithic( + base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None) + ) + @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: @@ -1552,6 +1512,7 @@ class Model(BaseModel): [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], + [-1100, "", 8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -1567,12 +1528,12 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: - respx_mock.post("/cards").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/v1/cards").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): await self.client.post( - "/cards", - body=cast(object, dict(type="SINGLE_USE")), + "/v1/cards", + body=cast(object, maybe_transform(dict(type="SINGLE_USE"), CardCreateParams)), cast_to=httpx.Response, options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, ) @@ -1582,14 +1543,167 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: - respx_mock.post("/cards").mock(return_value=httpx.Response(500)) + respx_mock.post("/v1/cards").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): await self.client.post( - "/cards", - body=cast(object, dict(type="SINGLE_USE")), + "/v1/cards", + body=cast(object, maybe_transform(dict(type="SINGLE_USE"), CardCreateParams)), cast_to=httpx.Response, options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, ) assert _get_open_connections(self.client) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + async def test_retries_taken( + self, + async_client: AsyncLithic, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/cards").mock(side_effect=retry_handler) + + response = await client.cards.with_raw_response.create(type="MERCHANT_LOCKED") + + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_omit_retry_count_header( + self, async_client: AsyncLithic, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/cards").mock(side_effect=retry_handler) + + response = await client.cards.with_raw_response.create( + type="MERCHANT_LOCKED", extra_headers={"x-stainless-retry-count": Omit()} + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_overwrite_retry_count_header( + self, async_client: AsyncLithic, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/cards").mock(side_effect=retry_handler) + + response = await client.cards.with_raw_response.create( + type="MERCHANT_LOCKED", extra_headers={"x-stainless-retry-count": "42"} + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("lithic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_retries_taken_new_response_class( + self, async_client: AsyncLithic, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/cards").mock(side_effect=retry_handler) + + async with client.cards.with_streaming_response.create(type="MERCHANT_LOCKED") as response: + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + def test_get_platform(self) -> None: + # A previous implementation of asyncify could leave threads unterminated when + # used with nest_asyncio. + # + # Since nest_asyncio.apply() is global and cannot be un-applied, this + # test is run in a separate process to avoid affecting other tests. + test_code = dedent(""" + import asyncio + import nest_asyncio + import threading + + from lithic._utils import asyncify + from lithic._base_client import get_platform + + async def test_main() -> None: + result = await asyncify(get_platform)() + print(result) + for thread in threading.enumerate(): + print(thread.name) + + nest_asyncio.apply() + asyncio.run(test_main()) + """) + with subprocess.Popen( + [sys.executable, "-c", test_code], + text=True, + ) as process: + timeout = 10 # seconds + + start_time = time.monotonic() + while True: + return_code = process.poll() + if return_code is not None: + if return_code != 0: + raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") + + # success + break + + if time.monotonic() - start_time > timeout: + process.kill() + raise AssertionError("calling get_platform using asyncify resulted in a hung process") + + time.sleep(0.1) diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py index c4de8b11..b513e83a 100644 --- a/tests/test_deepcopy.py +++ b/tests/test_deepcopy.py @@ -41,8 +41,7 @@ def test_nested_list() -> None: assert_different_identities(obj1[1], obj2[1]) -class MyObject: - ... +class MyObject: ... def test_ignores_other_types() -> None: diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py index 7bc981c5..e9a5972a 100644 --- a/tests/test_legacy_response.py +++ b/tests/test_legacy_response.py @@ -1,4 +1,6 @@ import json +from typing import Any, Union, cast +from typing_extensions import Annotated import httpx import pytest @@ -10,8 +12,7 @@ from lithic._legacy_response import LegacyAPIResponse -class PydanticModel(pydantic.BaseModel): - ... +class PydanticModel(pydantic.BaseModel): ... def test_response_parse_mismatched_basemodel(client: Lithic) -> None: @@ -31,6 +32,31 @@ def test_response_parse_mismatched_basemodel(client: Lithic) -> None: response.parse(to=PydanticModel) +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +def test_response_parse_bool(client: Lithic, content: str, expected: bool) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = response.parse(to=bool) + assert result is expected + + def test_response_parse_custom_stream(client: Lithic) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=b"foo"), @@ -63,3 +89,40 @@ def test_response_parse_custom_model(client: Lithic) -> None: obj = response.parse(to=CustomModel) assert obj.foo == "hello!" assert obj.bar == 2 + + +def test_response_parse_annotated_type(client: Lithic) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +class OtherModel(pydantic.BaseModel): + a: str + + +@pytest.mark.parametrize("client", [False], indirect=True) # loose validation +def test_response_parse_expect_model_union_non_json_content(client: Lithic) -> None: + response = LegacyAPIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" diff --git a/tests/test_models.py b/tests/test_models.py index 3c00abb2..154fc144 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,14 +1,15 @@ import json from typing import Any, Dict, List, Union, Optional, cast from datetime import datetime, timezone -from typing_extensions import Literal +from typing_extensions import Literal, Annotated, TypeAliasType import pytest import pydantic from pydantic import Field +from lithic._utils import PropertyInfo from lithic._compat import PYDANTIC_V2, parse_obj, model_dump, model_json -from lithic._models import BaseModel +from lithic._models import BaseModel, construct_type class BasicModel(BaseModel): @@ -30,7 +31,7 @@ class NestedModel(BaseModel): # mismatched types m = NestedModel.construct(nested="hello!") - assert m.nested == "hello!" + assert cast(Any, m.nested) == "hello!" def test_optional_nested_model() -> None: @@ -47,7 +48,7 @@ class NestedModel(BaseModel): # mismatched types m3 = NestedModel.construct(nested={"foo"}) assert isinstance(cast(Any, m3.nested), set) - assert m3.nested == {"foo"} + assert cast(Any, m3.nested) == {"foo"} def test_list_nested_model() -> None: @@ -244,7 +245,7 @@ class Model(BaseModel): assert m.foo is True m = Model.construct(foo="CARD_HOLDER") - assert m.foo is "CARD_HOLDER" + assert m.foo == "CARD_HOLDER" m = Model.construct(foo={"bar": False}) assert isinstance(m.foo, Submodel1) @@ -322,7 +323,7 @@ class Model(BaseModel): assert len(m.items) == 2 assert isinstance(m.items[0], Submodel1) assert m.items[0].level == -1 - assert m.items[1] == 156 + assert cast(Any, m.items[1]) == 156 def test_union_of_lists() -> None: @@ -354,7 +355,7 @@ class Model(BaseModel): assert len(m.items) == 2 assert isinstance(m.items[0], SubModel1) assert m.items[0].level == -1 - assert m.items[1] == 156 + assert cast(Any, m.items[1]) == 156 def test_dict_of_union() -> None: @@ -500,6 +501,38 @@ class Model(BaseModel): assert "resource_id" in m.model_fields_set +def test_to_dict() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.to_dict() == {"FOO": "hello"} + assert m.to_dict(use_api_names=False) == {"foo": "hello"} + + m2 = Model() + assert m2.to_dict() == {} + assert m2.to_dict(exclude_unset=False) == {"FOO": None} + assert m2.to_dict(exclude_unset=False, exclude_none=True) == {} + assert m2.to_dict(exclude_unset=False, exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.to_dict() == {"FOO": None} + assert m3.to_dict(exclude_none=True) == {} + assert m3.to_dict(exclude_defaults=True) == {} + + class Model2(BaseModel): + created_at: datetime + + time_str = "2024-03-21T11:39:01.275859" + m4 = Model2.construct(created_at=time_str) + assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} + assert m4.to_dict(mode="json") == {"created_at": time_str} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_dict(warnings=False) + + def test_forwards_compat_model_dump_method() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) @@ -521,9 +554,6 @@ class Model(BaseModel): assert m3.model_dump(exclude_none=True) == {} if not PYDANTIC_V2: - with pytest.raises(ValueError, match="mode is only supported in Pydantic v2"): - m.model_dump(mode="json") - with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump(round_trip=True) @@ -531,6 +561,42 @@ class Model(BaseModel): m.model_dump(warnings=False) +def test_compat_method_no_error_for_warnings() -> None: + class Model(BaseModel): + foo: Optional[str] + + m = Model(foo="hello") + assert isinstance(model_dump(m, warnings=False), dict) + + +def test_to_json() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.to_json()) == {"FOO": "hello"} + assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} + + if PYDANTIC_V2: + assert m.to_json(indent=None) == '{"FOO":"hello"}' + else: + assert m.to_json(indent=None) == '{"FOO": "hello"}' + + m2 = Model() + assert json.loads(m2.to_json()) == {} + assert json.loads(m2.to_json(exclude_unset=False)) == {"FOO": None} + assert json.loads(m2.to_json(exclude_unset=False, exclude_none=True)) == {} + assert json.loads(m2.to_json(exclude_unset=False, exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.to_json()) == {"FOO": None} + assert json.loads(m3.to_json(exclude_none=True)) == {} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_json(warnings=False) + + def test_forwards_compat_model_dump_json_method() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) @@ -571,3 +637,252 @@ class OurModel(BaseModel): foo: Optional[str] = None takes_pydantic(OurModel()) + + +def test_annotated_types() -> None: + class Model(BaseModel): + value: str + + m = construct_type( + value={"value": "foo"}, + type_=cast(Any, Annotated[Model, "random metadata"]), + ) + assert isinstance(m, Model) + assert m.value == "foo" + + +def test_discriminated_unions_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, A) + assert m.type == "a" + if PYDANTIC_V2: + assert m.data == 100 # type: ignore[comparison-overlap] + else: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + + +def test_discriminated_unions_unknown_variant() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "c", "data": None, "new_thing": "bar"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + + # just chooses the first variant + assert isinstance(m, A) + assert m.type == "c" # type: ignore[comparison-overlap] + assert m.data == None # type: ignore[unreachable] + assert m.new_thing == "bar" + + +def test_discriminated_unions_invalid_data_nested_unions() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + class C(BaseModel): + type: Literal["c"] + + data: bool + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "c", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, C) + assert m.type == "c" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_with_aliases_invalid_data() -> None: + class A(BaseModel): + foo_type: Literal["a"] = Field(alias="type") + + data: str + + class B(BaseModel): + foo_type: Literal["b"] = Field(alias="type") + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, B) + assert m.foo_type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, A) + assert m.foo_type == "a" + if PYDANTIC_V2: + assert m.data == 100 # type: ignore[comparison-overlap] + else: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + + +def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["a"] + + data: int + + m = construct_type( + value={"type": "a", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "a" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_invalid_data_uses_cache() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + UnionType = cast(Any, Union[A, B]) + + assert not hasattr(UnionType, "__discriminator__") + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + discriminator = UnionType.__discriminator__ + assert discriminator is not None + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + # if the discriminator details object stays the same between invocations then + # we hit the cache + assert UnionType.__discriminator__ is discriminator + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +def test_type_alias_type() -> None: + Alias = TypeAliasType("Alias", str) + + class Model(BaseModel): + alias: Alias + union: Union[int, Alias] + + m = construct_type(value={"alias": "foo", "union": "bar"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.alias, str) + assert m.alias == "foo" + assert isinstance(m.union, str) + assert m.union == "bar" + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +def test_field_named_cls() -> None: + class Model(BaseModel): + cls: str + + m = construct_type(value={"cls": "foo"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.cls, str) + + +def test_discriminated_union_case() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["b"] + + data: List[Union[A, object]] + + class ModelA(BaseModel): + type: Literal["modelA"] + + data: int + + class ModelB(BaseModel): + type: Literal["modelB"] + + required: str + + data: Union[A, B] + + # when constructing ModelA | ModelB, value data doesn't match ModelB exactly - missing `required` + m = construct_type( + value={"type": "modelB", "data": {"type": "a", "data": True}}, + type_=cast(Any, Annotated[Union[ModelA, ModelB], PropertyInfo(discriminator="type")]), + ) + + assert isinstance(m, ModelB) diff --git a/tests/test_response.py b/tests/test_response.py index ae1587a7..5c0709ed 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -1,5 +1,6 @@ import json -from typing import List +from typing import Any, List, Union, cast +from typing_extensions import Annotated import httpx import pytest @@ -18,16 +19,13 @@ from lithic._base_client import FinalRequestOptions -class ConcreteBaseAPIResponse(APIResponse[bytes]): - ... +class ConcreteBaseAPIResponse(APIResponse[bytes]): ... -class ConcreteAPIResponse(APIResponse[List[str]]): - ... +class ConcreteAPIResponse(APIResponse[List[str]]): ... -class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): - ... +class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): ... def test_extract_response_type_direct_classes() -> None: @@ -55,8 +53,7 @@ def test_extract_response_type_binary_response() -> None: assert extract_response_type(AsyncBinaryAPIResponse) == bytes -class PydanticModel(pydantic.BaseModel): - ... +class PydanticModel(pydantic.BaseModel): ... def test_response_parse_mismatched_basemodel(client: Lithic) -> None: @@ -157,3 +154,124 @@ async def test_async_response_parse_custom_model(async_client: AsyncLithic) -> N obj = await response.parse(to=CustomModel) assert obj.foo == "hello!" assert obj.bar == 2 + + +def test_response_parse_annotated_type(client: Lithic) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +async def test_async_response_parse_annotated_type(async_client: AsyncLithic) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +def test_response_parse_bool(client: Lithic, content: str, expected: bool) -> None: + response = APIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = response.parse(to=bool) + assert result is expected + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +async def test_async_response_parse_bool(client: AsyncLithic, content: str, expected: bool) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = await response.parse(to=bool) + assert result is expected + + +class OtherModel(BaseModel): + a: str + + +@pytest.mark.parametrize("client", [False], indirect=True) # loose validation +def test_response_parse_expect_model_union_non_json_content(client: Lithic) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation +async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncLithic) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" diff --git a/tests/test_streaming.py b/tests/test_streaming.py index 088a7677..2e09be02 100644 --- a/tests/test_streaming.py +++ b/tests/test_streaming.py @@ -1,104 +1,248 @@ +from __future__ import annotations + from typing import Iterator, AsyncIterator +import httpx import pytest -from lithic._streaming import SSEDecoder +from lithic import Lithic, AsyncLithic +from lithic._streaming import Stream, AsyncStream, ServerSentEvent @pytest.mark.asyncio -async def test_basic_async() -> None: - async def body() -> AsyncIterator[str]: - yield "event: completion" - yield 'data: {"foo":true}' - yield "" - - async for sse in SSEDecoder().aiter(body()): - assert sse.event == "completion" - assert sse.json() == {"foo": True} +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_basic(sync: bool, client: Lithic, async_client: AsyncLithic) -> None: + def body() -> Iterator[bytes]: + yield b"event: completion\n" + yield b'data: {"foo":true}\n' + yield b"\n" + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) -def test_basic() -> None: - def body() -> Iterator[str]: - yield "event: completion" - yield 'data: {"foo":true}' - yield "" - - it = SSEDecoder().iter(body()) - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "completion" assert sse.json() == {"foo": True} - with pytest.raises(StopIteration): - next(it) + await assert_empty_iter(iterator) -def test_data_missing_event() -> None: - def body() -> Iterator[str]: - yield 'data: {"foo":true}' - yield "" +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_missing_event(sync: bool, client: Lithic, async_client: AsyncLithic) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"foo":true}\n' + yield b"\n" - it = SSEDecoder().iter(body()) - sse = next(it) + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) assert sse.event is None assert sse.json() == {"foo": True} - with pytest.raises(StopIteration): - next(it) + await assert_empty_iter(iterator) + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_event_missing_data(sync: bool, client: Lithic, async_client: AsyncLithic) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" -def test_event_missing_data() -> None: - def body() -> Iterator[str]: - yield "event: ping" - yield "" + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) - it = SSEDecoder().iter(body()) - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "ping" assert sse.data == "" - with pytest.raises(StopIteration): - next(it) + await assert_empty_iter(iterator) -def test_multiple_events() -> None: - def body() -> Iterator[str]: - yield "event: ping" - yield "" - yield "event: completion" - yield "" +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events(sync: bool, client: Lithic, async_client: AsyncLithic) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + yield b"event: completion\n" + yield b"\n" - it = SSEDecoder().iter(body()) + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "ping" assert sse.data == "" - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "completion" assert sse.data == "" - with pytest.raises(StopIteration): - next(it) - - -def test_multiple_events_with_data() -> None: - def body() -> Iterator[str]: - yield "event: ping" - yield 'data: {"foo":true}' - yield "" - yield "event: completion" - yield 'data: {"bar":false}' - yield "" + await assert_empty_iter(iterator) - it = SSEDecoder().iter(body()) - sse = next(it) +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events_with_data(sync: bool, client: Lithic, async_client: AsyncLithic) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo":true}\n' + yield b"\n" + yield b"event: completion\n" + yield b'data: {"bar":false}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) assert sse.event == "ping" assert sse.json() == {"foo": True} - sse = next(it) + sse = await iter_next(iterator) assert sse.event == "completion" assert sse.json() == {"bar": False} - with pytest.raises(StopIteration): - next(it) + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines_with_empty_line(sync: bool, client: Lithic, async_client: AsyncLithic) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: \n" + yield b"data:\n" + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + assert sse.data == '{\n"foo":\n\n\ntrue}' + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_json_escaped_double_new_line(sync: bool, client: Lithic, async_client: AsyncLithic) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo": "my long\\n\\ncontent"}' + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": "my long\n\ncontent"} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines(sync: bool, client: Lithic, async_client: AsyncLithic) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_special_new_line_character( + sync: bool, + client: Lithic, + async_client: AsyncLithic, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":" culpa"}\n' + yield b"\n" + yield b'data: {"content":" \xe2\x80\xa8"}\n' + yield b"\n" + yield b'data: {"content":"foo"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " culpa"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " 
"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "foo"} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multi_byte_character_multiple_chunks( + sync: bool, + client: Lithic, + async_client: AsyncLithic, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":"' + # bytes taken from the string 'известни' and arbitrarily split + # so that some multi-byte characters span multiple chunks + yield b"\xd0" + yield b"\xb8\xd0\xb7\xd0" + yield b"\xb2\xd0\xb5\xd1\x81\xd1\x82\xd0\xbd\xd0\xb8" + yield b'"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "известни"} + + +async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]: + for chunk in iter: + yield chunk + + +async def iter_next(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> ServerSentEvent: + if isinstance(iter, AsyncIterator): + return await iter.__anext__() + + return next(iter) + + +async def assert_empty_iter(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> None: + with pytest.raises((StopAsyncIteration, RuntimeError)): + await iter_next(iter) + + +def make_event_iterator( + content: Iterator[bytes], + *, + sync: bool, + client: Lithic, + async_client: AsyncLithic, +) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]: + if sync: + return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events() + + return AsyncStream( + cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content)) + )._iter_events() diff --git a/tests/test_transform.py b/tests/test_transform.py index c57a6172..7ce3274b 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -1,22 +1,50 @@ from __future__ import annotations -from typing import Any, List, Union, Iterable, Optional, cast +import io +import pathlib +from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict import pytest -from lithic._utils import PropertyInfo, transform, parse_datetime +from lithic._types import Base64FileInput +from lithic._utils import ( + PropertyInfo, + transform as _transform, + parse_datetime, + async_transform as _async_transform, +) from lithic._compat import PYDANTIC_V2 from lithic._models import BaseModel +_T = TypeVar("_T") + +SAMPLE_FILE_PATH = pathlib.Path(__file__).parent.joinpath("sample_file.txt") + + +async def transform( + data: _T, + expected_type: object, + use_async: bool, +) -> _T: + if use_async: + return await _async_transform(data, expected_type=expected_type) + + return _transform(data, expected_type=expected_type) + + +parametrize = pytest.mark.parametrize("use_async", [False, True], ids=["sync", "async"]) + class Foo1(TypedDict): foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] -def test_top_level_alias() -> None: - assert transform({"foo_bar": "hello"}, expected_type=Foo1) == {"fooBar": "hello"} +@parametrize +@pytest.mark.asyncio +async def test_top_level_alias(use_async: bool) -> None: + assert await transform({"foo_bar": "hello"}, expected_type=Foo1, use_async=use_async) == {"fooBar": "hello"} class Foo2(TypedDict): @@ -32,9 +60,11 @@ class Baz2(TypedDict): my_baz: Annotated[str, PropertyInfo(alias="myBaz")] -def test_recursive_typeddict() -> None: - assert transform({"bar": {"this_thing": 1}}, Foo2) == {"bar": {"this__thing": 1}} - assert transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2) == {"bar": {"Baz": {"myBaz": "foo"}}} +@parametrize +@pytest.mark.asyncio +async def test_recursive_typeddict(use_async: bool) -> None: + assert await transform({"bar": {"this_thing": 1}}, Foo2, use_async) == {"bar": {"this__thing": 1}} + assert await transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2, use_async) == {"bar": {"Baz": {"myBaz": "foo"}}} class Foo3(TypedDict): @@ -45,8 +75,10 @@ class Bar3(TypedDict): my_field: Annotated[str, PropertyInfo(alias="myField")] -def test_list_of_typeddict() -> None: - result = transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, expected_type=Foo3) +@parametrize +@pytest.mark.asyncio +async def test_list_of_typeddict(use_async: bool) -> None: + result = await transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, Foo3, use_async) assert result == {"things": [{"myField": "foo"}, {"myField": "foo2"}]} @@ -62,10 +94,14 @@ class Baz4(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] -def test_union_of_typeddict() -> None: - assert transform({"foo": {"foo_bar": "bar"}}, Foo4) == {"foo": {"fooBar": "bar"}} - assert transform({"foo": {"foo_baz": "baz"}}, Foo4) == {"foo": {"fooBaz": "baz"}} - assert transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4) == {"foo": {"fooBaz": "baz", "fooBar": "bar"}} +@parametrize +@pytest.mark.asyncio +async def test_union_of_typeddict(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo4, use_async) == {"foo": {"fooBar": "bar"}} + assert await transform({"foo": {"foo_baz": "baz"}}, Foo4, use_async) == {"foo": {"fooBaz": "baz"}} + assert await transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4, use_async) == { + "foo": {"fooBaz": "baz", "fooBar": "bar"} + } class Foo5(TypedDict): @@ -80,9 +116,11 @@ class Baz5(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] -def test_union_of_list() -> None: - assert transform({"foo": {"foo_bar": "bar"}}, Foo5) == {"FOO": {"fooBar": "bar"}} - assert transform( +@parametrize +@pytest.mark.asyncio +async def test_union_of_list(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo5, use_async) == {"FOO": {"fooBar": "bar"}} + assert await transform( { "foo": [ {"foo_baz": "baz"}, @@ -90,6 +128,7 @@ def test_union_of_list() -> None: ] }, Foo5, + use_async, ) == {"FOO": [{"fooBaz": "baz"}, {"fooBaz": "baz"}]} @@ -97,8 +136,10 @@ class Foo6(TypedDict): bar: Annotated[str, PropertyInfo(alias="Bar")] -def test_includes_unknown_keys() -> None: - assert transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6) == { +@parametrize +@pytest.mark.asyncio +async def test_includes_unknown_keys(use_async: bool) -> None: + assert await transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6, use_async) == { "Bar": "bar", "baz_": {"FOO": 1}, } @@ -113,9 +154,11 @@ class Bar7(TypedDict): foo: str -def test_ignores_invalid_input() -> None: - assert transform({"bar": ""}, Foo7) == {"bAr": ""} - assert transform({"foo": ""}, Foo7) == {"foo": ""} +@parametrize +@pytest.mark.asyncio +async def test_ignores_invalid_input(use_async: bool) -> None: + assert await transform({"bar": ""}, Foo7, use_async) == {"bAr": ""} + assert await transform({"foo": ""}, Foo7, use_async) == {"foo": ""} class DatetimeDict(TypedDict, total=False): @@ -134,52 +177,81 @@ class DateDict(TypedDict, total=False): foo: Annotated[date, PropertyInfo(format="iso8601")] -def test_iso8601_format() -> None: +class DatetimeModel(BaseModel): + foo: datetime + + +class DateModel(BaseModel): + foo: Optional[date] + + +@parametrize +@pytest.mark.asyncio +async def test_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - assert transform({"foo": dt}, DatetimeDict) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + tz = "Z" if PYDANTIC_V2 else "+00:00" + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] dt = dt.replace(tzinfo=None) - assert transform({"foo": dt}, DatetimeDict) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] - - assert transform({"foo": None}, DateDict) == {"foo": None} # type: ignore[comparison-overlap] - assert transform({"foo": date.fromisoformat("2023-02-23")}, DateDict) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + + assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=None), Any, use_async) == {"foo": None} # type: ignore + assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=date.fromisoformat("2023-02-23")), DateDict, use_async) == { + "foo": "2023-02-23" + } # type: ignore[comparison-overlap] -def test_optional_iso8601_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_optional_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - assert transform({"bar": dt}, DatetimeDict) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform({"bar": dt}, DatetimeDict, use_async) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] - assert transform({"bar": None}, DatetimeDict) == {"bar": None} + assert await transform({"bar": None}, DatetimeDict, use_async) == {"bar": None} -def test_required_iso8601_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_required_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - assert transform({"required": dt}, DatetimeDict) == {"required": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform({"required": dt}, DatetimeDict, use_async) == { + "required": "2023-02-23T14:16:36.337692+00:00" + } # type: ignore[comparison-overlap] - assert transform({"required": None}, DatetimeDict) == {"required": None} + assert await transform({"required": None}, DatetimeDict, use_async) == {"required": None} -def test_union_datetime() -> None: +@parametrize +@pytest.mark.asyncio +async def test_union_datetime(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - assert transform({"union": dt}, DatetimeDict) == { # type: ignore[comparison-overlap] + assert await transform({"union": dt}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] "union": "2023-02-23T14:16:36.337692+00:00" } - assert transform({"union": "foo"}, DatetimeDict) == {"union": "foo"} + assert await transform({"union": "foo"}, DatetimeDict, use_async) == {"union": "foo"} -def test_nested_list_iso6801_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_nested_list_iso6801_format(use_async: bool) -> None: dt1 = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") dt2 = parse_datetime("2022-01-15T06:34:23Z") - assert transform({"list_": [dt1, dt2]}, DatetimeDict) == { # type: ignore[comparison-overlap] + assert await transform({"list_": [dt1, dt2]}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] "list_": ["2023-02-23T14:16:36.337692+00:00", "2022-01-15T06:34:23+00:00"] } -def test_datetime_custom_format() -> None: +@parametrize +@pytest.mark.asyncio +async def test_datetime_custom_format(use_async: bool) -> None: dt = parse_datetime("2022-01-15T06:34:23Z") - result = transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")]) + result = await transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], use_async) assert result == "06" # type: ignore[comparison-overlap] @@ -187,58 +259,74 @@ class DateDictWithRequiredAlias(TypedDict, total=False): required_prop: Required[Annotated[date, PropertyInfo(format="iso8601", alias="prop")]] -def test_datetime_with_alias() -> None: - assert transform({"required_prop": None}, DateDictWithRequiredAlias) == {"prop": None} # type: ignore[comparison-overlap] - assert transform({"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias) == { - "prop": "2023-02-23" - } # type: ignore[comparison-overlap] +@parametrize +@pytest.mark.asyncio +async def test_datetime_with_alias(use_async: bool) -> None: + assert await transform({"required_prop": None}, DateDictWithRequiredAlias, use_async) == {"prop": None} # type: ignore[comparison-overlap] + assert await transform( + {"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias, use_async + ) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] class MyModel(BaseModel): foo: str -def test_pydantic_model_to_dictionary() -> None: - assert transform(MyModel(foo="hi!"), Any) == {"foo": "hi!"} - assert transform(MyModel.construct(foo="hi!"), Any) == {"foo": "hi!"} +@parametrize +@pytest.mark.asyncio +async def test_pydantic_model_to_dictionary(use_async: bool) -> None: + assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"} -def test_pydantic_empty_model() -> None: - assert transform(MyModel.construct(), Any) == {} +@parametrize +@pytest.mark.asyncio +async def test_pydantic_empty_model(use_async: bool) -> None: + assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {} -def test_pydantic_unknown_field() -> None: - assert transform(MyModel.construct(my_untyped_field=True), Any) == {"my_untyped_field": True} +@parametrize +@pytest.mark.asyncio +async def test_pydantic_unknown_field(use_async: bool) -> None: + assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == { + "my_untyped_field": True + } -def test_pydantic_mismatched_types() -> None: +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_types(use_async: bool) -> None: model = MyModel.construct(foo=True) if PYDANTIC_V2: with pytest.warns(UserWarning): - params = transform(model, Any) + params = await transform(model, Any, use_async) else: - params = transform(model, Any) - assert params == {"foo": True} + params = await transform(model, Any, use_async) + assert cast(Any, params) == {"foo": True} -def test_pydantic_mismatched_object_type() -> None: +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_object_type(use_async: bool) -> None: model = MyModel.construct(foo=MyModel.construct(hello="world")) if PYDANTIC_V2: with pytest.warns(UserWarning): - params = transform(model, Any) + params = await transform(model, Any, use_async) else: - params = transform(model, Any) - assert params == {"foo": {"hello": "world"}} + params = await transform(model, Any, use_async) + assert cast(Any, params) == {"foo": {"hello": "world"}} class ModelNestedObjects(BaseModel): nested: MyModel -def test_pydantic_nested_objects() -> None: +@parametrize +@pytest.mark.asyncio +async def test_pydantic_nested_objects(use_async: bool) -> None: model = ModelNestedObjects.construct(nested={"foo": "stainless"}) assert isinstance(model.nested, MyModel) - assert transform(model, Any) == {"nested": {"foo": "stainless"}} + assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}} class ModelWithDefaultField(BaseModel): @@ -247,24 +335,26 @@ class ModelWithDefaultField(BaseModel): with_str_default: str = "foo" -def test_pydantic_default_field() -> None: +@parametrize +@pytest.mark.asyncio +async def test_pydantic_default_field(use_async: bool) -> None: # should be excluded when defaults are used model = ModelWithDefaultField.construct() assert model.with_none_default is None assert model.with_str_default == "foo" - assert transform(model, Any) == {} + assert cast(Any, await transform(model, Any, use_async)) == {} # should be included when the default value is explicitly given model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") assert model.with_none_default is None assert model.with_str_default == "foo" - assert transform(model, Any) == {"with_none_default": None, "with_str_default": "foo"} + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"} # should be included when a non-default value is explicitly given model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") assert model.with_none_default == "bar" assert model.with_str_default == "baz" - assert transform(model, Any) == {"with_none_default": "bar", "with_str_default": "baz"} + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} class TypedDictIterableUnion(TypedDict): @@ -279,21 +369,78 @@ class Baz8(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] -def test_iterable_of_dictionaries() -> None: - assert transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "bar"}]} - assert cast(Any, transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion)) == {"FOO": [{"fooBaz": "bar"}]} +@parametrize +@pytest.mark.asyncio +async def test_iterable_of_dictionaries(use_async: bool) -> None: + assert await transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "bar"}] + } + assert cast(Any, await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async)) == { + "FOO": [{"fooBaz": "bar"}] + } def my_iter() -> Iterable[Baz8]: yield {"foo_baz": "hello"} yield {"foo_baz": "world"} - assert transform({"foo": my_iter()}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}]} + assert await transform({"foo": my_iter()}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}] + } + + +@parametrize +@pytest.mark.asyncio +async def test_dictionary_items(use_async: bool) -> None: + class DictItems(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}} class TypedDictIterableUnionStr(TypedDict): foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] -def test_iterable_union_str() -> None: - assert transform({"foo": "bar"}, TypedDictIterableUnionStr) == {"FOO": "bar"} - assert cast(Any, transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]])) == [{"fooBaz": "bar"}] +@parametrize +@pytest.mark.asyncio +async def test_iterable_union_str(use_async: bool) -> None: + assert await transform({"foo": "bar"}, TypedDictIterableUnionStr, use_async) == {"FOO": "bar"} + assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [ + {"fooBaz": "bar"} + ] + + +class TypedDictBase64Input(TypedDict): + foo: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] + + +@parametrize +@pytest.mark.asyncio +async def test_base64_file_input(use_async: bool) -> None: + # strings are left as-is + assert await transform({"foo": "bar"}, TypedDictBase64Input, use_async) == {"foo": "bar"} + + # pathlib.Path is automatically converted to base64 + assert await transform({"foo": SAMPLE_FILE_PATH}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQo=" + } # type: ignore[comparison-overlap] + + # io instances are automatically converted to base64 + assert await transform({"foo": io.StringIO("Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] + assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_transform_skipping(use_async: bool) -> None: + # lists of ints are left as-is + data = [1, 2, 3] + assert await transform(data, List[int], use_async) is data + + # iterables of ints are converted to a list + data = iter([1, 2, 3]) + assert await transform(data, Iterable[int], use_async) == [1, 2, 3] diff --git a/tests/test_utils/test_typing.py b/tests/test_utils/test_typing.py index a75ffb27..35bbef91 100644 --- a/tests/test_utils/test_typing.py +++ b/tests/test_utils/test_typing.py @@ -9,24 +9,19 @@ _T3 = TypeVar("_T3") -class BaseGeneric(Generic[_T]): - ... +class BaseGeneric(Generic[_T]): ... -class SubclassGeneric(BaseGeneric[_T]): - ... +class SubclassGeneric(BaseGeneric[_T]): ... -class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): - ... +class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): ... -class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): - ... +class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): ... -class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): - ... +class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): ... def test_extract_type_var() -> None: diff --git a/tests/utils.py b/tests/utils.py index 4f4ed1ec..afd0d680 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,12 +8,15 @@ from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type -from lithic._types import NoneType +from lithic._types import Omit, NoneType from lithic._utils import ( is_dict, is_list, is_list_type, is_union_type, + extract_type_arg, + is_annotated_type, + is_type_alias_type, ) from lithic._compat import PYDANTIC_V2, field_outer_type, get_model_fields from lithic._models import BaseModel @@ -49,6 +52,13 @@ def assert_matches_type( path: list[str], allow_none: bool = False, ) -> None: + if is_type_alias_type(type_): + type_ = type_.__value__ + + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(type_): + type_ = extract_type_arg(type_, 0) + if allow_none and value is None: return @@ -91,7 +101,22 @@ def assert_matches_type( assert_matches_type(key_type, key, path=[*path, ""]) assert_matches_type(items_type, item, path=[*path, ""]) elif is_union_type(type_): - for i, variant in enumerate(get_args(type_)): + variants = get_args(type_) + + try: + none_index = variants.index(type(None)) + except ValueError: + pass + else: + # special case Optional[T] for better error messages + if len(variants) == 2: + if value is None: + # valid + return + + return assert_matches_type(type_=variants[not none_index], value=value, path=path) + + for i, variant in enumerate(variants): try: assert_matches_type(variant, value, path=[*path, f"variant {i}"]) return @@ -118,11 +143,15 @@ def _assert_list_type(type_: type[object], value: object) -> None: @contextlib.contextmanager -def update_env(**new_env: str) -> Iterator[None]: +def update_env(**new_env: str | Omit) -> Iterator[None]: old = os.environ.copy() try: - os.environ.update(new_env) + for name, value in new_env.items(): + if isinstance(value, Omit): + os.environ.pop(name, None) + else: + os.environ[name] = value yield None finally: