diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2969a756..820b5c1a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.111.0" + ".": "0.112.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2ec777a4..0e130cdf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 171 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-9791980619fc7ce8afb01f77dfe3c660a540975327378287cb666136ae4b0a99.yml -openapi_spec_hash: 0752074b2a7b0534329a1e3176c94a62 -config_hash: aab05d0cf41f1f6b9f4d5677273c1600 +configured_endpoints: 175 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-abe6a4f82f696099fa8ecb1cc44f08979e17d56578ae7ea68b0e9182e21df508.yml +openapi_spec_hash: d2ce51592a9a234c6f34a1168a31f91f +config_hash: f4b1d2f464e80527f970de61cba0c52f diff --git a/CHANGELOG.md b/CHANGELOG.md index b9aa57e1..9f8eb077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,47 @@ # Changelog +## 0.112.0 (2025-12-10) + +Full Changelog: [v0.111.0...v0.112.0](https://github.com/lithic-com/lithic-python/compare/v0.111.0...v0.112.0) + +### Features + +* **api:** add bulk card creation APIs ([8bb0722](https://github.com/lithic-com/lithic-python/commit/8bb072220516545e9adbd32ad3aef1f51c029a8b)) +* **api:** Add event_streams to auth rules APIs ([ad154eb](https://github.com/lithic-com/lithic-python/commit/ad154eb4737943666314f8a53d63e3bf4d48514e)) +* **api:** add Google WPP to SDKs ([75159c4](https://github.com/lithic-com/lithic-python/commit/75159c4e762e68281b00fec859e69052d3a45d3b)) +* **api:** add IS_AFTER / IS_BEFORE operators to Auth Rule APIs ([75159c4](https://github.com/lithic-com/lithic-python/commit/75159c4e762e68281b00fec859e69052d3a45d3b)) +* **api:** Add new fee types ([ad154eb](https://github.com/lithic-com/lithic-python/commit/ad154eb4737943666314f8a53d63e3bf4d48514e)) +* **api:** Add optional ach_hold_period ([ad154eb](https://github.com/lithic-com/lithic-python/commit/ad154eb4737943666314f8a53d63e3bf4d48514e)) +* **api:** add WALLET_RECOMMENDATION_REASONS attribute for tokenization rules ([78148ad](https://github.com/lithic-com/lithic-python/commit/78148adea18a42644b9778613c3f459f4003aeec)) +* **api:** add webhook schemas to SDKs - add parse and parse_unsafe ([d977e3f](https://github.com/lithic-com/lithic-python/commit/d977e3fc8beb01522aadb98d12df79e823cf128b)) +* **api:** provide a unified model for AuthRule ([78148ad](https://github.com/lithic-com/lithic-python/commit/78148adea18a42644b9778613c3f459f4003aeec)) +* **api:** support event_streams in auth_rules list endpoint ([befa333](https://github.com/lithic-com/lithic-python/commit/befa3335fabdc8099e8cf7dee8b32ae269c9c890)) + + +### Bug Fixes + +* **api:** extract several common types to reduce duplication ([8bb0722](https://github.com/lithic-com/lithic-python/commit/8bb072220516545e9adbd32ad3aef1f51c029a8b)) +* **api:** fix examples in spec that were not fully valid ([78148ad](https://github.com/lithic-com/lithic-python/commit/78148adea18a42644b9778613c3f459f4003aeec)) +* **api:** fix webhook parsing logic ([fc50832](https://github.com/lithic-com/lithic-python/commit/fc508320a25f3716b2f55880169df2c9c8faa056)) +* **api:** make certain payoff fields nullable ([78148ad](https://github.com/lithic-com/lithic-python/commit/78148adea18a42644b9778613c3f459f4003aeec)) +* ensure streams are always closed ([7a9e41f](https://github.com/lithic-com/lithic-python/commit/7a9e41ff7a639075315b721842d95c13b624e421)) +* **types:** allow pyright to infer TypedDict types within SequenceNotStr ([d290cbb](https://github.com/lithic-com/lithic-python/commit/d290cbb1df6ed2ad2280dc150c106e6b2b3ca441)) + + +### Chores + +* add missing docstrings ([d297283](https://github.com/lithic-com/lithic-python/commit/d2972836067fdb76678128af20212c0b9bb4e8a0)) +* add Python 3.14 classifier and testing ([95a6172](https://github.com/lithic-com/lithic-python/commit/95a6172000f72b77904fcd0fb21c6ca83319b5ed)) +* **deps:** mypy 1.18.1 has a regression, pin to 1.17 ([6b9f3ab](https://github.com/lithic-com/lithic-python/commit/6b9f3abd0737a2db85284a76a9281a340c89b630)) +* **docs:** use environment variables for authentication in code snippets ([29ae3a9](https://github.com/lithic-com/lithic-python/commit/29ae3a9b42a67775cb5225887c6bb6a37a327cf4)) +* replace custom webhook signature verification with standardwebhooks ([d977e3f](https://github.com/lithic-com/lithic-python/commit/d977e3fc8beb01522aadb98d12df79e823cf128b)) +* update lockfile ([a7a6330](https://github.com/lithic-com/lithic-python/commit/a7a63309b13085625edd790131f3ad588a3f472f)) + + +### Documentation + +* **api:** clarify error 422 for 3DS challenge response ([75159c4](https://github.com/lithic-com/lithic-python/commit/75159c4e762e68281b00fec859e69052d3a45d3b)) + ## 0.111.0 (2025-11-20) Full Changelog: [v0.110.1...v0.111.0](https://github.com/lithic-com/lithic-python/compare/v0.110.1...v0.111.0) diff --git a/README.md b/README.md index 0ab4218b..f9a17781 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ pip install lithic[aiohttp] Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: ```python +import os import asyncio from lithic import DefaultAioHttpClient from lithic import AsyncLithic @@ -92,7 +93,7 @@ from lithic import AsyncLithic async def main() -> None: async with AsyncLithic( - api_key="My Lithic API Key", + api_key=os.environ.get("LITHIC_API_KEY"), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: card = await client.cards.create( @@ -176,27 +177,70 @@ for card in first_page.data: # Remove `await` for non-async usage. ``` -## Webhook Verification +## Nested params + +Nested parameters are dictionaries. The SDK uses TypedDict for type validation, but you can pass regular dictionaries as shown below: + +```python +from lithic import Lithic + +client = Lithic() + +card = client.cards.create( + type="PHYSICAL", + shipping_address={ + "address1": "123", + "city": "NEW YORK", + "country": "USA", + "first_name": "Johnny", + "last_name": "Appleseed", + "postal_code": "10001", + "state": "NY", + }, +) +``` + +## Webhooks + +Lithic uses webhooks to notify your application when events happen. The library provides signature verification via the optional `standardwebhooks` package. -We provide helper methods for verifying that a webhook request came from Lithic, and not a malicious third party. +### Parsing and verifying webhooks -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. +```py +from lithic.types import CardCreatedWebhookEvent + +# Verifies signature and returns typed event +event = client.webhooks.parse( + request.body, # raw request body as string + headers=request.headers, + secret=os.environ["LITHIC_WEBHOOK_SECRET"] # optional, reads from env by default +) -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. +# Use isinstance to narrow the type +if isinstance(event, CardCreatedWebhookEvent): + print(f"Card created: {event.card_token}") +``` -For example, in [FastAPI](https://fastapi.tiangolo.com/): +### Parsing without verification ```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} +# Parse only - skips signature verification (not recommended for production) +event = client.webhooks.parse_unsafe(request.body) +``` + +### Verifying signatures only + +```py +# Verify signature without parsing (raises exception if invalid) +client.webhooks.verify_signature(request.body, headers=request.headers, secret=secret) +``` + +### Installing standardwebhooks (optional) + +To use signature verification, install the webhooks extra: + +```sh +pip install lithic[webhooks] ``` ## Handling errors diff --git a/api.md b/api.md index 41ec2abd..c07d4f4d 100644 --- a/api.md +++ b/api.md @@ -91,16 +91,11 @@ from lithic.types.auth_rules import ( ConditionalOperation, ConditionalTokenizationActionParameters, ConditionalValue, + EventStream, MerchantLockParameters, RuleStats, VelocityLimitParams, VelocityLimitPeriod, - V2CreateResponse, - V2RetrieveResponse, - V2UpdateResponse, - V2ListResponse, - V2DraftResponse, - V2PromoteResponse, V2RetrieveFeaturesResponse, V2RetrieveReportResponse, ) @@ -108,13 +103,13 @@ from lithic.types.auth_rules import ( 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.create(\*\*params) -> AuthRule +- client.auth_rules.v2.retrieve(auth_rule_token) -> AuthRule +- client.auth_rules.v2.update(auth_rule_token, \*\*params) -> AuthRule +- client.auth_rules.v2.list(\*\*params) -> SyncCursorPage[AuthRule] - client.auth_rules.v2.delete(auth_rule_token) -> None -- client.auth_rules.v2.draft(auth_rule_token, \*\*params) -> V2DraftResponse -- client.auth_rules.v2.promote(auth_rule_token) -> V2PromoteResponse +- client.auth_rules.v2.draft(auth_rule_token, \*\*params) -> AuthRule +- client.auth_rules.v2.promote(auth_rule_token) -> AuthRule - client.auth_rules.v2.retrieve_features(auth_rule_token, \*\*params) -> V2RetrieveFeaturesResponse - client.auth_rules.v2.retrieve_report(auth_rule_token, \*\*params) -> V2RetrieveReportResponse @@ -163,9 +158,13 @@ Types: ```python from lithic.types import ( + Device, + DigitalWalletTokenMetadata, Tokenization, - TokenizationSimulateResponse, - TokenizationUpdateDigitalCardArtResponse, + TokenizationDeclineReason, + TokenizationRuleResult, + TokenizationTfaReason, + WalletDecisioningInfo, ) ``` @@ -177,9 +176,9 @@ Methods: - 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.simulate(\*\*params) -> Tokenization - client.tokenizations.unpause(tokenization_token) -> None -- client.tokenizations.update_digital_card_art(tokenization_token, \*\*params) -> TokenizationUpdateDigitalCardArtResponse +- client.tokenizations.update_digital_card_art(tokenization_token, \*\*params) -> Tokenization # Cards @@ -229,15 +228,9 @@ Methods: ## Balances -Types: - -```python -from lithic.types.cards import BalanceListResponse -``` - Methods: -- client.cards.balances.list(card_token, \*\*params) -> SyncSinglePage[BalanceListResponse] +- client.cards.balances.list(card_token, \*\*params) -> SyncSinglePage[FinancialAccountBalance] ## FinancialTransactions @@ -246,6 +239,21 @@ Methods: - client.cards.financial_transactions.retrieve(financial_transaction_token, \*, card_token) -> FinancialTransaction - client.cards.financial_transactions.list(card_token, \*\*params) -> SyncSinglePage[FinancialTransaction] +# CardBulkOrders + +Types: + +```python +from lithic.types import CardBulkOrder +``` + +Methods: + +- client.card_bulk_orders.create(\*\*params) -> CardBulkOrder +- client.card_bulk_orders.retrieve(bulk_order_token) -> CardBulkOrder +- client.card_bulk_orders.update(bulk_order_token, \*\*params) -> CardBulkOrder +- client.card_bulk_orders.list(\*\*params) -> SyncCursorPage[CardBulkOrder] + # Balances Types: @@ -352,7 +360,13 @@ Methods: Types: ```python -from lithic.types import CategoryDetails, FinancialAccount, FinancialTransaction, StatementTotals +from lithic.types import ( + CategoryDetails, + FinancialAccount, + FinancialAccountBalance, + FinancialTransaction, + StatementTotals, +) ``` Methods: @@ -366,15 +380,9 @@ Methods: ## Balances -Types: - -```python -from lithic.types.financial_accounts import BalanceListResponse -``` - Methods: -- client.financial_accounts.balances.list(financial_account_token, \*\*params) -> SyncSinglePage[BalanceListResponse] +- client.financial_accounts.balances.list(financial_account_token, \*\*params) -> SyncSinglePage[FinancialAccountBalance] ## FinancialTransactions @@ -440,6 +448,8 @@ Types: ```python from lithic.types import ( + CardholderAuthentication, + TokenInfo, Transaction, TransactionSimulateAuthorizationResponse, TransactionSimulateAuthorizationAdviceResponse, @@ -512,6 +522,8 @@ Methods: - client.webhooks.unwrap(\*args) -> object - client.webhooks.verify_signature(\*args) -> None +- client.webhooks.parse(body, headers, secret) -> ParsedWebhookEvent +- client.webhooks.parse_unsafe(body) -> ParsedWebhookEvent # ExternalBankAccounts @@ -519,6 +531,7 @@ Types: ```python from lithic.types import ( + ExternalBankAccount, ExternalBankAccountAddress, OwnerType, VerificationMethod, @@ -527,8 +540,6 @@ from lithic.types import ( ExternalBankAccountUpdateResponse, ExternalBankAccountListResponse, ExternalBankAccountRetryMicroDepositsResponse, - ExternalBankAccountRetryPrenoteResponse, - ExternalBankAccountUnpauseResponse, ) ``` @@ -539,8 +550,8 @@ Methods: - 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 -- client.external_bank_accounts.unpause(external_bank_account_token) -> ExternalBankAccountUnpauseResponse +- client.external_bank_accounts.retry_prenote(external_bank_account_token, \*\*params) -> ExternalBankAccount +- client.external_bank_accounts.unpause(external_bank_account_token) -> ExternalBankAccount ## MicroDeposits @@ -584,17 +595,23 @@ Methods: # ThreeDS +Types: + +```python +from lithic.types import ThreeDSAuthentication +``` + ## Authentication Types: ```python -from lithic.types.three_ds import AuthenticationRetrieveResponse, AuthenticationSimulateResponse +from lithic.types.three_ds import AuthenticationSimulateResponse ``` Methods: -- client.three_ds.authentication.retrieve(three_ds_authentication_token) -> AuthenticationRetrieveResponse +- client.three_ds.authentication.retrieve(three_ds_authentication_token) -> ThreeDSAuthentication - client.three_ds.authentication.simulate(\*\*params) -> AuthenticationSimulateResponse - client.three_ds.authentication.simulate_otp_entry(\*\*params) -> None @@ -621,7 +638,7 @@ Methods: Types: ```python -from lithic.types import SettlementDetail, SettlementReport, SettlementSummaryDetails +from lithic.types import NetworkTotal, SettlementDetail, SettlementReport, SettlementSummaryDetails ``` ## Settlement @@ -633,16 +650,10 @@ Methods: ### NetworkTotals -Types: - -```python -from lithic.types.reports.settlement import NetworkTotalRetrieveResponse, NetworkTotalListResponse -``` - Methods: -- client.reports.settlement.network_totals.retrieve(token) -> NetworkTotalRetrieveResponse -- client.reports.settlement.network_totals.list(\*\*params) -> SyncCursorPage[NetworkTotalListResponse] +- client.reports.settlement.network_totals.retrieve(token) -> NetworkTotal +- client.reports.settlement.network_totals.list(\*\*params) -> SyncCursorPage[NetworkTotal] # CardPrograms @@ -745,22 +756,26 @@ Methods: - client.management_operations.list(\*\*params) -> SyncCursorPage[ManagementOperationTransaction] - client.management_operations.reverse(management_operation_token, \*\*params) -> ManagementOperationTransaction +# InternalTransaction + +Types: + +```python +from lithic.types import InternalTransaction +``` + # FundingEvents Types: ```python -from lithic.types import ( - FundingEventRetrieveResponse, - FundingEventListResponse, - FundingEventRetrieveDetailsResponse, -) +from lithic.types import FundingEvent, FundingEventRetrieveDetailsResponse ``` Methods: -- client.funding_events.retrieve(funding_event_token) -> FundingEventRetrieveResponse -- client.funding_events.list(\*\*params) -> SyncCursorPage[FundingEventListResponse] +- client.funding_events.retrieve(funding_event_token) -> FundingEvent +- client.funding_events.list(\*\*params) -> SyncCursorPage[FundingEvent] - client.funding_events.retrieve_details(funding_event_token) -> FundingEventRetrieveDetailsResponse # Fraud @@ -807,3 +822,67 @@ Methods: - client.account_activity.list(\*\*params) -> SyncCursorPage[AccountActivityListResponse] - client.account_activity.retrieve_transaction(transaction_token) -> AccountActivityRetrieveTransactionResponse + +# Webhooks + +Types: + +```python +from lithic.types import ( + AccountHolderCreatedWebhookEvent, + AccountHolderUpdatedWebhookEvent, + AccountHolderVerificationWebhookEvent, + AccountHolderDocumentUpdatedWebhookEvent, + AsaRequestWebhookEvent, + TokenizationDecisioningRequestWebhookEvent, + AuthRulesBacktestReportCreatedWebhookEvent, + BalanceUpdatedWebhookEvent, + BookTransferTransactionCreatedWebhookEvent, + BookTransferTransactionUpdatedWebhookEvent, + CardCreatedWebhookEvent, + CardConvertedWebhookEvent, + CardRenewedWebhookEvent, + CardReissuedWebhookEvent, + CardShippedWebhookEvent, + CardTransactionUpdatedWebhookEvent, + CardTransactionEnhancedDataCreatedWebhookEvent, + CardTransactionEnhancedDataUpdatedWebhookEvent, + DigitalWalletTokenizationApprovalRequestWebhookEvent, + DigitalWalletTokenizationResultWebhookEvent, + DigitalWalletTokenizationTwoFactorAuthenticationCodeWebhookEvent, + DigitalWalletTokenizationTwoFactorAuthenticationCodeSentWebhookEvent, + DigitalWalletTokenizationUpdatedWebhookEvent, + DisputeUpdatedWebhookEvent, + DisputeEvidenceUploadFailedWebhookEvent, + ExternalBankAccountCreatedWebhookEvent, + ExternalBankAccountUpdatedWebhookEvent, + ExternalPaymentCreatedWebhookEvent, + ExternalPaymentUpdatedWebhookEvent, + FinancialAccountCreatedWebhookEvent, + FinancialAccountUpdatedWebhookEvent, + FundingEventCreatedWebhookEvent, + LoanTapeCreatedWebhookEvent, + LoanTapeUpdatedWebhookEvent, + ManagementOperationCreatedWebhookEvent, + ManagementOperationUpdatedWebhookEvent, + InternalTransactionCreatedWebhookEvent, + InternalTransactionUpdatedWebhookEvent, + NetworkTotalCreatedWebhookEvent, + NetworkTotalUpdatedWebhookEvent, + PaymentTransactionCreatedWebhookEvent, + PaymentTransactionUpdatedWebhookEvent, + SettlementReportUpdatedWebhookEvent, + StatementsCreatedWebhookEvent, + ThreeDSAuthenticationCreatedWebhookEvent, + ThreeDSAuthenticationUpdatedWebhookEvent, + ThreeDSAuthenticationChallengeWebhookEvent, + TokenizationApprovalRequestWebhookEvent, + TokenizationResultWebhookEvent, + TokenizationTwoFactorAuthenticationCodeWebhookEvent, + TokenizationTwoFactorAuthenticationCodeSentWebhookEvent, + TokenizationUpdatedWebhookEvent, + DisputeTransactionCreatedWebhookEvent, + DisputeTransactionUpdatedWebhookEvent, + ParsedWebhookEvent, +) +``` diff --git a/pyproject.toml b/pyproject.toml index c3caf279..ba050cec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,22 @@ [project] name = "lithic" -version = "0.111.0" +version = "0.112.0" description = "The official Python library for the lithic API" dynamic = ["readme"] license = "Apache-2.0" authors = [ { name = "Lithic", email = "sdk-feedback@lithic.com" }, ] + dependencies = [ - "httpx>=0.23.0, <1", - "pydantic>=1.9.0, <3", - "typing-extensions>=4.10, <5", - "anyio>=3.5.0, <5", - "distro>=1.7.0, <2", - "sniffio", + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", + "typing-extensions>=4.10, <5", + "anyio>=3.5.0, <5", + "distro>=1.7.0, <2", + "sniffio", ] + requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", @@ -24,6 +26,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", @@ -39,13 +42,14 @@ Repository = "https://github.com/lithic-com/lithic-python" [project.optional-dependencies] aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] +webhooks = ["standardwebhooks"] [tool.rye] managed = true # version pins are in requirements-dev.lock dev-dependencies = [ "pyright==1.1.399", - "mypy", + "mypy==1.17", "respx", "pytest", "pytest-asyncio", @@ -56,6 +60,7 @@ dev-dependencies = [ "importlib-metadata>=6.7.0", "rich>=13.7.1", "pytest-xdist>=3.6.1", + "standardwebhooks", ] [tool.rye.scripts] diff --git a/requirements-dev.lock b/requirements-dev.lock index 0ef7413b..83c0dadf 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -12,40 +12,48 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.8 +aiohttp==3.13.2 # via httpx-aiohttp # via lithic -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.4.0 +anyio==4.12.0 # via httpx # via lithic -argcomplete==3.1.2 +argcomplete==3.6.3 # via nox async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 + # via nox + # via standardwebhooks +backports-asyncio-runner==1.2.0 + # via pytest-asyncio +certifi==2025.11.12 # via httpcore # via httpx -colorlog==6.7.0 +colorlog==6.10.1 + # via nox +dependency-groups==1.3.1 # via nox -dirty-equals==0.6.0 -distlib==0.3.7 +deprecated==1.3.1 + # via standardwebhooks +dirty-equals==0.11 +distlib==0.4.0 # via virtualenv -distro==1.8.0 +distro==1.9.0 # via lithic -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio # via pytest -execnet==2.1.1 +execnet==2.1.2 # via pytest-xdist -filelock==3.12.4 +filelock==3.19.1 # via virtualenv -frozenlist==1.6.2 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -56,82 +64,99 @@ httpx==0.28.1 # via httpx-aiohttp # via lithic # via respx + # via standardwebhooks httpx-aiohttp==0.1.9 # via lithic -idna==3.4 +humanize==4.13.0 + # via nox +idna==3.11 # via anyio # via httpx # via yarl -importlib-metadata==7.0.0 -iniconfig==2.0.0 +importlib-metadata==8.7.0 +iniconfig==2.1.0 # via pytest markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -multidict==6.4.4 +multidict==6.7.0 # via aiohttp # via yarl -mypy==1.14.1 -mypy-extensions==1.0.0 +mypy==1.17.0 +mypy-extensions==1.1.0 # via mypy -nodeenv==1.8.0 +nodeenv==1.9.1 # via pyright -nox==2023.4.22 -packaging==23.2 +nox==2025.11.12 +packaging==25.0 + # via dependency-groups # via nox # via pytest -platformdirs==3.11.0 +pathspec==0.12.1 + # via mypy +platformdirs==4.4.0 # via virtualenv -pluggy==1.5.0 +pluggy==1.6.0 # via pytest -propcache==0.3.1 +propcache==0.4.1 # via aiohttp # via yarl -pydantic==2.11.9 +pydantic==2.12.5 # via lithic -pydantic-core==2.33.2 +pydantic-core==2.41.5 # via pydantic -pygments==2.18.0 +pygments==2.19.2 + # via pytest # via rich pyright==1.1.399 -pytest==8.3.3 +pytest==8.4.2 # via pytest-asyncio # via pytest-xdist -pytest-asyncio==0.24.0 -pytest-xdist==3.7.0 -python-dateutil==2.8.2 +pytest-asyncio==1.2.0 +pytest-xdist==3.8.0 +python-dateutil==2.9.0.post0 + # via standardwebhooks # via time-machine -pytz==2023.3.post1 - # via dirty-equals respx==0.22.0 -rich==13.7.1 -ruff==0.9.4 -setuptools==68.2.2 - # via nodeenv -six==1.16.0 +rich==14.2.0 +ruff==0.14.7 +six==1.17.0 # via python-dateutil -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 + # via lithic +standardwebhooks==1.0.0 # via lithic -time-machine==2.9.0 -tomli==2.0.2 +time-machine==2.19.0 +tomli==2.3.0 + # via dependency-groups # via mypy + # via nox # via pytest -typing-extensions==4.12.2 +types-deprecated==1.3.1.20251101 + # via standardwebhooks +types-python-dateutil==2.9.0.20251115 + # via standardwebhooks +typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via lithic # via multidict # via mypy # via pydantic # via pydantic-core # via pyright + # via pytest-asyncio # via typing-inspection -typing-inspection==0.4.1 + # via virtualenv +typing-inspection==0.4.2 # via pydantic -virtualenv==20.24.5 +virtualenv==20.35.4 # via nox -yarl==1.20.0 +wrapt==2.0.1 + # via deprecated +yarl==1.22.0 # via aiohttp -zipp==3.17.0 +zipp==3.23.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index 3e496202..08c68c21 100644 --- a/requirements.lock +++ b/requirements.lock @@ -12,28 +12,31 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.8 +aiohttp==3.13.2 # via httpx-aiohttp # via lithic -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.4.0 +anyio==4.12.0 # via httpx # via lithic async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 + # via standardwebhooks +certifi==2025.11.12 # via httpcore # via httpx -distro==1.8.0 +deprecated==1.3.1 + # via standardwebhooks +distro==1.9.0 # via lithic -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio -frozenlist==1.6.2 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -43,33 +46,47 @@ httpcore==1.0.9 httpx==0.28.1 # via httpx-aiohttp # via lithic + # via standardwebhooks httpx-aiohttp==0.1.9 # via lithic -idna==3.4 +idna==3.11 # via anyio # via httpx # via yarl -multidict==6.4.4 +multidict==6.7.0 # via aiohttp # via yarl -propcache==0.3.1 +propcache==0.4.1 # via aiohttp # via yarl -pydantic==2.11.9 +pydantic==2.12.5 # via lithic -pydantic-core==2.33.2 +pydantic-core==2.41.5 # via pydantic -sniffio==1.3.0 - # via anyio +python-dateutil==2.9.0.post0 + # via standardwebhooks +six==1.17.0 + # via python-dateutil +sniffio==1.3.1 + # via lithic +standardwebhooks==1.0.0 # via lithic -typing-extensions==4.12.2 +types-deprecated==1.3.1.20251101 + # via standardwebhooks +types-python-dateutil==2.9.0.20251115 + # via standardwebhooks +typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via lithic # via multidict # via pydantic # via pydantic-core # via typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.2 # via pydantic -yarl==1.20.0 +wrapt==2.0.1 + # via deprecated +yarl==1.22.0 # via aiohttp diff --git a/src/lithic/__init__.py b/src/lithic/__init__.py index 207c4678..4617707e 100644 --- a/src/lithic/__init__.py +++ b/src/lithic/__init__.py @@ -35,6 +35,7 @@ InternalServerError, PermissionDeniedError, UnprocessableEntityError, + APIWebhookValidationError, APIResponseValidationError, ) from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient @@ -58,6 +59,7 @@ "APITimeoutError", "APIConnectionError", "APIResponseValidationError", + "APIWebhookValidationError", "BadRequestError", "AuthenticationError", "PermissionDeniedError", diff --git a/src/lithic/_client.py b/src/lithic/_client.py index 6582ea5c..363745a1 100644 --- a/src/lithic/_client.py +++ b/src/lithic/_client.py @@ -47,7 +47,6 @@ disputes, payments, three_ds, - webhooks, auth_rules, disputes_v2, transactions, @@ -58,6 +57,7 @@ account_holders, credit_products, account_activity, + card_bulk_orders, digital_card_art, network_programs, external_payments, @@ -73,6 +73,7 @@ from .resources.balances import Balances, AsyncBalances from .resources.disputes import Disputes, AsyncDisputes from .resources.payments import Payments, AsyncPayments + from .resources.webhooks import Webhooks, AsyncWebhooks from .resources.cards.cards import Cards, AsyncCards from .resources.disputes_v2 import DisputesV2, AsyncDisputesV2 from .resources.fraud.fraud import Fraud, AsyncFraud @@ -84,20 +85,45 @@ from .resources.account_holders import AccountHolders, AsyncAccountHolders from .resources.reports.reports import Reports, AsyncReports from .resources.account_activity import AccountActivity, AsyncAccountActivity - from .resources.digital_card_art import DigitalCardArtResource, AsyncDigitalCardArtResource + from .resources.card_bulk_orders import CardBulkOrders, AsyncCardBulkOrders + from .resources.digital_card_art import ( + DigitalCardArtResource, + AsyncDigitalCardArtResource, + ) from .resources.network_programs import NetworkPrograms, AsyncNetworkPrograms from .resources.external_payments import ExternalPayments, AsyncExternalPayments from .resources.three_ds.three_ds import ThreeDS, AsyncThreeDS from .resources.aggregate_balances import AggregateBalances, AsyncAggregateBalances - from .resources.responder_endpoints import ResponderEndpoints, AsyncResponderEndpoints + from .resources.responder_endpoints import ( + ResponderEndpoints, + AsyncResponderEndpoints, + ) from .resources.auth_rules.auth_rules import AuthRules, AsyncAuthRules - from .resources.management_operations import ManagementOperations, AsyncManagementOperations - from .resources.auth_stream_enrollment import AuthStreamEnrollment, AsyncAuthStreamEnrollment - from .resources.tokenization_decisioning import TokenizationDecisioning, AsyncTokenizationDecisioning + from .resources.management_operations import ( + ManagementOperations, + AsyncManagementOperations, + ) + from .resources.auth_stream_enrollment import ( + AuthStreamEnrollment, + AsyncAuthStreamEnrollment, + ) + from .resources.tokenization_decisioning import ( + TokenizationDecisioning, + AsyncTokenizationDecisioning, + ) from .resources.transactions.transactions import Transactions, AsyncTransactions - from .resources.credit_products.credit_products import CreditProducts, AsyncCreditProducts - from .resources.financial_accounts.financial_accounts import FinancialAccounts, AsyncFinancialAccounts - from .resources.external_bank_accounts.external_bank_accounts import ExternalBankAccounts, AsyncExternalBankAccounts + from .resources.credit_products.credit_products import ( + CreditProducts, + AsyncCreditProducts, + ) + from .resources.financial_accounts.financial_accounts import ( + FinancialAccounts, + AsyncFinancialAccounts, + ) + from .resources.external_bank_accounts.external_bank_accounts import ( + ExternalBankAccounts, + AsyncExternalBankAccounts, + ) __all__ = [ "ENVIRONMENTS", @@ -246,6 +272,12 @@ def cards(self) -> Cards: return Cards(self) + @cached_property + def card_bulk_orders(self) -> CardBulkOrders: + from .resources.card_bulk_orders import CardBulkOrders + + return CardBulkOrders(self) + @cached_property def balances(self) -> Balances: from .resources.balances import Balances @@ -360,12 +392,6 @@ def funding_events(self) -> FundingEvents: return FundingEvents(self) - @cached_property - def webhooks(self) -> webhooks.Webhooks: - from .resources.webhooks import Webhooks - - return Webhooks(self) - @cached_property def fraud(self) -> Fraud: from .resources.fraud import Fraud @@ -384,6 +410,12 @@ def account_activity(self) -> AccountActivity: return AccountActivity(self) + @cached_property + def webhooks(self) -> Webhooks: + from .resources.webhooks import Webhooks + + return Webhooks(self) + @cached_property def with_raw_response(self) -> LithicWithRawResponse: return LithicWithRawResponse(self) @@ -482,7 +514,10 @@ def api_status( return self.get( "/v1/status", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, ), cast_to=APIStatus, ) @@ -650,6 +685,12 @@ def cards(self) -> AsyncCards: return AsyncCards(self) + @cached_property + def card_bulk_orders(self) -> AsyncCardBulkOrders: + from .resources.card_bulk_orders import AsyncCardBulkOrders + + return AsyncCardBulkOrders(self) + @cached_property def balances(self) -> AsyncBalances: from .resources.balances import AsyncBalances @@ -783,7 +824,7 @@ def account_activity(self) -> AsyncAccountActivity: return AsyncAccountActivity(self) @cached_property - def webhooks(self) -> webhooks.AsyncWebhooks: + def webhooks(self) -> AsyncWebhooks: from .resources.webhooks import AsyncWebhooks return AsyncWebhooks(self) @@ -886,7 +927,10 @@ async def api_status( return await self.get( "/v1/status", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, ), cast_to=APIStatus, ) @@ -954,14 +998,22 @@ def auth_rules(self) -> auth_rules.AuthRulesWithRawResponse: return AuthRulesWithRawResponse(self._client.auth_rules) @cached_property - def auth_stream_enrollment(self) -> auth_stream_enrollment.AuthStreamEnrollmentWithRawResponse: - from .resources.auth_stream_enrollment import AuthStreamEnrollmentWithRawResponse + def auth_stream_enrollment( + self, + ) -> auth_stream_enrollment.AuthStreamEnrollmentWithRawResponse: + from .resources.auth_stream_enrollment import ( + AuthStreamEnrollmentWithRawResponse, + ) return AuthStreamEnrollmentWithRawResponse(self._client.auth_stream_enrollment) @cached_property - def tokenization_decisioning(self) -> tokenization_decisioning.TokenizationDecisioningWithRawResponse: - from .resources.tokenization_decisioning import TokenizationDecisioningWithRawResponse + def tokenization_decisioning( + self, + ) -> tokenization_decisioning.TokenizationDecisioningWithRawResponse: + from .resources.tokenization_decisioning import ( + TokenizationDecisioningWithRawResponse, + ) return TokenizationDecisioningWithRawResponse(self._client.tokenization_decisioning) @@ -977,6 +1029,12 @@ def cards(self) -> cards.CardsWithRawResponse: return CardsWithRawResponse(self._client.cards) + @cached_property + def card_bulk_orders(self) -> card_bulk_orders.CardBulkOrdersWithRawResponse: + from .resources.card_bulk_orders import CardBulkOrdersWithRawResponse + + return CardBulkOrdersWithRawResponse(self._client.card_bulk_orders) + @cached_property def balances(self) -> balances.BalancesWithRawResponse: from .resources.balances import BalancesWithRawResponse @@ -1020,14 +1078,20 @@ def transactions(self) -> transactions.TransactionsWithRawResponse: return TransactionsWithRawResponse(self._client.transactions) @cached_property - def responder_endpoints(self) -> responder_endpoints.ResponderEndpointsWithRawResponse: + def responder_endpoints( + self, + ) -> responder_endpoints.ResponderEndpointsWithRawResponse: from .resources.responder_endpoints import ResponderEndpointsWithRawResponse return ResponderEndpointsWithRawResponse(self._client.responder_endpoints) @cached_property - def external_bank_accounts(self) -> external_bank_accounts.ExternalBankAccountsWithRawResponse: - from .resources.external_bank_accounts import ExternalBankAccountsWithRawResponse + def external_bank_accounts( + self, + ) -> external_bank_accounts.ExternalBankAccountsWithRawResponse: + from .resources.external_bank_accounts import ( + ExternalBankAccountsWithRawResponse, + ) return ExternalBankAccountsWithRawResponse(self._client.external_bank_accounts) @@ -1056,7 +1120,9 @@ def card_programs(self) -> card_programs.CardProgramsWithRawResponse: return CardProgramsWithRawResponse(self._client.card_programs) @cached_property - def digital_card_art(self) -> digital_card_art.DigitalCardArtResourceWithRawResponse: + def digital_card_art( + self, + ) -> digital_card_art.DigitalCardArtResourceWithRawResponse: from .resources.digital_card_art import DigitalCardArtResourceWithRawResponse return DigitalCardArtResourceWithRawResponse(self._client.digital_card_art) @@ -1080,7 +1146,9 @@ def external_payments(self) -> external_payments.ExternalPaymentsWithRawResponse return ExternalPaymentsWithRawResponse(self._client.external_payments) @cached_property - def management_operations(self) -> management_operations.ManagementOperationsWithRawResponse: + def management_operations( + self, + ) -> management_operations.ManagementOperationsWithRawResponse: from .resources.management_operations import ManagementOperationsWithRawResponse return ManagementOperationsWithRawResponse(self._client.management_operations) @@ -1139,14 +1207,22 @@ def auth_rules(self) -> auth_rules.AsyncAuthRulesWithRawResponse: return AsyncAuthRulesWithRawResponse(self._client.auth_rules) @cached_property - def auth_stream_enrollment(self) -> auth_stream_enrollment.AsyncAuthStreamEnrollmentWithRawResponse: - from .resources.auth_stream_enrollment import AsyncAuthStreamEnrollmentWithRawResponse + def auth_stream_enrollment( + self, + ) -> auth_stream_enrollment.AsyncAuthStreamEnrollmentWithRawResponse: + from .resources.auth_stream_enrollment import ( + AsyncAuthStreamEnrollmentWithRawResponse, + ) return AsyncAuthStreamEnrollmentWithRawResponse(self._client.auth_stream_enrollment) @cached_property - def tokenization_decisioning(self) -> tokenization_decisioning.AsyncTokenizationDecisioningWithRawResponse: - from .resources.tokenization_decisioning import AsyncTokenizationDecisioningWithRawResponse + def tokenization_decisioning( + self, + ) -> tokenization_decisioning.AsyncTokenizationDecisioningWithRawResponse: + from .resources.tokenization_decisioning import ( + AsyncTokenizationDecisioningWithRawResponse, + ) return AsyncTokenizationDecisioningWithRawResponse(self._client.tokenization_decisioning) @@ -1162,6 +1238,12 @@ def cards(self) -> cards.AsyncCardsWithRawResponse: return AsyncCardsWithRawResponse(self._client.cards) + @cached_property + def card_bulk_orders(self) -> card_bulk_orders.AsyncCardBulkOrdersWithRawResponse: + from .resources.card_bulk_orders import AsyncCardBulkOrdersWithRawResponse + + return AsyncCardBulkOrdersWithRawResponse(self._client.card_bulk_orders) + @cached_property def balances(self) -> balances.AsyncBalancesWithRawResponse: from .resources.balances import AsyncBalancesWithRawResponse @@ -1169,7 +1251,9 @@ def balances(self) -> balances.AsyncBalancesWithRawResponse: return AsyncBalancesWithRawResponse(self._client.balances) @cached_property - def aggregate_balances(self) -> aggregate_balances.AsyncAggregateBalancesWithRawResponse: + def aggregate_balances( + self, + ) -> aggregate_balances.AsyncAggregateBalancesWithRawResponse: from .resources.aggregate_balances import AsyncAggregateBalancesWithRawResponse return AsyncAggregateBalancesWithRawResponse(self._client.aggregate_balances) @@ -1193,7 +1277,9 @@ def events(self) -> events.AsyncEventsWithRawResponse: return AsyncEventsWithRawResponse(self._client.events) @cached_property - def financial_accounts(self) -> financial_accounts.AsyncFinancialAccountsWithRawResponse: + def financial_accounts( + self, + ) -> financial_accounts.AsyncFinancialAccountsWithRawResponse: from .resources.financial_accounts import AsyncFinancialAccountsWithRawResponse return AsyncFinancialAccountsWithRawResponse(self._client.financial_accounts) @@ -1205,14 +1291,22 @@ def transactions(self) -> transactions.AsyncTransactionsWithRawResponse: return AsyncTransactionsWithRawResponse(self._client.transactions) @cached_property - def responder_endpoints(self) -> responder_endpoints.AsyncResponderEndpointsWithRawResponse: - from .resources.responder_endpoints import AsyncResponderEndpointsWithRawResponse + def responder_endpoints( + self, + ) -> responder_endpoints.AsyncResponderEndpointsWithRawResponse: + from .resources.responder_endpoints import ( + AsyncResponderEndpointsWithRawResponse, + ) return AsyncResponderEndpointsWithRawResponse(self._client.responder_endpoints) @cached_property - def external_bank_accounts(self) -> external_bank_accounts.AsyncExternalBankAccountsWithRawResponse: - from .resources.external_bank_accounts import AsyncExternalBankAccountsWithRawResponse + def external_bank_accounts( + self, + ) -> external_bank_accounts.AsyncExternalBankAccountsWithRawResponse: + from .resources.external_bank_accounts import ( + AsyncExternalBankAccountsWithRawResponse, + ) return AsyncExternalBankAccountsWithRawResponse(self._client.external_bank_accounts) @@ -1241,8 +1335,12 @@ def card_programs(self) -> card_programs.AsyncCardProgramsWithRawResponse: return AsyncCardProgramsWithRawResponse(self._client.card_programs) @cached_property - def digital_card_art(self) -> digital_card_art.AsyncDigitalCardArtResourceWithRawResponse: - from .resources.digital_card_art import AsyncDigitalCardArtResourceWithRawResponse + def digital_card_art( + self, + ) -> digital_card_art.AsyncDigitalCardArtResourceWithRawResponse: + from .resources.digital_card_art import ( + AsyncDigitalCardArtResourceWithRawResponse, + ) return AsyncDigitalCardArtResourceWithRawResponse(self._client.digital_card_art) @@ -1259,14 +1357,20 @@ def credit_products(self) -> credit_products.AsyncCreditProductsWithRawResponse: return AsyncCreditProductsWithRawResponse(self._client.credit_products) @cached_property - def external_payments(self) -> external_payments.AsyncExternalPaymentsWithRawResponse: + def external_payments( + self, + ) -> external_payments.AsyncExternalPaymentsWithRawResponse: from .resources.external_payments import AsyncExternalPaymentsWithRawResponse return AsyncExternalPaymentsWithRawResponse(self._client.external_payments) @cached_property - def management_operations(self) -> management_operations.AsyncManagementOperationsWithRawResponse: - from .resources.management_operations import AsyncManagementOperationsWithRawResponse + def management_operations( + self, + ) -> management_operations.AsyncManagementOperationsWithRawResponse: + from .resources.management_operations import ( + AsyncManagementOperationsWithRawResponse, + ) return AsyncManagementOperationsWithRawResponse(self._client.management_operations) @@ -1324,14 +1428,22 @@ def auth_rules(self) -> auth_rules.AuthRulesWithStreamingResponse: return AuthRulesWithStreamingResponse(self._client.auth_rules) @cached_property - def auth_stream_enrollment(self) -> auth_stream_enrollment.AuthStreamEnrollmentWithStreamingResponse: - from .resources.auth_stream_enrollment import AuthStreamEnrollmentWithStreamingResponse + def auth_stream_enrollment( + self, + ) -> auth_stream_enrollment.AuthStreamEnrollmentWithStreamingResponse: + from .resources.auth_stream_enrollment import ( + AuthStreamEnrollmentWithStreamingResponse, + ) return AuthStreamEnrollmentWithStreamingResponse(self._client.auth_stream_enrollment) @cached_property - def tokenization_decisioning(self) -> tokenization_decisioning.TokenizationDecisioningWithStreamingResponse: - from .resources.tokenization_decisioning import TokenizationDecisioningWithStreamingResponse + def tokenization_decisioning( + self, + ) -> tokenization_decisioning.TokenizationDecisioningWithStreamingResponse: + from .resources.tokenization_decisioning import ( + TokenizationDecisioningWithStreamingResponse, + ) return TokenizationDecisioningWithStreamingResponse(self._client.tokenization_decisioning) @@ -1347,6 +1459,12 @@ def cards(self) -> cards.CardsWithStreamingResponse: return CardsWithStreamingResponse(self._client.cards) + @cached_property + def card_bulk_orders(self) -> card_bulk_orders.CardBulkOrdersWithStreamingResponse: + from .resources.card_bulk_orders import CardBulkOrdersWithStreamingResponse + + return CardBulkOrdersWithStreamingResponse(self._client.card_bulk_orders) + @cached_property def balances(self) -> balances.BalancesWithStreamingResponse: from .resources.balances import BalancesWithStreamingResponse @@ -1354,7 +1472,9 @@ def balances(self) -> balances.BalancesWithStreamingResponse: return BalancesWithStreamingResponse(self._client.balances) @cached_property - def aggregate_balances(self) -> aggregate_balances.AggregateBalancesWithStreamingResponse: + def aggregate_balances( + self, + ) -> aggregate_balances.AggregateBalancesWithStreamingResponse: from .resources.aggregate_balances import AggregateBalancesWithStreamingResponse return AggregateBalancesWithStreamingResponse(self._client.aggregate_balances) @@ -1378,7 +1498,9 @@ def events(self) -> events.EventsWithStreamingResponse: return EventsWithStreamingResponse(self._client.events) @cached_property - def financial_accounts(self) -> financial_accounts.FinancialAccountsWithStreamingResponse: + def financial_accounts( + self, + ) -> financial_accounts.FinancialAccountsWithStreamingResponse: from .resources.financial_accounts import FinancialAccountsWithStreamingResponse return FinancialAccountsWithStreamingResponse(self._client.financial_accounts) @@ -1390,14 +1512,22 @@ def transactions(self) -> transactions.TransactionsWithStreamingResponse: return TransactionsWithStreamingResponse(self._client.transactions) @cached_property - def responder_endpoints(self) -> responder_endpoints.ResponderEndpointsWithStreamingResponse: - from .resources.responder_endpoints import ResponderEndpointsWithStreamingResponse + def responder_endpoints( + self, + ) -> responder_endpoints.ResponderEndpointsWithStreamingResponse: + from .resources.responder_endpoints import ( + ResponderEndpointsWithStreamingResponse, + ) return ResponderEndpointsWithStreamingResponse(self._client.responder_endpoints) @cached_property - def external_bank_accounts(self) -> external_bank_accounts.ExternalBankAccountsWithStreamingResponse: - from .resources.external_bank_accounts import ExternalBankAccountsWithStreamingResponse + def external_bank_accounts( + self, + ) -> external_bank_accounts.ExternalBankAccountsWithStreamingResponse: + from .resources.external_bank_accounts import ( + ExternalBankAccountsWithStreamingResponse, + ) return ExternalBankAccountsWithStreamingResponse(self._client.external_bank_accounts) @@ -1426,8 +1556,12 @@ def card_programs(self) -> card_programs.CardProgramsWithStreamingResponse: return CardProgramsWithStreamingResponse(self._client.card_programs) @cached_property - def digital_card_art(self) -> digital_card_art.DigitalCardArtResourceWithStreamingResponse: - from .resources.digital_card_art import DigitalCardArtResourceWithStreamingResponse + def digital_card_art( + self, + ) -> digital_card_art.DigitalCardArtResourceWithStreamingResponse: + from .resources.digital_card_art import ( + DigitalCardArtResourceWithStreamingResponse, + ) return DigitalCardArtResourceWithStreamingResponse(self._client.digital_card_art) @@ -1444,14 +1578,20 @@ def credit_products(self) -> credit_products.CreditProductsWithStreamingResponse return CreditProductsWithStreamingResponse(self._client.credit_products) @cached_property - def external_payments(self) -> external_payments.ExternalPaymentsWithStreamingResponse: + def external_payments( + self, + ) -> external_payments.ExternalPaymentsWithStreamingResponse: from .resources.external_payments import ExternalPaymentsWithStreamingResponse return ExternalPaymentsWithStreamingResponse(self._client.external_payments) @cached_property - def management_operations(self) -> management_operations.ManagementOperationsWithStreamingResponse: - from .resources.management_operations import ManagementOperationsWithStreamingResponse + def management_operations( + self, + ) -> management_operations.ManagementOperationsWithStreamingResponse: + from .resources.management_operations import ( + ManagementOperationsWithStreamingResponse, + ) return ManagementOperationsWithStreamingResponse(self._client.management_operations) @@ -1497,7 +1637,9 @@ def accounts(self) -> accounts.AsyncAccountsWithStreamingResponse: return AsyncAccountsWithStreamingResponse(self._client.accounts) @cached_property - def account_holders(self) -> account_holders.AsyncAccountHoldersWithStreamingResponse: + def account_holders( + self, + ) -> account_holders.AsyncAccountHoldersWithStreamingResponse: from .resources.account_holders import AsyncAccountHoldersWithStreamingResponse return AsyncAccountHoldersWithStreamingResponse(self._client.account_holders) @@ -1509,14 +1651,22 @@ def auth_rules(self) -> auth_rules.AsyncAuthRulesWithStreamingResponse: return AsyncAuthRulesWithStreamingResponse(self._client.auth_rules) @cached_property - def auth_stream_enrollment(self) -> auth_stream_enrollment.AsyncAuthStreamEnrollmentWithStreamingResponse: - from .resources.auth_stream_enrollment import AsyncAuthStreamEnrollmentWithStreamingResponse + def auth_stream_enrollment( + self, + ) -> auth_stream_enrollment.AsyncAuthStreamEnrollmentWithStreamingResponse: + from .resources.auth_stream_enrollment import ( + AsyncAuthStreamEnrollmentWithStreamingResponse, + ) return AsyncAuthStreamEnrollmentWithStreamingResponse(self._client.auth_stream_enrollment) @cached_property - def tokenization_decisioning(self) -> tokenization_decisioning.AsyncTokenizationDecisioningWithStreamingResponse: - from .resources.tokenization_decisioning import AsyncTokenizationDecisioningWithStreamingResponse + def tokenization_decisioning( + self, + ) -> tokenization_decisioning.AsyncTokenizationDecisioningWithStreamingResponse: + from .resources.tokenization_decisioning import ( + AsyncTokenizationDecisioningWithStreamingResponse, + ) return AsyncTokenizationDecisioningWithStreamingResponse(self._client.tokenization_decisioning) @@ -1532,6 +1682,14 @@ def cards(self) -> cards.AsyncCardsWithStreamingResponse: return AsyncCardsWithStreamingResponse(self._client.cards) + @cached_property + def card_bulk_orders( + self, + ) -> card_bulk_orders.AsyncCardBulkOrdersWithStreamingResponse: + from .resources.card_bulk_orders import AsyncCardBulkOrdersWithStreamingResponse + + return AsyncCardBulkOrdersWithStreamingResponse(self._client.card_bulk_orders) + @cached_property def balances(self) -> balances.AsyncBalancesWithStreamingResponse: from .resources.balances import AsyncBalancesWithStreamingResponse @@ -1539,8 +1697,12 @@ def balances(self) -> balances.AsyncBalancesWithStreamingResponse: return AsyncBalancesWithStreamingResponse(self._client.balances) @cached_property - def aggregate_balances(self) -> aggregate_balances.AsyncAggregateBalancesWithStreamingResponse: - from .resources.aggregate_balances import AsyncAggregateBalancesWithStreamingResponse + def aggregate_balances( + self, + ) -> aggregate_balances.AsyncAggregateBalancesWithStreamingResponse: + from .resources.aggregate_balances import ( + AsyncAggregateBalancesWithStreamingResponse, + ) return AsyncAggregateBalancesWithStreamingResponse(self._client.aggregate_balances) @@ -1563,8 +1725,12 @@ def events(self) -> events.AsyncEventsWithStreamingResponse: return AsyncEventsWithStreamingResponse(self._client.events) @cached_property - def financial_accounts(self) -> financial_accounts.AsyncFinancialAccountsWithStreamingResponse: - from .resources.financial_accounts import AsyncFinancialAccountsWithStreamingResponse + def financial_accounts( + self, + ) -> financial_accounts.AsyncFinancialAccountsWithStreamingResponse: + from .resources.financial_accounts import ( + AsyncFinancialAccountsWithStreamingResponse, + ) return AsyncFinancialAccountsWithStreamingResponse(self._client.financial_accounts) @@ -1575,14 +1741,22 @@ def transactions(self) -> transactions.AsyncTransactionsWithStreamingResponse: return AsyncTransactionsWithStreamingResponse(self._client.transactions) @cached_property - def responder_endpoints(self) -> responder_endpoints.AsyncResponderEndpointsWithStreamingResponse: - from .resources.responder_endpoints import AsyncResponderEndpointsWithStreamingResponse + def responder_endpoints( + self, + ) -> responder_endpoints.AsyncResponderEndpointsWithStreamingResponse: + from .resources.responder_endpoints import ( + AsyncResponderEndpointsWithStreamingResponse, + ) return AsyncResponderEndpointsWithStreamingResponse(self._client.responder_endpoints) @cached_property - def external_bank_accounts(self) -> external_bank_accounts.AsyncExternalBankAccountsWithStreamingResponse: - from .resources.external_bank_accounts import AsyncExternalBankAccountsWithStreamingResponse + def external_bank_accounts( + self, + ) -> external_bank_accounts.AsyncExternalBankAccountsWithStreamingResponse: + from .resources.external_bank_accounts import ( + AsyncExternalBankAccountsWithStreamingResponse, + ) return AsyncExternalBankAccountsWithStreamingResponse(self._client.external_bank_accounts) @@ -1611,8 +1785,12 @@ def card_programs(self) -> card_programs.AsyncCardProgramsWithStreamingResponse: return AsyncCardProgramsWithStreamingResponse(self._client.card_programs) @cached_property - def digital_card_art(self) -> digital_card_art.AsyncDigitalCardArtResourceWithStreamingResponse: - from .resources.digital_card_art import AsyncDigitalCardArtResourceWithStreamingResponse + def digital_card_art( + self, + ) -> digital_card_art.AsyncDigitalCardArtResourceWithStreamingResponse: + from .resources.digital_card_art import ( + AsyncDigitalCardArtResourceWithStreamingResponse, + ) return AsyncDigitalCardArtResourceWithStreamingResponse(self._client.digital_card_art) @@ -1623,20 +1801,30 @@ def book_transfers(self) -> book_transfers.AsyncBookTransfersWithStreamingRespon return AsyncBookTransfersWithStreamingResponse(self._client.book_transfers) @cached_property - def credit_products(self) -> credit_products.AsyncCreditProductsWithStreamingResponse: + def credit_products( + self, + ) -> credit_products.AsyncCreditProductsWithStreamingResponse: from .resources.credit_products import AsyncCreditProductsWithStreamingResponse return AsyncCreditProductsWithStreamingResponse(self._client.credit_products) @cached_property - def external_payments(self) -> external_payments.AsyncExternalPaymentsWithStreamingResponse: - from .resources.external_payments import AsyncExternalPaymentsWithStreamingResponse + def external_payments( + self, + ) -> external_payments.AsyncExternalPaymentsWithStreamingResponse: + from .resources.external_payments import ( + AsyncExternalPaymentsWithStreamingResponse, + ) return AsyncExternalPaymentsWithStreamingResponse(self._client.external_payments) @cached_property - def management_operations(self) -> management_operations.AsyncManagementOperationsWithStreamingResponse: - from .resources.management_operations import AsyncManagementOperationsWithStreamingResponse + def management_operations( + self, + ) -> management_operations.AsyncManagementOperationsWithStreamingResponse: + from .resources.management_operations import ( + AsyncManagementOperationsWithStreamingResponse, + ) return AsyncManagementOperationsWithStreamingResponse(self._client.management_operations) @@ -1653,14 +1841,22 @@ def fraud(self) -> fraud.AsyncFraudWithStreamingResponse: return AsyncFraudWithStreamingResponse(self._client.fraud) @cached_property - def network_programs(self) -> network_programs.AsyncNetworkProgramsWithStreamingResponse: - from .resources.network_programs import AsyncNetworkProgramsWithStreamingResponse + def network_programs( + self, + ) -> network_programs.AsyncNetworkProgramsWithStreamingResponse: + from .resources.network_programs import ( + AsyncNetworkProgramsWithStreamingResponse, + ) return AsyncNetworkProgramsWithStreamingResponse(self._client.network_programs) @cached_property - def account_activity(self) -> account_activity.AsyncAccountActivityWithStreamingResponse: - from .resources.account_activity import AsyncAccountActivityWithStreamingResponse + def account_activity( + self, + ) -> account_activity.AsyncAccountActivityWithStreamingResponse: + from .resources.account_activity import ( + AsyncAccountActivityWithStreamingResponse, + ) return AsyncAccountActivityWithStreamingResponse(self._client.account_activity) diff --git a/src/lithic/_exceptions.py b/src/lithic/_exceptions.py index 0d7465a0..b1190bfd 100644 --- a/src/lithic/_exceptions.py +++ b/src/lithic/_exceptions.py @@ -54,6 +54,10 @@ def __init__(self, response: httpx.Response, body: object | None, *, message: st self.status_code = response.status_code +class APIWebhookValidationError(APIError): + pass + + class APIStatusError(APIError): """Raised when an API response has a status code of 4xx or 5xx.""" diff --git a/src/lithic/_streaming.py b/src/lithic/_streaming.py index e0e5fa00..73afbbec 100644 --- a/src/lithic/_streaming.py +++ b/src/lithic/_streaming.py @@ -54,11 +54,12 @@ def __stream__(self) -> Iterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # As we might not fully consume the response stream, we need to close it explicitly - response.close() + try: + for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() def __enter__(self) -> Self: return self @@ -117,11 +118,12 @@ async def __stream__(self) -> AsyncIterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - async for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # As we might not fully consume the response stream, we need to close it explicitly - await response.aclose() + try: + async for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() async def __aenter__(self) -> Self: return self diff --git a/src/lithic/_types.py b/src/lithic/_types.py index e385ccbe..a20a5cc9 100644 --- a/src/lithic/_types.py +++ b/src/lithic/_types.py @@ -245,6 +245,9 @@ class HttpxSendArgs(TypedDict, total=False): if TYPE_CHECKING: # This works because str.__contains__ does not accept object (either in typeshed or at runtime) # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. class SequenceNotStr(Protocol[_T_co]): @overload def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... @@ -253,8 +256,6 @@ def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... def __contains__(self, value: object, /) -> bool: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_T_co]: ... - def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... - def count(self, value: Any, /) -> int: ... def __reversed__(self) -> Iterator[_T_co]: ... else: # just point this to a normal `Sequence` at runtime to avoid having to special case diff --git a/src/lithic/_version.py b/src/lithic/_version.py index 327f369c..768e4d15 100644 --- a/src/lithic/_version.py +++ b/src/lithic/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "lithic" -__version__ = "0.111.0" # x-release-please-version +__version__ = "0.112.0" # x-release-please-version diff --git a/src/lithic/resources/__init__.py b/src/lithic/resources/__init__.py index 9fdb3bac..face2018 100644 --- a/src/lithic/resources/__init__.py +++ b/src/lithic/resources/__init__.py @@ -153,6 +153,14 @@ AccountActivityWithStreamingResponse, AsyncAccountActivityWithStreamingResponse, ) +from .card_bulk_orders import ( + CardBulkOrders, + AsyncCardBulkOrders, + CardBulkOrdersWithRawResponse, + AsyncCardBulkOrdersWithRawResponse, + CardBulkOrdersWithStreamingResponse, + AsyncCardBulkOrdersWithStreamingResponse, +) from .digital_card_art import ( DigitalCardArtResource, AsyncDigitalCardArtResource, @@ -277,6 +285,12 @@ "AsyncCardsWithRawResponse", "CardsWithStreamingResponse", "AsyncCardsWithStreamingResponse", + "CardBulkOrders", + "AsyncCardBulkOrders", + "CardBulkOrdersWithRawResponse", + "AsyncCardBulkOrdersWithRawResponse", + "CardBulkOrdersWithStreamingResponse", + "AsyncCardBulkOrdersWithStreamingResponse", "Balances", "AsyncBalances", "BalancesWithRawResponse", @@ -325,8 +339,6 @@ "AsyncResponderEndpointsWithRawResponse", "ResponderEndpointsWithStreamingResponse", "AsyncResponderEndpointsWithStreamingResponse", - "Webhooks", - "AsyncWebhooks", "ExternalBankAccounts", "AsyncExternalBankAccounts", "ExternalBankAccountsWithRawResponse", @@ -411,4 +423,6 @@ "AsyncAccountActivityWithRawResponse", "AccountActivityWithStreamingResponse", "AsyncAccountActivityWithStreamingResponse", + "Webhooks", + "AsyncWebhooks", ] diff --git a/src/lithic/resources/account_activity.py b/src/lithic/resources/account_activity.py index ec727ae9..c9cd0223 100644 --- a/src/lithic/resources/account_activity.py +++ b/src/lithic/resources/account_activity.py @@ -60,6 +60,8 @@ def list( "CARD", "EXTERNAL_ACH", "EXTERNAL_CHECK", + "EXTERNAL_FEDNOW", + "EXTERNAL_RTP", "EXTERNAL_TRANSFER", "EXTERNAL_WIRE", "MANAGEMENT_ADJUSTMENT", @@ -228,6 +230,8 @@ def list( "CARD", "EXTERNAL_ACH", "EXTERNAL_CHECK", + "EXTERNAL_FEDNOW", + "EXTERNAL_RTP", "EXTERNAL_TRANSFER", "EXTERNAL_WIRE", "MANAGEMENT_ADJUSTMENT", diff --git a/src/lithic/resources/auth_rules/v2/v2.py b/src/lithic/resources/auth_rules/v2/v2.py index 379d1f9f..6e078eb2 100644 --- a/src/lithic/resources/auth_rules/v2/v2.py +++ b/src/lithic/resources/auth_rules/v2/v2.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Optional +from typing import List, Union, Optional from datetime import date from typing_extensions import Literal, overload @@ -25,6 +25,7 @@ from ....pagination import SyncCursorPage, AsyncCursorPage from ...._base_client import AsyncPaginator, make_request_options from ....types.auth_rules import ( + EventStream, v2_list_params, v2_draft_params, v2_create_params, @@ -32,12 +33,8 @@ v2_retrieve_report_params, v2_retrieve_features_params, ) -from ....types.auth_rules.v2_list_response import V2ListResponse -from ....types.auth_rules.v2_draft_response import V2DraftResponse -from ....types.auth_rules.v2_create_response import V2CreateResponse -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 +from ....types.auth_rules.auth_rule import AuthRule +from ....types.auth_rules.event_stream import EventStream from ....types.auth_rules.v2_retrieve_report_response import V2RetrieveReportResponse from ....types.auth_rules.v2_retrieve_features_response import V2RetrieveFeaturesResponse @@ -76,10 +73,7 @@ def create( type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT", "MERCHANT_LOCK", "CONDITIONAL_ACTION"], account_tokens: SequenceNotStr[str] | Omit = omit, business_account_tokens: SequenceNotStr[str] | Omit = omit, - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -87,7 +81,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2CreateResponse: + ) -> AuthRule: """ Creates a new V2 Auth rule in draft mode @@ -130,10 +124,7 @@ def create( card_tokens: SequenceNotStr[str], parameters: v2_create_params.CardLevelRuleParameters, type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT", "MERCHANT_LOCK", "CONDITIONAL_ACTION"], - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -141,7 +132,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2CreateResponse: + ) -> AuthRule: """ Creates a new V2 Auth rule in draft mode @@ -182,10 +173,7 @@ def create( parameters: v2_create_params.ProgramLevelRuleParameters, program_level: bool, type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT", "MERCHANT_LOCK", "CONDITIONAL_ACTION"], - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, excluded_card_tokens: SequenceNotStr[str] | Omit = omit, name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -194,7 +182,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2CreateResponse: + ) -> AuthRule: """ Creates a new V2 Auth rule in draft mode @@ -242,10 +230,7 @@ def create( type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT", "MERCHANT_LOCK", "CONDITIONAL_ACTION"], account_tokens: SequenceNotStr[str] | Omit = omit, business_account_tokens: SequenceNotStr[str] | Omit = omit, - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, name: Optional[str] | Omit = omit, card_tokens: SequenceNotStr[str] | Omit = omit, program_level: bool | Omit = omit, @@ -256,7 +241,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2CreateResponse: + ) -> AuthRule: return self._post( "/v2/auth_rules", body=maybe_transform( @@ -276,7 +261,7 @@ def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2CreateResponse, + cast_to=AuthRule, ) def retrieve( @@ -289,7 +274,7 @@ def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2RetrieveResponse: + ) -> AuthRule: """ Fetches a V2 Auth rule by its token @@ -309,7 +294,7 @@ def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2RetrieveResponse, + cast_to=AuthRule, ) @overload @@ -327,7 +312,7 @@ def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2UpdateResponse: + ) -> AuthRule: """ Updates a V2 Auth rule's properties @@ -372,7 +357,7 @@ def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2UpdateResponse: + ) -> AuthRule: """ Updates a V2 Auth rule's properties @@ -416,7 +401,7 @@ def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2UpdateResponse: + ) -> AuthRule: """ Updates a V2 Auth rule's properties @@ -464,7 +449,7 @@ def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2UpdateResponse: + ) -> AuthRule: 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( @@ -484,7 +469,7 @@ def update( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2UpdateResponse, + cast_to=AuthRule, ) def list( @@ -494,10 +479,8 @@ def list( business_account_token: str | Omit = omit, card_token: str | Omit = omit, ending_before: str | Omit = omit, - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, + event_streams: List[EventStream] | Omit = omit, page_size: int | Omit = omit, scope: Literal["PROGRAM", "ACCOUNT", "BUSINESS_ACCOUNT", "CARD", "ANY"] | Omit = omit, starting_after: str | Omit = omit, @@ -507,7 +490,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[V2ListResponse]: + ) -> SyncCursorPage[AuthRule]: """ Lists V2 Auth rules @@ -521,7 +504,12 @@ 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. - event_stream: Only return Auth rules that are executed during the provided event stream. + event_stream: Deprecated: Use event_streams instead. Only return Auth rules that are executed + during the provided event stream. + + event_streams: Only return Auth rules that are executed during any of the provided event + streams. If event_streams and event_stream are specified, the values will be + combined. page_size: Page size (for pagination). @@ -540,7 +528,7 @@ def list( """ return self._get_api_list( "/v2/auth_rules", - page=SyncCursorPage[V2ListResponse], + page=SyncCursorPage[AuthRule], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -553,6 +541,7 @@ def list( "card_token": card_token, "ending_before": ending_before, "event_stream": event_stream, + "event_streams": event_streams, "page_size": page_size, "scope": scope, "starting_after": starting_after, @@ -560,7 +549,7 @@ def list( v2_list_params.V2ListParams, ), ), - model=V2ListResponse, + model=AuthRule, ) def delete( @@ -607,7 +596,7 @@ def draft( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2DraftResponse: + ) -> AuthRule: """ Creates a new draft version of a rule that will be ran in shadow mode. @@ -633,7 +622,7 @@ def draft( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2DraftResponse, + cast_to=AuthRule, ) def promote( @@ -646,7 +635,7 @@ def promote( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2PromoteResponse: + ) -> AuthRule: """ Promotes the draft version of an Auth rule to the currently active version such that it is enforced in the respective stream. @@ -667,7 +656,7 @@ def promote( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2PromoteResponse, + cast_to=AuthRule, ) def retrieve_features( @@ -817,10 +806,7 @@ async def create( type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT", "MERCHANT_LOCK", "CONDITIONAL_ACTION"], account_tokens: SequenceNotStr[str] | Omit = omit, business_account_tokens: SequenceNotStr[str] | Omit = omit, - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -828,7 +814,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2CreateResponse: + ) -> AuthRule: """ Creates a new V2 Auth rule in draft mode @@ -871,10 +857,7 @@ async def create( card_tokens: SequenceNotStr[str], parameters: v2_create_params.CardLevelRuleParameters, type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT", "MERCHANT_LOCK", "CONDITIONAL_ACTION"], - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -882,7 +865,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2CreateResponse: + ) -> AuthRule: """ Creates a new V2 Auth rule in draft mode @@ -923,10 +906,7 @@ async def create( parameters: v2_create_params.ProgramLevelRuleParameters, program_level: bool, type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT", "MERCHANT_LOCK", "CONDITIONAL_ACTION"], - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, excluded_card_tokens: SequenceNotStr[str] | Omit = omit, name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -935,7 +915,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2CreateResponse: + ) -> AuthRule: """ Creates a new V2 Auth rule in draft mode @@ -983,10 +963,7 @@ async def create( type: Literal["CONDITIONAL_BLOCK", "VELOCITY_LIMIT", "MERCHANT_LOCK", "CONDITIONAL_ACTION"], account_tokens: SequenceNotStr[str] | Omit = omit, business_account_tokens: SequenceNotStr[str] | Omit = omit, - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, name: Optional[str] | Omit = omit, card_tokens: SequenceNotStr[str] | Omit = omit, program_level: bool | Omit = omit, @@ -997,7 +974,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2CreateResponse: + ) -> AuthRule: return await self._post( "/v2/auth_rules", body=await async_maybe_transform( @@ -1017,7 +994,7 @@ async def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2CreateResponse, + cast_to=AuthRule, ) async def retrieve( @@ -1030,7 +1007,7 @@ async def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2RetrieveResponse: + ) -> AuthRule: """ Fetches a V2 Auth rule by its token @@ -1050,7 +1027,7 @@ async def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2RetrieveResponse, + cast_to=AuthRule, ) @overload @@ -1068,7 +1045,7 @@ async def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2UpdateResponse: + ) -> AuthRule: """ Updates a V2 Auth rule's properties @@ -1113,7 +1090,7 @@ async def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2UpdateResponse: + ) -> AuthRule: """ Updates a V2 Auth rule's properties @@ -1157,7 +1134,7 @@ async def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2UpdateResponse: + ) -> AuthRule: """ Updates a V2 Auth rule's properties @@ -1205,7 +1182,7 @@ async def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2UpdateResponse: + ) -> AuthRule: 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( @@ -1225,7 +1202,7 @@ async def update( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2UpdateResponse, + cast_to=AuthRule, ) def list( @@ -1235,10 +1212,8 @@ def list( business_account_token: str | Omit = omit, card_token: str | Omit = omit, ending_before: str | Omit = omit, - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - | Omit = omit, + event_stream: EventStream | Omit = omit, + event_streams: List[EventStream] | Omit = omit, page_size: int | Omit = omit, scope: Literal["PROGRAM", "ACCOUNT", "BUSINESS_ACCOUNT", "CARD", "ANY"] | Omit = omit, starting_after: str | Omit = omit, @@ -1248,7 +1223,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[V2ListResponse, AsyncCursorPage[V2ListResponse]]: + ) -> AsyncPaginator[AuthRule, AsyncCursorPage[AuthRule]]: """ Lists V2 Auth rules @@ -1262,7 +1237,12 @@ 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. - event_stream: Only return Auth rules that are executed during the provided event stream. + event_stream: Deprecated: Use event_streams instead. Only return Auth rules that are executed + during the provided event stream. + + event_streams: Only return Auth rules that are executed during any of the provided event + streams. If event_streams and event_stream are specified, the values will be + combined. page_size: Page size (for pagination). @@ -1281,7 +1261,7 @@ def list( """ return self._get_api_list( "/v2/auth_rules", - page=AsyncCursorPage[V2ListResponse], + page=AsyncCursorPage[AuthRule], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1294,6 +1274,7 @@ def list( "card_token": card_token, "ending_before": ending_before, "event_stream": event_stream, + "event_streams": event_streams, "page_size": page_size, "scope": scope, "starting_after": starting_after, @@ -1301,7 +1282,7 @@ def list( v2_list_params.V2ListParams, ), ), - model=V2ListResponse, + model=AuthRule, ) async def delete( @@ -1348,7 +1329,7 @@ async def draft( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2DraftResponse: + ) -> AuthRule: """ Creates a new draft version of a rule that will be ran in shadow mode. @@ -1374,7 +1355,7 @@ async def draft( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2DraftResponse, + cast_to=AuthRule, ) async def promote( @@ -1387,7 +1368,7 @@ async def promote( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V2PromoteResponse: + ) -> AuthRule: """ Promotes the draft version of an Auth rule to the currently active version such that it is enforced in the respective stream. @@ -1408,7 +1389,7 @@ async def promote( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V2PromoteResponse, + cast_to=AuthRule, ) async def retrieve_features( diff --git a/src/lithic/resources/card_bulk_orders.py b/src/lithic/resources/card_bulk_orders.py new file mode 100644 index 00000000..7caaad9d --- /dev/null +++ b/src/lithic/resources/card_bulk_orders.py @@ -0,0 +1,516 @@ +# 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 card_bulk_order_list_params, card_bulk_order_create_params, card_bulk_order_update_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +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.card_bulk_order import CardBulkOrder + +__all__ = ["CardBulkOrders", "AsyncCardBulkOrders"] + + +class CardBulkOrders(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CardBulkOrdersWithRawResponse: + """ + 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 CardBulkOrdersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CardBulkOrdersWithStreamingResponse: + """ + 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 CardBulkOrdersWithStreamingResponse(self) + + def create( + self, + *, + customer_product_id: str, + shipping_address: object, + shipping_method: Literal["BULK_EXPEDITED"], + # 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, + ) -> CardBulkOrder: + """Create a new bulk order for physical card shipments **[BETA]**. + + Cards can be + added to the order via the POST /v1/cards endpoint by specifying the + bulk_order_token. Lock the order via PATCH + /v1/card_bulk_orders/{bulk_order_token} to prepare for shipment. Please work + with your Customer Success Manager and card personalization bureau to ensure + bulk shipping is supported for your program. + + Args: + customer_product_id: Customer-specified product configuration for physical card manufacturing. This + must be configured with Lithic before use + + shipping_address: Shipping address for all cards in this bulk order + + shipping_method: Shipping method for all cards in this bulk order + + 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/card_bulk_orders", + body=maybe_transform( + { + "customer_product_id": customer_product_id, + "shipping_address": shipping_address, + "shipping_method": shipping_method, + }, + card_bulk_order_create_params.CardBulkOrderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CardBulkOrder, + ) + + def retrieve( + self, + bulk_order_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, + ) -> CardBulkOrder: + """ + Retrieve a specific bulk order by token **[BETA]** + + 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 bulk_order_token: + raise ValueError(f"Expected a non-empty value for `bulk_order_token` but received {bulk_order_token!r}") + return self._get( + f"/v1/card_bulk_orders/{bulk_order_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CardBulkOrder, + ) + + def update( + self, + bulk_order_token: str, + *, + status: Literal["LOCKED"], + # 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, + ) -> CardBulkOrder: + """Update a bulk order **[BETA]**. + + Primarily used to lock the order, preventing + additional cards from being added + + Args: + status: Status to update the bulk order to. Use LOCKED to finalize the order + + 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 bulk_order_token: + raise ValueError(f"Expected a non-empty value for `bulk_order_token` but received {bulk_order_token!r}") + return self._patch( + f"/v1/card_bulk_orders/{bulk_order_token}", + body=maybe_transform({"status": status}, card_bulk_order_update_params.CardBulkOrderUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CardBulkOrder, + ) + + def list( + self, + *, + begin: Union[str, datetime] | Omit = omit, + end: Union[str, datetime] | Omit = omit, + ending_before: str | Omit = omit, + page_size: int | Omit = omit, + starting_after: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[CardBulkOrder]: + """ + List bulk orders for physical card shipments **[BETA]** + + Args: + begin: Date string in RFC 3339 format. Only entries created after the specified time + will be included. UTC time zone. + + 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. + + 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( + "/v1/card_bulk_orders", + page=SyncCursorPage[CardBulkOrder], + 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, + }, + card_bulk_order_list_params.CardBulkOrderListParams, + ), + ), + model=CardBulkOrder, + ) + + +class AsyncCardBulkOrders(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCardBulkOrdersWithRawResponse: + """ + 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 AsyncCardBulkOrdersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCardBulkOrdersWithStreamingResponse: + """ + 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 AsyncCardBulkOrdersWithStreamingResponse(self) + + async def create( + self, + *, + customer_product_id: str, + shipping_address: object, + shipping_method: Literal["BULK_EXPEDITED"], + # 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, + ) -> CardBulkOrder: + """Create a new bulk order for physical card shipments **[BETA]**. + + Cards can be + added to the order via the POST /v1/cards endpoint by specifying the + bulk_order_token. Lock the order via PATCH + /v1/card_bulk_orders/{bulk_order_token} to prepare for shipment. Please work + with your Customer Success Manager and card personalization bureau to ensure + bulk shipping is supported for your program. + + Args: + customer_product_id: Customer-specified product configuration for physical card manufacturing. This + must be configured with Lithic before use + + shipping_address: Shipping address for all cards in this bulk order + + shipping_method: Shipping method for all cards in this bulk order + + 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/card_bulk_orders", + body=await async_maybe_transform( + { + "customer_product_id": customer_product_id, + "shipping_address": shipping_address, + "shipping_method": shipping_method, + }, + card_bulk_order_create_params.CardBulkOrderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CardBulkOrder, + ) + + async def retrieve( + self, + bulk_order_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, + ) -> CardBulkOrder: + """ + Retrieve a specific bulk order by token **[BETA]** + + 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 bulk_order_token: + raise ValueError(f"Expected a non-empty value for `bulk_order_token` but received {bulk_order_token!r}") + return await self._get( + f"/v1/card_bulk_orders/{bulk_order_token}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CardBulkOrder, + ) + + async def update( + self, + bulk_order_token: str, + *, + status: Literal["LOCKED"], + # 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, + ) -> CardBulkOrder: + """Update a bulk order **[BETA]**. + + Primarily used to lock the order, preventing + additional cards from being added + + Args: + status: Status to update the bulk order to. Use LOCKED to finalize the order + + 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 bulk_order_token: + raise ValueError(f"Expected a non-empty value for `bulk_order_token` but received {bulk_order_token!r}") + return await self._patch( + f"/v1/card_bulk_orders/{bulk_order_token}", + body=await async_maybe_transform( + {"status": status}, card_bulk_order_update_params.CardBulkOrderUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CardBulkOrder, + ) + + def list( + self, + *, + begin: Union[str, datetime] | Omit = omit, + end: Union[str, datetime] | Omit = omit, + ending_before: str | Omit = omit, + page_size: int | Omit = omit, + starting_after: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[CardBulkOrder, AsyncCursorPage[CardBulkOrder]]: + """ + List bulk orders for physical card shipments **[BETA]** + + Args: + begin: Date string in RFC 3339 format. Only entries created after the specified time + will be included. UTC time zone. + + 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. + + 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( + "/v1/card_bulk_orders", + page=AsyncCursorPage[CardBulkOrder], + 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, + }, + card_bulk_order_list_params.CardBulkOrderListParams, + ), + ), + model=CardBulkOrder, + ) + + +class CardBulkOrdersWithRawResponse: + def __init__(self, card_bulk_orders: CardBulkOrders) -> None: + self._card_bulk_orders = card_bulk_orders + + self.create = _legacy_response.to_raw_response_wrapper( + card_bulk_orders.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + card_bulk_orders.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + card_bulk_orders.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + card_bulk_orders.list, + ) + + +class AsyncCardBulkOrdersWithRawResponse: + def __init__(self, card_bulk_orders: AsyncCardBulkOrders) -> None: + self._card_bulk_orders = card_bulk_orders + + self.create = _legacy_response.async_to_raw_response_wrapper( + card_bulk_orders.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + card_bulk_orders.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + card_bulk_orders.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + card_bulk_orders.list, + ) + + +class CardBulkOrdersWithStreamingResponse: + def __init__(self, card_bulk_orders: CardBulkOrders) -> None: + self._card_bulk_orders = card_bulk_orders + + self.create = to_streamed_response_wrapper( + card_bulk_orders.create, + ) + self.retrieve = to_streamed_response_wrapper( + card_bulk_orders.retrieve, + ) + self.update = to_streamed_response_wrapper( + card_bulk_orders.update, + ) + self.list = to_streamed_response_wrapper( + card_bulk_orders.list, + ) + + +class AsyncCardBulkOrdersWithStreamingResponse: + def __init__(self, card_bulk_orders: AsyncCardBulkOrders) -> None: + self._card_bulk_orders = card_bulk_orders + + self.create = async_to_streamed_response_wrapper( + card_bulk_orders.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + card_bulk_orders.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + card_bulk_orders.update, + ) + self.list = async_to_streamed_response_wrapper( + card_bulk_orders.list, + ) diff --git a/src/lithic/resources/cards/balances.py b/src/lithic/resources/cards/balances.py index f27b1805..44784aed 100644 --- a/src/lithic/resources/cards/balances.py +++ b/src/lithic/resources/cards/balances.py @@ -16,7 +16,7 @@ from ...pagination import SyncSinglePage, AsyncSinglePage from ...types.cards import balance_list_params from ..._base_client import AsyncPaginator, make_request_options -from ...types.cards.balance_list_response import BalanceListResponse +from ...types.financial_account_balance import FinancialAccountBalance __all__ = ["Balances", "AsyncBalances"] @@ -53,7 +53,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncSinglePage[BalanceListResponse]: + ) -> SyncSinglePage[FinancialAccountBalance]: """ Get the balances for a given card. @@ -76,7 +76,7 @@ def list( raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._get_api_list( f"/v1/cards/{card_token}/balances", - page=SyncSinglePage[BalanceListResponse], + page=SyncSinglePage[FinancialAccountBalance], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -90,7 +90,7 @@ def list( balance_list_params.BalanceListParams, ), ), - model=BalanceListResponse, + model=FinancialAccountBalance, ) @@ -126,7 +126,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BalanceListResponse, AsyncSinglePage[BalanceListResponse]]: + ) -> AsyncPaginator[FinancialAccountBalance, AsyncSinglePage[FinancialAccountBalance]]: """ Get the balances for a given card. @@ -149,7 +149,7 @@ def list( raise ValueError(f"Expected a non-empty value for `card_token` but received {card_token!r}") return self._get_api_list( f"/v1/cards/{card_token}/balances", - page=AsyncSinglePage[BalanceListResponse], + page=AsyncSinglePage[FinancialAccountBalance], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -163,7 +163,7 @@ def list( balance_list_params.BalanceListParams, ), ), - model=BalanceListResponse, + model=FinancialAccountBalance, ) diff --git a/src/lithic/resources/cards/cards.py b/src/lithic/resources/cards/cards.py index 645fccad..5325041a 100644 --- a/src/lithic/resources/cards/cards.py +++ b/src/lithic/resources/cards/cards.py @@ -6,7 +6,7 @@ import json import base64 import hashlib -from typing import Union +from typing import Any, Union, cast from datetime import datetime, timezone, timedelta from typing_extensions import Literal @@ -108,6 +108,7 @@ def create( *, type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"], account_token: str | Omit = omit, + bulk_order_token: str | Omit = omit, card_program_token: str | Omit = omit, carrier: Carrier | Omit = omit, digital_card_art_token: str | Omit = omit, @@ -134,7 +135,9 @@ def create( ] | Omit = omit, shipping_address: ShippingAddress | Omit = omit, - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] | Omit = omit, spend_limit: int | Omit = omit, spend_limit_duration: SpendLimitDuration | Omit = omit, @@ -175,6 +178,10 @@ def create( [/account_holders endpoint](https://docs.lithic.com/docs/account-holders-kyc). See [Managing Your Program](doc:managing-your-program) for more information. + bulk_order_token: Globally unique identifier for an existing bulk order to associate this card + with. When specified, the card will be added to the bulk order for batch + shipment. Only applicable to cards of type PHYSICAL + card_program_token: For card programs with more than one BIN range. This must be configured with Lithic before use. Identifies the card program/BIN range under which to create the card. If omitted, will utilize the program's default `card_program_token`. @@ -259,6 +266,7 @@ def create( tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping 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 @@ -302,6 +310,7 @@ def create( { "type": type, "account_token": account_token, + "bulk_order_token": bulk_order_token, "card_program_token": card_program_token, "carrier": carrier, "digital_card_art_token": digital_card_art_token, @@ -597,7 +606,9 @@ def convert_physical( shipping_address: ShippingAddress, carrier: Carrier | Omit = omit, product_id: str | Omit = omit, - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -642,6 +653,7 @@ def convert_physical( tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping extra_headers: Send extra headers @@ -939,7 +951,9 @@ def reissue( carrier: Carrier | Omit = omit, product_id: str | Omit = omit, shipping_address: ShippingAddress | Omit = omit, - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -979,6 +993,7 @@ def reissue( tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping extra_headers: Send extra headers @@ -1016,7 +1031,9 @@ def renew( exp_month: str | Omit = omit, exp_year: str | Omit = omit, product_id: str | Omit = omit, - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1066,6 +1083,7 @@ def renew( tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping extra_headers: Send extra headers @@ -1175,7 +1193,10 @@ def web_provision( self, card_token: str, *, - digital_wallet: Literal["APPLE_PAY"] | Omit = omit, + client_device_id: str | Omit = omit, + client_wallet_account_id: str | Omit = omit, + digital_wallet: Literal["APPLE_PAY", "GOOGLE_PAY"] | Omit = omit, + server_session_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1192,8 +1213,17 @@ def web_provision( for more information. Args: + client_device_id: Only applicable if `digital_wallet` is GOOGLE_PAY. Google Pay Web Push + Provisioning device identifier required for the tokenization flow + + client_wallet_account_id: Only applicable if `digital_wallet` is GOOGLE_PAY. Google Pay Web Push + Provisioning wallet account identifier required for the tokenization flow + digital_wallet: Name of digital wallet provider. + server_session_id: Only applicable if `digital_wallet` is GOOGLE_PAY. Google Pay Web Push + Provisioning session identifier required for the FPAN flow. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1204,13 +1234,26 @@ def web_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"/v1/cards/{card_token}/web_provision", - body=maybe_transform({"digital_wallet": digital_wallet}, card_web_provision_params.CardWebProvisionParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + return cast( + CardWebProvisionResponse, + self._post( + f"/v1/cards/{card_token}/web_provision", + body=maybe_transform( + { + "client_device_id": client_device_id, + "client_wallet_account_id": client_wallet_account_id, + "digital_wallet": digital_wallet, + "server_session_id": server_session_id, + }, + card_web_provision_params.CardWebProvisionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, CardWebProvisionResponse + ), # Union types cannot be passed in as arguments in the type system ), - cast_to=CardWebProvisionResponse, ) @@ -1251,6 +1294,7 @@ async def create( *, type: Literal["MERCHANT_LOCKED", "PHYSICAL", "SINGLE_USE", "VIRTUAL", "UNLOCKED", "DIGITAL_WALLET"], account_token: str | Omit = omit, + bulk_order_token: str | Omit = omit, card_program_token: str | Omit = omit, carrier: Carrier | Omit = omit, digital_card_art_token: str | Omit = omit, @@ -1277,7 +1321,9 @@ async def create( ] | Omit = omit, shipping_address: ShippingAddress | Omit = omit, - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] | Omit = omit, spend_limit: int | Omit = omit, spend_limit_duration: SpendLimitDuration | Omit = omit, @@ -1318,6 +1364,10 @@ async def create( [/account_holders endpoint](https://docs.lithic.com/docs/account-holders-kyc). See [Managing Your Program](doc:managing-your-program) for more information. + bulk_order_token: Globally unique identifier for an existing bulk order to associate this card + with. When specified, the card will be added to the bulk order for batch + shipment. Only applicable to cards of type PHYSICAL + card_program_token: For card programs with more than one BIN range. This must be configured with Lithic before use. Identifies the card program/BIN range under which to create the card. If omitted, will utilize the program's default `card_program_token`. @@ -1402,6 +1452,7 @@ async def create( tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping 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 @@ -1445,6 +1496,7 @@ async def create( { "type": type, "account_token": account_token, + "bulk_order_token": bulk_order_token, "card_program_token": card_program_token, "carrier": carrier, "digital_card_art_token": digital_card_art_token, @@ -1740,7 +1792,9 @@ async def convert_physical( shipping_address: ShippingAddress, carrier: Carrier | Omit = omit, product_id: str | Omit = omit, - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1785,6 +1839,7 @@ async def convert_physical( tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping extra_headers: Send extra headers @@ -2082,7 +2137,9 @@ async def reissue( carrier: Carrier | Omit = omit, product_id: str | Omit = omit, shipping_address: ShippingAddress | Omit = omit, - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2122,6 +2179,7 @@ async def reissue( tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping extra_headers: Send extra headers @@ -2159,7 +2217,9 @@ async def renew( exp_month: str | Omit = omit, exp_year: str | Omit = omit, product_id: str | Omit = omit, - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2209,6 +2269,7 @@ async def renew( tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping extra_headers: Send extra headers @@ -2318,7 +2379,10 @@ async def web_provision( self, card_token: str, *, - digital_wallet: Literal["APPLE_PAY"] | Omit = omit, + client_device_id: str | Omit = omit, + client_wallet_account_id: str | Omit = omit, + digital_wallet: Literal["APPLE_PAY", "GOOGLE_PAY"] | Omit = omit, + server_session_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2335,8 +2399,17 @@ async def web_provision( for more information. Args: + client_device_id: Only applicable if `digital_wallet` is GOOGLE_PAY. Google Pay Web Push + Provisioning device identifier required for the tokenization flow + + client_wallet_account_id: Only applicable if `digital_wallet` is GOOGLE_PAY. Google Pay Web Push + Provisioning wallet account identifier required for the tokenization flow + digital_wallet: Name of digital wallet provider. + server_session_id: Only applicable if `digital_wallet` is GOOGLE_PAY. Google Pay Web Push + Provisioning session identifier required for the FPAN flow. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2347,15 +2420,26 @@ async def web_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"/v1/cards/{card_token}/web_provision", - body=await async_maybe_transform( - {"digital_wallet": digital_wallet}, card_web_provision_params.CardWebProvisionParams - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + return cast( + CardWebProvisionResponse, + await self._post( + f"/v1/cards/{card_token}/web_provision", + body=await async_maybe_transform( + { + "client_device_id": client_device_id, + "client_wallet_account_id": client_wallet_account_id, + "digital_wallet": digital_wallet, + "server_session_id": server_session_id, + }, + card_web_provision_params.CardWebProvisionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, CardWebProvisionResponse + ), # Union types cannot be passed in as arguments in the type system ), - cast_to=CardWebProvisionResponse, ) 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 dbf5c333..0a3ee175 100644 --- a/src/lithic/resources/external_bank_accounts/external_bank_accounts.py +++ b/src/lithic/resources/external_bank_accounts/external_bank_accounts.py @@ -35,13 +35,12 @@ ) from ...types.owner_type import OwnerType from ...types.verification_method import VerificationMethod +from ...types.external_bank_account import ExternalBankAccount 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_unpause_response import ExternalBankAccountUnpauseResponse 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"] @@ -604,7 +603,7 @@ def retry_prenote( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExternalBankAccountRetryPrenoteResponse: + ) -> ExternalBankAccount: """ Retry external bank account prenote verification. @@ -630,7 +629,7 @@ def retry_prenote( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ExternalBankAccountRetryPrenoteResponse, + cast_to=ExternalBankAccount, ) def unpause( @@ -643,7 +642,7 @@ def unpause( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExternalBankAccountUnpauseResponse: + ) -> ExternalBankAccount: """ Unpause an external bank account @@ -665,7 +664,7 @@ def unpause( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ExternalBankAccountUnpauseResponse, + cast_to=ExternalBankAccount, ) @@ -1226,7 +1225,7 @@ async def retry_prenote( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExternalBankAccountRetryPrenoteResponse: + ) -> ExternalBankAccount: """ Retry external bank account prenote verification. @@ -1252,7 +1251,7 @@ async def retry_prenote( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ExternalBankAccountRetryPrenoteResponse, + cast_to=ExternalBankAccount, ) async def unpause( @@ -1265,7 +1264,7 @@ async def unpause( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExternalBankAccountUnpauseResponse: + ) -> ExternalBankAccount: """ Unpause an external bank account @@ -1287,7 +1286,7 @@ async def unpause( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ExternalBankAccountUnpauseResponse, + cast_to=ExternalBankAccount, ) diff --git a/src/lithic/resources/external_payments.py b/src/lithic/resources/external_payments.py index fee5b2c6..07c51674 100644 --- a/src/lithic/resources/external_payments.py +++ b/src/lithic/resources/external_payments.py @@ -53,7 +53,9 @@ def create( self, *, amount: int, - category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"], + category: Literal[ + "EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_FEDNOW", "EXTERNAL_RTP", "EXTERNAL_TRANSFER" + ], effective_date: Union[str, date], financial_account_token: str, payment_type: Literal["DEPOSIT", "WITHDRAWAL"], @@ -142,7 +144,10 @@ def list( *, begin: Union[str, datetime] | Omit = omit, business_account_token: str | Omit = omit, - category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"] | Omit = omit, + category: Literal[ + "EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_FEDNOW", "EXTERNAL_RTP", "EXTERNAL_TRANSFER" + ] + | Omit = omit, end: Union[str, datetime] | Omit = omit, ending_before: str | Omit = omit, financial_account_token: str | Omit = omit, @@ -423,7 +428,9 @@ async def create( self, *, amount: int, - category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"], + category: Literal[ + "EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_FEDNOW", "EXTERNAL_RTP", "EXTERNAL_TRANSFER" + ], effective_date: Union[str, date], financial_account_token: str, payment_type: Literal["DEPOSIT", "WITHDRAWAL"], @@ -512,7 +519,10 @@ def list( *, begin: Union[str, datetime] | Omit = omit, business_account_token: str | Omit = omit, - category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"] | Omit = omit, + category: Literal[ + "EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_FEDNOW", "EXTERNAL_RTP", "EXTERNAL_TRANSFER" + ] + | Omit = omit, end: Union[str, datetime] | Omit = omit, ending_before: str | Omit = omit, financial_account_token: str | Omit = omit, diff --git a/src/lithic/resources/financial_accounts/balances.py b/src/lithic/resources/financial_accounts/balances.py index 782be9b7..d280a862 100644 --- a/src/lithic/resources/financial_accounts/balances.py +++ b/src/lithic/resources/financial_accounts/balances.py @@ -16,7 +16,7 @@ from ...pagination import SyncSinglePage, AsyncSinglePage 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 +from ...types.financial_account_balance import FinancialAccountBalance __all__ = ["Balances", "AsyncBalances"] @@ -53,7 +53,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncSinglePage[BalanceListResponse]: + ) -> SyncSinglePage[FinancialAccountBalance]: """ Get the balances for a given financial account. @@ -78,7 +78,7 @@ def list( ) return self._get_api_list( f"/v1/financial_accounts/{financial_account_token}/balances", - page=SyncSinglePage[BalanceListResponse], + page=SyncSinglePage[FinancialAccountBalance], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -92,7 +92,7 @@ def list( balance_list_params.BalanceListParams, ), ), - model=BalanceListResponse, + model=FinancialAccountBalance, ) @@ -128,7 +128,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BalanceListResponse, AsyncSinglePage[BalanceListResponse]]: + ) -> AsyncPaginator[FinancialAccountBalance, AsyncSinglePage[FinancialAccountBalance]]: """ Get the balances for a given financial account. @@ -153,7 +153,7 @@ def list( ) return self._get_api_list( f"/v1/financial_accounts/{financial_account_token}/balances", - page=AsyncSinglePage[BalanceListResponse], + page=AsyncSinglePage[FinancialAccountBalance], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -167,7 +167,7 @@ def list( balance_list_params.BalanceListParams, ), ), - model=BalanceListResponse, + model=FinancialAccountBalance, ) diff --git a/src/lithic/resources/funding_events.py b/src/lithic/resources/funding_events.py index 43e63501..44599b96 100644 --- a/src/lithic/resources/funding_events.py +++ b/src/lithic/resources/funding_events.py @@ -13,8 +13,7 @@ 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.funding_event_list_response import FundingEventListResponse -from ..types.funding_event_retrieve_response import FundingEventRetrieveResponse +from ..types.funding_event import FundingEvent from ..types.funding_event_retrieve_details_response import FundingEventRetrieveDetailsResponse __all__ = ["FundingEvents", "AsyncFundingEvents"] @@ -50,7 +49,7 @@ def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> FundingEventRetrieveResponse: + ) -> FundingEvent: """ Get funding event for program by id @@ -72,7 +71,7 @@ def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=FundingEventRetrieveResponse, + cast_to=FundingEvent, ) def list( @@ -87,7 +86,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[FundingEventListResponse]: + ) -> SyncCursorPage[FundingEvent]: """ Get all funding events for program @@ -110,7 +109,7 @@ def list( """ return self._get_api_list( "/v1/funding_events", - page=SyncCursorPage[FundingEventListResponse], + page=SyncCursorPage[FundingEvent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -125,7 +124,7 @@ def list( funding_event_list_params.FundingEventListParams, ), ), - model=FundingEventListResponse, + model=FundingEvent, ) def retrieve_details( @@ -194,7 +193,7 @@ async def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> FundingEventRetrieveResponse: + ) -> FundingEvent: """ Get funding event for program by id @@ -216,7 +215,7 @@ async def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=FundingEventRetrieveResponse, + cast_to=FundingEvent, ) def list( @@ -231,7 +230,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[FundingEventListResponse, AsyncCursorPage[FundingEventListResponse]]: + ) -> AsyncPaginator[FundingEvent, AsyncCursorPage[FundingEvent]]: """ Get all funding events for program @@ -254,7 +253,7 @@ def list( """ return self._get_api_list( "/v1/funding_events", - page=AsyncCursorPage[FundingEventListResponse], + page=AsyncCursorPage[FundingEvent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -269,7 +268,7 @@ def list( funding_event_list_params.FundingEventListParams, ), ), - model=FundingEventListResponse, + model=FundingEvent, ) async def retrieve_details( diff --git a/src/lithic/resources/management_operations.py b/src/lithic/resources/management_operations.py index 02989df6..641599fb 100644 --- a/src/lithic/resources/management_operations.py +++ b/src/lithic/resources/management_operations.py @@ -79,6 +79,12 @@ def create( "DISPUTE_WON_REVERSAL", "DISBURSE", "DISBURSE_REVERSAL", + "ANNUAL", + "ANNUAL_REVERSAL", + "QUARTERLY", + "QUARTERLY_REVERSAL", + "MONTHLY", + "MONTHLY_REVERSAL", ], financial_account_token: str, token: str | Omit = omit, @@ -350,6 +356,12 @@ async def create( "DISPUTE_WON_REVERSAL", "DISBURSE", "DISBURSE_REVERSAL", + "ANNUAL", + "ANNUAL_REVERSAL", + "QUARTERLY", + "QUARTERLY_REVERSAL", + "MONTHLY", + "MONTHLY_REVERSAL", ], financial_account_token: str, token: str | Omit = omit, diff --git a/src/lithic/resources/reports/settlement/network_totals.py b/src/lithic/resources/reports/settlement/network_totals.py index 499594bc..4d4f9172 100644 --- a/src/lithic/resources/reports/settlement/network_totals.py +++ b/src/lithic/resources/reports/settlement/network_totals.py @@ -16,9 +16,8 @@ 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.network_total import NetworkTotal 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"] @@ -53,7 +52,7 @@ def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> NetworkTotalRetrieveResponse: + ) -> NetworkTotal: """Retrieve a specific network total record by token. Not available in sandbox. @@ -74,7 +73,7 @@ def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NetworkTotalRetrieveResponse, + cast_to=NetworkTotal, ) def list( @@ -97,7 +96,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[NetworkTotalListResponse]: + ) -> SyncCursorPage[NetworkTotal]: """List network total records with optional filters. Not available in sandbox. @@ -140,7 +139,7 @@ def list( """ return self._get_api_list( "/v1/reports/settlement/network_totals", - page=SyncCursorPage[NetworkTotalListResponse], + page=SyncCursorPage[NetworkTotal], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -163,7 +162,7 @@ def list( network_total_list_params.NetworkTotalListParams, ), ), - model=NetworkTotalListResponse, + model=NetworkTotal, ) @@ -197,7 +196,7 @@ async def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> NetworkTotalRetrieveResponse: + ) -> NetworkTotal: """Retrieve a specific network total record by token. Not available in sandbox. @@ -218,7 +217,7 @@ async def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NetworkTotalRetrieveResponse, + cast_to=NetworkTotal, ) def list( @@ -241,7 +240,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[NetworkTotalListResponse, AsyncCursorPage[NetworkTotalListResponse]]: + ) -> AsyncPaginator[NetworkTotal, AsyncCursorPage[NetworkTotal]]: """List network total records with optional filters. Not available in sandbox. @@ -284,7 +283,7 @@ def list( """ return self._get_api_list( "/v1/reports/settlement/network_totals", - page=AsyncCursorPage[NetworkTotalListResponse], + page=AsyncCursorPage[NetworkTotal], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -307,7 +306,7 @@ def list( network_total_list_params.NetworkTotalListParams, ), ), - model=NetworkTotalListResponse, + model=NetworkTotal, ) diff --git a/src/lithic/resources/three_ds/authentication.py b/src/lithic/resources/three_ds/authentication.py index ea8f5cf9..f2a6c75c 100644 --- a/src/lithic/resources/three_ds/authentication.py +++ b/src/lithic/resources/three_ds/authentication.py @@ -14,7 +14,7 @@ from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper 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 import ThreeDSAuthentication from ...types.three_ds.authentication_simulate_response import AuthenticationSimulateResponse __all__ = ["Authentication", "AsyncAuthentication"] @@ -50,7 +50,7 @@ def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AuthenticationRetrieveResponse: + ) -> ThreeDSAuthentication: """ Get 3DS Authentication by token @@ -72,7 +72,7 @@ def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AuthenticationRetrieveResponse, + cast_to=ThreeDSAuthentication, ) def simulate( @@ -211,7 +211,7 @@ async def retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AuthenticationRetrieveResponse: + ) -> ThreeDSAuthentication: """ Get 3DS Authentication by token @@ -233,7 +233,7 @@ async def retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=AuthenticationRetrieveResponse, + cast_to=ThreeDSAuthentication, ) async def simulate( diff --git a/src/lithic/resources/tokenizations.py b/src/lithic/resources/tokenizations.py index 66740f55..4307e20f 100644 --- a/src/lithic/resources/tokenizations.py +++ b/src/lithic/resources/tokenizations.py @@ -23,8 +23,6 @@ from ..pagination import SyncCursorPage, AsyncCursorPage from .._base_client import AsyncPaginator, make_request_options from ..types.tokenization import Tokenization -from ..types.tokenization_simulate_response import TokenizationSimulateResponse -from ..types.tokenization_update_digital_card_art_response import TokenizationUpdateDigitalCardArtResponse __all__ = ["Tokenizations", "AsyncTokenizations"] @@ -348,7 +346,7 @@ def simulate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TokenizationSimulateResponse: + ) -> Tokenization: """ This endpoint is used to simulate a card's tokenization in the Digital Wallet and merchant tokenization ecosystem. @@ -399,7 +397,7 @@ def simulate( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=TokenizationSimulateResponse, + cast_to=Tokenization, ) def unpause( @@ -453,7 +451,7 @@ def update_digital_card_art( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TokenizationUpdateDigitalCardArtResponse: + ) -> Tokenization: """ This endpoint is used update the digital card art for a digital wallet tokenization. A successful response indicates that the card network has updated @@ -489,7 +487,7 @@ def update_digital_card_art( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=TokenizationUpdateDigitalCardArtResponse, + cast_to=Tokenization, ) @@ -812,7 +810,7 @@ async def simulate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TokenizationSimulateResponse: + ) -> Tokenization: """ This endpoint is used to simulate a card's tokenization in the Digital Wallet and merchant tokenization ecosystem. @@ -863,7 +861,7 @@ async def simulate( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=TokenizationSimulateResponse, + cast_to=Tokenization, ) async def unpause( @@ -917,7 +915,7 @@ async def update_digital_card_art( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TokenizationUpdateDigitalCardArtResponse: + ) -> Tokenization: """ This endpoint is used update the digital card art for a digital wallet tokenization. A successful response indicates that the card network has updated @@ -953,7 +951,7 @@ async def update_digital_card_art( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=TokenizationUpdateDigitalCardArtResponse, + cast_to=Tokenization, ) diff --git a/src/lithic/resources/webhooks.py b/src/lithic/resources/webhooks.py index 8b5094f8..c871ec7d 100644 --- a/src/lithic/resources/webhooks.py +++ b/src/lithic/resources/webhooks.py @@ -1,22 +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 hmac import json -import math -import base64 -import hashlib -from datetime import datetime, timezone, timedelta +from typing import Mapping, cast from .._types import ( HeadersLike, ) from .._utils import ( - removeprefix, get_required_header, ) +from .._models import construct_type from .._resource import SyncAPIResource, AsyncAPIResource +from .._exceptions import LithicError +from ..types.parsed_webhook_event import ParsedWebhookEvent __all__ = ["Webhooks", "AsyncWebhooks"] @@ -40,78 +38,51 @@ def verify_signature( *, secret: str | None = None, ) -> None: - """Validates whether or not the webhook payload was sent by Lithic. + """Verifies that the given payload was sent by Lithic.""" + try: + from standardwebhooks import Webhook + except ImportError as exc: + raise LithicError("You need to install `lithic[webhooks]` to use this method") from exc - 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") + if secret is None: + raise ValueError( + "Cannot verify a webhook without a secret on either the client's webhook_secret or passed in as an argument" + ) + + headers = { + "webhook-id": get_required_header(headers, "webhook-id"), + "webhook-timestamp": get_required_header(headers, "webhook-timestamp"), + "webhook-signature": get_required_header(headers, "webhook-signature"), + } + Webhook(secret).verify(payload, headers) + + def parse( + self, + payload: str, + *, + headers: Mapping[str, str], + secret: str | bytes | None = None, + ) -> ParsedWebhookEvent: + secret = secret.decode("utf-8") if isinstance(secret, bytes) else secret + self.verify_signature(payload=payload, headers=headers, secret=secret) + return cast( + ParsedWebhookEvent, + construct_type( + type_=ParsedWebhookEvent, + value=json.loads(payload), + ), + ) + + def parse_unsafe(self, payload: str) -> ParsedWebhookEvent: + return cast( + ParsedWebhookEvent, + construct_type( + type_=ParsedWebhookEvent, + value=json.loads(payload), + ), + ) class AsyncWebhooks(AsyncAPIResource): @@ -133,75 +104,48 @@ def verify_signature( *, secret: str | None = None, ) -> None: - """Validates whether or not the webhook payload was sent by Lithic. + """Verifies that the given payload was sent by Lithic.""" + try: + from standardwebhooks import Webhook + except ImportError as exc: + raise LithicError("You need to install `lithic[webhooks]` to use this method") from exc - 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") + if secret is None: + raise ValueError( + "Cannot verify a webhook without a secret on either the client's webhook_secret or passed in as an argument" + ) + + headers = { + "webhook-id": get_required_header(headers, "webhook-id"), + "webhook-timestamp": get_required_header(headers, "webhook-timestamp"), + "webhook-signature": get_required_header(headers, "webhook-signature"), + } + Webhook(secret).verify(payload, headers) + + def parse( + self, + payload: str, + *, + headers: Mapping[str, str], + secret: str | bytes | None = None, + ) -> ParsedWebhookEvent: + secret = secret.decode("utf-8") if isinstance(secret, bytes) else secret + self.verify_signature(payload=payload, headers=headers, secret=secret) + return cast( + ParsedWebhookEvent, + construct_type( + type_=ParsedWebhookEvent, + value=json.loads(payload), + ), + ) + + def parse_unsafe(self, payload: str) -> ParsedWebhookEvent: + return cast( + ParsedWebhookEvent, + construct_type( + type_=ParsedWebhookEvent, + value=json.loads(payload), + ), + ) diff --git a/src/lithic/types/__init__.py b/src/lithic/types/__init__.py index af857e3b..89961e91 100644 --- a/src/lithic/types/__init__.py +++ b/src/lithic/types/__init__.py @@ -4,6 +4,7 @@ from .card import Card as Card from .event import Event as Event +from .device import Device as Device from .shared import ( Address as Address, Carrier as Carrier, @@ -24,11 +25,15 @@ from .api_status import APIStatus as APIStatus from .dispute_v2 import DisputeV2 as DisputeV2 from .owner_type import OwnerType as OwnerType +from .token_info import TokenInfo as TokenInfo from .transaction import Transaction as Transaction from .card_program import CardProgram as CardProgram from .non_pci_card import NonPCICard as NonPCICard from .tokenization import Tokenization as Tokenization +from .funding_event import FundingEvent as FundingEvent +from .network_total import NetworkTotal as NetworkTotal from .account_holder import AccountHolder as AccountHolder +from .card_bulk_order import CardBulkOrder as CardBulkOrder from .message_attempt import MessageAttempt as MessageAttempt from .network_program import NetworkProgram as NetworkProgram from .card_list_params import CardListParams as CardListParams @@ -66,11 +71,14 @@ from .verification_method import VerificationMethod as VerificationMethod from .account_spend_limits import AccountSpendLimits as AccountSpendLimits from .address_update_param import AddressUpdateParam as AddressUpdateParam +from .internal_transaction import InternalTransaction as InternalTransaction +from .parsed_webhook_event import ParsedWebhookEvent as ParsedWebhookEvent from .spend_limit_duration import SpendLimitDuration as SpendLimitDuration from .account_update_params import AccountUpdateParams as AccountUpdateParams 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 .external_bank_account import ExternalBankAccount as ExternalBankAccount from .financial_transaction import FinancialTransaction as FinancialTransaction from .payment_create_params import PaymentCreateParams as PaymentCreateParams from .payment_return_params import PaymentReturnParams as PaymentReturnParams @@ -80,72 +88,254 @@ from .card_provision_response import CardProvisionResponse as CardProvisionResponse from .disputes_v2_list_params import DisputesV2ListParams as DisputesV2ListParams from .payment_create_response import PaymentCreateResponse as PaymentCreateResponse +from .three_ds_authentication import ThreeDSAuthentication as ThreeDSAuthentication +from .tokenization_tfa_reason import TokenizationTfaReason as TokenizationTfaReason from .transaction_list_params import TransactionListParams as TransactionListParams +from .wallet_decisioning_info import WalletDecisioningInfo as WalletDecisioningInfo from .card_program_list_params import CardProgramListParams as CardProgramListParams from .tokenization_list_params import TokenizationListParams as TokenizationListParams +from .tokenization_rule_result import TokenizationRuleResult as TokenizationRuleResult +from .asa_request_webhook_event import AsaRequestWebhookEvent as AsaRequestWebhookEvent from .book_transfer_list_params import BookTransferListParams as BookTransferListParams from .card_get_embed_url_params import CardGetEmbedURLParams as CardGetEmbedURLParams from .card_search_by_pan_params import CardSearchByPanParams as CardSearchByPanParams from .card_web_provision_params import CardWebProvisionParams as CardWebProvisionParams +from .cardholder_authentication import ( + CardholderAuthentication as CardholderAuthentication, +) +from .financial_account_balance import ( + FinancialAccountBalance as FinancialAccountBalance, +) from .funding_event_list_params import FundingEventListParams as FundingEventListParams -from .responder_endpoint_status import ResponderEndpointStatus as ResponderEndpointStatus -from .account_holder_list_params import AccountHolderListParams as AccountHolderListParams +from .responder_endpoint_status import ( + ResponderEndpointStatus as ResponderEndpointStatus, +) +from .account_holder_list_params import ( + AccountHolderListParams as AccountHolderListParams, +) +from .card_created_webhook_event import ( + CardCreatedWebhookEvent as CardCreatedWebhookEvent, +) 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 .book_transfer_create_params import BookTransferCreateParams as BookTransferCreateParams -from .card_web_provision_response import CardWebProvisionResponse as CardWebProvisionResponse -from .funding_event_list_response import FundingEventListResponse as FundingEventListResponse -from .network_program_list_params import NetworkProgramListParams as NetworkProgramListParams -from .account_activity_list_params import AccountActivityListParams as AccountActivityListParams -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_activity_list_response import AccountActivityListResponse as AccountActivityListResponse -from .account_holder_create_response import AccountHolderCreateResponse as AccountHolderCreateResponse -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_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 .funding_event_retrieve_response import FundingEventRetrieveResponse as FundingEventRetrieveResponse -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 .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 .card_renewed_webhook_event import ( + CardRenewedWebhookEvent as CardRenewedWebhookEvent, +) +from .card_shipped_webhook_event import ( + CardShippedWebhookEvent as CardShippedWebhookEvent, +) +from .event_list_attempts_params import ( + EventListAttemptsParams as EventListAttemptsParams, +) +from .settlement_summary_details import ( + SettlementSummaryDetails as SettlementSummaryDetails, +) +from .book_transfer_create_params import ( + BookTransferCreateParams as BookTransferCreateParams, +) +from .card_bulk_order_list_params import ( + CardBulkOrderListParams as CardBulkOrderListParams, +) +from .card_reissued_webhook_event import ( + CardReissuedWebhookEvent as CardReissuedWebhookEvent, +) +from .card_web_provision_response import ( + CardWebProvisionResponse as CardWebProvisionResponse, +) +from .network_program_list_params import ( + NetworkProgramListParams as NetworkProgramListParams, +) +from .tokenization_decline_reason import ( + TokenizationDeclineReason as TokenizationDeclineReason, +) +from .account_activity_list_params import ( + AccountActivityListParams as AccountActivityListParams, +) +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 .card_converted_webhook_event import ( + CardConvertedWebhookEvent as CardConvertedWebhookEvent, +) +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 .balance_updated_webhook_event import ( + BalanceUpdatedWebhookEvent as BalanceUpdatedWebhookEvent, +) +from .card_bulk_order_create_params import ( + CardBulkOrderCreateParams as CardBulkOrderCreateParams, +) +from .card_bulk_order_update_params import ( + CardBulkOrderUpdateParams as CardBulkOrderUpdateParams, +) +from .digital_wallet_token_metadata import ( + DigitalWalletTokenMetadata as DigitalWalletTokenMetadata, +) +from .dispute_list_evidences_params import ( + DisputeListEvidencesParams as DisputeListEvidencesParams, +) +from .dispute_updated_webhook_event import ( + DisputeUpdatedWebhookEvent as DisputeUpdatedWebhookEvent, +) +from .external_bank_account_address import ( + ExternalBankAccountAddress as ExternalBankAccountAddress, +) +from .financial_account_list_params import ( + FinancialAccountListParams as FinancialAccountListParams, +) +from .account_activity_list_response import ( + AccountActivityListResponse as AccountActivityListResponse, +) +from .account_holder_create_response import ( + AccountHolderCreateResponse as AccountHolderCreateResponse, +) +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 .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 .loan_tape_created_webhook_event import ( + LoanTapeCreatedWebhookEvent as LoanTapeCreatedWebhookEvent, +) +from .loan_tape_updated_webhook_event import ( + LoanTapeUpdatedWebhookEvent as LoanTapeUpdatedWebhookEvent, +) +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 .statements_created_webhook_event import ( + StatementsCreatedWebhookEvent as StatementsCreatedWebhookEvent, +) +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 .tokenization_result_webhook_event import ( + TokenizationResultWebhookEvent as TokenizationResultWebhookEvent, +) +from .management_operation_create_params import ( + ManagementOperationCreateParams as ManagementOperationCreateParams, +) +from .responder_endpoint_create_response import ( + ResponderEndpointCreateResponse as ResponderEndpointCreateResponse, +) +from .tokenization_updated_webhook_event import ( + TokenizationUpdatedWebhookEvent as TokenizationUpdatedWebhookEvent, +) +from .transaction_simulate_return_params import ( + TransactionSimulateReturnParams as TransactionSimulateReturnParams, +) +from .transaction_simulate_void_response import ( + TransactionSimulateVoidResponse as TransactionSimulateVoidResponse, +) +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 .funding_event_created_webhook_event import ( + FundingEventCreatedWebhookEvent as FundingEventCreatedWebhookEvent, +) +from .management_operation_reverse_params import ( + ManagementOperationReverseParams as ManagementOperationReverseParams, +) +from .network_total_created_webhook_event import ( + NetworkTotalCreatedWebhookEvent as NetworkTotalCreatedWebhookEvent, +) +from .network_total_updated_webhook_event import ( + NetworkTotalUpdatedWebhookEvent as NetworkTotalUpdatedWebhookEvent, +) +from .account_holder_created_webhook_event import ( + AccountHolderCreatedWebhookEvent as AccountHolderCreatedWebhookEvent, +) +from .account_holder_updated_webhook_event import ( + AccountHolderUpdatedWebhookEvent as AccountHolderUpdatedWebhookEvent, +) +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 ( AccountHolderUploadDocumentParams as AccountHolderUploadDocumentParams, ) @@ -158,8 +348,14 @@ from .account_holder_list_documents_response import ( AccountHolderListDocumentsResponse as AccountHolderListDocumentsResponse, ) -from .external_bank_account_unpause_response import ( - ExternalBankAccountUnpauseResponse as ExternalBankAccountUnpauseResponse, +from .card_transaction_updated_webhook_event import ( + CardTransactionUpdatedWebhookEvent as CardTransactionUpdatedWebhookEvent, +) +from .external_payment_created_webhook_event import ( + ExternalPaymentCreatedWebhookEvent as ExternalPaymentCreatedWebhookEvent, +) +from .external_payment_updated_webhook_event import ( + ExternalPaymentUpdatedWebhookEvent as ExternalPaymentUpdatedWebhookEvent, ) from .financial_account_update_status_params import ( FinancialAccountUpdateStatusParams as FinancialAccountUpdateStatusParams, @@ -176,18 +372,63 @@ from .external_bank_account_retrieve_response import ( ExternalBankAccountRetrieveResponse as ExternalBankAccountRetrieveResponse, ) +from .financial_account_created_webhook_event import ( + FinancialAccountCreatedWebhookEvent as FinancialAccountCreatedWebhookEvent, +) +from .financial_account_updated_webhook_event import ( + FinancialAccountUpdatedWebhookEvent as FinancialAccountUpdatedWebhookEvent, +) from .funding_event_retrieve_details_response import ( FundingEventRetrieveDetailsResponse as FundingEventRetrieveDetailsResponse, ) +from .settlement_report_updated_webhook_event import ( + SettlementReportUpdatedWebhookEvent as SettlementReportUpdatedWebhookEvent, +) +from .account_holder_verification_webhook_event import ( + AccountHolderVerificationWebhookEvent as AccountHolderVerificationWebhookEvent, +) +from .dispute_transaction_created_webhook_event import ( + DisputeTransactionCreatedWebhookEvent as DisputeTransactionCreatedWebhookEvent, +) +from .dispute_transaction_updated_webhook_event import ( + DisputeTransactionUpdatedWebhookEvent as DisputeTransactionUpdatedWebhookEvent, +) +from .payment_transaction_created_webhook_event import ( + PaymentTransactionCreatedWebhookEvent as PaymentTransactionCreatedWebhookEvent, +) +from .payment_transaction_updated_webhook_event import ( + PaymentTransactionUpdatedWebhookEvent as PaymentTransactionUpdatedWebhookEvent, +) from .transaction_simulate_authorization_params import ( TransactionSimulateAuthorizationParams as TransactionSimulateAuthorizationParams, ) from .external_bank_account_retry_prenote_params import ( ExternalBankAccountRetryPrenoteParams as ExternalBankAccountRetryPrenoteParams, ) +from .internal_transaction_created_webhook_event import ( + InternalTransactionCreatedWebhookEvent as InternalTransactionCreatedWebhookEvent, +) +from .internal_transaction_updated_webhook_event import ( + InternalTransactionUpdatedWebhookEvent as InternalTransactionUpdatedWebhookEvent, +) +from .management_operation_created_webhook_event import ( + ManagementOperationCreatedWebhookEvent as ManagementOperationCreatedWebhookEvent, +) +from .management_operation_updated_webhook_event import ( + ManagementOperationUpdatedWebhookEvent as ManagementOperationUpdatedWebhookEvent, +) from .tokenization_resend_activation_code_params import ( TokenizationResendActivationCodeParams as TokenizationResendActivationCodeParams, ) +from .external_bank_account_created_webhook_event import ( + ExternalBankAccountCreatedWebhookEvent as ExternalBankAccountCreatedWebhookEvent, +) +from .external_bank_account_updated_webhook_event import ( + ExternalBankAccountUpdatedWebhookEvent as ExternalBankAccountUpdatedWebhookEvent, +) +from .tokenization_approval_request_webhook_event import ( + TokenizationApprovalRequestWebhookEvent as TokenizationApprovalRequestWebhookEvent, +) from .tokenization_update_digital_card_art_params import ( TokenizationUpdateDigitalCardArtParams as TokenizationUpdateDigitalCardArtParams, ) @@ -197,11 +438,17 @@ from .transaction_simulate_return_reversal_params import ( TransactionSimulateReturnReversalParams as TransactionSimulateReturnReversalParams, ) -from .external_bank_account_retry_prenote_response import ( - ExternalBankAccountRetryPrenoteResponse as ExternalBankAccountRetryPrenoteResponse, +from .dispute_evidence_upload_failed_webhook_event import ( + DisputeEvidenceUploadFailedWebhookEvent as DisputeEvidenceUploadFailedWebhookEvent, ) -from .tokenization_update_digital_card_art_response import ( - TokenizationUpdateDigitalCardArtResponse as TokenizationUpdateDigitalCardArtResponse, +from .account_holder_document_updated_webhook_event import ( + AccountHolderDocumentUpdatedWebhookEvent as AccountHolderDocumentUpdatedWebhookEvent, +) +from .three_ds_authentication_created_webhook_event import ( + ThreeDSAuthenticationCreatedWebhookEvent as ThreeDSAuthenticationCreatedWebhookEvent, +) +from .three_ds_authentication_updated_webhook_event import ( + ThreeDSAuthenticationUpdatedWebhookEvent as ThreeDSAuthenticationUpdatedWebhookEvent, ) from .transaction_simulate_return_reversal_response import ( TransactionSimulateReturnReversalResponse as TransactionSimulateReturnReversalResponse, @@ -209,12 +456,30 @@ from .account_activity_retrieve_transaction_response import ( AccountActivityRetrieveTransactionResponse as AccountActivityRetrieveTransactionResponse, ) +from .tokenization_decisioning_request_webhook_event import ( + TokenizationDecisioningRequestWebhookEvent as TokenizationDecisioningRequestWebhookEvent, +) +from .book_transfer_transaction_created_webhook_event import ( + BookTransferTransactionCreatedWebhookEvent as BookTransferTransactionCreatedWebhookEvent, +) +from .book_transfer_transaction_updated_webhook_event import ( + BookTransferTransactionUpdatedWebhookEvent as BookTransferTransactionUpdatedWebhookEvent, +) +from .three_ds_authentication_challenge_webhook_event import ( + ThreeDSAuthenticationChallengeWebhookEvent as ThreeDSAuthenticationChallengeWebhookEvent, +) from .tokenization_decisioning_rotate_secret_response import ( TokenizationDecisioningRotateSecretResponse as TokenizationDecisioningRotateSecretResponse, ) from .account_holder_simulate_enrollment_review_params import ( AccountHolderSimulateEnrollmentReviewParams as AccountHolderSimulateEnrollmentReviewParams, ) +from .auth_rules_backtest_report_created_webhook_event import ( + AuthRulesBacktestReportCreatedWebhookEvent as AuthRulesBacktestReportCreatedWebhookEvent, +) +from .digital_wallet_tokenization_result_webhook_event import ( + DigitalWalletTokenizationResultWebhookEvent as DigitalWalletTokenizationResultWebhookEvent, +) from .financial_account_register_account_number_params import ( FinancialAccountRegisterAccountNumberParams as FinancialAccountRegisterAccountNumberParams, ) @@ -224,6 +489,9 @@ from .transaction_simulate_credit_authorization_params import ( TransactionSimulateCreditAuthorizationParams as TransactionSimulateCreditAuthorizationParams, ) +from .digital_wallet_tokenization_updated_webhook_event import ( + DigitalWalletTokenizationUpdatedWebhookEvent as DigitalWalletTokenizationUpdatedWebhookEvent, +) from .external_bank_account_retry_micro_deposits_params import ( ExternalBankAccountRetryMicroDepositsParams as ExternalBankAccountRetryMicroDepositsParams, ) @@ -239,12 +507,33 @@ from .external_bank_account_retry_micro_deposits_response import ( ExternalBankAccountRetryMicroDepositsResponse as ExternalBankAccountRetryMicroDepositsResponse, ) +from .card_transaction_enhanced_data_created_webhook_event import ( + CardTransactionEnhancedDataCreatedWebhookEvent as CardTransactionEnhancedDataCreatedWebhookEvent, +) +from .card_transaction_enhanced_data_updated_webhook_event import ( + CardTransactionEnhancedDataUpdatedWebhookEvent as CardTransactionEnhancedDataUpdatedWebhookEvent, +) from .transaction_simulate_credit_authorization_advice_params import ( TransactionSimulateCreditAuthorizationAdviceParams as TransactionSimulateCreditAuthorizationAdviceParams, ) from .account_holder_simulate_enrollment_document_review_params import ( AccountHolderSimulateEnrollmentDocumentReviewParams as AccountHolderSimulateEnrollmentDocumentReviewParams, ) +from .tokenization_two_factor_authentication_code_webhook_event import ( + TokenizationTwoFactorAuthenticationCodeWebhookEvent as TokenizationTwoFactorAuthenticationCodeWebhookEvent, +) from .transaction_simulate_credit_authorization_advice_response import ( TransactionSimulateCreditAuthorizationAdviceResponse as TransactionSimulateCreditAuthorizationAdviceResponse, ) +from .digital_wallet_tokenization_approval_request_webhook_event import ( + DigitalWalletTokenizationApprovalRequestWebhookEvent as DigitalWalletTokenizationApprovalRequestWebhookEvent, +) +from .tokenization_two_factor_authentication_code_sent_webhook_event import ( + TokenizationTwoFactorAuthenticationCodeSentWebhookEvent as TokenizationTwoFactorAuthenticationCodeSentWebhookEvent, +) +from .digital_wallet_tokenization_two_factor_authentication_code_webhook_event import ( + DigitalWalletTokenizationTwoFactorAuthenticationCodeWebhookEvent as DigitalWalletTokenizationTwoFactorAuthenticationCodeWebhookEvent, +) +from .digital_wallet_tokenization_two_factor_authentication_code_sent_webhook_event import ( + DigitalWalletTokenizationTwoFactorAuthenticationCodeSentWebhookEvent as DigitalWalletTokenizationTwoFactorAuthenticationCodeSentWebhookEvent, +) diff --git a/src/lithic/types/account.py b/src/lithic/types/account.py index 11bbfec8..7cc0f683 100644 --- a/src/lithic/types/account.py +++ b/src/lithic/types/account.py @@ -10,6 +10,10 @@ class SpendLimit(BaseModel): + """ + Spend limit information for the user containing the daily, monthly, and lifetime spend limit of the account. Any charges to a card owned by this account will be declined once their transaction volume has surpassed the value in the applicable time limit (rolling). A lifetime limit of 0 indicates that the lifetime limit feature is disabled. + """ + daily: int """Daily spend limit (in cents).""" diff --git a/src/lithic/types/account_activity_list_params.py b/src/lithic/types/account_activity_list_params.py index 23c24a1f..7b28515b 100644 --- a/src/lithic/types/account_activity_list_params.py +++ b/src/lithic/types/account_activity_list_params.py @@ -35,6 +35,8 @@ class AccountActivityListParams(TypedDict, total=False): "CARD", "EXTERNAL_ACH", "EXTERNAL_CHECK", + "EXTERNAL_FEDNOW", + "EXTERNAL_RTP", "EXTERNAL_TRANSFER", "EXTERNAL_WIRE", "MANAGEMENT_ADJUSTMENT", diff --git a/src/lithic/types/account_activity_list_response.py b/src/lithic/types/account_activity_list_response.py index 3680ff4d..33992124 100644 --- a/src/lithic/types/account_activity_list_response.py +++ b/src/lithic/types/account_activity_list_response.py @@ -17,6 +17,8 @@ class FinancialTransaction(BaseModel): + """Financial transaction with inheritance from unified base transaction""" + token: str """Unique identifier for the transaction""" @@ -31,6 +33,8 @@ class FinancialTransaction(BaseModel): "CARD", "EXTERNAL_ACH", "EXTERNAL_CHECK", + "EXTERNAL_FEDNOW", + "EXTERNAL_RTP", "EXTERNAL_TRANSFER", "EXTERNAL_WIRE", "MANAGEMENT_ADJUSTMENT", @@ -77,6 +81,8 @@ class FinancialTransaction(BaseModel): class CardTransaction(Transaction): + """Card transaction with ledger base properties""" + token: str # type: ignore """Unique identifier for the transaction""" diff --git a/src/lithic/types/account_activity_retrieve_transaction_response.py b/src/lithic/types/account_activity_retrieve_transaction_response.py index a6b4dd4e..202fd3eb 100644 --- a/src/lithic/types/account_activity_retrieve_transaction_response.py +++ b/src/lithic/types/account_activity_retrieve_transaction_response.py @@ -17,6 +17,8 @@ class FinancialTransaction(BaseModel): + """Financial transaction with inheritance from unified base transaction""" + token: str """Unique identifier for the transaction""" @@ -31,6 +33,8 @@ class FinancialTransaction(BaseModel): "CARD", "EXTERNAL_ACH", "EXTERNAL_CHECK", + "EXTERNAL_FEDNOW", + "EXTERNAL_RTP", "EXTERNAL_TRANSFER", "EXTERNAL_WIRE", "MANAGEMENT_ADJUSTMENT", @@ -77,6 +81,8 @@ class FinancialTransaction(BaseModel): class CardTransaction(Transaction): + """Card transaction with ledger base properties""" + token: str # type: ignore """Unique identifier for the transaction""" diff --git a/src/lithic/types/account_holder.py b/src/lithic/types/account_holder.py index 25bf9509..dbe28588 100644 --- a/src/lithic/types/account_holder.py +++ b/src/lithic/types/account_holder.py @@ -56,6 +56,11 @@ class BeneficialOwnerEntity(BaseModel): class BeneficialOwnerIndividual(BaseModel): + """Information about an individual associated with an account holder. + + A subset of the information provided via KYC. For example, we do not return the government id. + """ + address: Address """Individual's current address""" @@ -79,6 +84,11 @@ class BeneficialOwnerIndividual(BaseModel): class BusinessEntity(BaseModel): + """Only present when user_type == "BUSINESS". + + Information about the business for which the account is being opened and KYB is being run. + """ + address: Address """ Business's physical address - PO boxes, UPS drops, and FedEx drops are not @@ -115,6 +125,13 @@ class BusinessEntity(BaseModel): class ControlPerson(BaseModel): + """ + 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. + """ + address: Address """Individual's current address""" @@ -138,6 +155,11 @@ class ControlPerson(BaseModel): class Individual(BaseModel): + """Only present when user_type == "INDIVIDUAL". + + Information about the individual for which the account is being opened and KYC is being run. + """ + address: Address """Individual's current address""" @@ -161,6 +183,8 @@ class Individual(BaseModel): class VerificationApplication(BaseModel): + """Information about the most recent identity verification attempt""" + created: Optional[datetime] = None """Timestamp of when the application was created.""" diff --git a/src/lithic/types/account_holder_create_params.py b/src/lithic/types/account_holder_create_params.py index d450f1e8..c90cfad2 100644 --- a/src/lithic/types/account_holder_create_params.py +++ b/src/lithic/types/account_holder_create_params.py @@ -92,6 +92,8 @@ class KYB(TypedDict, total=False): class KYBBeneficialOwnerIndividual(TypedDict, total=False): + """Individuals associated with a KYB application. Phone number is optional.""" + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -126,6 +128,10 @@ class KYBBeneficialOwnerIndividual(TypedDict, total=False): class KYBBusinessEntity(TypedDict, total=False): + """ + Information for business for which the account is being opened and KYB is being run. + """ + address: Required[Address] """ Business's physical address - PO boxes, UPS drops, and FedEx drops are not @@ -159,6 +165,13 @@ class KYBBusinessEntity(TypedDict, total=False): class KYBControlPerson(TypedDict, total=False): + """ + 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. + """ + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -278,6 +291,8 @@ class KYBDelegated(TypedDict, total=False): class KYBDelegatedBusinessEntity(TypedDict, total=False): + """Information for business for which the account is being opened.""" + address: Required[Address] """ Business's physical address - PO boxes, UPS drops, and FedEx drops are not @@ -311,6 +326,8 @@ class KYBDelegatedBusinessEntity(TypedDict, total=False): class KYBDelegatedBeneficialOwnerIndividual(TypedDict, total=False): + """Individuals associated with a KYB application. Phone number is optional.""" + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -345,6 +362,13 @@ class KYBDelegatedBeneficialOwnerIndividual(TypedDict, total=False): class KYBDelegatedControlPerson(TypedDict, total=False): + """ + 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. + """ + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -411,6 +435,10 @@ class KYC(TypedDict, total=False): class KYCIndividual(TypedDict, total=False): + """ + Information on individual for whom the account is being opened and KYC is being run. + """ + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not diff --git a/src/lithic/types/account_holder_created_webhook_event.py b/src/lithic/types/account_holder_created_webhook_event.py new file mode 100644 index 00000000..b7cfb961 --- /dev/null +++ b/src/lithic/types/account_holder_created_webhook_event.py @@ -0,0 +1,31 @@ +# 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__ = ["AccountHolderCreatedWebhookEvent"] + + +class AccountHolderCreatedWebhookEvent(BaseModel): + event_type: Literal["account_holder.created"] + """The type of event that occurred.""" + + token: Optional[str] = None + """The token of the account_holder that was created.""" + + account_token: Optional[str] = None + """The token of the account that was created.""" + + created: Optional[datetime] = None + """When the account_holder was created""" + + required_documents: Optional[List[RequiredDocument]] = None + + status: Optional[Literal["ACCEPTED", "PENDING_REVIEW"]] = None + """The status of the account_holder that was created.""" + + status_reason: Optional[List[str]] = None diff --git a/src/lithic/types/account_holder_document_updated_webhook_event.py b/src/lithic/types/account_holder_document_updated_webhook_event.py new file mode 100644 index 00000000..33324314 --- /dev/null +++ b/src/lithic/types/account_holder_document_updated_webhook_event.py @@ -0,0 +1,78 @@ +# 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 + +__all__ = ["AccountHolderDocumentUpdatedWebhookEvent", "RequiredDocumentUpload"] + + +class RequiredDocumentUpload(BaseModel): + """A document upload that belongs to the overall account holder document""" + + token: Optional[str] = None + """The token of the document upload""" + + accepted_entity_status_reasons: Optional[List[str]] = None + + created: Optional[datetime] = None + """When the document upload was created""" + + image_type: Optional[Literal["FRONT", "BACK"]] = None + """The type of image that was uploaded""" + + rejected_entity_status_reasons: Optional[List[str]] = None + + status: Optional[Literal["ACCEPTED", "REJECTED", "PENDING_UPLOAD", "UPLOADED", "PARTIAL_APPROVAL"]] = None + """The status of the document upload""" + + status_reasons: Optional[List[str]] = None + + updated: Optional[datetime] = None + """When the document upload was last updated""" + + +class AccountHolderDocumentUpdatedWebhookEvent(BaseModel): + event_type: Literal["account_holder_document.updated"] + """The type of event that occurred.""" + + token: Optional[str] = None + """The token of the account holder document""" + + account_holder_token: Optional[str] = None + """The token of the account_holder that the document belongs to""" + + created: Optional[datetime] = None + """When the account_holder was created""" + + document_type: Optional[ + 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", + ] + ] = None + """Type of documentation to be submitted for verification of an account holder""" + + entity_token: Optional[str] = None + """The token of the entity that the document belongs to""" + + required_document_uploads: Optional[List[RequiredDocumentUpload]] = None diff --git a/src/lithic/types/account_holder_simulate_enrollment_review_response.py b/src/lithic/types/account_holder_simulate_enrollment_review_response.py index a8af35c4..56f53d0d 100644 --- a/src/lithic/types/account_holder_simulate_enrollment_review_response.py +++ b/src/lithic/types/account_holder_simulate_enrollment_review_response.py @@ -21,6 +21,10 @@ class BeneficialOwnerIndividualAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + address1: str """Valid deliverable address (no PO boxes).""" @@ -80,6 +84,10 @@ class BeneficialOwnerIndividual(BaseModel): class ControlPersonAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + address1: str """Valid deliverable address (no PO boxes).""" @@ -112,6 +120,15 @@ class ControlPersonAddress(BaseModel): class ControlPerson(BaseModel): + """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. + """ + address: Optional[ControlPersonAddress] = None """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -139,6 +156,10 @@ class ControlPerson(BaseModel): class IndividualAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + address1: str """Valid deliverable address (no PO boxes).""" @@ -171,6 +192,11 @@ class IndividualAddress(BaseModel): class Individual(BaseModel): + """Only present when user_type == "INDIVIDUAL". + + Information about the individual for which the account is being opened and KYC is being run. + """ + address: Optional[IndividualAddress] = None """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -198,6 +224,8 @@ class Individual(BaseModel): class VerificationApplication(BaseModel): + """Information about the most recent identity verification attempt""" + created: datetime """Timestamp of when the application was created.""" diff --git a/src/lithic/types/account_holder_update_params.py b/src/lithic/types/account_holder_update_params.py index 4c92d81d..ed23ca4a 100644 --- a/src/lithic/types/account_holder_update_params.py +++ b/src/lithic/types/account_holder_update_params.py @@ -106,6 +106,8 @@ class KYBPatchRequestBeneficialOwnerEntity(TypedDict, total=False): class KYBPatchRequestBeneficialOwnerIndividual(TypedDict, total=False): + """Individuals associated with a KYB application. Phone number is optional.""" + entity_token: Required[str] """Globally unique identifier for an entity.""" @@ -144,6 +146,10 @@ class KYBPatchRequestBeneficialOwnerIndividual(TypedDict, total=False): class KYBPatchRequestBusinessEntity(TypedDict, total=False): + """ + Information for business for which the account is being opened and KYB is being run. + """ + entity_token: Required[str] """Globally unique identifier for an entity.""" @@ -180,6 +186,10 @@ class KYBPatchRequestBusinessEntity(TypedDict, total=False): class KYBPatchRequestControlPerson(TypedDict, total=False): + """ + 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. + """ + entity_token: Required[str] """Globally unique identifier for an entity.""" @@ -232,6 +242,10 @@ class KYCPatchRequest(TypedDict, total=False): class KYCPatchRequestIndividual(TypedDict, total=False): + """ + Information on the individual for whom the account is being opened and KYC is being run. + """ + entity_token: Required[str] """Globally unique identifier for an entity.""" diff --git a/src/lithic/types/account_holder_update_response.py b/src/lithic/types/account_holder_update_response.py index bbe58a3d..7c8a1340 100644 --- a/src/lithic/types/account_holder_update_response.py +++ b/src/lithic/types/account_holder_update_response.py @@ -24,6 +24,10 @@ class KybkycPatchResponseBeneficialOwnerIndividualAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + address1: str """Valid deliverable address (no PO boxes).""" @@ -83,6 +87,10 @@ class KybkycPatchResponseBeneficialOwnerIndividual(BaseModel): class KYBKYCPatchResponseControlPersonAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + address1: str """Valid deliverable address (no PO boxes).""" @@ -115,6 +123,15 @@ class KYBKYCPatchResponseControlPersonAddress(BaseModel): class KYBKYCPatchResponseControlPerson(BaseModel): + """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. + """ + address: Optional[KYBKYCPatchResponseControlPersonAddress] = None """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -142,6 +159,10 @@ class KYBKYCPatchResponseControlPerson(BaseModel): class KYBKYCPatchResponseIndividualAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + address1: str """Valid deliverable address (no PO boxes).""" @@ -174,6 +195,11 @@ class KYBKYCPatchResponseIndividualAddress(BaseModel): class KYBKYCPatchResponseIndividual(BaseModel): + """Only present when user_type == "INDIVIDUAL". + + Information about the individual for which the account is being opened and KYC is being run. + """ + address: Optional[KYBKYCPatchResponseIndividualAddress] = None """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -201,6 +227,8 @@ class KYBKYCPatchResponseIndividual(BaseModel): class KYBKYCPatchResponseVerificationApplication(BaseModel): + """Information about the most recent identity verification attempt""" + created: datetime """Timestamp of when the application was created.""" @@ -412,6 +440,8 @@ class KYBKYCPatchResponse(BaseModel): class PatchResponseAddress(BaseModel): + """The address for the account holder""" + address1: str """Valid deliverable address (no PO boxes).""" diff --git a/src/lithic/types/account_holder_updated_webhook_event.py b/src/lithic/types/account_holder_updated_webhook_event.py new file mode 100644 index 00000000..fcda2c62 --- /dev/null +++ b/src/lithic/types/account_holder_updated_webhook_event.py @@ -0,0 +1,358 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, TypeAlias + +from .._models import BaseModel +from .kyb_business_entity import KYBBusinessEntity + +__all__ = [ + "AccountHolderUpdatedWebhookEvent", + "KYBPayload", + "KYBPayloadUpdateRequest", + "KYBPayloadUpdateRequestBeneficialOwnerIndividual", + "KYBPayloadUpdateRequestBeneficialOwnerIndividualAddress", + "KYBPayloadUpdateRequestControlPerson", + "KYBPayloadUpdateRequestControlPersonAddress", + "KYCPayload", + "KYCPayloadUpdateRequest", + "KYCPayloadUpdateRequestIndividual", + "KYCPayloadUpdateRequestIndividualAddress", + "LegacyPayload", +] + + +class KYBPayloadUpdateRequestBeneficialOwnerIndividualAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + 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 KYBPayloadUpdateRequestBeneficialOwnerIndividual(BaseModel): + address: Optional[KYBPayloadUpdateRequestBeneficialOwnerIndividualAddress] = 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 KYBPayloadUpdateRequestControlPersonAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + 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 KYBPayloadUpdateRequestControlPerson(BaseModel): + """ + 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. + """ + + address: Optional[KYBPayloadUpdateRequestControlPersonAddress] = 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 KYBPayloadUpdateRequest(BaseModel): + """Original request to update the account holder.""" + + beneficial_owner_entities: Optional[List[KYBBusinessEntity]] = None + """Deprecated.""" + + beneficial_owner_individuals: Optional[List[KYBPayloadUpdateRequestBeneficialOwnerIndividual]] = None + """ + You must submit a list of all direct and indirect individuals with 25% or more + ownership in the company. A maximum of 4 beneficial owners can be submitted. If + no individual owns 25% of the company you do not need to send beneficial owner + information. 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: Optional[KYBBusinessEntity] = None + """ + Information for business for which the account is being opened and KYB is being + run. + """ + + control_person: Optional[KYBPayloadUpdateRequestControlPerson] = None + """ + 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. + """ + + +class KYBPayload(BaseModel): + """KYB payload for an updated account holder.""" + + token: str + """The token of the account_holder that was created.""" + + update_request: KYBPayloadUpdateRequest + """Original request to update the account holder.""" + + event_type: Optional[Literal["account_holder.updated"]] = None + """The type of event that occurred.""" + + external_id: Optional[str] = None + """ + A user provided id that can be used to link an account holder with an external + system + """ + + nature_of_business: Optional[str] = None + """ + Short description of the company's line of business (i.e., what does the company + do?). + """ + + website_url: Optional[str] = None + """Company website URL.""" + + +class KYCPayloadUpdateRequestIndividualAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + 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 KYCPayloadUpdateRequestIndividual(BaseModel): + """ + Information on the individual for whom the account is being opened and KYC is being run. + """ + + address: Optional[KYCPayloadUpdateRequestIndividualAddress] = 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 KYCPayloadUpdateRequest(BaseModel): + """Original request to update the account holder.""" + + individual: Optional[KYCPayloadUpdateRequestIndividual] = None + """ + Information on the individual for whom the account is being opened and KYC is + being run. + """ + + +class KYCPayload(BaseModel): + """KYC payload for an updated account holder.""" + + token: str + """The token of the account_holder that was created.""" + + update_request: KYCPayloadUpdateRequest + """Original request to update the account holder.""" + + event_type: Optional[Literal["account_holder.updated"]] = None + """The type of event that occurred.""" + + external_id: Optional[str] = None + """ + A user provided id that can be used to link an account holder with an external + system + """ + + +class LegacyPayload(BaseModel): + """Legacy payload for an updated account holder.""" + + token: str + """The token of the account_holder that was created.""" + + business_account_token: Optional[str] = None + """ + If applicable, represents the business account token associated with the + account_holder. + """ + + created: Optional[datetime] = None + """When the account_holder updated event was created""" + + email: Optional[str] = None + """ + If updated, the newly updated email associated with the account_holder otherwise + the existing email is provided. + """ + + event_type: Optional[Literal["account_holder.updated"]] = None + """The type of event that occurred.""" + + external_id: Optional[str] = None + """If applicable, represents the external_id associated with the account_holder.""" + + first_name: Optional[str] = None + """If applicable, represents the account_holder's first name.""" + + last_name: Optional[str] = None + """If applicable, represents the account_holder's last name.""" + + legal_business_name: Optional[str] = None + """If applicable, represents the account_holder's business name.""" + + phone_number: Optional[str] = None + """ + If updated, the newly updated phone_number associated with the account_holder + otherwise the existing phone_number is provided. + """ + + +AccountHolderUpdatedWebhookEvent: TypeAlias = Union[KYBPayload, KYCPayload, LegacyPayload] diff --git a/src/lithic/types/account_holder_verification_webhook_event.py b/src/lithic/types/account_holder_verification_webhook_event.py new file mode 100644 index 00000000..cac64adb --- /dev/null +++ b/src/lithic/types/account_holder_verification_webhook_event.py @@ -0,0 +1,28 @@ +# 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 + +__all__ = ["AccountHolderVerificationWebhookEvent"] + + +class AccountHolderVerificationWebhookEvent(BaseModel): + event_type: Literal["account_holder.verification"] + """The type of event that occurred.""" + + token: Optional[str] = None + """The token of the account_holder being verified.""" + + account_token: Optional[str] = None + """The token of the account being verified.""" + + created: Optional[datetime] = None + """When the account_holder verification status was updated""" + + status: Optional[Literal["ACCEPTED", "PENDING_REVIEW", "REJECTED"]] = None + """The status of the account_holder that was created""" + + status_reasons: Optional[List[str]] = None diff --git a/src/lithic/types/account_update_params.py b/src/lithic/types/account_update_params.py index 411f56b8..954590e8 100644 --- a/src/lithic/types/account_update_params.py +++ b/src/lithic/types/account_update_params.py @@ -88,6 +88,10 @@ class AccountUpdateParams(TypedDict, total=False): class VerificationAddress(TypedDict, total=False): + """ + Address used during Address Verification Service (AVS) checks during transactions if enabled via Auth Rules. This field is deprecated as AVS checks are no longer supported by Auth Rules. The field will be removed from the schema in a future release. + """ + address1: str address2: str diff --git a/src/lithic/types/aggregate_balance.py b/src/lithic/types/aggregate_balance.py index 571305cd..d74f4cee 100644 --- a/src/lithic/types/aggregate_balance.py +++ b/src/lithic/types/aggregate_balance.py @@ -9,6 +9,8 @@ class AggregateBalance(BaseModel): + """Aggregate Balance across all end-user accounts""" + available_amount: int """Funds available for spend in the currency's smallest unit (e.g., cents for USD)""" diff --git a/src/lithic/types/asa_request_webhook_event.py b/src/lithic/types/asa_request_webhook_event.py new file mode 100644 index 00000000..2a338e32 --- /dev/null +++ b/src/lithic/types/asa_request_webhook_event.py @@ -0,0 +1,440 @@ +# 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 .token_info import TokenInfo +from .shared.merchant import Merchant +from .cardholder_authentication import CardholderAuthentication + +__all__ = [ + "AsaRequestWebhookEvent", + "Avs", + "Card", + "FleetInfo", + "LatestChallenge", + "NetworkSpecificData", + "NetworkSpecificDataMastercard", + "NetworkSpecificDataMastercardOnBehalfServiceResult", + "NetworkSpecificDataVisa", + "Pos", + "PosEntryMode", + "PosTerminal", +] + + +class Avs(BaseModel): + address: str + """Cardholder address""" + + address_on_file_match: Literal["MATCH", "MATCH_ADDRESS_ONLY", "MATCH_ZIP_ONLY", "MISMATCH", "NOT_PRESENT"] + """ + Lithic's evaluation result comparing the transaction's address data with the + cardholder KYC data if it exists. In the event Lithic does not have any + Cardholder KYC data, or the transaction does not contain any address data, + NOT_PRESENT will be returned + """ + + zipcode: str + """Cardholder ZIP code""" + + +class Card(BaseModel): + """Card object in ASA""" + + token: Optional[str] = None + """Globally unique identifier for the card.""" + + hostname: Optional[str] = None + """Hostname of card’s locked merchant (will be empty if not applicable)""" + + last_four: Optional[str] = None + """Last four digits of the card number""" + + memo: Optional[str] = None + """Customizable name to identify the card. + + We recommend against using this field to store JSON data as it can cause + unexpected behavior. + """ + + spend_limit: Optional[int] = None + """Amount (in cents) to limit approved authorizations. + + Purchase requests above the spend limit will be declined (refunds and credits + will be approved). + + Note that while spend limits are enforced based on authorized and settled volume + on a card, they are not recommended to be used for balance or + reconciliation-level accuracy. Spend limits also cannot block force posted + charges (i.e., when a merchant sends a clearing message without a prior + authorization). + """ + + spend_limit_duration: Optional[Literal["ANNUALLY", "FOREVER", "MONTHLY", "TRANSACTION"]] = None + """ + Note that to support recurring monthly payments, which can occur on different + day every month, the time window we consider for MONTHLY velocity starts 6 days + after the current calendar date one month prior. + """ + + state: Optional[Literal["CLOSED", "OPEN", "PAUSED", "PENDING_ACTIVATION", "PENDING_FULFILLMENT"]] = None + + type: Optional[Literal["SINGLE_USE", "MERCHANT_LOCKED", "UNLOCKED", "PHYSICAL", "DIGITAL_WALLET", "VIRTUAL"]] = None + + +class FleetInfo(BaseModel): + """ + Optional Object containing information if the Card is a part of a Fleet managed program + """ + + fleet_prompt_code: Literal["NO_PROMPT", "VEHICLE_NUMBER", "DRIVER_NUMBER"] + """Code indicating what the driver was prompted to enter at time of purchase. + + This is configured at a program level and is a static configuration, and does + not change on a request to request basis + """ + + fleet_restriction_code: Literal["NO_RESTRICTIONS", "FUEL_ONLY"] + """Code indicating which restrictions, if any, there are on purchase. + + This is configured at a program level and is a static configuration, and does + not change on a request to request basis + """ + + driver_number: Optional[str] = None + """Number representing the driver""" + + vehicle_number: Optional[str] = None + """Number associated with the vehicle""" + + +class LatestChallenge(BaseModel): + """ + The latest Authorization Challenge that was issued to the cardholder for this merchant. + """ + + phone_number: str + """The phone number used for sending Authorization Challenge SMS.""" + + status: Literal["COMPLETED", "PENDING", "EXPIRED", "ERROR"] + """The status of the Authorization Challenge + + - `COMPLETED` - Challenge was successfully completed by the cardholder + - `PENDING` - Challenge is still open + - `EXPIRED` - Challenge has expired without being completed + - `ERROR` - There was an error processing the challenge + """ + + completed_at: Optional[datetime] = None + """The date and time when the Authorization Challenge was completed in UTC. + + Present only if the status is `COMPLETED`. + """ + + +class NetworkSpecificDataMastercardOnBehalfServiceResult(BaseModel): + result_1: str + """Indicates the results of the service processing.""" + + result_2: str + """Identifies the results of the service processing.""" + + service: str + """Indicates the service performed on the transaction.""" + + +class NetworkSpecificDataMastercard(BaseModel): + ecommerce_security_level_indicator: Optional[str] = None + """Indicates the electronic commerce security level and UCAF collection.""" + + on_behalf_service_result: Optional[List[NetworkSpecificDataMastercardOnBehalfServiceResult]] = None + """The On-behalf Service performed on the transaction and the results. + + Contains all applicable, on-behalf service results that were performed on a + given transaction. + """ + + transaction_type_identifier: Optional[str] = None + """Indicates the type of additional transaction purpose.""" + + +class NetworkSpecificDataVisa(BaseModel): + business_application_identifier: Optional[str] = None + """ + Identifies the purpose or category of a transaction, used to classify and + process transactions according to Visa’s rules. + """ + + +class NetworkSpecificData(BaseModel): + """ + Contains raw data provided by the card network, including attributes that provide further context about the authorization. If populated by the network, data is organized by Lithic and passed through without further modification. Please consult the official network documentation for more details about these values and how to use them. This object is only available to certain programs- contact your Customer Success Manager to discuss enabling access. + """ + + mastercard: Optional[NetworkSpecificDataMastercard] = None + + visa: Optional[NetworkSpecificDataVisa] = None + + +class PosEntryMode(BaseModel): + """POS > Entry Mode object in ASA""" + + card: Optional[Literal["PRESENT", "NOT_PRESENT", "UNKNOWN"]] = None + """Card Presence Indicator""" + + cardholder: Optional[ + Literal[ + "DEFERRED_BILLING", + "ELECTRONIC_ORDER", + "INSTALLMENT", + "MAIL_ORDER", + "NOT_PRESENT", + "PRESENT", + "REOCCURRING", + "TELEPHONE_ORDER", + "UNKNOWN", + ] + ] = None + """Cardholder Presence Indicator""" + + pan: Optional[ + Literal[ + "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", + ] + ] = None + """Method of entry for the PAN""" + + pin_entered: Optional[bool] = None + """Indicates whether the cardholder entered the PIN. True if the PIN was entered.""" + + +class PosTerminal(BaseModel): + attended: bool + """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 + must be used for the remainder. Example scenario: A $40 transaction is attempted + on a prepaid card with a $25 balance. If partial approval is enabled, $25 can be + authorized, at which point the POS will prompt the user for an additional + payment of $15. + """ + + pin_capability: Literal["CAPABLE", "INOPERATIVE", "NOT_CAPABLE", "UNSPECIFIED"] + """Status of whether the POS is able to accept PINs""" + + type: Literal[ + "ADMINISTRATIVE", + "ATM", + "AUTHORIZATION", + "COUPON_MACHINE", + "DIAL_TERMINAL", + "ECOMMERCE", + "ECR", + "FUEL_MACHINE", + "HOME_TERMINAL", + "MICR", + "OFF_PREMISE", + "PAYMENT", + "PDA", + "PHONE", + "POINT", + "POS_TERMINAL", + "PUBLIC_UTILITY", + "SELF_SERVICE", + "TELEVISION", + "TELLER", + "TRAVELERS_CHECK_MACHINE", + "VENDING", + "VOICE", + "UNKNOWN", + ] + """POS Type""" + + acceptor_terminal_id: Optional[str] = None + """ + Uniquely identifies a terminal at the card acceptor location of acquiring + institutions or merchant POS Systems. Left justified with trailing spaces. + """ + + +class Pos(BaseModel): + entry_mode: Optional[PosEntryMode] = None + """POS > Entry Mode object in ASA""" + + terminal: Optional[PosTerminal] = None + + +class AsaRequestWebhookEvent(BaseModel): + """The Auth Stream Access request payload that was sent to the ASA responder.""" + + token: str + """The provisional transaction group uuid associated with the authorization""" + + acquirer_fee: int + """Fee (in cents) assessed by the merchant and paid for by the cardholder. + + Will be zero if no fee is assessed. Rebates may be transmitted as a negative + value to indicate credited fees. + """ + + amount: int + """Authorization amount of the transaction (in cents), including any acquirer fees. + + The contents of this field are identical to `authorization_amount`. + """ + + authorization_amount: int + """The base transaction amount (in cents) plus the acquirer fee field. + + This is the amount the issuer should authorize against unless the issuer is + paying the acquirer fee on behalf of the cardholder. + """ + + avs: Avs + + card: Card + """Card object in ASA""" + + cardholder_currency: str + """3-character alphabetic ISO 4217 code for cardholder's billing currency.""" + + cash_amount: int + """ + The portion of the transaction requested as cash back by the cardholder, and + does not include any acquirer fees. The amount field includes the purchase + amount, the requested cash back amount, and any acquirer fees. + + If no cash back was requested, the value of this field will be 0, and the field + will always be present. + """ + + created: datetime + """Date and time when the transaction first occurred in UTC.""" + + merchant: Merchant + + merchant_amount: int + """ + The amount that the merchant will receive, denominated in `merchant_currency` + and in the smallest currency unit. Note the amount includes `acquirer_fee`, + similar to `authorization_amount`. It will be different from + `authorization_amount` if the merchant is taking payment in a different + currency. + """ + + merchant_currency: str + """3-character alphabetic ISO 4217 code for the local currency of the transaction.""" + + settled_amount: int + """ + Amount (in cents) of the transaction that has been settled, including any + acquirer fees + """ + + status: Literal[ + "AUTHORIZATION", + "CREDIT_AUTHORIZATION", + "FINANCIAL_AUTHORIZATION", + "FINANCIAL_CREDIT_AUTHORIZATION", + "BALANCE_INQUIRY", + ] + """The type of authorization request that this request is for. + + Note that `CREDIT_AUTHORIZATION` and `FINANCIAL_CREDIT_AUTHORIZATION` is only + available to users with credit decisioning via ASA enabled. + """ + + transaction_initiator: Literal["CARDHOLDER", "MERCHANT", "UNKNOWN"] + """The entity that initiated the transaction.""" + + account_type: Optional[Literal["CHECKING", "SAVINGS"]] = None + + cardholder_authentication: Optional[CardholderAuthentication] = None + + cashback: Optional[int] = None + """Deprecated, use `cash_amount`.""" + + conversion_rate: Optional[float] = None + """ + If the transaction was requested in a currency other than the settlement + currency, this field will be populated to indicate the rate used to translate + the merchant_amount to the amount (i.e., `merchant_amount` x `conversion_rate` = + `amount`). Note that the `merchant_amount` is in the local currency and the + amount is in the settlement currency. + """ + + event_token: Optional[str] = None + """The event token associated with the authorization. + + This field is only set for programs enrolled into the beta. + """ + + fleet_info: Optional[FleetInfo] = None + """ + Optional Object containing information if the Card is a part of a Fleet managed + program + """ + + latest_challenge: Optional[LatestChallenge] = None + """ + The latest Authorization Challenge that was issued to the cardholder for this + merchant. + """ + + network: Optional[Literal["AMEX", "INTERLINK", "MAESTRO", "MASTERCARD", "UNKNOWN", "VISA"]] = None + """Card network of the authorization.""" + + 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. + """ + + network_specific_data: Optional[NetworkSpecificData] = None + """ + Contains raw data provided by the card network, including attributes that + provide further context about the authorization. If populated by the network, + data is organized by Lithic and passed through without further modification. + Please consult the official network documentation for more details about these + values and how to use them. This object is only available to certain programs- + contact your Customer Success Manager to discuss enabling access. + """ + + pos: Optional[Pos] = None + + token_info: Optional[TokenInfo] = None + + ttl: Optional[datetime] = None + """Deprecated: approximate time-to-live for the authorization.""" diff --git a/src/lithic/types/auth_rules/__init__.py b/src/lithic/types/auth_rules/__init__.py index b07e3377..7f4cb281 100644 --- a/src/lithic/types/auth_rules/__init__.py +++ b/src/lithic/types/auth_rules/__init__.py @@ -2,19 +2,15 @@ from __future__ import annotations +from .auth_rule import AuthRule as AuthRule from .rule_stats import RuleStats as RuleStats +from .event_stream import EventStream as EventStream from .v2_list_params import V2ListParams as V2ListParams 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 .conditional_value import ConditionalValue as ConditionalValue -from .v2_draft_response import V2DraftResponse as V2DraftResponse -from .v2_create_response import V2CreateResponse as V2CreateResponse -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 .conditional_operation import ConditionalOperation as ConditionalOperation from .velocity_limit_params import VelocityLimitParams as VelocityLimitParams diff --git a/src/lithic/types/auth_rules/v2_list_response.py b/src/lithic/types/auth_rules/auth_rule.py similarity index 92% rename from src/lithic/types/auth_rules/v2_list_response.py rename to src/lithic/types/auth_rules/auth_rule.py index 89f52e9b..09f2f172 100644 --- a/src/lithic/types/auth_rules/v2_list_response.py +++ b/src/lithic/types/auth_rules/auth_rule.py @@ -4,6 +4,7 @@ from typing_extensions import Literal, TypeAlias from ..._models import BaseModel +from .event_stream import EventStream from .velocity_limit_params import VelocityLimitParams from .merchant_lock_parameters import MerchantLockParameters from .conditional_block_parameters import ConditionalBlockParameters @@ -12,7 +13,7 @@ from .conditional_tokenization_action_parameters import ConditionalTokenizationActionParameters from .conditional_authorization_action_parameters import ConditionalAuthorizationActionParameters -__all__ = ["V2ListResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] +__all__ = ["AuthRule", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] CurrentVersionParameters: TypeAlias = Union[ ConditionalBlockParameters, @@ -58,7 +59,7 @@ class DraftVersion(BaseModel): """ -class V2ListResponse(BaseModel): +class AuthRule(BaseModel): token: str """Auth Rule Token""" @@ -75,9 +76,7 @@ class V2ListResponse(BaseModel): draft_version: Optional[DraftVersion] = None - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] + event_stream: EventStream """The event stream during which the rule will be evaluated.""" lithic_managed: bool diff --git a/src/lithic/types/auth_rules/auth_rule_condition_param.py b/src/lithic/types/auth_rules/auth_rule_condition_param.py index 686fb311..02111a53 100644 --- a/src/lithic/types/auth_rules/auth_rule_condition_param.py +++ b/src/lithic/types/auth_rules/auth_rule_condition_param.py @@ -2,8 +2,9 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict +from ..._utils import PropertyInfo from .conditional_attribute import ConditionalAttribute from .conditional_operation import ConditionalOperation from .conditional_value_param import ConditionalValueParam @@ -68,5 +69,5 @@ class AuthRuleConditionParam(TypedDict, total=False): operation: Required[ConditionalOperation] """The operation to apply to the attribute""" - value: Required[ConditionalValueParam] + value: Required[Annotated[ConditionalValueParam, PropertyInfo(format="iso8601")]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/conditional_3ds_action_parameters_param.py b/src/lithic/types/auth_rules/conditional_3ds_action_parameters_param.py index 88a81e1b..de980efe 100644 --- a/src/lithic/types/auth_rules/conditional_3ds_action_parameters_param.py +++ b/src/lithic/types/auth_rules/conditional_3ds_action_parameters_param.py @@ -3,8 +3,9 @@ from __future__ import annotations from typing import Iterable -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict +from ..._utils import PropertyInfo from .conditional_operation import ConditionalOperation from .conditional_value_param import ConditionalValueParam @@ -54,7 +55,7 @@ class Condition(TypedDict, total=False): operation: Required[ConditionalOperation] """The operation to apply to the attribute""" - value: Required[ConditionalValueParam] + value: Required[Annotated[ConditionalValueParam, PropertyInfo(format="iso8601")]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/conditional_ach_action_parameters_param.py b/src/lithic/types/auth_rules/conditional_ach_action_parameters_param.py index 4e37d8b6..5c29f6e6 100644 --- a/src/lithic/types/auth_rules/conditional_ach_action_parameters_param.py +++ b/src/lithic/types/auth_rules/conditional_ach_action_parameters_param.py @@ -3,8 +3,9 @@ from __future__ import annotations from typing import Union, Iterable -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict +from ..._utils import PropertyInfo from .conditional_operation import ConditionalOperation from .conditional_value_param import ConditionalValueParam @@ -126,7 +127,7 @@ class Condition(TypedDict, total=False): operation: Required[ConditionalOperation] """The operation to apply to the attribute""" - value: Required[ConditionalValueParam] + value: Required[Annotated[ConditionalValueParam, PropertyInfo(format="iso8601")]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/conditional_authorization_action_parameters_param.py b/src/lithic/types/auth_rules/conditional_authorization_action_parameters_param.py index fd922029..04eee746 100644 --- a/src/lithic/types/auth_rules/conditional_authorization_action_parameters_param.py +++ b/src/lithic/types/auth_rules/conditional_authorization_action_parameters_param.py @@ -3,8 +3,9 @@ from __future__ import annotations from typing import Iterable -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict +from ..._utils import PropertyInfo from .conditional_operation import ConditionalOperation from .conditional_value_param import ConditionalValueParam @@ -94,7 +95,7 @@ class Condition(TypedDict, total=False): operation: Required[ConditionalOperation] """The operation to apply to the attribute""" - value: Required[ConditionalValueParam] + value: Required[Annotated[ConditionalValueParam, PropertyInfo(format="iso8601")]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/conditional_operation.py b/src/lithic/types/auth_rules/conditional_operation.py index e243ae14..42caeaa6 100644 --- a/src/lithic/types/auth_rules/conditional_operation.py +++ b/src/lithic/types/auth_rules/conditional_operation.py @@ -15,4 +15,9 @@ "IS_GREATER_THAN_OR_EQUAL_TO", "IS_LESS_THAN", "IS_LESS_THAN_OR_EQUAL_TO", + "IS_AFTER", + "IS_BEFORE", + "CONTAINS_ANY", + "CONTAINS_ALL", + "CONTAINS_NONE", ] diff --git a/src/lithic/types/auth_rules/conditional_tokenization_action_parameters.py b/src/lithic/types/auth_rules/conditional_tokenization_action_parameters.py index 479e382b..4e3a645a 100644 --- a/src/lithic/types/auth_rules/conditional_tokenization_action_parameters.py +++ b/src/lithic/types/auth_rules/conditional_tokenization_action_parameters.py @@ -78,6 +78,7 @@ class Condition(BaseModel): "WALLET_ACCOUNT_SCORE", "WALLET_DEVICE_SCORE", "WALLET_RECOMMENDED_DECISION", + "WALLET_RECOMMENDATION_REASONS", "TOKEN_REQUESTOR_ID", "WALLET_TOKEN_STATUS", ] @@ -103,6 +104,14 @@ class Condition(BaseModel): - `WALLET_RECOMMENDED_DECISION`: The decision recommended by the digital wallet provider. Valid values include APPROVE, DECLINE, REQUIRE_ADDITIONAL_AUTHENTICATION. + - `WALLET_RECOMMENDATION_REASONS`: List of reasons provided by the digital + wallet provider for the recommended decision. Valid values are + `ACCOUNT_CARD_TOO_NEW`, `ACCOUNT_RECENTLY_CHANGED`, `ACCOUNT_TOO_NEW`, + `ACCOUNT_TOO_NEW_SINCE_LAUNCH`, `DEVICE_RECENTLY_LOST`, + `HAS_SUSPENDED_TOKENS`, `HIGH_RISK`, `INACTIVE_ACCOUNT`, `LOW_ACCOUNT_SCORE`, + `LOW_DEVICE_SCORE`, `OUTSIDE_HOME_TERRITORY`, `SUSPICIOUS_ACTIVITY`, + `TOO_MANY_DIFFERENT_CARDHOLDERS`, `TOO_MANY_RECENT_ATTEMPTS`, + `TOO_MANY_RECENT_TOKENS`, `UNABLE_TO_ASSESS`. - `TOKEN_REQUESTOR_ID`: Unique identifier for the entity requesting the token. - `WALLET_TOKEN_STATUS`: The current status of the wallet token. """ diff --git a/src/lithic/types/auth_rules/conditional_tokenization_action_parameters_param.py b/src/lithic/types/auth_rules/conditional_tokenization_action_parameters_param.py index d0112164..6b733a47 100644 --- a/src/lithic/types/auth_rules/conditional_tokenization_action_parameters_param.py +++ b/src/lithic/types/auth_rules/conditional_tokenization_action_parameters_param.py @@ -3,8 +3,9 @@ from __future__ import annotations from typing import Union, Iterable -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict +from ..._utils import PropertyInfo from .conditional_operation import ConditionalOperation from .conditional_value_param import ConditionalValueParam @@ -76,6 +77,7 @@ class Condition(TypedDict, total=False): "WALLET_ACCOUNT_SCORE", "WALLET_DEVICE_SCORE", "WALLET_RECOMMENDED_DECISION", + "WALLET_RECOMMENDATION_REASONS", "TOKEN_REQUESTOR_ID", "WALLET_TOKEN_STATUS", ] @@ -102,6 +104,14 @@ class Condition(TypedDict, total=False): - `WALLET_RECOMMENDED_DECISION`: The decision recommended by the digital wallet provider. Valid values include APPROVE, DECLINE, REQUIRE_ADDITIONAL_AUTHENTICATION. + - `WALLET_RECOMMENDATION_REASONS`: List of reasons provided by the digital + wallet provider for the recommended decision. Valid values are + `ACCOUNT_CARD_TOO_NEW`, `ACCOUNT_RECENTLY_CHANGED`, `ACCOUNT_TOO_NEW`, + `ACCOUNT_TOO_NEW_SINCE_LAUNCH`, `DEVICE_RECENTLY_LOST`, + `HAS_SUSPENDED_TOKENS`, `HIGH_RISK`, `INACTIVE_ACCOUNT`, `LOW_ACCOUNT_SCORE`, + `LOW_DEVICE_SCORE`, `OUTSIDE_HOME_TERRITORY`, `SUSPICIOUS_ACTIVITY`, + `TOO_MANY_DIFFERENT_CARDHOLDERS`, `TOO_MANY_RECENT_ATTEMPTS`, + `TOO_MANY_RECENT_TOKENS`, `UNABLE_TO_ASSESS`. - `TOKEN_REQUESTOR_ID`: Unique identifier for the entity requesting the token. - `WALLET_TOKEN_STATUS`: The current status of the wallet token. """ @@ -109,7 +119,7 @@ class Condition(TypedDict, total=False): operation: Required[ConditionalOperation] """The operation to apply to the attribute""" - value: Required[ConditionalValueParam] + value: Required[Annotated[ConditionalValueParam, PropertyInfo(format="iso8601")]] """A regex string, to be used with `MATCHES` or `DOES_NOT_MATCH`""" diff --git a/src/lithic/types/auth_rules/conditional_value.py b/src/lithic/types/auth_rules/conditional_value.py index 34e53bac..2f0a8910 100644 --- a/src/lithic/types/auth_rules/conditional_value.py +++ b/src/lithic/types/auth_rules/conditional_value.py @@ -1,8 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union +from datetime import datetime from typing_extensions import TypeAlias __all__ = ["ConditionalValue"] -ConditionalValue: TypeAlias = Union[str, int, List[str]] +ConditionalValue: TypeAlias = Union[str, int, List[str], datetime] diff --git a/src/lithic/types/auth_rules/conditional_value_param.py b/src/lithic/types/auth_rules/conditional_value_param.py index db8f3d79..c1e27c62 100644 --- a/src/lithic/types/auth_rules/conditional_value_param.py +++ b/src/lithic/types/auth_rules/conditional_value_param.py @@ -3,10 +3,11 @@ from __future__ import annotations from typing import Union +from datetime import datetime from typing_extensions import TypeAlias from ..._types import SequenceNotStr __all__ = ["ConditionalValueParam"] -ConditionalValueParam: TypeAlias = Union[str, int, SequenceNotStr[str]] +ConditionalValueParam: TypeAlias = Union[str, int, SequenceNotStr[str], Union[str, datetime]] diff --git a/src/lithic/types/auth_rules/event_stream.py b/src/lithic/types/auth_rules/event_stream.py new file mode 100644 index 00000000..bd270b0f --- /dev/null +++ b/src/lithic/types/auth_rules/event_stream.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__ = ["EventStream"] + +EventStream: TypeAlias = Literal[ + "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" +] diff --git a/src/lithic/types/auth_rules/merchant_lock_parameters.py b/src/lithic/types/auth_rules/merchant_lock_parameters.py index dcb5eb10..651ea367 100644 --- a/src/lithic/types/auth_rules/merchant_lock_parameters.py +++ b/src/lithic/types/auth_rules/merchant_lock_parameters.py @@ -8,6 +8,11 @@ class Merchant(BaseModel): + """Represents a specific merchant lock based on their ID or descriptor. + + Each merchant object allows transaction rules to work at a granular level and requires at least one of merchant_id or descriptor. + """ + comment: Optional[str] = None """ A comment or explanation about the merchant, used internally for rule management diff --git a/src/lithic/types/auth_rules/merchant_lock_parameters_param.py b/src/lithic/types/auth_rules/merchant_lock_parameters_param.py index bfaf2dcb..cb0285cf 100644 --- a/src/lithic/types/auth_rules/merchant_lock_parameters_param.py +++ b/src/lithic/types/auth_rules/merchant_lock_parameters_param.py @@ -9,6 +9,11 @@ class Merchant(TypedDict, total=False): + """Represents a specific merchant lock based on their ID or descriptor. + + Each merchant object allows transaction rules to work at a granular level and requires at least one of merchant_id or descriptor. + """ + comment: str """ A comment or explanation about the merchant, used internally for rule management diff --git a/src/lithic/types/auth_rules/v2_create_params.py b/src/lithic/types/auth_rules/v2_create_params.py index d40a120b..e9f9d2d3 100644 --- a/src/lithic/types/auth_rules/v2_create_params.py +++ b/src/lithic/types/auth_rules/v2_create_params.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .event_stream import EventStream from .velocity_limit_params_param import VelocityLimitParamsParam from .merchant_lock_parameters_param import MerchantLockParametersParam from .conditional_block_parameters_param import ConditionalBlockParametersParam @@ -49,9 +50,7 @@ class AccountLevelRule(TypedDict, total=False): business_account_tokens: SequenceNotStr[str] """Business Account tokens to which the Auth Rule applies.""" - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] + event_stream: EventStream """The event stream during which the rule will be evaluated.""" name: Optional[str] @@ -90,9 +89,7 @@ class CardLevelRule(TypedDict, total=False): ACH_CREDIT_RECEIPT, or ACH_DEBIT_RECEIPT event stream. """ - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] + event_stream: EventStream """The event stream during which the rule will be evaluated.""" name: Optional[str] @@ -131,9 +128,7 @@ class ProgramLevelRule(TypedDict, total=False): ACH_CREDIT_RECEIPT, or ACH_DEBIT_RECEIPT event stream. """ - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] + event_stream: EventStream """The event stream during which the rule will be evaluated.""" excluded_card_tokens: SequenceNotStr[str] diff --git a/src/lithic/types/auth_rules/v2_create_response.py b/src/lithic/types/auth_rules/v2_create_response.py deleted file mode 100644 index 1dc0c3c6..00000000 --- a/src/lithic/types/auth_rules/v2_create_response.py +++ /dev/null @@ -1,113 +0,0 @@ -# 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 .merchant_lock_parameters import MerchantLockParameters -from .conditional_block_parameters import ConditionalBlockParameters -from .conditional_3ds_action_parameters import Conditional3DSActionParameters -from .conditional_ach_action_parameters import ConditionalACHActionParameters -from .conditional_tokenization_action_parameters import ConditionalTokenizationActionParameters -from .conditional_authorization_action_parameters import ConditionalAuthorizationActionParameters - -__all__ = ["V2CreateResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] - -CurrentVersionParameters: TypeAlias = Union[ - ConditionalBlockParameters, - VelocityLimitParams, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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.""" - - business_account_tokens: List[str] - """Business 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 - - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - """The event stream during which the rule will be evaluated.""" - - lithic_managed: bool - """Indicates whether this auth rule is managed by Lithic. - - If true, the rule cannot be modified or deleted by the user - """ - - 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", "MERCHANT_LOCK", "CONDITIONAL_ACTION"] - """The type of Auth Rule. - - For certain rule types, this determines the event stream during which it will be - evaluated. For rules that can be applied to one of several event streams, the - effective one is defined by the separate `event_stream` field. - - - `CONDITIONAL_BLOCK`: AUTHORIZATION event stream. - - `VELOCITY_LIMIT`: AUTHORIZATION event stream. - - `MERCHANT_LOCK`: AUTHORIZATION event stream. - - `CONDITIONAL_ACTION`: AUTHORIZATION, THREE_DS_AUTHENTICATION, TOKENIZATION, - ACH_CREDIT_RECEIPT, or ACH_DEBIT_RECEIPT event stream. - """ - - 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_response.py b/src/lithic/types/auth_rules/v2_draft_response.py deleted file mode 100644 index f69603f2..00000000 --- a/src/lithic/types/auth_rules/v2_draft_response.py +++ /dev/null @@ -1,113 +0,0 @@ -# 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 .merchant_lock_parameters import MerchantLockParameters -from .conditional_block_parameters import ConditionalBlockParameters -from .conditional_3ds_action_parameters import Conditional3DSActionParameters -from .conditional_ach_action_parameters import ConditionalACHActionParameters -from .conditional_tokenization_action_parameters import ConditionalTokenizationActionParameters -from .conditional_authorization_action_parameters import ConditionalAuthorizationActionParameters - -__all__ = ["V2DraftResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] - -CurrentVersionParameters: TypeAlias = Union[ - ConditionalBlockParameters, - VelocityLimitParams, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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.""" - - business_account_tokens: List[str] - """Business 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 - - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - """The event stream during which the rule will be evaluated.""" - - lithic_managed: bool - """Indicates whether this auth rule is managed by Lithic. - - If true, the rule cannot be modified or deleted by the user - """ - - 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", "MERCHANT_LOCK", "CONDITIONAL_ACTION"] - """The type of Auth Rule. - - For certain rule types, this determines the event stream during which it will be - evaluated. For rules that can be applied to one of several event streams, the - effective one is defined by the separate `event_stream` field. - - - `CONDITIONAL_BLOCK`: AUTHORIZATION event stream. - - `VELOCITY_LIMIT`: AUTHORIZATION event stream. - - `MERCHANT_LOCK`: AUTHORIZATION event stream. - - `CONDITIONAL_ACTION`: AUTHORIZATION, THREE_DS_AUTHENTICATION, TOKENIZATION, - ACH_CREDIT_RECEIPT, or ACH_DEBIT_RECEIPT event stream. - """ - - 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_list_params.py b/src/lithic/types/auth_rules/v2_list_params.py index 600502af..af94a849 100644 --- a/src/lithic/types/auth_rules/v2_list_params.py +++ b/src/lithic/types/auth_rules/v2_list_params.py @@ -2,8 +2,11 @@ from __future__ import annotations +from typing import List from typing_extensions import Literal, TypedDict +from .event_stream import EventStream + __all__ = ["V2ListParams"] @@ -23,10 +26,18 @@ class V2ListParams(TypedDict, total=False): Used to retrieve the previous page of results before this item. """ - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - """Only return Auth rules that are executed during the provided event stream.""" + event_stream: EventStream + """Deprecated: Use event_streams instead. + + Only return Auth rules that are executed during the provided event stream. + """ + + event_streams: List[EventStream] + """ + Only return Auth rules that are executed during any of the provided event + streams. If event_streams and event_stream are specified, the values will be + combined. + """ page_size: int """Page size (for pagination).""" diff --git a/src/lithic/types/auth_rules/v2_promote_response.py b/src/lithic/types/auth_rules/v2_promote_response.py deleted file mode 100644 index e5b64fdd..00000000 --- a/src/lithic/types/auth_rules/v2_promote_response.py +++ /dev/null @@ -1,113 +0,0 @@ -# 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 .merchant_lock_parameters import MerchantLockParameters -from .conditional_block_parameters import ConditionalBlockParameters -from .conditional_3ds_action_parameters import Conditional3DSActionParameters -from .conditional_ach_action_parameters import ConditionalACHActionParameters -from .conditional_tokenization_action_parameters import ConditionalTokenizationActionParameters -from .conditional_authorization_action_parameters import ConditionalAuthorizationActionParameters - -__all__ = ["V2PromoteResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] - -CurrentVersionParameters: TypeAlias = Union[ - ConditionalBlockParameters, - VelocityLimitParams, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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.""" - - business_account_tokens: List[str] - """Business 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 - - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - """The event stream during which the rule will be evaluated.""" - - lithic_managed: bool - """Indicates whether this auth rule is managed by Lithic. - - If true, the rule cannot be modified or deleted by the user - """ - - 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", "MERCHANT_LOCK", "CONDITIONAL_ACTION"] - """The type of Auth Rule. - - For certain rule types, this determines the event stream during which it will be - evaluated. For rules that can be applied to one of several event streams, the - effective one is defined by the separate `event_stream` field. - - - `CONDITIONAL_BLOCK`: AUTHORIZATION event stream. - - `VELOCITY_LIMIT`: AUTHORIZATION event stream. - - `MERCHANT_LOCK`: AUTHORIZATION event stream. - - `CONDITIONAL_ACTION`: AUTHORIZATION, THREE_DS_AUTHENTICATION, TOKENIZATION, - ACH_CREDIT_RECEIPT, or ACH_DEBIT_RECEIPT event stream. - """ - - 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_retrieve_response.py b/src/lithic/types/auth_rules/v2_retrieve_response.py deleted file mode 100644 index 918c013f..00000000 --- a/src/lithic/types/auth_rules/v2_retrieve_response.py +++ /dev/null @@ -1,113 +0,0 @@ -# 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 .merchant_lock_parameters import MerchantLockParameters -from .conditional_block_parameters import ConditionalBlockParameters -from .conditional_3ds_action_parameters import Conditional3DSActionParameters -from .conditional_ach_action_parameters import ConditionalACHActionParameters -from .conditional_tokenization_action_parameters import ConditionalTokenizationActionParameters -from .conditional_authorization_action_parameters import ConditionalAuthorizationActionParameters - -__all__ = ["V2RetrieveResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] - -CurrentVersionParameters: TypeAlias = Union[ - ConditionalBlockParameters, - VelocityLimitParams, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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.""" - - business_account_tokens: List[str] - """Business 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 - - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - """The event stream during which the rule will be evaluated.""" - - lithic_managed: bool - """Indicates whether this auth rule is managed by Lithic. - - If true, the rule cannot be modified or deleted by the user - """ - - 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", "MERCHANT_LOCK", "CONDITIONAL_ACTION"] - """The type of Auth Rule. - - For certain rule types, this determines the event stream during which it will be - evaluated. For rules that can be applied to one of several event streams, the - effective one is defined by the separate `event_stream` field. - - - `CONDITIONAL_BLOCK`: AUTHORIZATION event stream. - - `VELOCITY_LIMIT`: AUTHORIZATION event stream. - - `MERCHANT_LOCK`: AUTHORIZATION event stream. - - `CONDITIONAL_ACTION`: AUTHORIZATION, THREE_DS_AUTHENTICATION, TOKENIZATION, - ACH_CREDIT_RECEIPT, or ACH_DEBIT_RECEIPT event stream. - """ - - 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_response.py b/src/lithic/types/auth_rules/v2_update_response.py deleted file mode 100644 index 550597f1..00000000 --- a/src/lithic/types/auth_rules/v2_update_response.py +++ /dev/null @@ -1,113 +0,0 @@ -# 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 .merchant_lock_parameters import MerchantLockParameters -from .conditional_block_parameters import ConditionalBlockParameters -from .conditional_3ds_action_parameters import Conditional3DSActionParameters -from .conditional_ach_action_parameters import ConditionalACHActionParameters -from .conditional_tokenization_action_parameters import ConditionalTokenizationActionParameters -from .conditional_authorization_action_parameters import ConditionalAuthorizationActionParameters - -__all__ = ["V2UpdateResponse", "CurrentVersion", "CurrentVersionParameters", "DraftVersion", "DraftVersionParameters"] - -CurrentVersionParameters: TypeAlias = Union[ - ConditionalBlockParameters, - VelocityLimitParams, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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, - MerchantLockParameters, - Conditional3DSActionParameters, - ConditionalAuthorizationActionParameters, - ConditionalACHActionParameters, - ConditionalTokenizationActionParameters, -] - - -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.""" - - business_account_tokens: List[str] - """Business 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 - - event_stream: Literal[ - "AUTHORIZATION", "THREE_DS_AUTHENTICATION", "TOKENIZATION", "ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT" - ] - """The event stream during which the rule will be evaluated.""" - - lithic_managed: bool - """Indicates whether this auth rule is managed by Lithic. - - If true, the rule cannot be modified or deleted by the user - """ - - 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", "MERCHANT_LOCK", "CONDITIONAL_ACTION"] - """The type of Auth Rule. - - For certain rule types, this determines the event stream during which it will be - evaluated. For rules that can be applied to one of several event streams, the - effective one is defined by the separate `event_stream` field. - - - `CONDITIONAL_BLOCK`: AUTHORIZATION event stream. - - `VELOCITY_LIMIT`: AUTHORIZATION event stream. - - `MERCHANT_LOCK`: AUTHORIZATION event stream. - - `CONDITIONAL_ACTION`: AUTHORIZATION, THREE_DS_AUTHENTICATION, TOKENIZATION, - ACH_CREDIT_RECEIPT, or ACH_DEBIT_RECEIPT event stream. - """ - - 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_period.py b/src/lithic/types/auth_rules/velocity_limit_period.py index d4b43f6c..7ac08d9f 100644 --- a/src/lithic/types/auth_rules/velocity_limit_period.py +++ b/src/lithic/types/auth_rules/velocity_limit_period.py @@ -27,10 +27,16 @@ class TrailingWindowObject(BaseModel): class FixedWindowDay(BaseModel): + """Velocity over the current day since 00:00 / 12 AM in Eastern Time""" + type: Literal["DAY"] class FixedWindowWeek(BaseModel): + """ + Velocity over the current week since 00:00 / 12 AM in Eastern Time on specified `day_of_week` + """ + type: Literal["WEEK"] day_of_week: Optional[int] = None @@ -42,6 +48,10 @@ class FixedWindowWeek(BaseModel): class FixedWindowMonth(BaseModel): + """ + Velocity over the current month since 00:00 / 12 AM in Eastern Time on specified `day_of_month`. + """ + type: Literal["MONTH"] day_of_month: Optional[int] = None @@ -54,6 +64,10 @@ class FixedWindowMonth(BaseModel): class FixedWindowYear(BaseModel): + """ + Velocity over the current year since 00:00 / 12 AM in Eastern Time on specified `month` and `day_of_month`. This validates the month and day of the year to start from is a real date. In the event that February 29th is selected, in non-leap years, the window will start from February 28th. + """ + type: Literal["YEAR"] day_of_month: Optional[int] = None diff --git a/src/lithic/types/auth_rules/velocity_limit_period_param.py b/src/lithic/types/auth_rules/velocity_limit_period_param.py index 1fc31ccf..d55b61f6 100644 --- a/src/lithic/types/auth_rules/velocity_limit_period_param.py +++ b/src/lithic/types/auth_rules/velocity_limit_period_param.py @@ -27,10 +27,16 @@ class TrailingWindowObject(TypedDict, total=False): class FixedWindowDay(TypedDict, total=False): + """Velocity over the current day since 00:00 / 12 AM in Eastern Time""" + type: Required[Literal["DAY"]] class FixedWindowWeek(TypedDict, total=False): + """ + Velocity over the current week since 00:00 / 12 AM in Eastern Time on specified `day_of_week` + """ + type: Required[Literal["WEEK"]] day_of_week: int @@ -42,6 +48,10 @@ class FixedWindowWeek(TypedDict, total=False): class FixedWindowMonth(TypedDict, total=False): + """ + Velocity over the current month since 00:00 / 12 AM in Eastern Time on specified `day_of_month`. + """ + type: Required[Literal["MONTH"]] day_of_month: int @@ -54,6 +64,10 @@ class FixedWindowMonth(TypedDict, total=False): class FixedWindowYear(TypedDict, total=False): + """ + Velocity over the current year since 00:00 / 12 AM in Eastern Time on specified `month` and `day_of_month`. This validates the month and day of the year to start from is a real date. In the event that February 29th is selected, in non-leap years, the window will start from February 28th. + """ + type: Required[Literal["YEAR"]] day_of_month: int diff --git a/src/lithic/types/auth_rules_backtest_report_created_webhook_event.py b/src/lithic/types/auth_rules_backtest_report_created_webhook_event.py new file mode 100644 index 00000000..836e9050 --- /dev/null +++ b/src/lithic/types/auth_rules_backtest_report_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .auth_rules.v2.backtest_results import BacktestResults + +__all__ = ["AuthRulesBacktestReportCreatedWebhookEvent"] + + +class AuthRulesBacktestReportCreatedWebhookEvent(BacktestResults): + event_type: Literal["auth_rules.backtest_report.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/balance.py b/src/lithic/types/balance.py index f969f42a..1b12f089 100644 --- a/src/lithic/types/balance.py +++ b/src/lithic/types/balance.py @@ -9,6 +9,8 @@ class Balance(BaseModel): + """Balance""" + available_amount: int """Funds available for spend in the currency's smallest unit (e.g., cents for USD)""" diff --git a/src/lithic/types/balance_updated_webhook_event.py b/src/lithic/types/balance_updated_webhook_event.py new file mode 100644 index 00000000..522ffaac --- /dev/null +++ b/src/lithic/types/balance_updated_webhook_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from .._models import BaseModel +from .financial_account_balance import FinancialAccountBalance + +__all__ = ["BalanceUpdatedWebhookEvent"] + + +class BalanceUpdatedWebhookEvent(BaseModel): + data: List[FinancialAccountBalance] + + event_type: Literal["balance.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/book_transfer_response.py b/src/lithic/types/book_transfer_response.py index 17f29897..a9648905 100644 --- a/src/lithic/types/book_transfer_response.py +++ b/src/lithic/types/book_transfer_response.py @@ -11,6 +11,8 @@ class Event(BaseModel): + """Book transfer Event""" + token: str """Globally unique identifier.""" @@ -78,6 +80,8 @@ class Event(BaseModel): class TransactionSeries(BaseModel): + """A series of transactions that are grouped together""" + related_transaction_event_token: Optional[str] = None related_transaction_token: Optional[str] = None @@ -86,6 +90,8 @@ class TransactionSeries(BaseModel): class BookTransferResponse(BaseModel): + """Book transfer transaction""" + token: str """Unique identifier for the transaction""" diff --git a/src/lithic/types/book_transfer_transaction_created_webhook_event.py b/src/lithic/types/book_transfer_transaction_created_webhook_event.py new file mode 100644 index 00000000..a90acd39 --- /dev/null +++ b/src/lithic/types/book_transfer_transaction_created_webhook_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .book_transfer_response import BookTransferResponse + +__all__ = ["BookTransferTransactionCreatedWebhookEvent"] + + +class BookTransferTransactionCreatedWebhookEvent(BookTransferResponse): + """Book transfer transaction""" + + event_type: Literal["book_transfer_transaction.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/book_transfer_transaction_updated_webhook_event.py b/src/lithic/types/book_transfer_transaction_updated_webhook_event.py new file mode 100644 index 00000000..55dc6d8f --- /dev/null +++ b/src/lithic/types/book_transfer_transaction_updated_webhook_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .book_transfer_response import BookTransferResponse + +__all__ = ["BookTransferTransactionUpdatedWebhookEvent"] + + +class BookTransferTransactionUpdatedWebhookEvent(BookTransferResponse): + """Book transfer transaction""" + + event_type: Literal["book_transfer_transaction.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/card.py b/src/lithic/types/card.py index 61bf6a0b..99b56ba7 100644 --- a/src/lithic/types/card.py +++ b/src/lithic/types/card.py @@ -8,6 +8,10 @@ class Card(NonPCICard): + """ + Card details with potentially PCI sensitive information for Enterprise customers + """ + cvv: Optional[str] = None """Three digit cvv printed on the back of the card.""" diff --git a/src/lithic/types/card_bulk_order.py b/src/lithic/types/card_bulk_order.py new file mode 100644 index 00000000..c8ad3abc --- /dev/null +++ b/src/lithic/types/card_bulk_order.py @@ -0,0 +1,44 @@ +# 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 + +__all__ = ["CardBulkOrder"] + + +class CardBulkOrder(BaseModel): + """Represents a bulk order for physical card shipments""" + + token: str + """Globally unique identifier for the bulk order""" + + card_tokens: List[str] + """List of card tokens associated with this bulk order""" + + created: datetime + """An RFC 3339 timestamp for when the bulk order was created. UTC time zone""" + + customer_product_id: Optional[str] = None + """Customer-specified product configuration for physical card manufacturing. + + This must be configured with Lithic before use + """ + + shipping_address: object + """Shipping address for all cards in this bulk order""" + + shipping_method: Literal["BULK_EXPEDITED"] + """Shipping method for all cards in this bulk order""" + + status: Literal["OPEN", "LOCKED"] + """Status of the bulk order. + + OPEN indicates the order is accepting cards. LOCKED indicates the order is + finalized and no more cards can be added + """ + + updated: datetime + """An RFC 3339 timestamp for when the bulk order was last updated. UTC time zone""" diff --git a/src/lithic/types/card_bulk_order_create_params.py b/src/lithic/types/card_bulk_order_create_params.py new file mode 100644 index 00000000..641747b6 --- /dev/null +++ b/src/lithic/types/card_bulk_order_create_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["CardBulkOrderCreateParams"] + + +class CardBulkOrderCreateParams(TypedDict, total=False): + customer_product_id: Required[str] + """Customer-specified product configuration for physical card manufacturing. + + This must be configured with Lithic before use + """ + + shipping_address: Required[object] + """Shipping address for all cards in this bulk order""" + + shipping_method: Required[Literal["BULK_EXPEDITED"]] + """Shipping method for all cards in this bulk order""" diff --git a/src/lithic/types/card_bulk_order_list_params.py b/src/lithic/types/card_bulk_order_list_params.py new file mode 100644 index 00000000..06bb77fc --- /dev/null +++ b/src/lithic/types/card_bulk_order_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 datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["CardBulkOrderListParams"] + + +class CardBulkOrderListParams(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. + """ + + 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. + """ + + 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/card_bulk_order_update_params.py b/src/lithic/types/card_bulk_order_update_params.py new file mode 100644 index 00000000..20cd8820 --- /dev/null +++ b/src/lithic/types/card_bulk_order_update_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 Literal, Required, TypedDict + +__all__ = ["CardBulkOrderUpdateParams"] + + +class CardBulkOrderUpdateParams(TypedDict, total=False): + status: Required[Literal["LOCKED"]] + """Status to update the bulk order to. Use LOCKED to finalize the order""" diff --git a/src/lithic/types/card_convert_physical_params.py b/src/lithic/types/card_convert_physical_params.py index 879679cf..03911cf0 100644 --- a/src/lithic/types/card_convert_physical_params.py +++ b/src/lithic/types/card_convert_physical_params.py @@ -24,7 +24,9 @@ class CardConvertPhysicalParams(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", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] """Shipping method for the card. Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` @@ -41,4 +43,5 @@ class CardConvertPhysicalParams(TypedDict, total=False): tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping """ diff --git a/src/lithic/types/card_converted_webhook_event.py b/src/lithic/types/card_converted_webhook_event.py new file mode 100644 index 00000000..2042a579 --- /dev/null +++ b/src/lithic/types/card_converted_webhook_event.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["CardConvertedWebhookEvent"] + + +class CardConvertedWebhookEvent(BaseModel): + card_token: str + """The token of the card that was created.""" + + event_type: Literal["card.converted"] + """The type of event that occurred.""" diff --git a/src/lithic/types/card_create_params.py b/src/lithic/types/card_create_params.py index d29aa0e7..648a3dcb 100644 --- a/src/lithic/types/card_create_params.py +++ b/src/lithic/types/card_create_params.py @@ -40,6 +40,13 @@ class CardCreateParams(TypedDict, total=False): See [Managing Your Program](doc:managing-your-program) for more information. """ + bulk_order_token: str + """ + Globally unique identifier for an existing bulk order to associate this card + with. When specified, the card will be added to the bulk order for batch + shipment. Only applicable to cards of type PHYSICAL + """ + card_program_token: str """For card programs with more than one BIN range. @@ -158,7 +165,9 @@ class CardCreateParams(TypedDict, total=False): shipping_address: ShippingAddress - shipping_method: Literal["2_DAY", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING"] + shipping_method: Literal[ + "2_DAY", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] """Shipping method for the card. Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` @@ -175,6 +184,7 @@ class CardCreateParams(TypedDict, total=False): tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping """ spend_limit: int diff --git a/src/lithic/types/card_created_webhook_event.py b/src/lithic/types/card_created_webhook_event.py new file mode 100644 index 00000000..8b1e8550 --- /dev/null +++ b/src/lithic/types/card_created_webhook_event.py @@ -0,0 +1,19 @@ +# 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__ = ["CardCreatedWebhookEvent"] + + +class CardCreatedWebhookEvent(BaseModel): + card_token: str + """The token of the card that was created.""" + + event_type: Literal["card.created"] + """The type of event that occurred.""" + + replacement_for: Optional[str] = None + """The token of the card that was replaced, if the new card is a replacement card.""" diff --git a/src/lithic/types/card_program.py b/src/lithic/types/card_program.py index a70e2a24..a00fad51 100644 --- a/src/lithic/types/card_program.py +++ b/src/lithic/types/card_program.py @@ -12,6 +12,12 @@ class CardProgram(BaseModel): token: str """Globally unique identifier.""" + account_level_management_enabled: bool + """Whether the card program is participating in Account Level Management. + + Currently applicable to Visa card programs only. + """ + created: datetime """Timestamp of when the card program was created.""" @@ -24,12 +30,6 @@ class CardProgram(BaseModel): pan_range_start: str """The first digits of the card number that this card program starts with.""" - account_level_management_enabled: Optional[bool] = None - """Whether the card program is participating in Account Level Management. - - Currently applicable to Visa card programs only. - """ - cardholder_currency: Optional[str] = None """3-character alphabetic ISO 4217 code for the currency of the cardholder.""" diff --git a/src/lithic/types/card_reissue_params.py b/src/lithic/types/card_reissue_params.py index 2067ba9c..828ff228 100644 --- a/src/lithic/types/card_reissue_params.py +++ b/src/lithic/types/card_reissue_params.py @@ -24,7 +24,9 @@ class CardReissueParams(TypedDict, total=False): 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", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] """Shipping method for the card. Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` @@ -41,4 +43,5 @@ class CardReissueParams(TypedDict, total=False): tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping """ diff --git a/src/lithic/types/card_reissued_webhook_event.py b/src/lithic/types/card_reissued_webhook_event.py new file mode 100644 index 00000000..5a31af38 --- /dev/null +++ b/src/lithic/types/card_reissued_webhook_event.py @@ -0,0 +1,16 @@ +# 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__ = ["CardReissuedWebhookEvent"] + + +class CardReissuedWebhookEvent(BaseModel): + event_type: Literal["card.reissued"] + """The type of event that occurred.""" + + card_token: Optional[str] = None + """The token of the card that was reissued.""" diff --git a/src/lithic/types/card_renew_params.py b/src/lithic/types/card_renew_params.py index 9c832103..e6fcffe2 100644 --- a/src/lithic/types/card_renew_params.py +++ b/src/lithic/types/card_renew_params.py @@ -38,7 +38,9 @@ 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", "BULK_EXPEDITED", "EXPEDITED", "EXPRESS", "PRIORITY", "STANDARD", "STANDARD_WITH_TRACKING" + ] """Shipping method for the card. Only applies to cards of type PHYSICAL. Use of options besides `STANDARD` @@ -55,4 +57,5 @@ class CardRenewParams(TypedDict, total=False): tracking - `EXPEDITED` - FedEx or UPS depending on card manufacturer, Standard Overnight or similar international option, with tracking + - `BULK_EXPEDITED` - Bulk shipment with Expedited shipping """ diff --git a/src/lithic/types/card_renewed_webhook_event.py b/src/lithic/types/card_renewed_webhook_event.py new file mode 100644 index 00000000..22c07ad3 --- /dev/null +++ b/src/lithic/types/card_renewed_webhook_event.py @@ -0,0 +1,28 @@ +# 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__ = ["CardRenewedWebhookEvent"] + + +class CardRenewedWebhookEvent(BaseModel): + event_type: Literal["card.renewed"] + """The type of event that occurred.""" + + card_token: Optional[str] = None + """The token of the card that was renewed.""" + + exp_month: Optional[str] = None + """The new expiration month of the card.""" + + exp_year: Optional[str] = None + """The new expiration year of the card.""" + + previous_exp_month: Optional[str] = None + """The previous expiration month of the card.""" + + previous_exp_year: Optional[str] = None + """The previous expiration year of the card.""" diff --git a/src/lithic/types/card_shipped_webhook_event.py b/src/lithic/types/card_shipped_webhook_event.py new file mode 100644 index 00000000..e6dbe96f --- /dev/null +++ b/src/lithic/types/card_shipped_webhook_event.py @@ -0,0 +1,37 @@ +# 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__ = ["CardShippedWebhookEvent"] + + +class CardShippedWebhookEvent(BaseModel): + bulk_order_token: Optional[str] = None + """The token of the bulk order associated with this card shipment, if applicable.""" + + card_token: str + """The token of the card that was shipped.""" + + event_type: Literal["card.shipped"] + """The type of event that occurred.""" + + shipping_method: Literal[ + "Ex-US expedited with tracking", + "Ex-US standard with tracking", + "Ex-US standard without tracking", + "FedEx 2 days", + "FedEx express", + "FedEx overnight", + "USPS priority", + "USPS with tracking", + "USPS without tracking envelope", + "USPS without tracking envelope non-machine", + "USPS without tracking flat", + ] + """The specific shipping method used to ship the card.""" + + tracking_number: Optional[str] = None + """The tracking number of the shipment.""" diff --git a/src/lithic/types/card_transaction_enhanced_data_created_webhook_event.py b/src/lithic/types/card_transaction_enhanced_data_created_webhook_event.py new file mode 100644 index 00000000..708f2082 --- /dev/null +++ b/src/lithic/types/card_transaction_enhanced_data_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .transactions.events.enhanced_data import EnhancedData + +__all__ = ["CardTransactionEnhancedDataCreatedWebhookEvent"] + + +class CardTransactionEnhancedDataCreatedWebhookEvent(EnhancedData): + event_type: Literal["card_transaction.enhanced_data.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/card_transaction_enhanced_data_updated_webhook_event.py b/src/lithic/types/card_transaction_enhanced_data_updated_webhook_event.py new file mode 100644 index 00000000..d58b3eb0 --- /dev/null +++ b/src/lithic/types/card_transaction_enhanced_data_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .transactions.events.enhanced_data import EnhancedData + +__all__ = ["CardTransactionEnhancedDataUpdatedWebhookEvent"] + + +class CardTransactionEnhancedDataUpdatedWebhookEvent(EnhancedData): + event_type: Literal["card_transaction.enhanced_data.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/card_transaction_updated_webhook_event.py b/src/lithic/types/card_transaction_updated_webhook_event.py new file mode 100644 index 00000000..09ad978e --- /dev/null +++ b/src/lithic/types/card_transaction_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .transaction import Transaction + +__all__ = ["CardTransactionUpdatedWebhookEvent"] + + +class CardTransactionUpdatedWebhookEvent(Transaction): + event_type: Literal["card_transaction.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/card_web_provision_params.py b/src/lithic/types/card_web_provision_params.py index d294cd3f..bc24e45c 100644 --- a/src/lithic/types/card_web_provision_params.py +++ b/src/lithic/types/card_web_provision_params.py @@ -8,5 +8,25 @@ class CardWebProvisionParams(TypedDict, total=False): - digital_wallet: Literal["APPLE_PAY"] + client_device_id: str + """Only applicable if `digital_wallet` is GOOGLE_PAY. + + Google Pay Web Push Provisioning device identifier required for the tokenization + flow + """ + + client_wallet_account_id: str + """Only applicable if `digital_wallet` is GOOGLE_PAY. + + Google Pay Web Push Provisioning wallet account identifier required for the + tokenization flow + """ + + digital_wallet: Literal["APPLE_PAY", "GOOGLE_PAY"] """Name of digital wallet provider.""" + + server_session_id: str + """Only applicable if `digital_wallet` is GOOGLE_PAY. + + Google Pay Web Push Provisioning session identifier required for the FPAN flow. + """ diff --git a/src/lithic/types/card_web_provision_response.py b/src/lithic/types/card_web_provision_response.py index 864d0e64..0f8f2de0 100644 --- a/src/lithic/types/card_web_provision_response.py +++ b/src/lithic/types/card_web_provision_response.py @@ -1,19 +1,32 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Union, Optional +from typing_extensions import TypeAlias from .._models import BaseModel -__all__ = ["CardWebProvisionResponse", "Jws", "JwsHeader"] +__all__ = [ + "CardWebProvisionResponse", + "AppleWebPushProvisioningResponse", + "AppleWebPushProvisioningResponseJws", + "AppleWebPushProvisioningResponseJwsHeader", + "GoogleWebPushProvisioningResponse", +] -class JwsHeader(BaseModel): +class AppleWebPushProvisioningResponseJwsHeader(BaseModel): + """ + JWS unprotected headers containing header parameters that aren't integrity-protected by the JWS signature. + """ + kid: Optional[str] = None """The ID for the JWS Public Key of the key pair used to generate the signature.""" -class Jws(BaseModel): - header: Optional[JwsHeader] = None +class AppleWebPushProvisioningResponseJws(BaseModel): + """JWS object required for handoff to Apple's script.""" + + header: Optional[AppleWebPushProvisioningResponseJwsHeader] = None """ JWS unprotected headers containing header parameters that aren't integrity-protected by the JWS signature. @@ -32,9 +45,26 @@ class Jws(BaseModel): """Base64url encoded signature of the JWS object.""" -class CardWebProvisionResponse(BaseModel): - jws: Optional[Jws] = None +class AppleWebPushProvisioningResponse(BaseModel): + jws: Optional[AppleWebPushProvisioningResponseJws] = None """JWS object required for handoff to Apple's script.""" state: Optional[str] = None """A unique identifier for the JWS object.""" + + +class GoogleWebPushProvisioningResponse(BaseModel): + google_opc: Optional[str] = None + """ + A base64 encoded and encrypted payload representing card data for the Google Pay + UWPP FPAN flow. + """ + + tsp_opc: Optional[str] = None + """ + A base64 encoded and encrypted payload representing card data for the Google Pay + UWPP tokenization flow. + """ + + +CardWebProvisionResponse: TypeAlias = Union[AppleWebPushProvisioningResponse, GoogleWebPushProvisioningResponse] diff --git a/src/lithic/types/cardholder_authentication.py b/src/lithic/types/cardholder_authentication.py new file mode 100644 index 00000000..e0c2aa36 --- /dev/null +++ b/src/lithic/types/cardholder_authentication.py @@ -0,0 +1,43 @@ +# 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__ = ["CardholderAuthentication"] + + +class CardholderAuthentication(BaseModel): + authentication_method: Literal["FRICTIONLESS", "CHALLENGE", "NONE"] + """Indicates the method used to authenticate the cardholder.""" + + authentication_result: Literal["ATTEMPTS", "DECLINE", "NONE", "SUCCESS"] + """Indicates the outcome of the 3DS authentication process.""" + + decision_made_by: Literal[ + "CUSTOMER_RULES", "CUSTOMER_ENDPOINT", "LITHIC_DEFAULT", "LITHIC_RULES", "NETWORK", "UNKNOWN" + ] + """Indicates which party made the 3DS authentication decision.""" + + liability_shift: Literal["3DS_AUTHENTICATED", "TOKEN_AUTHENTICATED", "NONE"] + """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. + - `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. + """ + + 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. + """ diff --git a/src/lithic/types/cards/__init__.py b/src/lithic/types/cards/__init__.py index 5c7241a4..63dfd5cb 100644 --- a/src/lithic/types/cards/__init__.py +++ b/src/lithic/types/cards/__init__.py @@ -3,7 +3,6 @@ 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_response.py b/src/lithic/types/cards/aggregate_balance_list_response.py index 0b3728ca..0075a93e 100644 --- a/src/lithic/types/cards/aggregate_balance_list_response.py +++ b/src/lithic/types/cards/aggregate_balance_list_response.py @@ -8,6 +8,8 @@ class AggregateBalanceListResponse(BaseModel): + """Card Aggregate Balance across all end-user accounts""" + available_amount: int """Funds available for spend in the currency's smallest unit (e.g., cents for USD)""" diff --git a/src/lithic/types/device.py b/src/lithic/types/device.py new file mode 100644 index 00000000..ceaeacfb --- /dev/null +++ b/src/lithic/types/device.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["Device"] + + +class Device(BaseModel): + imei: Optional[str] = None + """The IMEI number of the device being provisioned. + + For Amex, this field contains device ID instead as IMEI is not provided + """ + + ip_address: Optional[str] = None + """The IP address of the device initiating the request""" + + location: Optional[str] = None + """ + Latitude and longitude where the device is located during the authorization + attempt + """ diff --git a/src/lithic/types/digital_wallet_token_metadata.py b/src/lithic/types/digital_wallet_token_metadata.py new file mode 100644 index 00000000..2e26e00c --- /dev/null +++ b/src/lithic/types/digital_wallet_token_metadata.py @@ -0,0 +1,78 @@ +# 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__ = ["DigitalWalletTokenMetadata", "PaymentAccountInfo", "PaymentAccountInfoAccountHolderData"] + + +class PaymentAccountInfoAccountHolderData(BaseModel): + """ + Additional information that can be used to identify the account holder, such as name, address, etc + """ + + phone_number: Optional[str] = None + """ + The phone number, may contain country code along with phone number when + countryDialInCode is not present + """ + + +class PaymentAccountInfo(BaseModel): + """Contains the information of the account responsible for the payment.""" + + account_holder_data: PaymentAccountInfoAccountHolderData + """ + Additional information that can be used to identify the account holder, such as + name, address, etc + """ + + pan_unique_reference: Optional[str] = None + """Reference to the PAN that is unique per Wallet Provider""" + + payment_account_reference: Optional[str] = None + """The unique account reference assigned to the PAN""" + + token_unique_reference: Optional[str] = None + """ + A unique reference assigned following the allocation of a token used to identify + the token for the duration of its lifetime. + """ + + +class DigitalWalletTokenMetadata(BaseModel): + """Contains the metadata for the digital wallet being tokenized.""" + + payment_account_info: PaymentAccountInfo + """Contains the information of the account responsible for the payment.""" + + status: str + """The current status of the digital wallet token. Pending or declined.""" + + payment_app_instance_id: Optional[str] = None + """ + The identifier of the Payment App instance within a device that will be + provisioned with a token + """ + + token_requestor_id: Optional[str] = None + """The party that requested the digitization""" + + token_requestor_name: Optional[ + Literal[ + "AMAZON_ONE", + "ANDROID_PAY", + "APPLE_PAY", + "FACEBOOK", + "FITBIT_PAY", + "GARMIN_PAY", + "MICROSOFT_PAY", + "NETFLIX", + "SAMSUNG_PAY", + "UNKNOWN", + "VISA_CHECKOUT", + ] + ] = None + """Human-readable name of the wallet that the token_requestor_id maps to.""" diff --git a/src/lithic/types/digital_wallet_tokenization_approval_request_webhook_event.py b/src/lithic/types/digital_wallet_tokenization_approval_request_webhook_event.py new file mode 100644 index 00000000..ab01de75 --- /dev/null +++ b/src/lithic/types/digital_wallet_tokenization_approval_request_webhook_event.py @@ -0,0 +1,83 @@ +# 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 .device import Device +from .._models import BaseModel +from .tokenization_tfa_reason import TokenizationTfaReason +from .wallet_decisioning_info import WalletDecisioningInfo +from .tokenization_rule_result import TokenizationRuleResult +from .tokenization_decline_reason import TokenizationDeclineReason +from .digital_wallet_token_metadata import DigitalWalletTokenMetadata + +__all__ = ["DigitalWalletTokenizationApprovalRequestWebhookEvent", "CustomerTokenizationDecision"] + + +class CustomerTokenizationDecision(BaseModel): + """Contains the metadata for the customer tokenization decision.""" + + outcome: Literal[ + "APPROVED", "DECLINED", "ERROR", "INVALID_RESPONSE", "REQUIRE_ADDITIONAL_AUTHENTICATION", "TIMEOUT" + ] + """The outcome of the customer's decision""" + + responder_url: str + """The customer's subscribed URL""" + + latency: Optional[str] = None + """Time in ms it took for the customer's URL to respond""" + + response_code: Optional[str] = None + """The response code that the customer provided""" + + +class DigitalWalletTokenizationApprovalRequestWebhookEvent(BaseModel): + account_token: str + """Unique identifier for the user tokenizing a card""" + + card_token: str + """Unique identifier for the card being tokenized""" + + created: datetime + """Indicate when the request was received from Mastercard or Visa""" + + customer_tokenization_decision: Optional[CustomerTokenizationDecision] = None + """Contains the metadata for the customer tokenization decision.""" + + event_type: Literal["digital_wallet.tokenization_approval_request"] + """The name of this event""" + + issuer_decision: Literal["APPROVED", "DENIED", "VERIFICATION_REQUIRED"] + """Whether Lithic decisioned on the token, and if so, what the decision was. + + APPROVED/VERIFICATION_REQUIRED/DENIED. + """ + + tokenization_channel: Literal["DIGITAL_WALLET", "MERCHANT"] + """The channel through which the tokenization was made.""" + + tokenization_token: str + """Unique identifier for the digital wallet token attempt""" + + wallet_decisioning_info: WalletDecisioningInfo + + device: Optional[Device] = None + + digital_wallet_token_metadata: Optional[DigitalWalletTokenMetadata] = None + """Contains the metadata for the digital wallet being tokenized.""" + + rule_results: Optional[List[TokenizationRuleResult]] = None + """Results from rules that were evaluated for this tokenization""" + + tokenization_decline_reasons: Optional[List[TokenizationDeclineReason]] = None + """List of reasons why the tokenization was declined""" + + tokenization_source: Optional[ + Literal["ACCOUNT_ON_FILE", "CONTACTLESS_TAP", "MANUAL_PROVISION", "PUSH_PROVISION", "TOKEN", "UNKNOWN"] + ] = None + """The source of the tokenization.""" + + tokenization_tfa_reasons: Optional[List[TokenizationTfaReason]] = None + """List of reasons why two-factor authentication was required""" diff --git a/src/lithic/types/digital_wallet_tokenization_result_webhook_event.py b/src/lithic/types/digital_wallet_tokenization_result_webhook_event.py new file mode 100644 index 00000000..7e4a4328 --- /dev/null +++ b/src/lithic/types/digital_wallet_tokenization_result_webhook_event.py @@ -0,0 +1,71 @@ +# 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 .tokenization_tfa_reason import TokenizationTfaReason +from .tokenization_rule_result import TokenizationRuleResult + +__all__ = ["DigitalWalletTokenizationResultWebhookEvent", "TokenizationResultDetails"] + + +class TokenizationResultDetails(BaseModel): + """The result of the tokenization request.""" + + issuer_decision: str + """Lithic's tokenization decision.""" + + tokenization_decline_reasons: List[ + Literal[ + "ACCOUNT_SCORE_1", + "ALL_WALLET_DECLINE_REASONS_PRESENT", + "CARD_EXPIRY_MONTH_MISMATCH", + "CARD_EXPIRY_YEAR_MISMATCH", + "CARD_INVALID_STATE", + "CUSTOMER_RED_PATH", + "CVC_MISMATCH", + "DEVICE_SCORE_1", + "GENERIC_DECLINE", + "INVALID_CUSTOMER_RESPONSE", + "NETWORK_FAILURE", + "WALLET_RECOMMENDED_DECISION_RED", + ] + ] + """List of reasons why the tokenization was declined""" + + customer_decision: Optional[str] = None + """The customer's tokenization decision if applicable.""" + + rule_results: Optional[List[TokenizationRuleResult]] = None + """Results from rules that were evaluated for this tokenization""" + + token_activated_date_time: Optional[datetime] = None + """An RFC 3339 timestamp indicating when the tokenization succeeded.""" + + tokenization_tfa_reasons: Optional[List[TokenizationTfaReason]] = None + """List of reasons why two-factor authentication was required""" + + wallet_decision: Optional[str] = None + """The wallet's recommended decision.""" + + +class DigitalWalletTokenizationResultWebhookEvent(BaseModel): + account_token: str + """Account token""" + + card_token: str + """Card token""" + + created: datetime + """Created date""" + + event_type: Literal["digital_wallet.tokenization_result"] + """The type of event that occurred.""" + + tokenization_result_details: TokenizationResultDetails + """The result of the tokenization request.""" + + tokenization_token: str + """Tokenization token""" diff --git a/src/lithic/types/digital_wallet_tokenization_two_factor_authentication_code_sent_webhook_event.py b/src/lithic/types/digital_wallet_tokenization_two_factor_authentication_code_sent_webhook_event.py new file mode 100644 index 00000000..f177dd05 --- /dev/null +++ b/src/lithic/types/digital_wallet_tokenization_two_factor_authentication_code_sent_webhook_event.py @@ -0,0 +1,43 @@ +# 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__ = ["DigitalWalletTokenizationTwoFactorAuthenticationCodeSentWebhookEvent", "ActivationMethod"] + + +class ActivationMethod(BaseModel): + 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" + """ + + value: str + """ + The location to which the authentication code was sent. The format depends on + the ActivationMethod.Type field. If Type is Email, the Value will be the email + address. If the Type is Sms, the Value will be the phone number. + """ + + +class DigitalWalletTokenizationTwoFactorAuthenticationCodeSentWebhookEvent(BaseModel): + account_token: str + """Unique identifier for the user tokenizing a card""" + + activation_method: ActivationMethod + + card_token: str + """Unique identifier for the card being tokenized""" + + created: datetime + """Indicate when the request was received from Mastercard or Visa""" + + event_type: Literal["digital_wallet.tokenization_two_factor_authentication_code_sent"] + """The type of event that occurred.""" + + tokenization_token: str + """Unique identifier for the tokenization""" diff --git a/src/lithic/types/digital_wallet_tokenization_two_factor_authentication_code_webhook_event.py b/src/lithic/types/digital_wallet_tokenization_two_factor_authentication_code_webhook_event.py new file mode 100644 index 00000000..54bfb368 --- /dev/null +++ b/src/lithic/types/digital_wallet_tokenization_two_factor_authentication_code_webhook_event.py @@ -0,0 +1,46 @@ +# 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__ = ["DigitalWalletTokenizationTwoFactorAuthenticationCodeWebhookEvent", "ActivationMethod"] + + +class ActivationMethod(BaseModel): + 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" + """ + + value: str + """ + The location where the user wants to receive the authentication code. The format + depends on the ActivationMethod.Type field. If Type is Email, the Value will be + the email address. If the Type is Sms, the Value will be the phone number. + """ + + +class DigitalWalletTokenizationTwoFactorAuthenticationCodeWebhookEvent(BaseModel): + account_token: str + """Unique identifier for the user tokenizing a card""" + + activation_method: ActivationMethod + + authentication_code: str + """Authentication code to provide to the user tokenizing a card.""" + + card_token: str + """Unique identifier for the card being tokenized""" + + created: datetime + """Indicate when the request was received from Mastercard or Visa""" + + event_type: Literal["digital_wallet.tokenization_two_factor_authentication_code"] + """The type of event that occurred.""" + + tokenization_token: str + """Unique identifier for the tokenization""" diff --git a/src/lithic/types/digital_wallet_tokenization_updated_webhook_event.py b/src/lithic/types/digital_wallet_tokenization_updated_webhook_event.py new file mode 100644 index 00000000..608370e6 --- /dev/null +++ b/src/lithic/types/digital_wallet_tokenization_updated_webhook_event.py @@ -0,0 +1,25 @@ +# 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 +from .tokenization import Tokenization + +__all__ = ["DigitalWalletTokenizationUpdatedWebhookEvent"] + + +class DigitalWalletTokenizationUpdatedWebhookEvent(BaseModel): + account_token: str + """Account token""" + + card_token: str + """Card token""" + + created: datetime + """Created date""" + + event_type: Literal["digital_wallet.tokenization_updated"] + """The type of event that occurred.""" + + tokenization: Tokenization diff --git a/src/lithic/types/dispute.py b/src/lithic/types/dispute.py index 38a26a97..6a827ee4 100644 --- a/src/lithic/types/dispute.py +++ b/src/lithic/types/dispute.py @@ -10,6 +10,8 @@ class Dispute(BaseModel): + """Dispute.""" + token: str """Globally unique identifier.""" diff --git a/src/lithic/types/dispute_evidence.py b/src/lithic/types/dispute_evidence.py index cd6f1958..02dd0c1f 100644 --- a/src/lithic/types/dispute_evidence.py +++ b/src/lithic/types/dispute_evidence.py @@ -10,6 +10,8 @@ class DisputeEvidence(BaseModel): + """Dispute evidence.""" + token: str """Globally unique identifier.""" diff --git a/src/lithic/types/dispute_evidence_upload_failed_webhook_event.py b/src/lithic/types/dispute_evidence_upload_failed_webhook_event.py new file mode 100644 index 00000000..881449dd --- /dev/null +++ b/src/lithic/types/dispute_evidence_upload_failed_webhook_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .dispute_evidence import DisputeEvidence + +__all__ = ["DisputeEvidenceUploadFailedWebhookEvent"] + + +class DisputeEvidenceUploadFailedWebhookEvent(DisputeEvidence): + """Dispute evidence.""" + + event_type: Literal["dispute_evidence.upload_failed"] + """The type of event that occurred.""" diff --git a/src/lithic/types/dispute_transaction_created_webhook_event.py b/src/lithic/types/dispute_transaction_created_webhook_event.py new file mode 100644 index 00000000..ad9e8fe1 --- /dev/null +++ b/src/lithic/types/dispute_transaction_created_webhook_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .dispute_v2 import DisputeV2 + +__all__ = ["DisputeTransactionCreatedWebhookEvent"] + + +class DisputeTransactionCreatedWebhookEvent(DisputeV2): + """ + The Dispute object tracks the progression of a dispute throughout its lifecycle. + """ + + event_type: Literal["dispute_transaction.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/dispute_transaction_updated_webhook_event.py b/src/lithic/types/dispute_transaction_updated_webhook_event.py new file mode 100644 index 00000000..4c9d4b53 --- /dev/null +++ b/src/lithic/types/dispute_transaction_updated_webhook_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .dispute_v2 import DisputeV2 + +__all__ = ["DisputeTransactionUpdatedWebhookEvent"] + + +class DisputeTransactionUpdatedWebhookEvent(DisputeV2): + """ + The Dispute object tracks the progression of a dispute throughout its lifecycle. + """ + + event_type: Literal["dispute_transaction.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/dispute_updated_webhook_event.py b/src/lithic/types/dispute_updated_webhook_event.py new file mode 100644 index 00000000..5a26bc15 --- /dev/null +++ b/src/lithic/types/dispute_updated_webhook_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .dispute import Dispute + +__all__ = ["DisputeUpdatedWebhookEvent"] + + +class DisputeUpdatedWebhookEvent(Dispute): + """Dispute.""" + + event_type: Literal["dispute.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/dispute_v2.py b/src/lithic/types/dispute_v2.py index 49248838..7b09d20d 100644 --- a/src/lithic/types/dispute_v2.py +++ b/src/lithic/types/dispute_v2.py @@ -21,6 +21,8 @@ class EventDataWorkflow(BaseModel): + """Details specific to workflow events""" + action: Literal["OPENED", "CLOSED", "REOPENED"] """Action taken in this stage""" @@ -41,6 +43,8 @@ class EventDataWorkflow(BaseModel): class EventDataFinancial(BaseModel): + """Details specific to financial events""" + amount: int """Amount in minor units""" @@ -55,6 +59,8 @@ class EventDataFinancial(BaseModel): class EventDataCardholderLiability(BaseModel): + """Details specific to cardholder liability events""" + action: Literal["PROVISIONAL_CREDIT_GRANTED", "PROVISIONAL_CREDIT_REVERSED", "WRITTEN_OFF"] """Action taken regarding cardholder liability""" @@ -74,6 +80,8 @@ class EventDataCardholderLiability(BaseModel): class Event(BaseModel): + """Event that occurred in the dispute lifecycle""" + token: str """Unique identifier for the event, in UUID format""" @@ -88,6 +96,8 @@ class Event(BaseModel): class LiabilityAllocation(BaseModel): + """Current breakdown of how liability is allocated for the disputed amount""" + denied_amount: int """The amount that has been denied to the cardholder""" @@ -110,6 +120,10 @@ class LiabilityAllocation(BaseModel): class TransactionSeries(BaseModel): + """ + Contains identifiers for the transaction and specific event within being disputed; null if no transaction can be identified + """ + related_transaction_event_token: Optional[str] = None """ Token of the specific event in the original transaction being disputed, in UUID @@ -127,6 +141,10 @@ class TransactionSeries(BaseModel): class DisputeV2(BaseModel): + """ + The Dispute object tracks the progression of a dispute throughout its lifecycle. + """ + token: str """Token assigned by Lithic for the dispute, in UUID format.""" diff --git a/src/lithic/types/event.py b/src/lithic/types/event.py index a3c87151..9ac810f3 100644 --- a/src/lithic/types/event.py +++ b/src/lithic/types/event.py @@ -10,6 +10,8 @@ class Event(BaseModel): + """A single event that affects the transaction state and lifecycle.""" + token: str """Globally unique identifier.""" diff --git a/src/lithic/types/event_subscription.py b/src/lithic/types/event_subscription.py index cb921413..2ff52a1f 100644 --- a/src/lithic/types/event_subscription.py +++ b/src/lithic/types/event_subscription.py @@ -9,6 +9,8 @@ class EventSubscription(BaseModel): + """A subscription to specific event types.""" + token: str """Globally unique identifier.""" diff --git a/src/lithic/types/external_bank_account_unpause_response.py b/src/lithic/types/external_bank_account.py similarity index 96% rename from src/lithic/types/external_bank_account_unpause_response.py rename to src/lithic/types/external_bank_account.py index 97982c04..eb165522 100644 --- a/src/lithic/types/external_bank_account_unpause_response.py +++ b/src/lithic/types/external_bank_account.py @@ -9,10 +9,10 @@ from .verification_method import VerificationMethod from .external_bank_account_address import ExternalBankAccountAddress -__all__ = ["ExternalBankAccountUnpauseResponse"] +__all__ = ["ExternalBankAccount"] -class ExternalBankAccountUnpauseResponse(BaseModel): +class ExternalBankAccount(BaseModel): token: str """ A globally unique identifier for this record of an external bank account diff --git a/src/lithic/types/external_bank_account_created_webhook_event.py b/src/lithic/types/external_bank_account_created_webhook_event.py new file mode 100644 index 00000000..a47d282c --- /dev/null +++ b/src/lithic/types/external_bank_account_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .external_bank_account import ExternalBankAccount + +__all__ = ["ExternalBankAccountCreatedWebhookEvent"] + + +class ExternalBankAccountCreatedWebhookEvent(ExternalBankAccount): + event_type: Literal["external_bank_account.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/external_bank_account_retry_prenote_response.py b/src/lithic/types/external_bank_account_retry_prenote_response.py deleted file mode 100644 index 141d4cf3..00000000 --- a/src/lithic/types/external_bank_account_retry_prenote_response.py +++ /dev/null @@ -1,105 +0,0 @@ -# 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_updated_webhook_event.py b/src/lithic/types/external_bank_account_updated_webhook_event.py new file mode 100644 index 00000000..4b7c5e3c --- /dev/null +++ b/src/lithic/types/external_bank_account_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .external_bank_account import ExternalBankAccount + +__all__ = ["ExternalBankAccountUpdatedWebhookEvent"] + + +class ExternalBankAccountUpdatedWebhookEvent(ExternalBankAccount): + event_type: Literal["external_bank_account.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/external_payment.py b/src/lithic/types/external_payment.py index 874dc552..47264c7d 100644 --- a/src/lithic/types/external_payment.py +++ b/src/lithic/types/external_payment.py @@ -45,6 +45,16 @@ class Event(BaseModel): "EXTERNAL_CHECK_SETTLED", "EXTERNAL_CHECK_REVERSED", "EXTERNAL_CHECK_RELEASED", + "EXTERNAL_FEDNOW_INITIATED", + "EXTERNAL_FEDNOW_CANCELED", + "EXTERNAL_FEDNOW_SETTLED", + "EXTERNAL_FEDNOW_REVERSED", + "EXTERNAL_FEDNOW_RELEASED", + "EXTERNAL_RTP_INITIATED", + "EXTERNAL_RTP_CANCELED", + "EXTERNAL_RTP_SETTLED", + "EXTERNAL_RTP_REVERSED", + "EXTERNAL_RTP_RELEASED", ] @@ -61,7 +71,11 @@ class ExternalPayment(BaseModel): updated: datetime """ISO 8601 timestamp of when the transaction was last updated""" - category: Optional[Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"]] = None + category: Optional[ + Literal[ + "EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_FEDNOW", "EXTERNAL_RTP", "EXTERNAL_TRANSFER" + ] + ] = None currency: Optional[str] = None diff --git a/src/lithic/types/external_payment_create_params.py b/src/lithic/types/external_payment_create_params.py index 7e384cc7..0977186c 100644 --- a/src/lithic/types/external_payment_create_params.py +++ b/src/lithic/types/external_payment_create_params.py @@ -14,7 +14,11 @@ class ExternalPaymentCreateParams(TypedDict, total=False): amount: Required[int] - category: Required[Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"]] + category: Required[ + Literal[ + "EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_FEDNOW", "EXTERNAL_RTP", "EXTERNAL_TRANSFER" + ] + ] effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] diff --git a/src/lithic/types/external_payment_created_webhook_event.py b/src/lithic/types/external_payment_created_webhook_event.py new file mode 100644 index 00000000..e58e8357 --- /dev/null +++ b/src/lithic/types/external_payment_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .external_payment import ExternalPayment + +__all__ = ["ExternalPaymentCreatedWebhookEvent"] + + +class ExternalPaymentCreatedWebhookEvent(ExternalPayment): + event_type: Literal["external_payment.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/external_payment_list_params.py b/src/lithic/types/external_payment_list_params.py index 1cca6e22..f54ac91c 100644 --- a/src/lithic/types/external_payment_list_params.py +++ b/src/lithic/types/external_payment_list_params.py @@ -20,7 +20,9 @@ class ExternalPaymentListParams(TypedDict, total=False): business_account_token: str - category: Literal["EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_TRANSFER"] + category: Literal[ + "EXTERNAL_WIRE", "EXTERNAL_ACH", "EXTERNAL_CHECK", "EXTERNAL_FEDNOW", "EXTERNAL_RTP", "EXTERNAL_TRANSFER" + ] """External Payment category to be returned.""" end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] diff --git a/src/lithic/types/external_payment_updated_webhook_event.py b/src/lithic/types/external_payment_updated_webhook_event.py new file mode 100644 index 00000000..8412e44a --- /dev/null +++ b/src/lithic/types/external_payment_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .external_payment import ExternalPayment + +__all__ = ["ExternalPaymentUpdatedWebhookEvent"] + + +class ExternalPaymentUpdatedWebhookEvent(ExternalPayment): + event_type: Literal["external_payment.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/external_resource.py b/src/lithic/types/external_resource.py index ab60ac08..97cbc595 100644 --- a/src/lithic/types/external_resource.py +++ b/src/lithic/types/external_resource.py @@ -9,6 +9,8 @@ class ExternalResource(BaseModel): + """External resource associated with the management operation""" + external_resource_token: str """Token identifying the external resource""" diff --git a/src/lithic/types/cards/balance_list_response.py b/src/lithic/types/financial_account_balance.py similarity index 90% rename from src/lithic/types/cards/balance_list_response.py rename to src/lithic/types/financial_account_balance.py index 7fbe6daf..79bd194e 100644 --- a/src/lithic/types/cards/balance_list_response.py +++ b/src/lithic/types/financial_account_balance.py @@ -3,12 +3,14 @@ from datetime import datetime from typing_extensions import Literal -from ..._models import BaseModel +from .._models import BaseModel -__all__ = ["BalanceListResponse"] +__all__ = ["FinancialAccountBalance"] -class BalanceListResponse(BaseModel): +class FinancialAccountBalance(BaseModel): + """Balance of a Financial Account""" + token: str """Globally unique identifier for the financial account that holds this balance.""" diff --git a/src/lithic/types/financial_account_created_webhook_event.py b/src/lithic/types/financial_account_created_webhook_event.py new file mode 100644 index 00000000..bb052470 --- /dev/null +++ b/src/lithic/types/financial_account_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .financial_account import FinancialAccount + +__all__ = ["FinancialAccountCreatedWebhookEvent"] + + +class FinancialAccountCreatedWebhookEvent(FinancialAccount): + event_type: Literal["financial_account.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/financial_account_updated_webhook_event.py b/src/lithic/types/financial_account_updated_webhook_event.py new file mode 100644 index 00000000..b1ffbcbe --- /dev/null +++ b/src/lithic/types/financial_account_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .financial_account import FinancialAccount + +__all__ = ["FinancialAccountUpdatedWebhookEvent"] + + +class FinancialAccountUpdatedWebhookEvent(FinancialAccount): + event_type: Literal["financial_account.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/financial_accounts/__init__.py b/src/lithic/types/financial_accounts/__init__.py index 1616fa89..0a094585 100644 --- a/src/lithic/types/financial_accounts/__init__.py +++ b/src/lithic/types/financial_accounts/__init__.py @@ -7,7 +7,6 @@ from .statements import Statements as Statements from .category_balances import CategoryBalances as CategoryBalances 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 diff --git a/src/lithic/types/financial_accounts/balance_list_response.py b/src/lithic/types/financial_accounts/balance_list_response.py deleted file mode 100644 index 7fbe6daf..00000000 --- a/src/lithic/types/financial_accounts/balance_list_response.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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", "SECURITY"] - """Type of financial account.""" - - updated: datetime - """Date and time for when the balance was last updated.""" diff --git a/src/lithic/types/financial_accounts/loan_tape.py b/src/lithic/types/financial_accounts/loan_tape.py index f14ce634..8e4a585c 100644 --- a/src/lithic/types/financial_accounts/loan_tape.py +++ b/src/lithic/types/financial_accounts/loan_tape.py @@ -21,6 +21,8 @@ class AccountStandingFinancialAccountState(BaseModel): + """Information about the financial account state""" + status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"] """Status of the financial account""" diff --git a/src/lithic/types/financial_accounts/statement.py b/src/lithic/types/financial_accounts/statement.py index 18909ea1..37b8884d 100644 --- a/src/lithic/types/financial_accounts/statement.py +++ b/src/lithic/types/financial_accounts/statement.py @@ -19,6 +19,8 @@ class AccountStandingFinancialAccountState(BaseModel): + """Information about the financial account state""" + status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"] """Status of the financial account""" @@ -83,6 +85,8 @@ class InterestDetails(BaseModel): class PayoffDetails(BaseModel): + """Details on number and size of payments to pay off balance""" + minimum_payment_months: str """ The number of months it would take to pay off the balance in full by only paying @@ -95,16 +99,16 @@ class PayoffDetails(BaseModel): monthly payment. "NA" will signal negative or zero amortization """ - payoff_period_length_months: int + payoff_period_length_months: Optional[int] = None """Number of months to full pay off""" - payoff_period_monthly_payment_amount: int + payoff_period_monthly_payment_amount: Optional[int] = None """ The amount needed to be paid, in cents, each month in order to pay off current balance in the payoff period """ - payoff_period_payment_total: int + payoff_period_payment_total: Optional[int] = None """ The sum of all interest and principal paid, in cents, when paying off in the payoff period diff --git a/src/lithic/types/financial_accounts/statements/statement_line_items.py b/src/lithic/types/financial_accounts/statements/statement_line_items.py index c8ad115e..c1816c30 100644 --- a/src/lithic/types/financial_accounts/statements/statement_line_items.py +++ b/src/lithic/types/financial_accounts/statements/statement_line_items.py @@ -27,6 +27,8 @@ class Data(BaseModel): "CARD", "EXTERNAL_ACH", "EXTERNAL_CHECK", + "EXTERNAL_FEDNOW", + "EXTERNAL_RTP", "EXTERNAL_TRANSFER", "EXTERNAL_WIRE", "MANAGEMENT_ADJUSTMENT", @@ -93,6 +95,16 @@ class Data(BaseModel): "EXTERNAL_CHECK_RELEASED", "EXTERNAL_CHECK_REVERSED", "EXTERNAL_CHECK_SETTLED", + "EXTERNAL_FEDNOW_CANCELED", + "EXTERNAL_FEDNOW_INITIATED", + "EXTERNAL_FEDNOW_RELEASED", + "EXTERNAL_FEDNOW_REVERSED", + "EXTERNAL_FEDNOW_SETTLED", + "EXTERNAL_RTP_CANCELED", + "EXTERNAL_RTP_INITIATED", + "EXTERNAL_RTP_RELEASED", + "EXTERNAL_RTP_REVERSED", + "EXTERNAL_RTP_SETTLED", "EXTERNAL_TRANSFER_CANCELED", "EXTERNAL_TRANSFER_INITIATED", "EXTERNAL_TRANSFER_RELEASED", @@ -121,6 +133,12 @@ class Data(BaseModel): "RETURNED_PAYMENT", "RETURNED_PAYMENT_REVERSAL", "LITHIC_NETWORK_PAYMENT", + "ANNUAL", + "ANNUAL_REVERSAL", + "QUARTERLY", + "QUARTERLY_REVERSAL", + "MONTHLY", + "MONTHLY_REVERSAL", ] financial_account_token: str diff --git a/src/lithic/types/funding_event_list_response.py b/src/lithic/types/funding_event.py similarity index 90% rename from src/lithic/types/funding_event_list_response.py rename to src/lithic/types/funding_event.py index 74f0a5e4..be0e0c73 100644 --- a/src/lithic/types/funding_event_list_response.py +++ b/src/lithic/types/funding_event.py @@ -6,7 +6,7 @@ from .._models import BaseModel -__all__ = ["FundingEventListResponse", "NetworkSettlementSummary"] +__all__ = ["FundingEvent", "NetworkSettlementSummary"] class NetworkSettlementSummary(BaseModel): @@ -15,7 +15,7 @@ class NetworkSettlementSummary(BaseModel): settled_gross_amount: int -class FundingEventListResponse(BaseModel): +class FundingEvent(BaseModel): token: str """Unique token ID""" diff --git a/src/lithic/types/funding_event_created_webhook_event.py b/src/lithic/types/funding_event_created_webhook_event.py new file mode 100644 index 00000000..cf914d9c --- /dev/null +++ b/src/lithic/types/funding_event_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .funding_event import FundingEvent + +__all__ = ["FundingEventCreatedWebhookEvent"] + + +class FundingEventCreatedWebhookEvent(FundingEvent): + event_type: Literal["funding_event.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/funding_event_retrieve_response.py b/src/lithic/types/funding_event_retrieve_response.py deleted file mode 100644 index b9d5b047..00000000 --- a/src/lithic/types/funding_event_retrieve_response.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from datetime import date, datetime -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["FundingEventRetrieveResponse", "NetworkSettlementSummary"] - - -class NetworkSettlementSummary(BaseModel): - network_settlement_date: date - - settled_gross_amount: int - - -class FundingEventRetrieveResponse(BaseModel): - token: str - """Unique token ID""" - - collection_resource_type: Literal["BOOK_TRANSFER", "PAYMENT"] - """Collection resource type""" - - collection_tokens: List[str] - """ - IDs of collections, further information can be gathered from the appropriate - collection API based on collection_resource_type - """ - - created: datetime - """Time of the creation""" - - high_watermark: datetime - """Time of the high watermark""" - - network_settlement_summary: List[NetworkSettlementSummary] - """Network settlement summary breakdown by network settlement date""" - - previous_high_watermark: datetime - """Time of the previous high watermark""" - - updated: datetime - """Time of the update""" diff --git a/src/lithic/types/internal_transaction.py b/src/lithic/types/internal_transaction.py new file mode 100644 index 00000000..47281d49 --- /dev/null +++ b/src/lithic/types/internal_transaction.py @@ -0,0 +1,45 @@ +# 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__ = ["InternalTransaction", "Event"] + + +class Event(BaseModel): + token: str + + amount: int + + created: datetime + + result: Literal["APPROVED", "DECLINED"] + + type: Literal["INTERNAL_ADJUSTMENT"] + + +class InternalTransaction(BaseModel): + token: str + + category: Literal["INTERNAL"] + + created: datetime + + currency: str + + descriptor: str + + events: List[Event] + + pending_amount: int + + result: Literal["APPROVED", "DECLINED"] + + settled_amount: int + + status: Literal["PENDING", "SETTLED", "DECLINED", "REVERSED", "CANCELED", "RETURNED"] + + updated: datetime diff --git a/src/lithic/types/internal_transaction_created_webhook_event.py b/src/lithic/types/internal_transaction_created_webhook_event.py new file mode 100644 index 00000000..f3a3043d --- /dev/null +++ b/src/lithic/types/internal_transaction_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .internal_transaction import InternalTransaction + +__all__ = ["InternalTransactionCreatedWebhookEvent"] + + +class InternalTransactionCreatedWebhookEvent(InternalTransaction): + event_type: Literal["internal_transaction.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/internal_transaction_updated_webhook_event.py b/src/lithic/types/internal_transaction_updated_webhook_event.py new file mode 100644 index 00000000..818cbf22 --- /dev/null +++ b/src/lithic/types/internal_transaction_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .internal_transaction import InternalTransaction + +__all__ = ["InternalTransactionUpdatedWebhookEvent"] + + +class InternalTransactionUpdatedWebhookEvent(InternalTransaction): + event_type: Literal["internal_transaction.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/kyb_business_entity.py b/src/lithic/types/kyb_business_entity.py index 3e7b1d6a..a61f35ca 100644 --- a/src/lithic/types/kyb_business_entity.py +++ b/src/lithic/types/kyb_business_entity.py @@ -8,6 +8,10 @@ class Address(BaseModel): + """ + Business''s physical address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. + """ + address1: str """Valid deliverable address (no PO boxes).""" diff --git a/src/lithic/types/kyb_param.py b/src/lithic/types/kyb_param.py index 9977f636..b3131cd4 100644 --- a/src/lithic/types/kyb_param.py +++ b/src/lithic/types/kyb_param.py @@ -12,6 +12,8 @@ class BeneficialOwnerIndividual(TypedDict, total=False): + """Individuals associated with a KYB application. Phone number is optional.""" + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not @@ -46,6 +48,10 @@ class BeneficialOwnerIndividual(TypedDict, total=False): class BusinessEntity(TypedDict, total=False): + """ + Information for business for which the account is being opened and KYB is being run. + """ + address: Required[Address] """ Business's physical address - PO boxes, UPS drops, and FedEx drops are not @@ -79,6 +85,13 @@ class BusinessEntity(TypedDict, total=False): class ControlPerson(TypedDict, total=False): + """ + 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. + """ + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not diff --git a/src/lithic/types/kyc_param.py b/src/lithic/types/kyc_param.py index fef6b452..f4bbffb3 100644 --- a/src/lithic/types/kyc_param.py +++ b/src/lithic/types/kyc_param.py @@ -10,6 +10,10 @@ class Individual(TypedDict, total=False): + """ + Information on individual for whom the account is being opened and KYC is being run. + """ + address: Required[Address] """ Individual's current address - PO boxes, UPS drops, and FedEx drops are not diff --git a/src/lithic/types/loan_tape_created_webhook_event.py b/src/lithic/types/loan_tape_created_webhook_event.py new file mode 100644 index 00000000..f2e66b35 --- /dev/null +++ b/src/lithic/types/loan_tape_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .financial_accounts.loan_tape import LoanTape + +__all__ = ["LoanTapeCreatedWebhookEvent"] + + +class LoanTapeCreatedWebhookEvent(LoanTape): + event_type: Literal["loan_tape.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/loan_tape_updated_webhook_event.py b/src/lithic/types/loan_tape_updated_webhook_event.py new file mode 100644 index 00000000..93d2ab16 --- /dev/null +++ b/src/lithic/types/loan_tape_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .financial_accounts.loan_tape import LoanTape + +__all__ = ["LoanTapeUpdatedWebhookEvent"] + + +class LoanTapeUpdatedWebhookEvent(LoanTape): + event_type: Literal["loan_tape.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/management_operation_create_params.py b/src/lithic/types/management_operation_create_params.py index 525651f1..47d2dada 100644 --- a/src/lithic/types/management_operation_create_params.py +++ b/src/lithic/types/management_operation_create_params.py @@ -49,6 +49,12 @@ class ManagementOperationCreateParams(TypedDict, total=False): "DISPUTE_WON_REVERSAL", "DISBURSE", "DISBURSE_REVERSAL", + "ANNUAL", + "ANNUAL_REVERSAL", + "QUARTERLY", + "QUARTERLY_REVERSAL", + "MONTHLY", + "MONTHLY_REVERSAL", ] ] diff --git a/src/lithic/types/management_operation_created_webhook_event.py b/src/lithic/types/management_operation_created_webhook_event.py new file mode 100644 index 00000000..e98b2246 --- /dev/null +++ b/src/lithic/types/management_operation_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .management_operation_transaction import ManagementOperationTransaction + +__all__ = ["ManagementOperationCreatedWebhookEvent"] + + +class ManagementOperationCreatedWebhookEvent(ManagementOperationTransaction): + event_type: Literal["management_operation.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/management_operation_transaction.py b/src/lithic/types/management_operation_transaction.py index c1bbc11c..16b8134a 100644 --- a/src/lithic/types/management_operation_transaction.py +++ b/src/lithic/types/management_operation_transaction.py @@ -45,6 +45,12 @@ class Event(BaseModel): "DISPUTE_WON_REVERSAL", "DISBURSE", "DISBURSE_REVERSAL", + "ANNUAL", + "ANNUAL_REVERSAL", + "QUARTERLY", + "QUARTERLY_REVERSAL", + "MONTHLY", + "MONTHLY_REVERSAL", ] subtype: Optional[str] = None diff --git a/src/lithic/types/management_operation_updated_webhook_event.py b/src/lithic/types/management_operation_updated_webhook_event.py new file mode 100644 index 00000000..2c05902e --- /dev/null +++ b/src/lithic/types/management_operation_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .management_operation_transaction import ManagementOperationTransaction + +__all__ = ["ManagementOperationUpdatedWebhookEvent"] + + +class ManagementOperationUpdatedWebhookEvent(ManagementOperationTransaction): + event_type: Literal["management_operation.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/message_attempt.py b/src/lithic/types/message_attempt.py index a3443db0..66a1825f 100644 --- a/src/lithic/types/message_attempt.py +++ b/src/lithic/types/message_attempt.py @@ -9,6 +9,8 @@ class MessageAttempt(BaseModel): + """A subscription to specific event types.""" + token: str """Globally unique identifier.""" diff --git a/src/lithic/types/reports/settlement/network_total_list_response.py b/src/lithic/types/network_total.py similarity index 94% rename from src/lithic/types/reports/settlement/network_total_list_response.py rename to src/lithic/types/network_total.py index e934f099..132c96a4 100644 --- a/src/lithic/types/reports/settlement/network_total_list_response.py +++ b/src/lithic/types/network_total.py @@ -4,9 +4,9 @@ from datetime import date, datetime from typing_extensions import Literal -from ...._models import BaseModel +from .._models import BaseModel -__all__ = ["NetworkTotalListResponse", "Amounts"] +__all__ = ["NetworkTotal", "Amounts"] class Amounts(BaseModel): @@ -26,7 +26,7 @@ class Amounts(BaseModel): """Charges specific to Visa/Interlink, in currency's smallest unit.""" -class NetworkTotalListResponse(BaseModel): +class NetworkTotal(BaseModel): token: str """Globally unique identifier.""" diff --git a/src/lithic/types/network_total_created_webhook_event.py b/src/lithic/types/network_total_created_webhook_event.py new file mode 100644 index 00000000..09036dbc --- /dev/null +++ b/src/lithic/types/network_total_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .network_total import NetworkTotal + +__all__ = ["NetworkTotalCreatedWebhookEvent"] + + +class NetworkTotalCreatedWebhookEvent(NetworkTotal): + event_type: Literal["network_total.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/network_total_updated_webhook_event.py b/src/lithic/types/network_total_updated_webhook_event.py new file mode 100644 index 00000000..a783741b --- /dev/null +++ b/src/lithic/types/network_total_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .network_total import NetworkTotal + +__all__ = ["NetworkTotalUpdatedWebhookEvent"] + + +class NetworkTotalUpdatedWebhookEvent(NetworkTotal): + event_type: Literal["network_total.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/non_pci_card.py b/src/lithic/types/non_pci_card.py index fabbd4d1..7c4bc07c 100644 --- a/src/lithic/types/non_pci_card.py +++ b/src/lithic/types/non_pci_card.py @@ -11,6 +11,8 @@ class Funding(BaseModel): + """Deprecated: Funding account for the card.""" + token: str """A globally unique identifier for this FundingAccount.""" @@ -49,6 +51,8 @@ class Funding(BaseModel): class NonPCICard(BaseModel): + """Card details without PCI information""" + token: str """Globally unique identifier.""" @@ -136,6 +140,12 @@ class NonPCICard(BaseModel): `/auth_rules` endpoints to fetch Auth Rule information instead. """ + bulk_order_token: Optional[str] = None + """Globally unique identifier for the bulk order associated with this card. + + Only applicable to physical cards that are part of a bulk shipment + """ + cardholder_currency: Optional[str] = None """3-character alphabetic ISO 4217 code for the currency of the cardholder.""" diff --git a/src/lithic/types/parsed_webhook_event.py b/src/lithic/types/parsed_webhook_event.py new file mode 100644 index 00000000..25abe119 --- /dev/null +++ b/src/lithic/types/parsed_webhook_event.py @@ -0,0 +1,480 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, TypeAlias + +from .._models import BaseModel +from .kyb_business_entity import KYBBusinessEntity +from .three_ds_authentication import ThreeDSAuthentication +from .asa_request_webhook_event import AsaRequestWebhookEvent +from .card_created_webhook_event import CardCreatedWebhookEvent +from .card_renewed_webhook_event import CardRenewedWebhookEvent +from .card_shipped_webhook_event import CardShippedWebhookEvent +from .card_reissued_webhook_event import CardReissuedWebhookEvent +from .card_converted_webhook_event import CardConvertedWebhookEvent +from .balance_updated_webhook_event import BalanceUpdatedWebhookEvent +from .dispute_updated_webhook_event import DisputeUpdatedWebhookEvent +from .loan_tape_created_webhook_event import LoanTapeCreatedWebhookEvent +from .loan_tape_updated_webhook_event import LoanTapeUpdatedWebhookEvent +from .statements_created_webhook_event import StatementsCreatedWebhookEvent +from .tokenization_result_webhook_event import TokenizationResultWebhookEvent +from .tokenization_updated_webhook_event import TokenizationUpdatedWebhookEvent +from .funding_event_created_webhook_event import FundingEventCreatedWebhookEvent +from .network_total_created_webhook_event import NetworkTotalCreatedWebhookEvent +from .network_total_updated_webhook_event import NetworkTotalUpdatedWebhookEvent +from .account_holder_created_webhook_event import AccountHolderCreatedWebhookEvent +from .card_transaction_updated_webhook_event import CardTransactionUpdatedWebhookEvent +from .external_payment_created_webhook_event import ExternalPaymentCreatedWebhookEvent +from .external_payment_updated_webhook_event import ExternalPaymentUpdatedWebhookEvent +from .financial_account_created_webhook_event import FinancialAccountCreatedWebhookEvent +from .financial_account_updated_webhook_event import FinancialAccountUpdatedWebhookEvent +from .settlement_report_updated_webhook_event import SettlementReportUpdatedWebhookEvent +from .account_holder_verification_webhook_event import AccountHolderVerificationWebhookEvent +from .dispute_transaction_created_webhook_event import DisputeTransactionCreatedWebhookEvent +from .dispute_transaction_updated_webhook_event import DisputeTransactionUpdatedWebhookEvent +from .payment_transaction_created_webhook_event import PaymentTransactionCreatedWebhookEvent +from .payment_transaction_updated_webhook_event import PaymentTransactionUpdatedWebhookEvent +from .internal_transaction_created_webhook_event import InternalTransactionCreatedWebhookEvent +from .internal_transaction_updated_webhook_event import InternalTransactionUpdatedWebhookEvent +from .management_operation_created_webhook_event import ManagementOperationCreatedWebhookEvent +from .management_operation_updated_webhook_event import ManagementOperationUpdatedWebhookEvent +from .external_bank_account_created_webhook_event import ExternalBankAccountCreatedWebhookEvent +from .external_bank_account_updated_webhook_event import ExternalBankAccountUpdatedWebhookEvent +from .tokenization_approval_request_webhook_event import TokenizationApprovalRequestWebhookEvent +from .dispute_evidence_upload_failed_webhook_event import DisputeEvidenceUploadFailedWebhookEvent +from .account_holder_document_updated_webhook_event import AccountHolderDocumentUpdatedWebhookEvent +from .three_ds_authentication_created_webhook_event import ThreeDSAuthenticationCreatedWebhookEvent +from .three_ds_authentication_updated_webhook_event import ThreeDSAuthenticationUpdatedWebhookEvent +from .tokenization_decisioning_request_webhook_event import TokenizationDecisioningRequestWebhookEvent +from .book_transfer_transaction_created_webhook_event import BookTransferTransactionCreatedWebhookEvent +from .book_transfer_transaction_updated_webhook_event import BookTransferTransactionUpdatedWebhookEvent +from .three_ds_authentication_challenge_webhook_event import ThreeDSAuthenticationChallengeWebhookEvent +from .auth_rules_backtest_report_created_webhook_event import AuthRulesBacktestReportCreatedWebhookEvent +from .digital_wallet_tokenization_result_webhook_event import DigitalWalletTokenizationResultWebhookEvent +from .digital_wallet_tokenization_updated_webhook_event import DigitalWalletTokenizationUpdatedWebhookEvent +from .card_transaction_enhanced_data_created_webhook_event import CardTransactionEnhancedDataCreatedWebhookEvent +from .card_transaction_enhanced_data_updated_webhook_event import CardTransactionEnhancedDataUpdatedWebhookEvent +from .tokenization_two_factor_authentication_code_webhook_event import ( + TokenizationTwoFactorAuthenticationCodeWebhookEvent, +) +from .digital_wallet_tokenization_approval_request_webhook_event import ( + DigitalWalletTokenizationApprovalRequestWebhookEvent, +) +from .tokenization_two_factor_authentication_code_sent_webhook_event import ( + TokenizationTwoFactorAuthenticationCodeSentWebhookEvent, +) +from .digital_wallet_tokenization_two_factor_authentication_code_webhook_event import ( + DigitalWalletTokenizationTwoFactorAuthenticationCodeWebhookEvent, +) +from .digital_wallet_tokenization_two_factor_authentication_code_sent_webhook_event import ( + DigitalWalletTokenizationTwoFactorAuthenticationCodeSentWebhookEvent, +) + +__all__ = [ + "ParsedWebhookEvent", + "KYBPayload", + "KYBPayloadUpdateRequest", + "KYBPayloadUpdateRequestBeneficialOwnerIndividual", + "KYBPayloadUpdateRequestBeneficialOwnerIndividualAddress", + "KYBPayloadUpdateRequestControlPerson", + "KYBPayloadUpdateRequestControlPersonAddress", + "KYCPayload", + "KYCPayloadUpdateRequest", + "KYCPayloadUpdateRequestIndividual", + "KYCPayloadUpdateRequestIndividualAddress", + "LegacyPayload", +] + + +class KYBPayloadUpdateRequestBeneficialOwnerIndividualAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + 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 KYBPayloadUpdateRequestBeneficialOwnerIndividual(BaseModel): + address: Optional[KYBPayloadUpdateRequestBeneficialOwnerIndividualAddress] = 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 KYBPayloadUpdateRequestControlPersonAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + 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 KYBPayloadUpdateRequestControlPerson(BaseModel): + """ + 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. + """ + + address: Optional[KYBPayloadUpdateRequestControlPersonAddress] = 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 KYBPayloadUpdateRequest(BaseModel): + """Original request to update the account holder.""" + + beneficial_owner_entities: Optional[List[KYBBusinessEntity]] = None + """Deprecated.""" + + beneficial_owner_individuals: Optional[List[KYBPayloadUpdateRequestBeneficialOwnerIndividual]] = None + """ + You must submit a list of all direct and indirect individuals with 25% or more + ownership in the company. A maximum of 4 beneficial owners can be submitted. If + no individual owns 25% of the company you do not need to send beneficial owner + information. 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: Optional[KYBBusinessEntity] = None + """ + Information for business for which the account is being opened and KYB is being + run. + """ + + control_person: Optional[KYBPayloadUpdateRequestControlPerson] = None + """ + 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. + """ + + +class KYBPayload(BaseModel): + """KYB payload for an updated account holder.""" + + token: str + """The token of the account_holder that was created.""" + + update_request: KYBPayloadUpdateRequest + """Original request to update the account holder.""" + + event_type: Optional[Literal["account_holder.updated"]] = None + """The type of event that occurred.""" + + external_id: Optional[str] = None + """ + A user provided id that can be used to link an account holder with an external + system + """ + + nature_of_business: Optional[str] = None + """ + Short description of the company's line of business (i.e., what does the company + do?). + """ + + website_url: Optional[str] = None + """Company website URL.""" + + +class KYCPayloadUpdateRequestIndividualAddress(BaseModel): + """ + Individual's current address - PO boxes, UPS drops, and FedEx drops are not acceptable; APO/FPO are acceptable. Only USA addresses are currently supported. + """ + + 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 KYCPayloadUpdateRequestIndividual(BaseModel): + """ + Information on the individual for whom the account is being opened and KYC is being run. + """ + + address: Optional[KYCPayloadUpdateRequestIndividualAddress] = 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 KYCPayloadUpdateRequest(BaseModel): + """Original request to update the account holder.""" + + individual: Optional[KYCPayloadUpdateRequestIndividual] = None + """ + Information on the individual for whom the account is being opened and KYC is + being run. + """ + + +class KYCPayload(BaseModel): + """KYC payload for an updated account holder.""" + + token: str + """The token of the account_holder that was created.""" + + update_request: KYCPayloadUpdateRequest + """Original request to update the account holder.""" + + event_type: Optional[Literal["account_holder.updated"]] = None + """The type of event that occurred.""" + + external_id: Optional[str] = None + """ + A user provided id that can be used to link an account holder with an external + system + """ + + +class LegacyPayload(BaseModel): + """Legacy payload for an updated account holder.""" + + token: str + """The token of the account_holder that was created.""" + + business_account_token: Optional[str] = None + """ + If applicable, represents the business account token associated with the + account_holder. + """ + + created: Optional[datetime] = None + """When the account_holder updated event was created""" + + email: Optional[str] = None + """ + If updated, the newly updated email associated with the account_holder otherwise + the existing email is provided. + """ + + event_type: Optional[Literal["account_holder.updated"]] = None + """The type of event that occurred.""" + + external_id: Optional[str] = None + """If applicable, represents the external_id associated with the account_holder.""" + + first_name: Optional[str] = None + """If applicable, represents the account_holder's first name.""" + + last_name: Optional[str] = None + """If applicable, represents the account_holder's last name.""" + + legal_business_name: Optional[str] = None + """If applicable, represents the account_holder's business name.""" + + phone_number: Optional[str] = None + """ + If updated, the newly updated phone_number associated with the account_holder + otherwise the existing phone_number is provided. + """ + + +ParsedWebhookEvent: TypeAlias = Union[ + AccountHolderCreatedWebhookEvent, + KYBPayload, + KYCPayload, + LegacyPayload, + AccountHolderVerificationWebhookEvent, + AccountHolderDocumentUpdatedWebhookEvent, + AsaRequestWebhookEvent, + TokenizationDecisioningRequestWebhookEvent, + AuthRulesBacktestReportCreatedWebhookEvent, + BalanceUpdatedWebhookEvent, + BookTransferTransactionCreatedWebhookEvent, + BookTransferTransactionUpdatedWebhookEvent, + CardCreatedWebhookEvent, + CardConvertedWebhookEvent, + CardRenewedWebhookEvent, + CardReissuedWebhookEvent, + CardShippedWebhookEvent, + CardTransactionUpdatedWebhookEvent, + CardTransactionEnhancedDataCreatedWebhookEvent, + CardTransactionEnhancedDataUpdatedWebhookEvent, + DigitalWalletTokenizationApprovalRequestWebhookEvent, + DigitalWalletTokenizationResultWebhookEvent, + DigitalWalletTokenizationTwoFactorAuthenticationCodeWebhookEvent, + DigitalWalletTokenizationTwoFactorAuthenticationCodeSentWebhookEvent, + DigitalWalletTokenizationUpdatedWebhookEvent, + DisputeUpdatedWebhookEvent, + DisputeEvidenceUploadFailedWebhookEvent, + ExternalBankAccountCreatedWebhookEvent, + ExternalBankAccountUpdatedWebhookEvent, + ExternalPaymentCreatedWebhookEvent, + ExternalPaymentUpdatedWebhookEvent, + FinancialAccountCreatedWebhookEvent, + FinancialAccountUpdatedWebhookEvent, + FundingEventCreatedWebhookEvent, + LoanTapeCreatedWebhookEvent, + LoanTapeUpdatedWebhookEvent, + ManagementOperationCreatedWebhookEvent, + ManagementOperationUpdatedWebhookEvent, + InternalTransactionCreatedWebhookEvent, + InternalTransactionUpdatedWebhookEvent, + NetworkTotalCreatedWebhookEvent, + NetworkTotalUpdatedWebhookEvent, + PaymentTransactionCreatedWebhookEvent, + PaymentTransactionUpdatedWebhookEvent, + SettlementReportUpdatedWebhookEvent, + StatementsCreatedWebhookEvent, + ThreeDSAuthenticationCreatedWebhookEvent, + ThreeDSAuthenticationUpdatedWebhookEvent, + ThreeDSAuthenticationChallengeWebhookEvent, + TokenizationApprovalRequestWebhookEvent, + TokenizationResultWebhookEvent, + TokenizationTwoFactorAuthenticationCodeWebhookEvent, + TokenizationTwoFactorAuthenticationCodeSentWebhookEvent, + TokenizationUpdatedWebhookEvent, + ThreeDSAuthentication, + DisputeTransactionCreatedWebhookEvent, + DisputeTransactionUpdatedWebhookEvent, +] diff --git a/src/lithic/types/payment.py b/src/lithic/types/payment.py index 3e77e3d3..3b32efaa 100644 --- a/src/lithic/types/payment.py +++ b/src/lithic/types/payment.py @@ -18,6 +18,8 @@ class Event(BaseModel): + """Payment Event""" + token: str """Globally unique identifier.""" @@ -98,6 +100,9 @@ class MethodAttributesACHMethodAttributes(BaseModel): sec_code: Literal["CCD", "PPD", "WEB", "TEL", "CIE", "CTX"] """SEC code for ACH transaction""" + ach_hold_period: Optional[int] = None + """Number of days the ACH transaction is on hold""" + addenda: Optional[str] = None """Addenda information""" @@ -142,6 +147,8 @@ class MethodAttributesWireMethodAttributes(BaseModel): class RelatedAccountTokens(BaseModel): + """Account tokens related to a payment transaction""" + account_token: Optional[str] = None """Globally unique identifier for the account""" @@ -150,6 +157,8 @@ class RelatedAccountTokens(BaseModel): class Payment(BaseModel): + """Payment transaction""" + token: str """Unique identifier for the transaction""" @@ -164,6 +173,8 @@ class Payment(BaseModel): "CARD", "EXTERNAL_ACH", "EXTERNAL_CHECK", + "EXTERNAL_FEDNOW", + "EXTERNAL_RTP", "EXTERNAL_TRANSFER", "EXTERNAL_WIRE", "MANAGEMENT_ADJUSTMENT", diff --git a/src/lithic/types/payment_create_params.py b/src/lithic/types/payment_create_params.py index 33bd7c7f..41c0e5f6 100644 --- a/src/lithic/types/payment_create_params.py +++ b/src/lithic/types/payment_create_params.py @@ -3,7 +3,9 @@ from __future__ import annotations from typing import Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict + +from .._utils import PropertyInfo __all__ = ["PaymentCreateParams", "MethodAttributes"] @@ -35,4 +37,7 @@ class PaymentCreateParams(TypedDict, total=False): class MethodAttributes(TypedDict, total=False): sec_code: Required[Literal["CCD", "PPD", "WEB"]] + ach_hold_period: Annotated[int, PropertyInfo(alias="ach_hold__period")] + """Number of days to hold the ACH payment""" + addenda: Optional[str] diff --git a/src/lithic/types/payment_create_response.py b/src/lithic/types/payment_create_response.py index faffaa24..bc71fce3 100644 --- a/src/lithic/types/payment_create_response.py +++ b/src/lithic/types/payment_create_response.py @@ -9,5 +9,7 @@ class PaymentCreateResponse(Payment): + """Payment transaction""" + balance: Optional[Balance] = None """Balance""" diff --git a/src/lithic/types/payment_retry_response.py b/src/lithic/types/payment_retry_response.py index fe0577c3..da6979e8 100644 --- a/src/lithic/types/payment_retry_response.py +++ b/src/lithic/types/payment_retry_response.py @@ -9,5 +9,7 @@ class PaymentRetryResponse(Payment): + """Payment transaction""" + balance: Optional[Balance] = None """Balance""" diff --git a/src/lithic/types/payment_transaction_created_webhook_event.py b/src/lithic/types/payment_transaction_created_webhook_event.py new file mode 100644 index 00000000..2d5f4a6e --- /dev/null +++ b/src/lithic/types/payment_transaction_created_webhook_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .payment import Payment + +__all__ = ["PaymentTransactionCreatedWebhookEvent"] + + +class PaymentTransactionCreatedWebhookEvent(Payment): + """Payment transaction""" + + event_type: Literal["payment_transaction.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/payment_transaction_updated_webhook_event.py b/src/lithic/types/payment_transaction_updated_webhook_event.py new file mode 100644 index 00000000..2a65aad2 --- /dev/null +++ b/src/lithic/types/payment_transaction_updated_webhook_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .payment import Payment + +__all__ = ["PaymentTransactionUpdatedWebhookEvent"] + + +class PaymentTransactionUpdatedWebhookEvent(Payment): + """Payment transaction""" + + event_type: Literal["payment_transaction.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/provision_response.py b/src/lithic/types/provision_response.py index 0572d427..29a2446e 100644 --- a/src/lithic/types/provision_response.py +++ b/src/lithic/types/provision_response.py @@ -10,6 +10,11 @@ class ProvisionResponse(BaseModel): + """Object containing the fields required to add a card to Apple Pay. + + Applies only to Apple Pay wallet. + """ + activation_data: Optional[str] = FieldInfo(alias="activationData", default=None) encrypted_data: Optional[str] = FieldInfo(alias="encryptedData", default=None) diff --git a/src/lithic/types/reports/settlement/__init__.py b/src/lithic/types/reports/settlement/__init__.py index a1847cd0..1d367429 100644 --- a/src/lithic/types/reports/settlement/__init__.py +++ b/src/lithic/types/reports/settlement/__init__.py @@ -3,5 +3,3 @@ 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_retrieve_response.py b/src/lithic/types/reports/settlement/network_total_retrieve_response.py deleted file mode 100644 index 07fe0c8e..00000000 --- a/src/lithic/types/reports/settlement/network_total_retrieve_response.py +++ /dev/null @@ -1,77 +0,0 @@ -# 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). - """ - - is_complete: bool - """ - Indicates that all settlement records related to this Network Total are - available in the details endpoint. - """ - - network: Literal["AMEX", "VISA", "MASTERCARD", "MAESTRO", "INTERLINK"] - """Card network where the transaction took place. - - AMEX, 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/settlement_detail.py b/src/lithic/types/settlement_detail.py index 91c8a07c..a8cd5fa3 100644 --- a/src/lithic/types/settlement_detail.py +++ b/src/lithic/types/settlement_detail.py @@ -12,6 +12,8 @@ class OtherFeesDetails(BaseModel): + """The total gross amount of other fees by type.""" + isa: Optional[int] = FieldInfo(alias="ISA", default=None) diff --git a/src/lithic/types/settlement_report_updated_webhook_event.py b/src/lithic/types/settlement_report_updated_webhook_event.py new file mode 100644 index 00000000..c7435d5a --- /dev/null +++ b/src/lithic/types/settlement_report_updated_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .settlement_report import SettlementReport + +__all__ = ["SettlementReportUpdatedWebhookEvent"] + + +class SettlementReportUpdatedWebhookEvent(SettlementReport): + event_type: Literal["settlement_report.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/shared/document.py b/src/lithic/types/shared/document.py index 84ba5670..04ddcafd 100644 --- a/src/lithic/types/shared/document.py +++ b/src/lithic/types/shared/document.py @@ -10,6 +10,8 @@ class RequiredDocumentUpload(BaseModel): + """Represents a single image of the document to upload.""" + token: str """Globally unique identifier for the document upload.""" @@ -64,6 +66,11 @@ class RequiredDocumentUpload(BaseModel): class Document(BaseModel): + """ + Describes the document and the required document image uploads + required to re-run KYC + """ + token: str """Globally unique identifier for the document.""" diff --git a/src/lithic/types/shared/financial_event.py b/src/lithic/types/shared/financial_event.py index 72188dc7..c2c1bb6a 100644 --- a/src/lithic/types/shared/financial_event.py +++ b/src/lithic/types/shared/financial_event.py @@ -10,6 +10,8 @@ class FinancialEvent(BaseModel): + """Financial Event""" + token: Optional[str] = None """Globally unique identifier.""" @@ -73,6 +75,16 @@ class FinancialEvent(BaseModel): "EXTERNAL_CHECK_RELEASED", "EXTERNAL_CHECK_REVERSED", "EXTERNAL_CHECK_SETTLED", + "EXTERNAL_FEDNOW_CANCELED", + "EXTERNAL_FEDNOW_INITIATED", + "EXTERNAL_FEDNOW_RELEASED", + "EXTERNAL_FEDNOW_REVERSED", + "EXTERNAL_FEDNOW_SETTLED", + "EXTERNAL_RTP_CANCELED", + "EXTERNAL_RTP_INITIATED", + "EXTERNAL_RTP_RELEASED", + "EXTERNAL_RTP_REVERSED", + "EXTERNAL_RTP_SETTLED", "EXTERNAL_TRANSFER_CANCELED", "EXTERNAL_TRANSFER_INITIATED", "EXTERNAL_TRANSFER_RELEASED", @@ -101,5 +113,11 @@ class FinancialEvent(BaseModel): "RETURNED_PAYMENT", "RETURNED_PAYMENT_REVERSAL", "LITHIC_NETWORK_PAYMENT", + "ANNUAL", + "ANNUAL_REVERSAL", + "QUARTERLY", + "QUARTERLY_REVERSAL", + "MONTHLY", + "MONTHLY_REVERSAL", ] ] = None diff --git a/src/lithic/types/statements_created_webhook_event.py b/src/lithic/types/statements_created_webhook_event.py new file mode 100644 index 00000000..35f22f42 --- /dev/null +++ b/src/lithic/types/statements_created_webhook_event.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .financial_accounts.statement import Statement + +__all__ = ["StatementsCreatedWebhookEvent"] + + +class StatementsCreatedWebhookEvent(Statement): + event_type: Literal["statements.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/three_ds/__init__.py b/src/lithic/types/three_ds/__init__.py index 0060e40a..b80df9b3 100644 --- a/src/lithic/types/three_ds/__init__.py +++ b/src/lithic/types/three_ds/__init__.py @@ -4,7 +4,6 @@ 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 ( diff --git a/src/lithic/types/three_ds/authentication_simulate_params.py b/src/lithic/types/three_ds/authentication_simulate_params.py index da9488ef..2393ecae 100644 --- a/src/lithic/types/three_ds/authentication_simulate_params.py +++ b/src/lithic/types/three_ds/authentication_simulate_params.py @@ -25,6 +25,8 @@ class AuthenticationSimulateParams(TypedDict, total=False): class Merchant(TypedDict, total=False): + """Merchant information for the simulated transaction""" + id: Required[str] """Unique identifier to identify the payment card acceptor. @@ -53,6 +55,8 @@ class Merchant(TypedDict, total=False): class Transaction(TypedDict, total=False): + """Transaction details for the simulation""" + amount: Required[int] """Amount (in cents) to authenticate.""" diff --git a/src/lithic/types/three_ds/authentication_retrieve_response.py b/src/lithic/types/three_ds_authentication.py similarity index 93% rename from src/lithic/types/three_ds/authentication_retrieve_response.py rename to src/lithic/types/three_ds_authentication.py index 5de77b1b..3aebc6f8 100644 --- a/src/lithic/types/three_ds/authentication_retrieve_response.py +++ b/src/lithic/types/three_ds_authentication.py @@ -4,10 +4,10 @@ from datetime import datetime from typing_extensions import Literal -from ..._models import BaseModel +from .._models import BaseModel __all__ = [ - "AuthenticationRetrieveResponse", + "ThreeDSAuthentication", "Cardholder", "CardholderBillingAddress", "CardholderShippingAddress", @@ -22,6 +22,8 @@ class CardholderBillingAddress(BaseModel): + """Object containing data on the billing address provided during the transaction.""" + address1: Optional[str] = None """First line of the street address provided by the cardholder.""" @@ -45,6 +47,8 @@ class CardholderBillingAddress(BaseModel): class CardholderShippingAddress(BaseModel): + """Object containing data on the shipping address provided during the transaction.""" + address1: Optional[str] = None """First line of the street address provided by the cardholder.""" @@ -68,6 +72,8 @@ class CardholderShippingAddress(BaseModel): class Cardholder(BaseModel): + """Object containing data about the cardholder provided during the transaction.""" + address_match: Optional[bool] = None """ Indicates whether the shipping address and billing address provided by the @@ -121,6 +127,10 @@ class Cardholder(BaseModel): class MerchantRiskIndicator(BaseModel): + """ + Object containing additional data indicating additional risk factors related to the e-commerce transaction. + """ + delivery_email_address: Optional[str] = None """ In transactions with electronic delivery, email address to which merchandise is @@ -197,6 +207,10 @@ class MerchantRiskIndicator(BaseModel): class Merchant(BaseModel): + """ + Object containing data about the merchant involved in the e-commerce transaction. + """ + risk_indicator: MerchantRiskIndicator """ Object containing additional data indicating additional risk factors related to @@ -234,6 +248,10 @@ class Merchant(BaseModel): class AdditionalData(BaseModel): + """ + Object containing additional data about the 3DS request that is beyond the EMV 3DS standard spec (e.g., specific fields that only certain card networks send but are not required across all 3DS requests). + """ + network_decision: Optional[Literal["LOW_RISK", "NOT_LOW_RISK"]] = None """ Mastercard only: Indicates whether the network would have considered the @@ -249,6 +267,11 @@ class AdditionalData(BaseModel): class App(BaseModel): + """Object containing data about the app used in the e-commerce transaction. + + Present if the channel is 'APP_BASED'. + """ + device: Optional[str] = None """Device model: e.g. "Apple iPhone 16".""" @@ -287,6 +310,11 @@ class App(BaseModel): class Browser(BaseModel): + """Object containing data about the browser used in the e-commerce transaction. + + Present if the channel is 'BROWSER'. + """ + accept_header: Optional[str] = None """ Content of the HTTP accept headers as sent from the cardholder's browser to the @@ -332,6 +360,11 @@ class Browser(BaseModel): class ChallengeMetadata(BaseModel): + """Metadata about the challenge method and delivery. + + Only present when a challenge is triggered. + """ + method_type: Literal["SMS_OTP", "OUT_OF_BAND"] """The type of challenge method used for authentication.""" @@ -369,6 +402,10 @@ class ChallengeMetadata(BaseModel): class Transaction(BaseModel): + """ + Object containing data about the e-commerce transaction for which the merchant is requesting authentication. + """ + amount: float """Amount of the purchase in minor units of currency with all punctuation removed. @@ -416,7 +453,9 @@ class Transaction(BaseModel): """ -class AuthenticationRetrieveResponse(BaseModel): +class ThreeDSAuthentication(BaseModel): + """Represents a 3DS authentication""" + token: str """Globally unique identifier for the 3DS authentication. diff --git a/src/lithic/types/three_ds_authentication_challenge_webhook_event.py b/src/lithic/types/three_ds_authentication_challenge_webhook_event.py new file mode 100644 index 00000000..3c04ea95 --- /dev/null +++ b/src/lithic/types/three_ds_authentication_challenge_webhook_event.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .three_ds_authentication import ThreeDSAuthentication + +__all__ = ["ThreeDSAuthenticationChallengeWebhookEvent", "Challenge"] + + +class Challenge(BaseModel): + """Represents a challenge object for 3DS authentication""" + + challenge_method_type: Literal["OUT_OF_BAND"] + """The type of challenge method issued to the cardholder""" + + expiry_time: datetime + """ISO-8601 time at which the challenge expires""" + + start_time: datetime + """ISO-8601 time at which the challenge has started""" + + app_requestor_url: Optional[str] = None + """Fully qualified app URL of the merchant app. + + This should be used to redirect the cardholder back to the merchant app after + completing an app-based challenge. This URL will only be populated if the 3DS + Requestor App is provided to the 3DS SDK. + """ + + +class ThreeDSAuthenticationChallengeWebhookEvent(BaseModel): + authentication_object: ThreeDSAuthentication + """Represents a 3DS authentication""" + + challenge: Challenge + """Represents a challenge object for 3DS authentication""" + + event_type: Literal["three_ds_authentication.challenge"] diff --git a/src/lithic/types/three_ds_authentication_created_webhook_event.py b/src/lithic/types/three_ds_authentication_created_webhook_event.py new file mode 100644 index 00000000..571ff99c --- /dev/null +++ b/src/lithic/types/three_ds_authentication_created_webhook_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .three_ds_authentication import ThreeDSAuthentication + +__all__ = ["ThreeDSAuthenticationCreatedWebhookEvent"] + + +class ThreeDSAuthenticationCreatedWebhookEvent(ThreeDSAuthentication): + """Represents a 3DS authentication""" + + event_type: Literal["three_ds_authentication.created"] + """The type of event that occurred.""" diff --git a/src/lithic/types/three_ds_authentication_updated_webhook_event.py b/src/lithic/types/three_ds_authentication_updated_webhook_event.py new file mode 100644 index 00000000..edb8a479 --- /dev/null +++ b/src/lithic/types/three_ds_authentication_updated_webhook_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .three_ds_authentication import ThreeDSAuthentication + +__all__ = ["ThreeDSAuthenticationUpdatedWebhookEvent"] + + +class ThreeDSAuthenticationUpdatedWebhookEvent(ThreeDSAuthentication): + """Represents a 3DS authentication""" + + event_type: Literal["three_ds_authentication.updated"] + """The type of event that occurred.""" diff --git a/src/lithic/types/token_info.py b/src/lithic/types/token_info.py new file mode 100644 index 00000000..6a425179 --- /dev/null +++ b/src/lithic/types/token_info.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["TokenInfo"] + + +class TokenInfo(BaseModel): + 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. + """ diff --git a/src/lithic/types/tokenization.py b/src/lithic/types/tokenization.py index 543a3581..60467429 100644 --- a/src/lithic/types/tokenization.py +++ b/src/lithic/types/tokenization.py @@ -5,28 +5,11 @@ from typing_extensions import Literal from .._models import BaseModel +from .tokenization_tfa_reason import TokenizationTfaReason +from .tokenization_rule_result import TokenizationRuleResult +from .tokenization_decline_reason import TokenizationDeclineReason -__all__ = ["Tokenization", "Event", "EventRuleResult"] - - -class EventRuleResult(BaseModel): - auth_rule_token: Optional[str] = None - """The Auth Rule Token associated with the rule. - - If this is set to null, then the result was not associated with a - customer-configured rule. This may happen in cases where a tokenization is - declined or requires TFA 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 result""" - - name: Optional[str] = None - """The name for the rule, if any was configured""" - - result: Literal["APPROVED", "DECLINED", "REQUIRE_TFA", "ERROR"] - """The result associated with this rule""" +__all__ = ["Tokenization", "Event"] class Event(BaseModel): @@ -54,51 +37,13 @@ class Event(BaseModel): ] = None """Enum representing the result of the tokenization event""" - rule_results: Optional[List[EventRuleResult]] = None + rule_results: Optional[List[TokenizationRuleResult]] = None """Results from rules that were evaluated for this tokenization""" - tokenization_decline_reasons: Optional[ - List[ - Literal[ - "ACCOUNT_SCORE_1", - "DEVICE_SCORE_1", - "ALL_WALLET_DECLINE_REASONS_PRESENT", - "WALLET_RECOMMENDED_DECISION_RED", - "CVC_MISMATCH", - "CARD_EXPIRY_MONTH_MISMATCH", - "CARD_EXPIRY_YEAR_MISMATCH", - "CARD_INVALID_STATE", - "CUSTOMER_RED_PATH", - "INVALID_CUSTOMER_RESPONSE", - "NETWORK_FAILURE", - "GENERIC_DECLINE", - "DIGITAL_CARD_ART_REQUIRED", - ] - ] - ] = None + tokenization_decline_reasons: Optional[List[TokenizationDeclineReason]] = None """List of reasons why the tokenization was declined""" - tokenization_tfa_reasons: Optional[ - List[ - Literal[ - "WALLET_RECOMMENDED_TFA", - "SUSPICIOUS_ACTIVITY", - "DEVICE_RECENTLY_LOST", - "TOO_MANY_RECENT_ATTEMPTS", - "TOO_MANY_RECENT_TOKENS", - "TOO_MANY_DIFFERENT_CARDHOLDERS", - "OUTSIDE_HOME_TERRITORY", - "HAS_SUSPENDED_TOKENS", - "HIGH_RISK", - "ACCOUNT_SCORE_LOW", - "DEVICE_SCORE_LOW", - "CARD_STATE_TFA", - "HARDCODED_TFA", - "CUSTOMER_RULE_TFA", - "DEVICE_HOST_CARD_EMULATION", - ] - ] - ] = None + tokenization_tfa_reasons: Optional[List[TokenizationTfaReason]] = None """List of reasons why two-factor authentication was required""" type: Optional[ @@ -126,7 +71,6 @@ class Tokenization(BaseModel): created_at: datetime """Date and time when the tokenization first occurred. UTC time zone.""" - status: Literal["ACTIVE", "DEACTIVATED", "INACTIVE", "PAUSED", "PENDING_2FA", "PENDING_ACTIVATION", "UNKNOWN"] """The status of the tokenization request""" diff --git a/src/lithic/types/tokenization_approval_request_webhook_event.py b/src/lithic/types/tokenization_approval_request_webhook_event.py new file mode 100644 index 00000000..9e6e6f11 --- /dev/null +++ b/src/lithic/types/tokenization_approval_request_webhook_event.py @@ -0,0 +1,83 @@ +# 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 .device import Device +from .._models import BaseModel +from .tokenization_tfa_reason import TokenizationTfaReason +from .wallet_decisioning_info import WalletDecisioningInfo +from .tokenization_rule_result import TokenizationRuleResult +from .tokenization_decline_reason import TokenizationDeclineReason +from .digital_wallet_token_metadata import DigitalWalletTokenMetadata + +__all__ = ["TokenizationApprovalRequestWebhookEvent", "CustomerTokenizationDecision"] + + +class CustomerTokenizationDecision(BaseModel): + """Contains the metadata for the customer tokenization decision.""" + + outcome: Literal[ + "APPROVED", "DECLINED", "ERROR", "INVALID_RESPONSE", "REQUIRE_ADDITIONAL_AUTHENTICATION", "TIMEOUT" + ] + """The outcome of the customer's decision""" + + responder_url: str + """The customer's subscribed URL""" + + latency: Optional[str] = None + """Time in ms it took for the customer's URL to respond""" + + response_code: Optional[str] = None + """The response code that the customer provided""" + + +class TokenizationApprovalRequestWebhookEvent(BaseModel): + account_token: str + """Unique identifier for the user tokenizing a card""" + + card_token: str + """Unique identifier for the card being tokenized""" + + created: datetime + """Indicate when the request was received from Mastercard or Visa""" + + customer_tokenization_decision: Optional[CustomerTokenizationDecision] = None + """Contains the metadata for the customer tokenization decision.""" + + event_type: Literal["tokenization.approval_request"] + """The name of this event""" + + issuer_decision: Literal["APPROVED", "DENIED", "VERIFICATION_REQUIRED"] + """Whether Lithic decisioned on the token, and if so, what the decision was. + + APPROVED/VERIFICATION_REQUIRED/DENIED. + """ + + tokenization_channel: Literal["DIGITAL_WALLET", "MERCHANT"] + """The channel through which the tokenization was made.""" + + tokenization_token: str + """Unique identifier for the digital wallet token attempt""" + + wallet_decisioning_info: WalletDecisioningInfo + + device: Optional[Device] = None + + digital_wallet_token_metadata: Optional[DigitalWalletTokenMetadata] = None + """Contains the metadata for the digital wallet being tokenized.""" + + rule_results: Optional[List[TokenizationRuleResult]] = None + """Results from rules that were evaluated for this tokenization""" + + tokenization_decline_reasons: Optional[List[TokenizationDeclineReason]] = None + """List of reasons why the tokenization was declined""" + + tokenization_source: Optional[ + Literal["ACCOUNT_ON_FILE", "CONTACTLESS_TAP", "MANUAL_PROVISION", "PUSH_PROVISION", "TOKEN", "UNKNOWN"] + ] = None + """The source of the tokenization.""" + + tokenization_tfa_reasons: Optional[List[TokenizationTfaReason]] = None + """List of reasons why two-factor authentication was required""" diff --git a/src/lithic/types/tokenization_decisioning_request_webhook_event.py b/src/lithic/types/tokenization_decisioning_request_webhook_event.py new file mode 100644 index 00000000..957d6266 --- /dev/null +++ b/src/lithic/types/tokenization_decisioning_request_webhook_event.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from .device import Device +from .._models import BaseModel +from .wallet_decisioning_info import WalletDecisioningInfo +from .digital_wallet_token_metadata import DigitalWalletTokenMetadata + +__all__ = ["TokenizationDecisioningRequestWebhookEvent"] + + +class TokenizationDecisioningRequestWebhookEvent(BaseModel): + """ + A webhook for tokenization decisioning sent to the customer's responder endpoint + """ + + account_token: str + """Unique identifier for the user tokenizing a card""" + + card_token: str + """Unique identifier for the card being tokenized""" + + created: datetime + """Indicate when the request was received from Mastercard or Visa""" + + event_type: Literal["digital_wallet.tokenization_approval_request"] + """The name of this event""" + + issuer_decision: Literal["APPROVED", "DENIED", "VERIFICATION_REQUIRED"] + """Whether Lithic decisioned on the token, and if so, what the decision was. + + APPROVED/VERIFICATION_REQUIRED/DENIED. + """ + + tokenization_channel: Literal["DIGITAL_WALLET", "MERCHANT"] + """The channel through which the tokenization was made.""" + + tokenization_token: str + """Unique identifier for the digital wallet token attempt""" + + wallet_decisioning_info: WalletDecisioningInfo + + device: Optional[Device] = None + + digital_wallet_token_metadata: Optional[DigitalWalletTokenMetadata] = None + """Contains the metadata for the digital wallet being tokenized.""" + + tokenization_source: Optional[ + Literal["ACCOUNT_ON_FILE", "CONTACTLESS_TAP", "MANUAL_PROVISION", "PUSH_PROVISION", "TOKEN", "UNKNOWN"] + ] = None + """The source of the tokenization.""" diff --git a/src/lithic/types/tokenization_decline_reason.py b/src/lithic/types/tokenization_decline_reason.py new file mode 100644 index 00000000..29ed2118 --- /dev/null +++ b/src/lithic/types/tokenization_decline_reason.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["TokenizationDeclineReason"] + +TokenizationDeclineReason: TypeAlias = Literal[ + "ACCOUNT_SCORE_1", + "DEVICE_SCORE_1", + "ALL_WALLET_DECLINE_REASONS_PRESENT", + "WALLET_RECOMMENDED_DECISION_RED", + "CVC_MISMATCH", + "CARD_EXPIRY_MONTH_MISMATCH", + "CARD_EXPIRY_YEAR_MISMATCH", + "CARD_INVALID_STATE", + "CUSTOMER_RED_PATH", + "INVALID_CUSTOMER_RESPONSE", + "NETWORK_FAILURE", + "GENERIC_DECLINE", + "DIGITAL_CARD_ART_REQUIRED", +] diff --git a/src/lithic/types/tokenization_result_webhook_event.py b/src/lithic/types/tokenization_result_webhook_event.py new file mode 100644 index 00000000..7be8bf57 --- /dev/null +++ b/src/lithic/types/tokenization_result_webhook_event.py @@ -0,0 +1,71 @@ +# 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 .tokenization_tfa_reason import TokenizationTfaReason +from .tokenization_rule_result import TokenizationRuleResult + +__all__ = ["TokenizationResultWebhookEvent", "TokenizationResultDetails"] + + +class TokenizationResultDetails(BaseModel): + """The result of the tokenization request.""" + + issuer_decision: str + """Lithic's tokenization decision.""" + + tokenization_decline_reasons: List[ + Literal[ + "ACCOUNT_SCORE_1", + "ALL_WALLET_DECLINE_REASONS_PRESENT", + "CARD_EXPIRY_MONTH_MISMATCH", + "CARD_EXPIRY_YEAR_MISMATCH", + "CARD_INVALID_STATE", + "CUSTOMER_RED_PATH", + "CVC_MISMATCH", + "DEVICE_SCORE_1", + "GENERIC_DECLINE", + "INVALID_CUSTOMER_RESPONSE", + "NETWORK_FAILURE", + "WALLET_RECOMMENDED_DECISION_RED", + ] + ] + """List of reasons why the tokenization was declined""" + + customer_decision: Optional[str] = None + """The customer's tokenization decision if applicable.""" + + rule_results: Optional[List[TokenizationRuleResult]] = None + """Results from rules that were evaluated for this tokenization""" + + token_activated_date_time: Optional[datetime] = None + """An RFC 3339 timestamp indicating when the tokenization succeeded.""" + + tokenization_tfa_reasons: Optional[List[TokenizationTfaReason]] = None + """List of reasons why two-factor authentication was required""" + + wallet_decision: Optional[str] = None + """The wallet's recommended decision.""" + + +class TokenizationResultWebhookEvent(BaseModel): + account_token: str + """Account token""" + + card_token: str + """Card token""" + + created: datetime + """Created date""" + + event_type: Literal["tokenization.result"] + """The type of event that occurred.""" + + tokenization_result_details: TokenizationResultDetails + """The result of the tokenization request.""" + + tokenization_token: str + """Tokenization token""" diff --git a/src/lithic/types/tokenization_rule_result.py b/src/lithic/types/tokenization_rule_result.py new file mode 100644 index 00000000..baef6611 --- /dev/null +++ b/src/lithic/types/tokenization_rule_result.py @@ -0,0 +1,28 @@ +# 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__ = ["TokenizationRuleResult"] + + +class TokenizationRuleResult(BaseModel): + auth_rule_token: Optional[str] = None + """The Auth Rule Token associated with the rule. + + If this is set to null, then the result was not associated with a + customer-configured rule. This may happen in cases where a tokenization is + declined or requires TFA 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 result""" + + name: Optional[str] = None + """The name for the rule, if any was configured""" + + result: Literal["APPROVED", "DECLINED", "REQUIRE_TFA", "ERROR"] + """The result associated with this rule""" diff --git a/src/lithic/types/tokenization_simulate_response.py b/src/lithic/types/tokenization_simulate_response.py deleted file mode 100644 index a991e422..00000000 --- a/src/lithic/types/tokenization_simulate_response.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional - -from .._models import BaseModel -from .tokenization import Tokenization - -__all__ = ["TokenizationSimulateResponse"] - - -class TokenizationSimulateResponse(BaseModel): - data: Optional[List[Tokenization]] = None diff --git a/src/lithic/types/tokenization_tfa_reason.py b/src/lithic/types/tokenization_tfa_reason.py new file mode 100644 index 00000000..deba1157 --- /dev/null +++ b/src/lithic/types/tokenization_tfa_reason.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["TokenizationTfaReason"] + +TokenizationTfaReason: TypeAlias = Literal[ + "WALLET_RECOMMENDED_TFA", + "SUSPICIOUS_ACTIVITY", + "DEVICE_RECENTLY_LOST", + "TOO_MANY_RECENT_ATTEMPTS", + "TOO_MANY_RECENT_TOKENS", + "TOO_MANY_DIFFERENT_CARDHOLDERS", + "OUTSIDE_HOME_TERRITORY", + "HAS_SUSPENDED_TOKENS", + "HIGH_RISK", + "ACCOUNT_SCORE_LOW", + "DEVICE_SCORE_LOW", + "CARD_STATE_TFA", + "HARDCODED_TFA", + "CUSTOMER_RULE_TFA", + "DEVICE_HOST_CARD_EMULATION", +] diff --git a/src/lithic/types/tokenization_two_factor_authentication_code_sent_webhook_event.py b/src/lithic/types/tokenization_two_factor_authentication_code_sent_webhook_event.py new file mode 100644 index 00000000..a2109317 --- /dev/null +++ b/src/lithic/types/tokenization_two_factor_authentication_code_sent_webhook_event.py @@ -0,0 +1,43 @@ +# 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__ = ["TokenizationTwoFactorAuthenticationCodeSentWebhookEvent", "ActivationMethod"] + + +class ActivationMethod(BaseModel): + 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" + """ + + value: str + """ + The location to which the authentication code was sent. The format depends on + the ActivationMethod.Type field. If Type is Email, the Value will be the email + address. If the Type is Sms, the Value will be the phone number. + """ + + +class TokenizationTwoFactorAuthenticationCodeSentWebhookEvent(BaseModel): + account_token: str + """Unique identifier for the user tokenizing a card""" + + activation_method: ActivationMethod + + card_token: str + """Unique identifier for the card being tokenized""" + + created: datetime + """Indicate when the request was received from Mastercard or Visa""" + + event_type: Literal["tokenization.two_factor_authentication_code_sent"] + """The type of event that occurred.""" + + tokenization_token: str + """Unique identifier for the tokenization""" diff --git a/src/lithic/types/tokenization_two_factor_authentication_code_webhook_event.py b/src/lithic/types/tokenization_two_factor_authentication_code_webhook_event.py new file mode 100644 index 00000000..8ef559c5 --- /dev/null +++ b/src/lithic/types/tokenization_two_factor_authentication_code_webhook_event.py @@ -0,0 +1,46 @@ +# 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__ = ["TokenizationTwoFactorAuthenticationCodeWebhookEvent", "ActivationMethod"] + + +class ActivationMethod(BaseModel): + 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" + """ + + value: str + """ + The location where the user wants to receive the authentication code. The format + depends on the ActivationMethod.Type field. If Type is Email, the Value will be + the email address. If the Type is Sms, the Value will be the phone number. + """ + + +class TokenizationTwoFactorAuthenticationCodeWebhookEvent(BaseModel): + account_token: str + """Unique identifier for the user tokenizing a card""" + + activation_method: ActivationMethod + + authentication_code: str + """Authentication code to provide to the user tokenizing a card.""" + + card_token: str + """Unique identifier for the card being tokenized""" + + created: datetime + """Indicate when the request was received from Mastercard or Visa""" + + event_type: Literal["tokenization.two_factor_authentication_code"] + """The type of event that occurred.""" + + tokenization_token: str + """Unique identifier for the tokenization""" diff --git a/src/lithic/types/tokenization_update_digital_card_art_response.py b/src/lithic/types/tokenization_update_digital_card_art_response.py deleted file mode 100644 index 9bcb5616..00000000 --- a/src/lithic/types/tokenization_update_digital_card_art_response.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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/tokenization_updated_webhook_event.py b/src/lithic/types/tokenization_updated_webhook_event.py new file mode 100644 index 00000000..9f3f090d --- /dev/null +++ b/src/lithic/types/tokenization_updated_webhook_event.py @@ -0,0 +1,25 @@ +# 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 +from .tokenization import Tokenization + +__all__ = ["TokenizationUpdatedWebhookEvent"] + + +class TokenizationUpdatedWebhookEvent(BaseModel): + account_token: str + """Account token""" + + card_token: str + """Card token""" + + created: datetime + """Created date""" + + event_type: Literal["tokenization.updated"] + """The type of event that occurred.""" + + tokenization: Tokenization diff --git a/src/lithic/types/transaction.py b/src/lithic/types/transaction.py index cbdbea4b..6c0657ab 100644 --- a/src/lithic/types/transaction.py +++ b/src/lithic/types/transaction.py @@ -5,8 +5,10 @@ from typing_extensions import Literal from .._models import BaseModel +from .token_info import TokenInfo from .shared.currency import Currency from .shared.merchant import Merchant +from .cardholder_authentication import CardholderAuthentication __all__ = [ "Transaction", @@ -16,11 +18,9 @@ "AmountsMerchant", "AmountsSettlement", "Avs", - "CardholderAuthentication", "Pos", "PosEntryMode", "PosTerminal", - "TokenInfo", "Event", "EventAmounts", "EventAmountsCardholder", @@ -98,41 +98,6 @@ class Avs(BaseModel): """Cardholder ZIP code""" -class CardholderAuthentication(BaseModel): - authentication_method: Literal["FRICTIONLESS", "CHALLENGE", "NONE"] - """Indicates the method used to authenticate the cardholder.""" - - authentication_result: Literal["ATTEMPTS", "DECLINE", "NONE", "SUCCESS"] - """Indicates the outcome of the 3DS authentication process.""" - - decision_made_by: Literal[ - "CUSTOMER_RULES", "CUSTOMER_ENDPOINT", "LITHIC_DEFAULT", "LITHIC_RULES", "NETWORK", "UNKNOWN" - ] - """Indicates which party made the 3DS authentication decision.""" - - liability_shift: Literal["3DS_AUTHENTICATED", "TOKEN_AUTHENTICATED", "NONE"] - """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. - - `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. - """ - - 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. - """ - - class PosEntryMode(BaseModel): card: Literal["NOT_PRESENT", "PREAUTHORIZED", "PRESENT", "UNKNOWN"] """Card presence indicator""" @@ -241,16 +206,6 @@ class Pos(BaseModel): terminal: PosTerminal -class TokenInfo(BaseModel): - 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 EventAmountsCardholder(BaseModel): amount: int """Amount of the event in the cardholder billing currency.""" @@ -383,6 +338,11 @@ class EventNetworkInfoVisa(BaseModel): class EventNetworkInfo(BaseModel): + """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. + """ + acquirer: Optional[EventNetworkInfoAcquirer] = None amex: Optional[EventNetworkInfoAmex] = None diff --git a/src/lithic/types/transactions/events/enhanced_data.py b/src/lithic/types/transactions/events/enhanced_data.py index 2954359d..21e0c7a3 100644 --- a/src/lithic/types/transactions/events/enhanced_data.py +++ b/src/lithic/types/transactions/events/enhanced_data.py @@ -10,6 +10,8 @@ class CommonLineItem(BaseModel): + """An L2/L3 enhanced commercial data line item.""" + amount: Optional[str] = None """The price of the item purchased in merchant currency.""" diff --git a/src/lithic/types/wallet_decisioning_info.py b/src/lithic/types/wallet_decisioning_info.py new file mode 100644 index 00000000..d75b3992 --- /dev/null +++ b/src/lithic/types/wallet_decisioning_info.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel + +__all__ = ["WalletDecisioningInfo"] + + +class WalletDecisioningInfo(BaseModel): + account_score: Optional[str] = None + """Score given to the account by the Wallet Provider""" + + device_score: Optional[str] = None + """Score given to the device by the Wallet Provider""" + + recommended_decision: Optional[str] = None + """The decision recommended by the Wallet Provider""" + + recommendation_reasons: Optional[List[str]] = None + """ + Reasons provided to the Wallet Provider on how the recommended decision was + reached + """ diff --git a/tests/api_resources/auth_rules/test_v2.py b/tests/api_resources/auth_rules/test_v2.py index 20b4e519..3167da4d 100644 --- a/tests/api_resources/auth_rules/test_v2.py +++ b/tests/api_resources/auth_rules/test_v2.py @@ -12,12 +12,7 @@ from lithic._utils import parse_date from lithic.pagination import SyncCursorPage, AsyncCursorPage from lithic.types.auth_rules import ( - V2ListResponse, - V2DraftResponse, - V2CreateResponse, - V2UpdateResponse, - V2PromoteResponse, - V2RetrieveResponse, + AuthRule, V2RetrieveReportResponse, V2RetrieveFeaturesResponse, ) @@ -42,7 +37,7 @@ def test_method_create_overload_1(self, client: Lithic) -> None: }, type="CONDITIONAL_BLOCK", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_method_create_with_all_params_overload_1(self, client: Lithic) -> None: @@ -62,7 +57,7 @@ def test_method_create_with_all_params_overload_1(self, client: Lithic) -> None: event_stream="AUTHORIZATION", name="name", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_create_overload_1(self, client: Lithic) -> None: @@ -82,7 +77,7 @@ def test_raw_response_create_overload_1(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_create_overload_1(self, client: Lithic) -> None: @@ -102,7 +97,7 @@ def test_streaming_response_create_overload_1(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -121,7 +116,7 @@ def test_method_create_overload_2(self, client: Lithic) -> None: }, type="CONDITIONAL_BLOCK", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_method_create_with_all_params_overload_2(self, client: Lithic) -> None: @@ -140,7 +135,7 @@ def test_method_create_with_all_params_overload_2(self, client: Lithic) -> None: event_stream="AUTHORIZATION", name="name", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_create_overload_2(self, client: Lithic) -> None: @@ -161,7 +156,7 @@ def test_raw_response_create_overload_2(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_create_overload_2(self, client: Lithic) -> None: @@ -182,7 +177,7 @@ def test_streaming_response_create_overload_2(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -201,7 +196,7 @@ def test_method_create_overload_3(self, client: Lithic) -> None: program_level=True, type="CONDITIONAL_BLOCK", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_method_create_with_all_params_overload_3(self, client: Lithic) -> None: @@ -221,7 +216,7 @@ def test_method_create_with_all_params_overload_3(self, client: Lithic) -> None: excluded_card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], name="name", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_create_overload_3(self, client: Lithic) -> None: @@ -242,7 +237,7 @@ def test_raw_response_create_overload_3(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_create_overload_3(self, client: Lithic) -> None: @@ -263,7 +258,7 @@ def test_streaming_response_create_overload_3(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -272,7 +267,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: @@ -283,7 +278,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: @@ -294,7 +289,7 @@ def test_streaming_response_retrieve(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2RetrieveResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -310,7 +305,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_method_update_with_all_params_overload_1(self, client: Lithic) -> None: @@ -321,7 +316,7 @@ def test_method_update_with_all_params_overload_1(self, client: Lithic) -> None: name="name", state="INACTIVE", ) - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_update_overload_1(self, client: Lithic) -> None: @@ -332,7 +327,7 @@ def test_raw_response_update_overload_1(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_update_overload_1(self, client: Lithic) -> None: @@ -343,7 +338,7 @@ def test_streaming_response_update_overload_1(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -359,7 +354,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_method_update_with_all_params_overload_2(self, client: Lithic) -> None: @@ -369,7 +364,7 @@ def test_method_update_with_all_params_overload_2(self, client: Lithic) -> None: name="name", state="INACTIVE", ) - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_update_overload_2(self, client: Lithic) -> None: @@ -380,7 +375,7 @@ def test_raw_response_update_overload_2(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_update_overload_2(self, client: Lithic) -> None: @@ -391,7 +386,7 @@ def test_streaming_response_update_overload_2(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -407,7 +402,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_method_update_with_all_params_overload_3(self, client: Lithic) -> None: @@ -418,7 +413,7 @@ def test_method_update_with_all_params_overload_3(self, client: Lithic) -> None: program_level=True, state="INACTIVE", ) - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_update_overload_3(self, client: Lithic) -> None: @@ -429,7 +424,7 @@ def test_raw_response_update_overload_3(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_update_overload_3(self, client: Lithic) -> None: @@ -440,7 +435,7 @@ def test_streaming_response_update_overload_3(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -454,7 +449,7 @@ def test_path_params_update_overload_3(self, client: Lithic) -> None: @parametrize def test_method_list(self, client: Lithic) -> None: v2 = client.auth_rules.v2.list() - assert_matches_type(SyncCursorPage[V2ListResponse], v2, path=["response"]) + assert_matches_type(SyncCursorPage[AuthRule], v2, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: @@ -464,11 +459,12 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ending_before="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", event_stream="AUTHORIZATION", + event_streams=["AUTHORIZATION"], page_size=1, scope="PROGRAM", starting_after="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncCursorPage[V2ListResponse], v2, path=["response"]) + assert_matches_type(SyncCursorPage[AuthRule], v2, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: @@ -477,7 +473,7 @@ def test_raw_response_list(self, client: Lithic) -> None: 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"]) + assert_matches_type(SyncCursorPage[AuthRule], v2, path=["response"]) @parametrize def test_streaming_response_list(self, client: Lithic) -> None: @@ -486,7 +482,7 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(SyncCursorPage[V2ListResponse], v2, path=["response"]) + assert_matches_type(SyncCursorPage[AuthRule], v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -533,7 +529,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_method_draft_with_all_params(self, client: Lithic) -> None: @@ -549,7 +545,7 @@ def test_method_draft_with_all_params(self, client: Lithic) -> None: ] }, ) - assert_matches_type(V2DraftResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_draft(self, client: Lithic) -> None: @@ -560,7 +556,7 @@ def test_raw_response_draft(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_draft(self, client: Lithic) -> None: @@ -571,7 +567,7 @@ def test_streaming_response_draft(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2DraftResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -587,7 +583,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_raw_response_promote(self, client: Lithic) -> None: @@ -598,7 +594,7 @@ def test_raw_response_promote(self, client: Lithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize def test_streaming_response_promote(self, client: Lithic) -> None: @@ -609,7 +605,7 @@ def test_streaming_response_promote(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = response.parse() - assert_matches_type(V2PromoteResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -733,7 +729,7 @@ async def test_method_create_overload_1(self, async_client: AsyncLithic) -> None }, type="CONDITIONAL_BLOCK", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncLithic) -> None: @@ -753,7 +749,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn event_stream="AUTHORIZATION", name="name", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) -> None: @@ -773,7 +769,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncLithic) - 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncLithic) -> None: @@ -793,7 +789,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncLit assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -812,7 +808,7 @@ async def test_method_create_overload_2(self, async_client: AsyncLithic) -> None }, type="CONDITIONAL_BLOCK", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncLithic) -> None: @@ -831,7 +827,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn event_stream="AUTHORIZATION", name="name", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncLithic) -> None: @@ -852,7 +848,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncLithic) - 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncLithic) -> None: @@ -873,7 +869,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncLit assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -892,7 +888,7 @@ async def test_method_create_overload_3(self, async_client: AsyncLithic) -> None program_level=True, type="CONDITIONAL_BLOCK", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_method_create_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: @@ -912,7 +908,7 @@ async def test_method_create_with_all_params_overload_3(self, async_client: Asyn excluded_card_tokens=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"], name="name", ) - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_create_overload_3(self, async_client: AsyncLithic) -> None: @@ -933,7 +929,7 @@ async def test_raw_response_create_overload_3(self, async_client: AsyncLithic) - 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_create_overload_3(self, async_client: AsyncLithic) -> None: @@ -954,7 +950,7 @@ async def test_streaming_response_create_overload_3(self, async_client: AsyncLit assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2CreateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -963,7 +959,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @@ -974,7 +970,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: @@ -985,7 +981,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2RetrieveResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1001,7 +997,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_method_update_with_all_params_overload_1(self, async_client: AsyncLithic) -> None: @@ -1012,7 +1008,7 @@ async def test_method_update_with_all_params_overload_1(self, async_client: Asyn name="name", state="INACTIVE", ) - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_update_overload_1(self, async_client: AsyncLithic) -> None: @@ -1023,7 +1019,7 @@ async def test_raw_response_update_overload_1(self, async_client: AsyncLithic) - 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_update_overload_1(self, async_client: AsyncLithic) -> None: @@ -1034,7 +1030,7 @@ async def test_streaming_response_update_overload_1(self, async_client: AsyncLit assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1050,7 +1046,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_method_update_with_all_params_overload_2(self, async_client: AsyncLithic) -> None: @@ -1060,7 +1056,7 @@ async def test_method_update_with_all_params_overload_2(self, async_client: Asyn name="name", state="INACTIVE", ) - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_update_overload_2(self, async_client: AsyncLithic) -> None: @@ -1071,7 +1067,7 @@ async def test_raw_response_update_overload_2(self, async_client: AsyncLithic) - 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_update_overload_2(self, async_client: AsyncLithic) -> None: @@ -1082,7 +1078,7 @@ async def test_streaming_response_update_overload_2(self, async_client: AsyncLit assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1098,7 +1094,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_method_update_with_all_params_overload_3(self, async_client: AsyncLithic) -> None: @@ -1109,7 +1105,7 @@ async def test_method_update_with_all_params_overload_3(self, async_client: Asyn program_level=True, state="INACTIVE", ) - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_update_overload_3(self, async_client: AsyncLithic) -> None: @@ -1120,7 +1116,7 @@ async def test_raw_response_update_overload_3(self, async_client: AsyncLithic) - 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_update_overload_3(self, async_client: AsyncLithic) -> None: @@ -1131,7 +1127,7 @@ async def test_streaming_response_update_overload_3(self, async_client: AsyncLit assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2UpdateResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1145,7 +1141,7 @@ async def test_path_params_update_overload_3(self, async_client: AsyncLithic) -> @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"]) + assert_matches_type(AsyncCursorPage[AuthRule], v2, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: @@ -1155,11 +1151,12 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ending_before="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", event_stream="AUTHORIZATION", + event_streams=["AUTHORIZATION"], page_size=1, scope="PROGRAM", starting_after="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncCursorPage[V2ListResponse], v2, path=["response"]) + assert_matches_type(AsyncCursorPage[AuthRule], v2, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: @@ -1168,7 +1165,7 @@ async def test_raw_response_list(self, async_client: AsyncLithic) -> None: 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"]) + assert_matches_type(AsyncCursorPage[AuthRule], v2, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: @@ -1177,7 +1174,7 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(AsyncCursorPage[V2ListResponse], v2, path=["response"]) + assert_matches_type(AsyncCursorPage[AuthRule], v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1224,7 +1221,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_method_draft_with_all_params(self, async_client: AsyncLithic) -> None: @@ -1240,7 +1237,7 @@ async def test_method_draft_with_all_params(self, async_client: AsyncLithic) -> ] }, ) - assert_matches_type(V2DraftResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_draft(self, async_client: AsyncLithic) -> None: @@ -1251,7 +1248,7 @@ async def test_raw_response_draft(self, async_client: AsyncLithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_draft(self, async_client: AsyncLithic) -> None: @@ -1262,7 +1259,7 @@ async def test_streaming_response_draft(self, async_client: AsyncLithic) -> None assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2DraftResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1278,7 +1275,7 @@ 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_raw_response_promote(self, async_client: AsyncLithic) -> None: @@ -1289,7 +1286,7 @@ async def test_raw_response_promote(self, async_client: AsyncLithic) -> None: 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"]) + assert_matches_type(AuthRule, v2, path=["response"]) @parametrize async def test_streaming_response_promote(self, async_client: AsyncLithic) -> None: @@ -1300,7 +1297,7 @@ async def test_streaming_response_promote(self, async_client: AsyncLithic) -> No assert response.http_request.headers.get("X-Stainless-Lang") == "python" v2 = await response.parse() - assert_matches_type(V2PromoteResponse, v2, path=["response"]) + assert_matches_type(AuthRule, v2, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/cards/test_balances.py b/tests/api_resources/cards/test_balances.py index 63f50490..87a8896f 100644 --- a/tests/api_resources/cards/test_balances.py +++ b/tests/api_resources/cards/test_balances.py @@ -9,9 +9,9 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type +from lithic.types import FinancialAccountBalance 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") @@ -24,7 +24,7 @@ def test_method_list(self, client: Lithic) -> None: balance = client.cards.balances.list( card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(SyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: @@ -33,7 +33,7 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), last_transaction_event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(SyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: @@ -44,7 +44,7 @@ def test_raw_response_list(self, client: Lithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(SyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize def test_streaming_response_list(self, client: Lithic) -> None: @@ -55,7 +55,7 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(SyncSinglePage[FinancialAccountBalance], balance, path=["response"]) assert cast(Any, response.is_closed) is True @@ -77,7 +77,7 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: balance = await async_client.cards.balances.list( card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: @@ -86,7 +86,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), last_transaction_event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: @@ -97,7 +97,7 @@ async def test_raw_response_list(self, async_client: AsyncLithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: @@ -108,7 +108,7 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = await response.parse() - assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[FinancialAccountBalance], balance, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/financial_accounts/test_balances.py b/tests/api_resources/financial_accounts/test_balances.py index db71f8f9..2d2680e7 100644 --- a/tests/api_resources/financial_accounts/test_balances.py +++ b/tests/api_resources/financial_accounts/test_balances.py @@ -9,9 +9,9 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type +from lithic.types import FinancialAccountBalance 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") @@ -24,7 +24,7 @@ def test_method_list(self, client: Lithic) -> None: balance = client.financial_accounts.balances.list( financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(SyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: @@ -33,7 +33,7 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), last_transaction_event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(SyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: @@ -44,7 +44,7 @@ def test_raw_response_list(self, client: Lithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(SyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize def test_streaming_response_list(self, client: Lithic) -> None: @@ -55,7 +55,7 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(SyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(SyncSinglePage[FinancialAccountBalance], balance, path=["response"]) assert cast(Any, response.is_closed) is True @@ -79,7 +79,7 @@ async def test_method_list(self, async_client: AsyncLithic) -> None: balance = await async_client.financial_accounts.balances.list( financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: @@ -88,7 +88,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N balance_date=parse_datetime("2019-12-27T18:11:19.117Z"), last_transaction_event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: @@ -99,7 +99,7 @@ async def test_raw_response_list(self, async_client: AsyncLithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = response.parse() - assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[FinancialAccountBalance], balance, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: @@ -110,7 +110,7 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" balance = await response.parse() - assert_matches_type(AsyncSinglePage[BalanceListResponse], balance, path=["response"]) + assert_matches_type(AsyncSinglePage[FinancialAccountBalance], balance, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/reports/settlement/test_network_totals.py b/tests/api_resources/reports/settlement/test_network_totals.py index acd5a25f..256915ae 100644 --- a/tests/api_resources/reports/settlement/test_network_totals.py +++ b/tests/api_resources/reports/settlement/test_network_totals.py @@ -9,12 +9,9 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type +from lithic.types import NetworkTotal 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") @@ -27,7 +24,7 @@ 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"]) + assert_matches_type(NetworkTotal, network_total, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: @@ -38,7 +35,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: 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"]) + assert_matches_type(NetworkTotal, network_total, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: @@ -49,7 +46,7 @@ def test_streaming_response_retrieve(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" network_total = response.parse() - assert_matches_type(NetworkTotalRetrieveResponse, network_total, path=["response"]) + assert_matches_type(NetworkTotal, network_total, path=["response"]) assert cast(Any, response.is_closed) is True @@ -63,7 +60,7 @@ def test_path_params_retrieve(self, client: Lithic) -> None: @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"]) + assert_matches_type(SyncCursorPage[NetworkTotal], network_total, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: @@ -80,7 +77,7 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: settlement_institution_id="settlement_institution_id", starting_after="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(SyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + assert_matches_type(SyncCursorPage[NetworkTotal], network_total, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: @@ -89,7 +86,7 @@ def test_raw_response_list(self, client: Lithic) -> None: 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"]) + assert_matches_type(SyncCursorPage[NetworkTotal], network_total, path=["response"]) @parametrize def test_streaming_response_list(self, client: Lithic) -> None: @@ -98,7 +95,7 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" network_total = response.parse() - assert_matches_type(SyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + assert_matches_type(SyncCursorPage[NetworkTotal], network_total, path=["response"]) assert cast(Any, response.is_closed) is True @@ -113,7 +110,7 @@ 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"]) + assert_matches_type(NetworkTotal, network_total, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @@ -124,7 +121,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: 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"]) + assert_matches_type(NetworkTotal, network_total, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: @@ -135,7 +132,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" network_total = await response.parse() - assert_matches_type(NetworkTotalRetrieveResponse, network_total, path=["response"]) + assert_matches_type(NetworkTotal, network_total, path=["response"]) assert cast(Any, response.is_closed) is True @@ -149,7 +146,7 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: @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"]) + assert_matches_type(AsyncCursorPage[NetworkTotal], network_total, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: @@ -166,7 +163,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N settlement_institution_id="settlement_institution_id", starting_after="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AsyncCursorPage[NetworkTotalListResponse], network_total, path=["response"]) + assert_matches_type(AsyncCursorPage[NetworkTotal], network_total, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: @@ -175,7 +172,7 @@ async def test_raw_response_list(self, async_client: AsyncLithic) -> None: 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"]) + assert_matches_type(AsyncCursorPage[NetworkTotal], network_total, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: @@ -184,6 +181,6 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: 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_matches_type(AsyncCursorPage[NetworkTotal], network_total, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_account_holders.py b/tests/api_resources/test_account_holders.py index ef040ea1..3c723a7d 100644 --- a/tests/api_resources/test_account_holders.py +++ b/tests/api_resources/test_account_holders.py @@ -1068,7 +1068,7 @@ def test_method_upload_document(self, client: Lithic) -> None: account_holder = client.account_holders.upload_document( account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", document_type="EIN_LETTER", - entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + entity_token="83cf25ae-c14f-4d10-9fa2-0119f36c7286", ) assert_matches_type(Document, account_holder, path=["response"]) @@ -1077,7 +1077,7 @@ def test_raw_response_upload_document(self, client: Lithic) -> None: response = client.account_holders.with_raw_response.upload_document( account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", document_type="EIN_LETTER", - entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + entity_token="83cf25ae-c14f-4d10-9fa2-0119f36c7286", ) assert response.is_closed is True @@ -1090,7 +1090,7 @@ def test_streaming_response_upload_document(self, client: Lithic) -> None: with client.account_holders.with_streaming_response.upload_document( account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", document_type="EIN_LETTER", - entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + entity_token="83cf25ae-c14f-4d10-9fa2-0119f36c7286", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1106,7 +1106,7 @@ def test_path_params_upload_document(self, client: Lithic) -> None: client.account_holders.with_raw_response.upload_document( account_holder_token="", document_type="EIN_LETTER", - entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + entity_token="83cf25ae-c14f-4d10-9fa2-0119f36c7286", ) @@ -2157,7 +2157,7 @@ async def test_method_upload_document(self, async_client: AsyncLithic) -> None: account_holder = await async_client.account_holders.upload_document( account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", document_type="EIN_LETTER", - entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + entity_token="83cf25ae-c14f-4d10-9fa2-0119f36c7286", ) assert_matches_type(Document, account_holder, path=["response"]) @@ -2166,7 +2166,7 @@ async def test_raw_response_upload_document(self, async_client: AsyncLithic) -> response = await async_client.account_holders.with_raw_response.upload_document( account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", document_type="EIN_LETTER", - entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + entity_token="83cf25ae-c14f-4d10-9fa2-0119f36c7286", ) assert response.is_closed is True @@ -2179,7 +2179,7 @@ async def test_streaming_response_upload_document(self, async_client: AsyncLithi async with async_client.account_holders.with_streaming_response.upload_document( account_holder_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", document_type="EIN_LETTER", - entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + entity_token="83cf25ae-c14f-4d10-9fa2-0119f36c7286", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -2195,5 +2195,5 @@ async def test_path_params_upload_document(self, async_client: AsyncLithic) -> N await async_client.account_holders.with_raw_response.upload_document( account_holder_token="", document_type="EIN_LETTER", - entity_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + entity_token="83cf25ae-c14f-4d10-9fa2-0119f36c7286", ) diff --git a/tests/api_resources/test_card_bulk_orders.py b/tests/api_resources/test_card_bulk_orders.py new file mode 100644 index 00000000..a8ec599d --- /dev/null +++ b/tests/api_resources/test_card_bulk_orders.py @@ -0,0 +1,382 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types import ( + CardBulkOrder, +) +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 TestCardBulkOrders: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + card_bulk_order = client.card_bulk_orders.create( + customer_product_id="custom-card-design-123", + shipping_address={ + "address1": "123 Main Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Johnny", + "last_name": "Appleseed", + "postal_code": "10001", + "state": "NY", + }, + shipping_method="BULK_EXPEDITED", + ) + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Lithic) -> None: + response = client.card_bulk_orders.with_raw_response.create( + customer_product_id="custom-card-design-123", + shipping_address={ + "address1": "123 Main Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Johnny", + "last_name": "Appleseed", + "postal_code": "10001", + "state": "NY", + }, + shipping_method="BULK_EXPEDITED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Lithic) -> None: + with client.card_bulk_orders.with_streaming_response.create( + customer_product_id="custom-card-design-123", + shipping_address={ + "address1": "123 Main Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Johnny", + "last_name": "Appleseed", + "postal_code": "10001", + "state": "NY", + }, + shipping_method="BULK_EXPEDITED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + card_bulk_order = client.card_bulk_orders.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.card_bulk_orders.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" + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.card_bulk_orders.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" + + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, 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 `bulk_order_token` but received ''"): + client.card_bulk_orders.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: Lithic) -> None: + card_bulk_order = client.card_bulk_orders.update( + bulk_order_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="LOCKED", + ) + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Lithic) -> None: + response = client.card_bulk_orders.with_raw_response.update( + bulk_order_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="LOCKED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Lithic) -> None: + with client.card_bulk_orders.with_streaming_response.update( + bulk_order_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="LOCKED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, 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 `bulk_order_token` but received ''"): + client.card_bulk_orders.with_raw_response.update( + bulk_order_token="", + status="LOCKED", + ) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + card_bulk_order = client.card_bulk_orders.list() + assert_matches_type(SyncCursorPage[CardBulkOrder], card_bulk_order, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + card_bulk_order = client.card_bulk_orders.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + page_size=1, + starting_after="starting_after", + ) + assert_matches_type(SyncCursorPage[CardBulkOrder], card_bulk_order, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.card_bulk_orders.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + card_bulk_order = response.parse() + assert_matches_type(SyncCursorPage[CardBulkOrder], card_bulk_order, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.card_bulk_orders.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + card_bulk_order = response.parse() + assert_matches_type(SyncCursorPage[CardBulkOrder], card_bulk_order, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncCardBulkOrders: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncLithic) -> None: + card_bulk_order = await async_client.card_bulk_orders.create( + customer_product_id="custom-card-design-123", + shipping_address={ + "address1": "123 Main Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Johnny", + "last_name": "Appleseed", + "postal_code": "10001", + "state": "NY", + }, + shipping_method="BULK_EXPEDITED", + ) + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncLithic) -> None: + response = await async_client.card_bulk_orders.with_raw_response.create( + customer_product_id="custom-card-design-123", + shipping_address={ + "address1": "123 Main Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Johnny", + "last_name": "Appleseed", + "postal_code": "10001", + "state": "NY", + }, + shipping_method="BULK_EXPEDITED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: + async with async_client.card_bulk_orders.with_streaming_response.create( + customer_product_id="custom-card-design-123", + shipping_address={ + "address1": "123 Main Street", + "city": "NEW YORK", + "country": "USA", + "first_name": "Johnny", + "last_name": "Appleseed", + "postal_code": "10001", + "state": "NY", + }, + shipping_method="BULK_EXPEDITED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + card_bulk_order = await response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + card_bulk_order = await async_client.card_bulk_orders.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.card_bulk_orders.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" + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.card_bulk_orders.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" + + card_bulk_order = await response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, 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 `bulk_order_token` but received ''"): + await async_client.card_bulk_orders.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncLithic) -> None: + card_bulk_order = await async_client.card_bulk_orders.update( + bulk_order_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="LOCKED", + ) + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncLithic) -> None: + response = await async_client.card_bulk_orders.with_raw_response.update( + bulk_order_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="LOCKED", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + card_bulk_order = response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: + async with async_client.card_bulk_orders.with_streaming_response.update( + bulk_order_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + status="LOCKED", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + card_bulk_order = await response.parse() + assert_matches_type(CardBulkOrder, card_bulk_order, 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 `bulk_order_token` but received ''"): + await async_client.card_bulk_orders.with_raw_response.update( + bulk_order_token="", + status="LOCKED", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncLithic) -> None: + card_bulk_order = await async_client.card_bulk_orders.list() + assert_matches_type(AsyncCursorPage[CardBulkOrder], card_bulk_order, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: + card_bulk_order = await async_client.card_bulk_orders.list( + begin=parse_datetime("2019-12-27T18:11:19.117Z"), + end=parse_datetime("2019-12-27T18:11:19.117Z"), + ending_before="ending_before", + page_size=1, + starting_after="starting_after", + ) + assert_matches_type(AsyncCursorPage[CardBulkOrder], card_bulk_order, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncLithic) -> None: + response = await async_client.card_bulk_orders.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + card_bulk_order = response.parse() + assert_matches_type(AsyncCursorPage[CardBulkOrder], card_bulk_order, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: + async with async_client.card_bulk_orders.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + card_bulk_order = await response.parse() + assert_matches_type(AsyncCursorPage[CardBulkOrder], card_bulk_order, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_cards.py b/tests/api_resources/test_cards.py index de81e51c..e7f30b11 100644 --- a/tests/api_resources/test_cards.py +++ b/tests/api_resources/test_cards.py @@ -37,17 +37,18 @@ def test_method_create_with_all_params(self, client: Lithic) -> None: card = client.cards.create( type="VIRTUAL", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - card_program_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + bulk_order_token="5e9483eb-8103-4e16-9794-2106111b2eca", + card_program_token="5e9483eb-8103-4e16-9794-2106111b2eca", carrier={"qr_code_url": "qr_code_url"}, - digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + digital_card_art_token="5e9483eb-8103-4e16-9794-2106111b2eca", exp_month="06", exp_year="2027", memo="New Card", pin="pin", product_id="1", - replacement_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + replacement_account_token="5e9483eb-8103-4e16-9794-2106111b2eca", replacement_comment="replacement_comment", - replacement_for="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + replacement_for="5e9483eb-8103-4e16-9794-2106111b2eca", replacement_substatus="LOST", shipping_address={ "address1": "5 Broad Street", @@ -143,9 +144,9 @@ def test_method_update_with_all_params(self, client: Lithic) -> None: card = client.cards.update( card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", comment="comment", - digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + digital_card_art_token="00000000-0000-0000-1000-000000000000", memo="Updated Name", - network_program_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + network_program_token="00000000-0000-0000-1000-000000000000", pin="pin", pin_status="OK", spend_limit=100, @@ -659,7 +660,10 @@ def test_method_web_provision(self, client: Lithic) -> None: def test_method_web_provision_with_all_params(self, client: Lithic) -> None: card = client.cards.web_provision( card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + client_device_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + client_wallet_account_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", digital_wallet="APPLE_PAY", + server_session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(CardWebProvisionResponse, card, path=["response"]) @@ -712,17 +716,18 @@ async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> card = await async_client.cards.create( type="VIRTUAL", account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", - card_program_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + bulk_order_token="5e9483eb-8103-4e16-9794-2106111b2eca", + card_program_token="5e9483eb-8103-4e16-9794-2106111b2eca", carrier={"qr_code_url": "qr_code_url"}, - digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + digital_card_art_token="5e9483eb-8103-4e16-9794-2106111b2eca", exp_month="06", exp_year="2027", memo="New Card", pin="pin", product_id="1", - replacement_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + replacement_account_token="5e9483eb-8103-4e16-9794-2106111b2eca", replacement_comment="replacement_comment", - replacement_for="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + replacement_for="5e9483eb-8103-4e16-9794-2106111b2eca", replacement_substatus="LOST", shipping_address={ "address1": "5 Broad Street", @@ -818,9 +823,9 @@ async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> card = await async_client.cards.update( card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", comment="comment", - digital_card_art_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + digital_card_art_token="00000000-0000-0000-1000-000000000000", memo="Updated Name", - network_program_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + network_program_token="00000000-0000-0000-1000-000000000000", pin="pin", pin_status="OK", spend_limit=100, @@ -1334,7 +1339,10 @@ async def test_method_web_provision(self, async_client: AsyncLithic) -> None: async def test_method_web_provision_with_all_params(self, async_client: AsyncLithic) -> None: card = await async_client.cards.web_provision( card_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + client_device_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + client_wallet_account_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", digital_wallet="APPLE_PAY", + server_session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) assert_matches_type(CardWebProvisionResponse, card, path=["response"]) diff --git a/tests/api_resources/test_external_bank_accounts.py b/tests/api_resources/test_external_bank_accounts.py index 809bafe2..9418e6bc 100644 --- a/tests/api_resources/test_external_bank_accounts.py +++ b/tests/api_resources/test_external_bank_accounts.py @@ -10,12 +10,11 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type from lithic.types import ( + ExternalBankAccount, ExternalBankAccountListResponse, ExternalBankAccountCreateResponse, ExternalBankAccountUpdateResponse, - ExternalBankAccountUnpauseResponse, ExternalBankAccountRetrieveResponse, - ExternalBankAccountRetryPrenoteResponse, ExternalBankAccountRetryMicroDepositsResponse, ) from lithic._utils import parse_date @@ -470,7 +469,7 @@ 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"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize def test_method_retry_prenote_with_all_params(self, client: Lithic) -> None: @@ -478,7 +477,7 @@ def test_method_retry_prenote_with_all_params(self, client: Lithic) -> None: 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"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize def test_raw_response_retry_prenote(self, client: Lithic) -> None: @@ -489,7 +488,7 @@ def test_raw_response_retry_prenote(self, client: Lithic) -> None: 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"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize def test_streaming_response_retry_prenote(self, client: Lithic) -> None: @@ -500,7 +499,7 @@ def test_streaming_response_retry_prenote(self, client: Lithic) -> None: 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_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) assert cast(Any, response.is_closed) is True @@ -518,7 +517,7 @@ def test_method_unpause(self, client: Lithic) -> None: external_bank_account = client.external_bank_accounts.unpause( "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(ExternalBankAccountUnpauseResponse, external_bank_account, path=["response"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize def test_raw_response_unpause(self, client: Lithic) -> None: @@ -529,7 +528,7 @@ def test_raw_response_unpause(self, client: Lithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" external_bank_account = response.parse() - assert_matches_type(ExternalBankAccountUnpauseResponse, external_bank_account, path=["response"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize def test_streaming_response_unpause(self, client: Lithic) -> None: @@ -540,7 +539,7 @@ def test_streaming_response_unpause(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" external_bank_account = response.parse() - assert_matches_type(ExternalBankAccountUnpauseResponse, external_bank_account, path=["response"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1002,7 +1001,7 @@ 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"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize async def test_method_retry_prenote_with_all_params(self, async_client: AsyncLithic) -> None: @@ -1010,7 +1009,7 @@ async def test_method_retry_prenote_with_all_params(self, async_client: AsyncLit 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"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize async def test_raw_response_retry_prenote(self, async_client: AsyncLithic) -> None: @@ -1021,7 +1020,7 @@ async def test_raw_response_retry_prenote(self, async_client: AsyncLithic) -> No 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"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize async def test_streaming_response_retry_prenote(self, async_client: AsyncLithic) -> None: @@ -1032,7 +1031,7 @@ async def test_streaming_response_retry_prenote(self, async_client: AsyncLithic) 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_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1050,7 +1049,7 @@ async def test_method_unpause(self, async_client: AsyncLithic) -> None: external_bank_account = await async_client.external_bank_accounts.unpause( "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(ExternalBankAccountUnpauseResponse, external_bank_account, path=["response"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize async def test_raw_response_unpause(self, async_client: AsyncLithic) -> None: @@ -1061,7 +1060,7 @@ async def test_raw_response_unpause(self, async_client: AsyncLithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" external_bank_account = response.parse() - assert_matches_type(ExternalBankAccountUnpauseResponse, external_bank_account, path=["response"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) @parametrize async def test_streaming_response_unpause(self, async_client: AsyncLithic) -> None: @@ -1072,7 +1071,7 @@ async def test_streaming_response_unpause(self, async_client: AsyncLithic) -> No assert response.http_request.headers.get("X-Stainless-Lang") == "python" external_bank_account = await response.parse() - assert_matches_type(ExternalBankAccountUnpauseResponse, external_bank_account, path=["response"]) + assert_matches_type(ExternalBankAccount, external_bank_account, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_funding_events.py b/tests/api_resources/test_funding_events.py index ee78113e..fad60692 100644 --- a/tests/api_resources/test_funding_events.py +++ b/tests/api_resources/test_funding_events.py @@ -9,11 +9,7 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type -from lithic.types import ( - FundingEventListResponse, - FundingEventRetrieveResponse, - FundingEventRetrieveDetailsResponse, -) +from lithic.types import FundingEvent, FundingEventRetrieveDetailsResponse from lithic.pagination import SyncCursorPage, AsyncCursorPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -27,7 +23,7 @@ def test_method_retrieve(self, client: Lithic) -> None: funding_event = client.funding_events.retrieve( "funding_event_token", ) - assert_matches_type(FundingEventRetrieveResponse, funding_event, path=["response"]) + assert_matches_type(FundingEvent, funding_event, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: @@ -38,7 +34,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" funding_event = response.parse() - assert_matches_type(FundingEventRetrieveResponse, funding_event, path=["response"]) + assert_matches_type(FundingEvent, funding_event, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: @@ -49,7 +45,7 @@ def test_streaming_response_retrieve(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" funding_event = response.parse() - assert_matches_type(FundingEventRetrieveResponse, funding_event, path=["response"]) + assert_matches_type(FundingEvent, funding_event, path=["response"]) assert cast(Any, response.is_closed) is True @@ -63,7 +59,7 @@ def test_path_params_retrieve(self, client: Lithic) -> None: @parametrize def test_method_list(self, client: Lithic) -> None: funding_event = client.funding_events.list() - assert_matches_type(SyncCursorPage[FundingEventListResponse], funding_event, path=["response"]) + assert_matches_type(SyncCursorPage[FundingEvent], funding_event, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Lithic) -> None: @@ -72,7 +68,7 @@ def test_method_list_with_all_params(self, client: Lithic) -> None: page_size=1, starting_after="starting_after", ) - assert_matches_type(SyncCursorPage[FundingEventListResponse], funding_event, path=["response"]) + assert_matches_type(SyncCursorPage[FundingEvent], funding_event, path=["response"]) @parametrize def test_raw_response_list(self, client: Lithic) -> None: @@ -81,7 +77,7 @@ def test_raw_response_list(self, client: Lithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" funding_event = response.parse() - assert_matches_type(SyncCursorPage[FundingEventListResponse], funding_event, path=["response"]) + assert_matches_type(SyncCursorPage[FundingEvent], funding_event, path=["response"]) @parametrize def test_streaming_response_list(self, client: Lithic) -> None: @@ -90,7 +86,7 @@ def test_streaming_response_list(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" funding_event = response.parse() - assert_matches_type(SyncCursorPage[FundingEventListResponse], funding_event, path=["response"]) + assert_matches_type(SyncCursorPage[FundingEvent], funding_event, path=["response"]) assert cast(Any, response.is_closed) is True @@ -143,7 +139,7 @@ async def test_method_retrieve(self, async_client: AsyncLithic) -> None: funding_event = await async_client.funding_events.retrieve( "funding_event_token", ) - assert_matches_type(FundingEventRetrieveResponse, funding_event, path=["response"]) + assert_matches_type(FundingEvent, funding_event, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @@ -154,7 +150,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" funding_event = response.parse() - assert_matches_type(FundingEventRetrieveResponse, funding_event, path=["response"]) + assert_matches_type(FundingEvent, funding_event, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: @@ -165,7 +161,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" funding_event = await response.parse() - assert_matches_type(FundingEventRetrieveResponse, funding_event, path=["response"]) + assert_matches_type(FundingEvent, funding_event, path=["response"]) assert cast(Any, response.is_closed) is True @@ -179,7 +175,7 @@ async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: @parametrize async def test_method_list(self, async_client: AsyncLithic) -> None: funding_event = await async_client.funding_events.list() - assert_matches_type(AsyncCursorPage[FundingEventListResponse], funding_event, path=["response"]) + assert_matches_type(AsyncCursorPage[FundingEvent], funding_event, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: @@ -188,7 +184,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> N page_size=1, starting_after="starting_after", ) - assert_matches_type(AsyncCursorPage[FundingEventListResponse], funding_event, path=["response"]) + assert_matches_type(AsyncCursorPage[FundingEvent], funding_event, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncLithic) -> None: @@ -197,7 +193,7 @@ async def test_raw_response_list(self, async_client: AsyncLithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" funding_event = response.parse() - assert_matches_type(AsyncCursorPage[FundingEventListResponse], funding_event, path=["response"]) + assert_matches_type(AsyncCursorPage[FundingEvent], funding_event, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: @@ -206,7 +202,7 @@ async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" funding_event = await response.parse() - assert_matches_type(AsyncCursorPage[FundingEventListResponse], funding_event, path=["response"]) + assert_matches_type(AsyncCursorPage[FundingEvent], funding_event, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_payments.py b/tests/api_resources/test_payments.py index 8eea30e8..5a17ca96 100644 --- a/tests/api_resources/test_payments.py +++ b/tests/api_resources/test_payments.py @@ -48,6 +48,7 @@ def test_method_create_with_all_params(self, client: Lithic) -> None: method="ACH_NEXT_DAY", method_attributes={ "sec_code": "CCD", + "ach_hold_period": 0, "addenda": "addenda", }, type="COLLECTION", @@ -469,6 +470,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> method="ACH_NEXT_DAY", method_attributes={ "sec_code": "CCD", + "ach_hold_period": 0, "addenda": "addenda", }, type="COLLECTION", diff --git a/tests/api_resources/test_tokenizations.py b/tests/api_resources/test_tokenizations.py index b2733063..28ee7ed1 100644 --- a/tests/api_resources/test_tokenizations.py +++ b/tests/api_resources/test_tokenizations.py @@ -11,8 +11,6 @@ from tests.utils import assert_matches_type from lithic.types import ( Tokenization, - TokenizationSimulateResponse, - TokenizationUpdateDigitalCardArtResponse, ) from lithic._utils import parse_date from lithic.pagination import SyncCursorPage, AsyncCursorPage @@ -268,7 +266,7 @@ def test_method_simulate(self, client: Lithic) -> None: pan="4111111289144142", tokenization_source="APPLE_PAY", ) - assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize def test_method_simulate_with_all_params(self, client: Lithic) -> None: @@ -282,7 +280,7 @@ def test_method_simulate_with_all_params(self, client: Lithic) -> None: entity="entity", wallet_recommended_decision="APPROVED", ) - assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize def test_raw_response_simulate(self, client: Lithic) -> None: @@ -296,7 +294,7 @@ def test_raw_response_simulate(self, client: Lithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tokenization = response.parse() - assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize def test_streaming_response_simulate(self, client: Lithic) -> None: @@ -310,7 +308,7 @@ def test_streaming_response_simulate(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" tokenization = response.parse() - assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) assert cast(Any, response.is_closed) is True @@ -357,15 +355,15 @@ 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"]) + assert_matches_type(Tokenization, 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", + digital_card_art_token="5e9483eb-8103-4e16-9794-2106111b2eca", ) - assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize def test_raw_response_update_digital_card_art(self, client: Lithic) -> None: @@ -376,7 +374,7 @@ def test_raw_response_update_digital_card_art(self, client: Lithic) -> None: 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"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize def test_streaming_response_update_digital_card_art(self, client: Lithic) -> None: @@ -387,7 +385,7 @@ def test_streaming_response_update_digital_card_art(self, client: Lithic) -> Non assert response.http_request.headers.get("X-Stainless-Lang") == "python" tokenization = response.parse() - assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) assert cast(Any, response.is_closed) is True @@ -649,7 +647,7 @@ async def test_method_simulate(self, async_client: AsyncLithic) -> None: pan="4111111289144142", tokenization_source="APPLE_PAY", ) - assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize async def test_method_simulate_with_all_params(self, async_client: AsyncLithic) -> None: @@ -663,7 +661,7 @@ async def test_method_simulate_with_all_params(self, async_client: AsyncLithic) entity="entity", wallet_recommended_decision="APPROVED", ) - assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize async def test_raw_response_simulate(self, async_client: AsyncLithic) -> None: @@ -677,7 +675,7 @@ async def test_raw_response_simulate(self, async_client: AsyncLithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tokenization = response.parse() - assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize async def test_streaming_response_simulate(self, async_client: AsyncLithic) -> None: @@ -691,7 +689,7 @@ async def test_streaming_response_simulate(self, async_client: AsyncLithic) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" tokenization = await response.parse() - assert_matches_type(TokenizationSimulateResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) assert cast(Any, response.is_closed) is True @@ -738,15 +736,15 @@ async def test_method_update_digital_card_art(self, async_client: AsyncLithic) - tokenization = await async_client.tokenizations.update_digital_card_art( tokenization_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, 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", + digital_card_art_token="5e9483eb-8103-4e16-9794-2106111b2eca", ) - assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize async def test_raw_response_update_digital_card_art(self, async_client: AsyncLithic) -> None: @@ -757,7 +755,7 @@ async def test_raw_response_update_digital_card_art(self, async_client: AsyncLit 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"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) @parametrize async def test_streaming_response_update_digital_card_art(self, async_client: AsyncLithic) -> None: @@ -768,7 +766,7 @@ async def test_streaming_response_update_digital_card_art(self, async_client: As assert response.http_request.headers.get("X-Stainless-Lang") == "python" tokenization = await response.parse() - assert_matches_type(TokenizationUpdateDigitalCardArtResponse, tokenization, path=["response"]) + assert_matches_type(Tokenization, tokenization, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_webhooks.py b/tests/api_resources/test_webhooks.py index c3fc25b5..a2aab3ce 100644 --- a/tests/api_resources/test_webhooks.py +++ b/tests/api_resources/test_webhooks.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,8 +9,10 @@ import pytest import time_machine +import standardwebhooks from lithic import Lithic, AsyncLithic +from lithic.types import AccountHolderCreatedWebhookEvent base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -48,20 +50,21 @@ def test_verify_signature(self, client: Lithic) -> None: assert verify(payload=payload, headers=headers, secret=secret) is None - with pytest.raises(ValueError, match="Webhook timestamp is too old"): + with pytest.raises(standardwebhooks.WebhookVerificationError, match="timestamp too old"): with time_machine.travel(self.fake_now + timedelta(minutes=6)): - assert verify(payload=payload, headers=headers, secret=secret) is False + verify(payload=payload, headers=headers, secret=secret) - with pytest.raises(ValueError, match="Webhook timestamp is too new"): + with pytest.raises(standardwebhooks.WebhookVerificationError, match="timestamp too new"): with time_machine.travel(self.fake_now - timedelta(minutes=6)): - assert verify(payload=payload, headers=headers, secret=secret) is False + verify(payload=payload, headers=headers, secret=secret) # wrong secret - with pytest.raises(ValueError, match=r"Bad secret"): - verify(payload=payload, headers=headers, secret="invalid secret") + wrong_secret = f"whsec_{base64.b64encode(b'wrong').decode('utf-8')}" + with pytest.raises(standardwebhooks.WebhookVerificationError, match=r"No matching signature"): + verify(payload=payload, headers=headers, secret=wrong_secret) - invalid_signature_message = "None of the given webhook signatures match the expected signature" - with pytest.raises(ValueError, match=invalid_signature_message): + invalid_signature_message = "No matching signature found" + with pytest.raises(standardwebhooks.WebhookVerificationError, match=invalid_signature_message): verify( payload=payload, headers=headers, @@ -73,14 +76,17 @@ def test_verify_signature(self, client: Lithic) -> None: assert ( verify( payload=payload, - headers={**headers, "webhook-signature": f"v1,{invalid_signature} v1,{signature}"}, + 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): + with pytest.raises(standardwebhooks.WebhookVerificationError, match=invalid_signature_message): verify( payload=payload, headers={**headers, "webhook-signature": f"v2,{signature}"}, @@ -90,28 +96,77 @@ def test_verify_signature(self, client: Lithic) -> None: assert ( verify( payload=payload, - headers={**headers, "webhook-signature": f"v2,{signature} v1,{signature}"}, + headers={ + **headers, + "webhook-signature": f"v2,{signature} v1,{signature}", + }, secret=secret, ) is None ) - # missing version - with pytest.raises(ValueError, match=invalid_signature_message): + # missing version - this throws ValueError not WebhookVerificationError + with pytest.raises(ValueError, match=r"not enough values to unpack"): 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"): + # non-string payload - this throws AttributeError not WebhookVerificationError + with pytest.raises(AttributeError, match=r"no attribute 'decode'"): verify( payload=cast(Any, {"payload": payload}), headers=headers, secret=secret, ) + @time_machine.travel(fake_now) + def test_parse(self, client: Lithic) -> None: + valid_payload = """{"event_type":"account_holder.created","token":"00000000-0000-0000-0000-000000000001","account_token":"00000000-0000-0000-0000-000000000001","created":"2019-12-27T18:11:19.117Z","status":"ACCEPTED"}""" + secret = self.secret + + # Generate valid webhook signature for the payload + hook = standardwebhooks.Webhook(secret) + msg_id = "msg_test123" + timestamp = self.fake_now + sig = hook.sign(msg_id=msg_id, timestamp=timestamp, data=valid_payload) + headers = { + "webhook-id": msg_id, + "webhook-timestamp": str(int(timestamp.timestamp())), + "webhook-signature": sig, + } + + result = client.webhooks.parse(valid_payload, headers=headers, secret=secret) + assert result is not None + + # Test type narrowing with isinstance + assert isinstance(result, AccountHolderCreatedWebhookEvent) + assert result.event_type == "account_holder.created" + assert result.token == "00000000-0000-0000-0000-000000000001" + assert result.account_token == "00000000-0000-0000-0000-000000000001" + assert result.status == "ACCEPTED" + + # test with bytes secret + result_bytes = client.webhooks.parse(valid_payload, headers=headers, secret=secret.encode("utf-8")) + assert result_bytes is not None + + # test that parse verifies signature - use wrong secret with proper format + wrong_secret = f"whsec_{base64.b64encode(b'wrong').decode('utf-8')}" + with pytest.raises(standardwebhooks.WebhookVerificationError): + client.webhooks.parse(valid_payload, headers=headers, secret=wrong_secret) + + def test_parse_unsafe(self, client: Lithic) -> None: + valid_payload = """{"event_type":"account_holder.created","token":"00000000-0000-0000-0000-000000000001","account_token":"00000000-0000-0000-0000-000000000001","created":"2019-12-27T18:11:19.117Z","status":"ACCEPTED"}""" + + result = client.webhooks.parse_unsafe(valid_payload) + assert result is not None + + # Test type narrowing with isinstance + assert isinstance(result, AccountHolderCreatedWebhookEvent) + assert result.event_type == "account_holder.created" + assert result.token == "00000000-0000-0000-0000-000000000001" + class TestAsyncWebhooks: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -137,7 +192,7 @@ def test_unwrap(self, async_client: AsyncLithic) -> None: async_client.webhooks.unwrap(payload, headers, secret=secret) @time_machine.travel(fake_now) - def test_verify_signature(self, async_client: Lithic) -> None: + def test_verify_signature(self, async_client: AsyncLithic) -> None: payload = self.payload headers = self.headers secret = self.secret @@ -146,20 +201,21 @@ def test_verify_signature(self, async_client: Lithic) -> None: assert verify(payload=payload, headers=headers, secret=secret) is None - with pytest.raises(ValueError, match="Webhook timestamp is too old"): + with pytest.raises(standardwebhooks.WebhookVerificationError, match="timestamp too old"): with time_machine.travel(self.fake_now + timedelta(minutes=6)): - assert verify(payload=payload, headers=headers, secret=secret) is False + verify(payload=payload, headers=headers, secret=secret) - with pytest.raises(ValueError, match="Webhook timestamp is too new"): + with pytest.raises(standardwebhooks.WebhookVerificationError, match="timestamp too new"): with time_machine.travel(self.fake_now - timedelta(minutes=6)): - assert verify(payload=payload, headers=headers, secret=secret) is False + verify(payload=payload, headers=headers, secret=secret) # wrong secret - with pytest.raises(ValueError, match=r"Bad secret"): - verify(payload=payload, headers=headers, secret="invalid secret") + wrong_secret = f"whsec_{base64.b64encode(b'wrong').decode('utf-8')}" + with pytest.raises(standardwebhooks.WebhookVerificationError, match=r"No matching signature"): + verify(payload=payload, headers=headers, secret=wrong_secret) - invalid_signature_message = "None of the given webhook signatures match the expected signature" - with pytest.raises(ValueError, match=invalid_signature_message): + invalid_signature_message = "No matching signature found" + with pytest.raises(standardwebhooks.WebhookVerificationError, match=invalid_signature_message): verify( payload=payload, headers=headers, @@ -171,14 +227,17 @@ def test_verify_signature(self, async_client: Lithic) -> None: assert ( verify( payload=payload, - headers={**headers, "webhook-signature": f"v1,{invalid_signature} v1,{signature}"}, + 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): + with pytest.raises(standardwebhooks.WebhookVerificationError, match=invalid_signature_message): verify( payload=payload, headers={**headers, "webhook-signature": f"v2,{signature}"}, @@ -188,24 +247,73 @@ def test_verify_signature(self, async_client: Lithic) -> None: assert ( verify( payload=payload, - headers={**headers, "webhook-signature": f"v2,{signature} v1,{signature}"}, + headers={ + **headers, + "webhook-signature": f"v2,{signature} v1,{signature}", + }, secret=secret, ) is None ) - # missing version - with pytest.raises(ValueError, match=invalid_signature_message): + # missing version - this throws ValueError not WebhookVerificationError + with pytest.raises(ValueError, match=r"not enough values to unpack"): 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"): + # non-string payload - this throws AttributeError not WebhookVerificationError + with pytest.raises(AttributeError, match=r"no attribute 'decode'"): verify( payload=cast(Any, {"payload": payload}), headers=headers, secret=secret, ) + + @time_machine.travel(fake_now) + def test_parse(self, async_client: AsyncLithic) -> None: + valid_payload = """{"event_type":"account_holder.created","token":"00000000-0000-0000-0000-000000000001","account_token":"00000000-0000-0000-0000-000000000001","created":"2019-12-27T18:11:19.117Z","status":"ACCEPTED"}""" + secret = self.secret + + # Generate valid webhook signature for the payload + hook = standardwebhooks.Webhook(secret) + msg_id = "msg_test123" + timestamp = self.fake_now + sig = hook.sign(msg_id=msg_id, timestamp=timestamp, data=valid_payload) + headers = { + "webhook-id": msg_id, + "webhook-timestamp": str(int(timestamp.timestamp())), + "webhook-signature": sig, + } + + result = async_client.webhooks.parse(valid_payload, headers=headers, secret=secret) + assert result is not None + + # Test type narrowing with isinstance + assert isinstance(result, AccountHolderCreatedWebhookEvent) + assert result.event_type == "account_holder.created" + assert result.token == "00000000-0000-0000-0000-000000000001" + assert result.account_token == "00000000-0000-0000-0000-000000000001" + assert result.status == "ACCEPTED" + + # test with bytes secret + result_bytes = async_client.webhooks.parse(valid_payload, headers=headers, secret=secret.encode("utf-8")) + assert result_bytes is not None + + # test that parse verifies signature - use wrong secret with proper format + wrong_secret = f"whsec_{base64.b64encode(b'wrong').decode('utf-8')}" + with pytest.raises(standardwebhooks.WebhookVerificationError): + async_client.webhooks.parse(valid_payload, headers=headers, secret=wrong_secret) + + def test_parse_unsafe(self, async_client: AsyncLithic) -> None: + valid_payload = """{"event_type":"account_holder.created","token":"00000000-0000-0000-0000-000000000001","account_token":"00000000-0000-0000-0000-000000000001","created":"2019-12-27T18:11:19.117Z","status":"ACCEPTED"}""" + + result = async_client.webhooks.parse_unsafe(valid_payload) + assert result is not None + + # Test type narrowing with isinstance + assert isinstance(result, AccountHolderCreatedWebhookEvent) + assert result.event_type == "account_holder.created" + assert result.token == "00000000-0000-0000-0000-000000000001" diff --git a/tests/api_resources/three_ds/test_authentication.py b/tests/api_resources/three_ds/test_authentication.py index 05d603ca..51b25d61 100644 --- a/tests/api_resources/three_ds/test_authentication.py +++ b/tests/api_resources/three_ds/test_authentication.py @@ -9,8 +9,8 @@ from lithic import Lithic, AsyncLithic from tests.utils import assert_matches_type +from lithic.types import ThreeDSAuthentication from lithic.types.three_ds import ( - AuthenticationRetrieveResponse, AuthenticationSimulateResponse, ) @@ -25,7 +25,7 @@ def test_method_retrieve(self, client: Lithic) -> None: authentication = client.three_ds.authentication.retrieve( "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AuthenticationRetrieveResponse, authentication, path=["response"]) + assert_matches_type(ThreeDSAuthentication, authentication, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Lithic) -> None: @@ -36,7 +36,7 @@ def test_raw_response_retrieve(self, client: Lithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" authentication = response.parse() - assert_matches_type(AuthenticationRetrieveResponse, authentication, path=["response"]) + assert_matches_type(ThreeDSAuthentication, authentication, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Lithic) -> None: @@ -47,7 +47,7 @@ def test_streaming_response_retrieve(self, client: Lithic) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" authentication = response.parse() - assert_matches_type(AuthenticationRetrieveResponse, authentication, path=["response"]) + assert_matches_type(ThreeDSAuthentication, authentication, path=["response"]) assert cast(Any, response.is_closed) is True @@ -184,7 +184,7 @@ async def test_method_retrieve(self, async_client: AsyncLithic) -> None: authentication = await async_client.three_ds.authentication.retrieve( "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) - assert_matches_type(AuthenticationRetrieveResponse, authentication, path=["response"]) + assert_matches_type(ThreeDSAuthentication, authentication, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: @@ -195,7 +195,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" authentication = response.parse() - assert_matches_type(AuthenticationRetrieveResponse, authentication, path=["response"]) + assert_matches_type(ThreeDSAuthentication, authentication, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: @@ -206,7 +206,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" authentication = await response.parse() - assert_matches_type(AuthenticationRetrieveResponse, authentication, path=["response"]) + assert_matches_type(ThreeDSAuthentication, authentication, path=["response"]) assert cast(Any, response.is_closed) is True