diff --git a/pyproject.toml b/pyproject.toml index 2bc800b..415a080 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "blindpay" -version = "2.2.0" +version = "2.3.0" description = "Official Python SDK for the Blindpay API — Global payments infrastructure" readme = "README.md" authors = [{ name = "Blindpay", email = "alves@blindpay.com" }] diff --git a/src/blindpay/__init__.py b/src/blindpay/__init__.py index 2372588..fe2fb4c 100644 --- a/src/blindpay/__init__.py +++ b/src/blindpay/__init__.py @@ -1,9 +1,10 @@ -__version__ = "2.2.0" +__version__ = "2.3.0" from ._internal.exceptions import BlindPayError from .client import BlindPay, BlindPaySync from .types import ( AccountClass, + AipriseDocumentType, BankAccountType, BankingPartner, BlindpayApiResponse, @@ -13,6 +14,7 @@ Currency, CurrencyType, ErrorResponse, + ManualExecutionStatus, Network, PaginationMetadata, PaginationParams, @@ -27,6 +29,7 @@ TrackingTransaction, TransactionDocumentType, TransactionStatus, + WebhookEvent, ) __all__ = [ @@ -34,11 +37,13 @@ "BlindPaySync", "BlindPayError", "AccountClass", + "AipriseDocumentType", "BankAccountType", "BankingPartner", "Country", "Currency", "CurrencyType", + "ManualExecutionStatus", "Network", "PaymentMethod", "Rail", @@ -46,6 +51,7 @@ "StablecoinToken", "TransactionDocumentType", "TransactionStatus", + "WebhookEvent", "BlindpayApiResponse", "BlindpayErrorResponse", "BlindpaySuccessResponse", diff --git a/src/blindpay/client.py b/src/blindpay/client.py index 78c3c57..41e2609 100644 --- a/src/blindpay/client.py +++ b/src/blindpay/client.py @@ -17,8 +17,15 @@ CustodialWalletsResource, CustodialWalletsResourceSync, ) + from blindpay.resources.customers.customers import CustomersResource, CustomersResourceSync + from blindpay.resources.customers.bank_accounts import CustomerBankAccountsResource, CustomerBankAccountsResourceSync + from blindpay.resources.customers.blockchain_wallets import CustomerBlockchainWalletsResource, CustomerBlockchainWalletsResourceSync + from blindpay.resources.customers.virtual_accounts import CustomerVirtualAccountsResource, CustomerVirtualAccountsResourceSync + from blindpay.resources.customers.wallets import CustomerWalletsResource, CustomerWalletsResourceSync + from blindpay.resources.customers.offramp_wallets import CustomerOfframpWalletsResource, CustomerOfframpWalletsResourceSync from blindpay.resources.fees.fees import FeesResource, FeesResourceSync from blindpay.resources.instances.instances import InstancesResource, InstancesResourceSync + from blindpay.resources.ownership.ownership import OwnershipResource, OwnershipResourceSync from blindpay.resources.partner_fees.partner_fees import PartnerFeesResource, PartnerFeesResourceSync from blindpay.resources.payins.payins import PayinsResource, PayinsResourceSync from blindpay.resources.payins.quotes import PayinQuotesResource, PayinQuotesResourceSync @@ -39,7 +46,7 @@ from blindpay.resources.wallets.offramp import OfframpWalletsResource, OfframpWalletsResourceSync from blindpay.resources.webhooks.webhooks import WebhookEndpointsResource, WebhookEndpointsResourceSync -__version__ = "2.2.0" +__version__ = "2.3.0" T = TypeVar("T") @@ -224,6 +231,51 @@ def __getattr__(self, name: str) -> Any: return getattr(self._base, name) +class _CustomersNamespace: + def __init__(self, instance_id: str, api_client: ApiClientImpl) -> None: + self._instance_id = instance_id + self._api = api_client + + @cached_property + def _base(self) -> "CustomersResource": + from blindpay.resources.customers.customers import create_customers_resource + + return create_customers_resource(self._instance_id, self._api) + + @cached_property + def bank_accounts(self) -> "CustomerBankAccountsResource": + from blindpay.resources.customers.bank_accounts import create_customer_bank_accounts_resource + + return create_customer_bank_accounts_resource(self._instance_id, self._api) + + @cached_property + def blockchain_wallets(self) -> "CustomerBlockchainWalletsResource": + from blindpay.resources.customers.blockchain_wallets import create_customer_blockchain_wallets_resource + + return create_customer_blockchain_wallets_resource(self._instance_id, self._api) + + @cached_property + def virtual_accounts(self) -> "CustomerVirtualAccountsResource": + from blindpay.resources.customers.virtual_accounts import create_customer_virtual_accounts_resource + + return create_customer_virtual_accounts_resource(self._instance_id, self._api) + + @cached_property + def wallets(self) -> "CustomerWalletsResource": + from blindpay.resources.customers.wallets import create_customer_wallets_resource + + return create_customer_wallets_resource(self._instance_id, self._api) + + @cached_property + def offramp_wallets(self) -> "CustomerOfframpWalletsResource": + from blindpay.resources.customers.offramp_wallets import create_customer_offramp_wallets_resource + + return create_customer_offramp_wallets_resource(self._instance_id, self._api) + + def __getattr__(self, name: str) -> Any: + return getattr(self._base, name) + + class _WalletsNamespace: def __init__(self, instance_id: str, api_client: ApiClientImpl) -> None: self._instance_id = instance_id @@ -309,6 +361,16 @@ def payouts(self) -> "PayoutsResource": def receivers(self) -> _ReceiversNamespace: return _ReceiversNamespace(self._instance_id, self._api) + @cached_property + def customers(self) -> _CustomersNamespace: + return _CustomersNamespace(self._instance_id, self._api) + + @cached_property + def ownership(self) -> "OwnershipResource": + from blindpay.resources.ownership import create_ownership_resource + + return create_ownership_resource(self._instance_id, self._api) + @cached_property def virtual_accounts(self) -> "VirtualAccountsResource": from blindpay.resources.virtual_accounts import create_virtual_accounts_resource @@ -454,6 +516,51 @@ def __getattr__(self, name: str) -> Any: return getattr(self._base, name) +class _CustomersNamespaceSync: + def __init__(self, instance_id: str, api_client: ApiClientImplSync) -> None: + self._instance_id = instance_id + self._api = api_client + + @cached_property + def _base(self) -> "CustomersResourceSync": + from blindpay.resources.customers.customers import create_customers_resource_sync + + return create_customers_resource_sync(self._instance_id, self._api) + + @cached_property + def bank_accounts(self) -> "CustomerBankAccountsResourceSync": + from blindpay.resources.customers.bank_accounts import create_customer_bank_accounts_resource_sync + + return create_customer_bank_accounts_resource_sync(self._instance_id, self._api) + + @cached_property + def blockchain_wallets(self) -> "CustomerBlockchainWalletsResourceSync": + from blindpay.resources.customers.blockchain_wallets import create_customer_blockchain_wallets_resource_sync + + return create_customer_blockchain_wallets_resource_sync(self._instance_id, self._api) + + @cached_property + def virtual_accounts(self) -> "CustomerVirtualAccountsResourceSync": + from blindpay.resources.customers.virtual_accounts import create_customer_virtual_accounts_resource_sync + + return create_customer_virtual_accounts_resource_sync(self._instance_id, self._api) + + @cached_property + def wallets(self) -> "CustomerWalletsResourceSync": + from blindpay.resources.customers.wallets import create_customer_wallets_resource_sync + + return create_customer_wallets_resource_sync(self._instance_id, self._api) + + @cached_property + def offramp_wallets(self) -> "CustomerOfframpWalletsResourceSync": + from blindpay.resources.customers.offramp_wallets import create_customer_offramp_wallets_resource_sync + + return create_customer_offramp_wallets_resource_sync(self._instance_id, self._api) + + def __getattr__(self, name: str) -> Any: + return getattr(self._base, name) + + class _WalletsNamespaceSync: def __init__(self, instance_id: str, api_client: ApiClientImplSync) -> None: self._instance_id = instance_id @@ -539,6 +646,16 @@ def payouts(self) -> "PayoutsResourceSync": def receivers(self) -> _ReceiversNamespaceSync: return _ReceiversNamespaceSync(self._instance_id, self._api) + @cached_property + def customers(self) -> _CustomersNamespaceSync: + return _CustomersNamespaceSync(self._instance_id, self._api) + + @cached_property + def ownership(self) -> "OwnershipResourceSync": + from blindpay.resources.ownership import create_ownership_resource_sync + + return create_ownership_resource_sync(self._instance_id, self._api) + @cached_property def virtual_accounts(self) -> "VirtualAccountsResourceSync": from blindpay.resources.virtual_accounts import create_virtual_accounts_resource_sync diff --git a/src/blindpay/resources/__init__.py b/src/blindpay/resources/__init__.py index 3819881..e4ff246 100644 --- a/src/blindpay/resources/__init__.py +++ b/src/blindpay/resources/__init__.py @@ -2,8 +2,10 @@ from .available import create_available_resource from .bank_accounts import create_bank_accounts_resource from .custodial_wallets import create_custodial_wallets_resource +from .customers import create_customers_resource from .fees import create_fees_resource from .instances import create_instances_resource +from .ownership import create_ownership_resource from .partner_fees import create_partner_fees_resource from .payins import create_payin_quotes_resource, create_payins_resource from .payouts import create_payouts_resource @@ -21,8 +23,10 @@ "create_available_resource", "create_bank_accounts_resource", "create_custodial_wallets_resource", + "create_customers_resource", "create_fees_resource", "create_instances_resource", + "create_ownership_resource", "create_partner_fees_resource", "create_payins_resource", "create_payin_quotes_resource", diff --git a/src/blindpay/resources/customers/__init__.py b/src/blindpay/resources/customers/__init__.py new file mode 100644 index 0000000..76f1d9b --- /dev/null +++ b/src/blindpay/resources/customers/__init__.py @@ -0,0 +1,125 @@ +from .bank_accounts import ( + BankAccount, + CreateBankAccountInput, + CustomerBankAccountsResource, + CustomerBankAccountsResourceSync, + ListBankAccountsResponse, + create_customer_bank_accounts_resource, + create_customer_bank_accounts_resource_sync, +) +from .blockchain_wallets import ( + BlockchainWallet, + BlockchainWalletMessage, + CreateBlockchainWalletInput, + CustomerBlockchainWalletsResource, + CustomerBlockchainWalletsResourceSync, + ListBlockchainWalletsResponse, + create_customer_blockchain_wallets_resource, + create_customer_blockchain_wallets_resource_sync, +) +from .customers import ( + CreateCustomerInput, + CreateCustomerResponse, + Customer, + CustomersResource, + CustomersResourceSync, + GetCustomerLimitsResponse, + LimitIncreaseRequestInput, + LimitIncreaseRequestResponse, + ListCustomersResponse, + Rfi, + UpdateCustomerInput, + create_customers_resource, + create_customers_resource_sync, +) +from .offramp_wallets import ( + CreateOfframpWalletInput, + CreateOfframpWalletResponse, + CustomerOfframpWalletsResource, + CustomerOfframpWalletsResourceSync, + ListOfframpWalletsResponse, + OfframpWallet, + create_customer_offramp_wallets_resource, + create_customer_offramp_wallets_resource_sync, +) +from .virtual_accounts import ( + CreateVirtualAccountInput, + CustomerVirtualAccountsResource, + CustomerVirtualAccountsResourceSync, + ListVirtualAccountsResponse, + UpdateVirtualAccountInput, + VirtualAccount, + create_customer_virtual_accounts_resource, + create_customer_virtual_accounts_resource_sync, +) +from .wallets import ( + CreateWalletInput, + CustomerWalletsResource, + CustomerWalletsResourceSync, + ListWalletsResponse, + Wallet, + WalletBalance, + create_customer_wallets_resource, + create_customer_wallets_resource_sync, +) + +__all__ = [ + # Main customers resource + "CustomersResource", + "CustomersResourceSync", + "create_customers_resource", + "create_customers_resource_sync", + "CreateCustomerInput", + "CreateCustomerResponse", + "Customer", + "UpdateCustomerInput", + "ListCustomersResponse", + "LimitIncreaseRequestInput", + "LimitIncreaseRequestResponse", + "GetCustomerLimitsResponse", + "Rfi", + # Bank accounts + "CustomerBankAccountsResource", + "CustomerBankAccountsResourceSync", + "create_customer_bank_accounts_resource", + "create_customer_bank_accounts_resource_sync", + "CreateBankAccountInput", + "BankAccount", + "ListBankAccountsResponse", + # Blockchain wallets + "CustomerBlockchainWalletsResource", + "CustomerBlockchainWalletsResourceSync", + "create_customer_blockchain_wallets_resource", + "create_customer_blockchain_wallets_resource_sync", + "CreateBlockchainWalletInput", + "BlockchainWallet", + "BlockchainWalletMessage", + "ListBlockchainWalletsResponse", + # Virtual accounts + "CustomerVirtualAccountsResource", + "CustomerVirtualAccountsResourceSync", + "create_customer_virtual_accounts_resource", + "create_customer_virtual_accounts_resource_sync", + "CreateVirtualAccountInput", + "UpdateVirtualAccountInput", + "VirtualAccount", + "ListVirtualAccountsResponse", + # Wallets + "CustomerWalletsResource", + "CustomerWalletsResourceSync", + "create_customer_wallets_resource", + "create_customer_wallets_resource_sync", + "CreateWalletInput", + "Wallet", + "WalletBalance", + "ListWalletsResponse", + # Offramp wallets + "CustomerOfframpWalletsResource", + "CustomerOfframpWalletsResourceSync", + "create_customer_offramp_wallets_resource", + "create_customer_offramp_wallets_resource_sync", + "CreateOfframpWalletInput", + "CreateOfframpWalletResponse", + "OfframpWallet", + "ListOfframpWalletsResponse", +] \ No newline at end of file diff --git a/src/blindpay/resources/customers/bank_accounts.py b/src/blindpay/resources/customers/bank_accounts.py new file mode 100644 index 0000000..3d7cb29 --- /dev/null +++ b/src/blindpay/resources/customers/bank_accounts.py @@ -0,0 +1,248 @@ +from typing import List, Literal, Optional + +from typing_extensions import TypedDict + +from ..._internal.api_client import InternalApiClient, InternalApiClientSync +from ...types import ( + AccountClass, + BankAccountType, + BlindpayApiResponse, + Country, + RecipientRelationship, + SpeiProtocol, +) + +BankAccountStatus = Literal["verifying", "approved", "rejected", "deprecated"] + +BankAccountTypeEnum = Literal[ + "ach_us", + "wire_us", + "pix_br", + "pix_safe_br", + "spei_mx", + "transfers_ar", + "ach_cop_co", + "swift_international", + "sepa_eu", + "rtp_us", + "ted_br" +] + +ArgentinaTransfers = Literal["CVU", "CBU", "ALIAS"] + +AchCopDocumentType = Literal["CC", "CE", "NIT", "PASS", "PEP"] + + +class CreateBankAccountInput(TypedDict): + type: BankAccountTypeEnum + name: str + status: Optional[BankAccountStatus] + recipient_relationship: Optional[RecipientRelationship] + swift_payment_code: Optional[str] + pix_key: Optional[str] + force_cpf_cnpj: Optional[bool] + beneficiary_name: Optional[str] + routing_number: Optional[str] + account_number: Optional[str] + account_type: Optional[BankAccountType] + account_class: Optional[AccountClass] + address_line_1: Optional[str] + address_line_2: Optional[str] + city: Optional[str] + state_province_region: Optional[str] + country: Optional[Country] + postal_code: Optional[str] + checkbook_account_id: Optional[str] + checkbook_user_key: Optional[str] + onemoney_external_account_id: Optional[str] + pix_safe_bank_code: Optional[str] + pix_safe_branch_code: Optional[str] + pix_safe_cpf_cnpj: Optional[str] + ted_bank_code: Optional[str] + ted_branch_code: Optional[str] + ted_cpf_cnpj: Optional[str] + spei_protocol: Optional[SpeiProtocol] + spei_institution_code: Optional[str] + spei_clabe: Optional[str] + transfers_type: Optional[ArgentinaTransfers] + transfers_account: Optional[str] + ach_cop_beneficiary_first_name: Optional[str] + ach_cop_beneficiary_last_name: Optional[str] + ach_cop_document_id: Optional[str] + ach_cop_document_type: Optional[AchCopDocumentType] + ach_cop_email: Optional[str] + ach_cop_bank_code: Optional[str] + ach_cop_bank_account: Optional[str] + swift_code_bic: Optional[str] + swift_account_holder_name: Optional[str] + swift_account_number_iban: Optional[str] + swift_beneficiary_address_line_1: Optional[str] + swift_beneficiary_address_line_2: Optional[str] + swift_beneficiary_country: Optional[Country] + swift_beneficiary_city: Optional[str] + swift_beneficiary_state_province_region: Optional[str] + swift_beneficiary_postal_code: Optional[str] + swift_bank_name: Optional[str] + swift_bank_address_line_1: Optional[str] + swift_bank_address_line_2: Optional[str] + swift_bank_country: Optional[Country] + swift_bank_city: Optional[str] + swift_bank_state_province_region: Optional[str] + swift_bank_postal_code: Optional[str] + swift_ifsc_branch_code: Optional[str] + swift_intermediary_bank_swift_code_bic: Optional[str] + swift_intermediary_bank_account_number_iban: Optional[str] + swift_intermediary_bank_name: Optional[str] + swift_intermediary_bank_country: Optional[Country] + sepa_iban: Optional[str] + sepa_beneficiary_bic: Optional[str] + sepa_beneficiary_legal_name: Optional[str] + sepa_beneficiary_address_line_1: Optional[str] + sepa_beneficiary_address_line_2: Optional[str] + sepa_beneficiary_city: Optional[str] + sepa_beneficiary_state_province_region: Optional[str] + sepa_beneficiary_postal_code: Optional[str] + sepa_beneficiary_country: Optional[Country] + business_industry: Optional[str] + phone_number: Optional[str] + tax_id: Optional[str] + date_of_birth: Optional[str] + + +class BankAccount(TypedDict): + id: str + type: BankAccountTypeEnum + name: str + pix_key: Optional[str] + beneficiary_name: Optional[str] + routing_number: Optional[str] + account_number: Optional[str] + account_type: Optional[BankAccountType] + account_class: Optional[AccountClass] + address_line_1: Optional[str] + address_line_2: Optional[str] + city: Optional[str] + state_province_region: Optional[str] + country: Optional[Country] + postal_code: Optional[str] + spei_protocol: Optional[SpeiProtocol] + spei_institution_code: Optional[str] + spei_clabe: Optional[str] + transfers_type: Optional[ArgentinaTransfers] + transfers_account: Optional[str] + # Additional fields as mentioned in changelog - there are 53+ more fields + ach_cop_beneficiary_first_name: Optional[str] + ach_cop_beneficiary_last_name: Optional[str] + ach_cop_document_id: Optional[str] + ach_cop_document_type: Optional[AchCopDocumentType] + ach_cop_email: Optional[str] + ach_cop_bank_code: Optional[str] + ach_cop_bank_account: Optional[str] + swift_code_bic: Optional[str] + swift_account_holder_name: Optional[str] + swift_account_number_iban: Optional[str] + swift_beneficiary_address_line_1: Optional[str] + swift_beneficiary_address_line_2: Optional[str] + swift_beneficiary_country: Optional[Country] + swift_beneficiary_city: Optional[str] + swift_beneficiary_state_province_region: Optional[str] + swift_beneficiary_postal_code: Optional[str] + swift_bank_name: Optional[str] + swift_bank_address_line_1: Optional[str] + swift_bank_address_line_2: Optional[str] + swift_bank_country: Optional[Country] + swift_bank_city: Optional[str] + swift_bank_state_province_region: Optional[str] + swift_bank_postal_code: Optional[str] + swift_ifsc_branch_code: Optional[str] + swift_intermediary_bank_swift_code_bic: Optional[str] + swift_intermediary_bank_account_number_iban: Optional[str] + swift_intermediary_bank_name: Optional[str] + swift_intermediary_bank_country: Optional[Country] + sepa_iban: Optional[str] + sepa_beneficiary_bic: Optional[str] + sepa_beneficiary_legal_name: Optional[str] + sepa_beneficiary_address_line_1: Optional[str] + sepa_beneficiary_address_line_2: Optional[str] + sepa_beneficiary_city: Optional[str] + sepa_beneficiary_state_province_region: Optional[str] + sepa_beneficiary_postal_code: Optional[str] + sepa_beneficiary_country: Optional[Country] + business_industry: Optional[str] + phone_number: Optional[str] + tax_id: Optional[str] + date_of_birth: Optional[str] + created_at: str + updated_at: str + + +class ListBankAccountsResponse(TypedDict): + data: List[BankAccount] + + +class Success(TypedDict): + success: bool + + +class CustomerBankAccountsResource: + def __init__(self, instance_id: str, client: InternalApiClient): + self._instance_id = instance_id + self._client = client + + async def create( + self, customer_id: str, data: CreateBankAccountInput + ) -> BlindpayApiResponse[BankAccount]: + return await self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", data + ) + + async def list(self, customer_id: str) -> BlindpayApiResponse[ListBankAccountsResponse]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts" + ) + + async def get(self, customer_id: str, bank_account_id: str) -> BlindpayApiResponse[BankAccount]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}" + ) + + async def delete(self, customer_id: str, bank_account_id: str) -> BlindpayApiResponse[Success]: + return await self._client.delete( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}" + ) + + +class CustomerBankAccountsResourceSync: + def __init__(self, instance_id: str, client: InternalApiClientSync): + self._instance_id = instance_id + self._client = client + + def create( + self, customer_id: str, data: CreateBankAccountInput + ) -> BlindpayApiResponse[BankAccount]: + return self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", data + ) + + def list(self, customer_id: str) -> BlindpayApiResponse[ListBankAccountsResponse]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts" + ) + + def get(self, customer_id: str, bank_account_id: str) -> BlindpayApiResponse[BankAccount]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}" + ) + + def delete(self, customer_id: str, bank_account_id: str) -> BlindpayApiResponse[Success]: + return self._client.delete( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}" + ) + + +def create_customer_bank_accounts_resource(instance_id: str, client: InternalApiClient) -> CustomerBankAccountsResource: + return CustomerBankAccountsResource(instance_id, client) + + +def create_customer_bank_accounts_resource_sync(instance_id: str, client: InternalApiClientSync) -> CustomerBankAccountsResourceSync: + return CustomerBankAccountsResourceSync(instance_id, client) \ No newline at end of file diff --git a/src/blindpay/resources/customers/blockchain_wallets.py b/src/blindpay/resources/customers/blockchain_wallets.py new file mode 100644 index 0000000..f1fa7ff --- /dev/null +++ b/src/blindpay/resources/customers/blockchain_wallets.py @@ -0,0 +1,112 @@ +from typing import List, Optional + +from typing_extensions import TypedDict + +from ..._internal.api_client import InternalApiClient, InternalApiClientSync +from ...types import BlindpayApiResponse, Network + + +class CreateBlockchainWalletInput(TypedDict): + name: str + network: Network + signature_tx_hash: Optional[str] + address: Optional[str] + is_account_abstraction: Optional[bool] + + +class BlockchainWallet(TypedDict): + id: str + name: str + network: Network + address: Optional[str] + signature_tx_hash: Optional[str] + is_account_abstraction: Optional[bool] + receiver_id: str + created_at: str + updated_at: str + + +class ListBlockchainWalletsResponse(TypedDict): + data: List[BlockchainWallet] + + +class BlockchainWalletMessage(TypedDict): + message: str + + +class Success(TypedDict): + success: bool + + +class CustomerBlockchainWalletsResource: + def __init__(self, instance_id: str, client: InternalApiClient): + self._instance_id = instance_id + self._client = client + + async def create( + self, customer_id: str, data: CreateBlockchainWalletInput + ) -> BlindpayApiResponse[BlockchainWallet]: + return await self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets", data + ) + + async def list(self, customer_id: str) -> BlindpayApiResponse[ListBlockchainWalletsResponse]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets" + ) + + async def get(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[BlockchainWallet]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/{wallet_id}" + ) + + async def delete(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[Success]: + return await self._client.delete( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/{wallet_id}" + ) + + async def get_sign_message(self, customer_id: str) -> BlindpayApiResponse[BlockchainWalletMessage]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/sign-message" + ) + + +class CustomerBlockchainWalletsResourceSync: + def __init__(self, instance_id: str, client: InternalApiClientSync): + self._instance_id = instance_id + self._client = client + + def create( + self, customer_id: str, data: CreateBlockchainWalletInput + ) -> BlindpayApiResponse[BlockchainWallet]: + return self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets", data + ) + + def list(self, customer_id: str) -> BlindpayApiResponse[ListBlockchainWalletsResponse]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets" + ) + + def get(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[BlockchainWallet]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/{wallet_id}" + ) + + def delete(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[Success]: + return self._client.delete( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/{wallet_id}" + ) + + def get_sign_message(self, customer_id: str) -> BlindpayApiResponse[BlockchainWalletMessage]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/sign-message" + ) + + +def create_customer_blockchain_wallets_resource(instance_id: str, client: InternalApiClient) -> CustomerBlockchainWalletsResource: + return CustomerBlockchainWalletsResource(instance_id, client) + + +def create_customer_blockchain_wallets_resource_sync(instance_id: str, client: InternalApiClientSync) -> CustomerBlockchainWalletsResourceSync: + return CustomerBlockchainWalletsResourceSync(instance_id, client) \ No newline at end of file diff --git a/src/blindpay/resources/customers/customers.py b/src/blindpay/resources/customers/customers.py new file mode 100644 index 0000000..f7fe243 --- /dev/null +++ b/src/blindpay/resources/customers/customers.py @@ -0,0 +1,423 @@ +from typing import List, Optional, Union +from urllib.parse import urlencode + +from typing_extensions import Literal, TypedDict + +from ..._internal.api_client import InternalApiClient, InternalApiClientSync +from ...types import ( + BlindpayApiResponse, + Country, + PaginationMetadata, + PaginationParams, +) + +IndividualType = Literal["individual"] +BusinessType = Literal["business"] +StandardKycType = Literal["standard"] +EnhancedKycType = Literal["enhanced"] +KycType = Literal["light", "standard", "enhanced"] +KycStatus = Literal["verifying", "approved", "rejected", "deprecated", "pending_review", "awaiting_contract", "compliance_request"] + +ProofOfAddressDocType = Literal[ + "UTILITY_BILL", "BANK_STATEMENT", "RENTAL_AGREEMENT", "TAX_DOCUMENT", "GOVERNMENT_CORRESPONDENCE" +] + +IdDocType = Literal["PASSPORT", "ID_CARD", "DRIVERS"] + +AchCopDocumentType = Literal["CC", "CE", "NIT", "PASS", "PEP"] + +PurposeOfTransactions = Literal[ + "business_transactions", + "charitable_donations", + "investment_purposes", + "payments_to_friends_or_family_abroad", + "personal_or_living_expenses", + "protect_wealth", + "purchase_good_and_services", + "receive_payment_for_freelancing", + "receive_salary", + "other", +] + +AccountPurpose = Literal[ + "charitable_donations", + "ecommerce_retail_payments", + "investment_purposes", + "business_expenses", + "payments_to_friends_or_family_abroad", + "personal_or_living_expenses", + "protect_wealth", + "purchase_goods_and_services", + "receive_payments_for_goods_and_services", + "tax_optimization", + "third_party_money_transmission", + "payroll", + "treasury_management", + "other", +] + +BusinessType = Literal["corporation", "llc", "partnership", "sole_proprietorship", "trust", "non_profit"] + +BusinessIndustry = Literal[ + "111998", "112120", "113310", "115114", "541211", "541810", "541430", "541715", "541930", + "561422", "561311", "561612", "561740", "561730", "236115", "236220", "237310", "238210", + "811111", "812111", "812112", "532111", "624410", "541922", "811210", "812199", "611110", + "611310", "611410", "611710", "211120", "212114", "221310", "562111", "562920", "213112", + "522110", "522210", "522320", "523150", "523940", "523999", "524113", "813110", "813211", + "813219", "551112", "721110", "722511", "722513", "561510", "713110", "713210", "712110", + "711110", "711211", "621111", "621210", "622110", "623110", "621511", "623220", "541940", + "621399", "621910", "541110", "311421", "337121", "322220", "339920", "334210", "339930", + "312130", "334111", "334118", "325412", "339112", "336110", "336390", "315990", "313110", + "339910", "516120", "513130", "512250", "519130", "711410", "711510", "531110", "531120", + "531130", "531190", "531210", "531311", "531312", "531320", "531390", "454110", "445110", + "455110", "457110", "449210", "444110", "459210", "459120", "445320", "458110", "458210", + "458310", "455219", "424210", "456110", "541511", "541512", "541519", "518210", "511210", + "517111", "517112", "517410", "481111", "483111", "485210", "488510", "484121", "493110", + "423430", "423690", "423110", "423830", "423840", "423510", "424690", "424990", "424410", + "424480", "423940", "541611", "541618", "541330", "541990", "541214", "561499", "dapp", + "exchange", "gambling", "gaming", "infra", "marketplace", "neo_bank", "other", "saas", + "social", "wallet", +] + +EstimatedAnnualRevenue = Literal[ + "0_99999", + "100000_999999", + "1000000_9999999", + "10000000_49999999", + "50000000_249999999", + "250000000_plus", +] + +SourceOfWealth = Literal[ + "business_dividends_or_profits", + "investments", + "asset_sales", + "client_investor_contributions", + "gambling", + "charitable_contributions", + "inheritance", + "affiliate_or_royalty_income", +] + +SourceOfFundsDocType = Literal[ + "business_income", + "gambling_proceeds", + "gifts", + "government_benefits", + "inheritance", + "investment_loans", + "pension_retirement", + "salary", + "sale_of_assets_real_estate", + "savings", + "esops", + "investment_proceeds", + "someone_else_funds", +] + +SupportingDocumentType = Literal[ + "individual_bank_statement", + "individual_tax_return", + "individual_proof_of_income", + "business_bank_statement", + "business_financial_statements", + "business_tax_return", +] + + +class AdditionalInfoItem(TypedDict): + key: str + value: str + + +class CreateCustomerInput(TypedDict): + type: Literal["individual", "business"] + kyc_type: KycType + email: str + tax_id: Optional[str] + address_line_1: Optional[str] + address_line_2: Optional[str] + city: Optional[str] + state_province_region: Optional[str] + country: Country + postal_code: Optional[str] + ip_address: Optional[str] + image_url: Optional[str] + phone_number: Optional[str] + proof_of_address_doc_type: Optional[ProofOfAddressDocType] + proof_of_address_doc_file: Optional[str] + first_name: Optional[str] + last_name: Optional[str] + date_of_birth: Optional[str] + id_doc_country: Optional[Country] + id_doc_type: Optional[IdDocType] + id_doc_front_file: Optional[str] + id_doc_back_file: Optional[str] + legal_name: Optional[str] + alternate_name: Optional[str] + formation_date: Optional[str] + website: Optional[str] + owners: Optional[List[dict]] + incorporation_doc_file: Optional[str] + proof_of_ownership_doc_file: Optional[str] + source_of_funds_doc_type: Optional[SourceOfFundsDocType] + source_of_funds_doc_file: Optional[str] + selfie_file: Optional[str] + purpose_of_transactions: Optional[PurposeOfTransactions] + purpose_of_transactions_explanation: Optional[str] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + business_type: Optional[BusinessType] + business_description: Optional[str] + business_industry: Optional[BusinessIndustry] + estimated_annual_revenue: Optional[EstimatedAnnualRevenue] + source_of_wealth: Optional[SourceOfWealth] + publicly_traded: Optional[bool] + occupation: Optional[str] + external_id: Optional[str] + tos_id: Optional[str] + additional_info: Optional[List[AdditionalInfoItem]] + + +class CreateCustomerResponse(TypedDict): + id: str + customer_id: str + + +class UpdateCustomerInput(TypedDict): + email: str + tax_id: Optional[str] + address_line_1: Optional[str] + address_line_2: Optional[str] + city: Optional[str] + state_province_region: Optional[str] + country: Country + postal_code: Optional[str] + ip_address: Optional[str] + image_url: Optional[str] + phone_number: Optional[str] + proof_of_address_doc_type: Optional[ProofOfAddressDocType] + proof_of_address_doc_file: Optional[str] + first_name: Optional[str] + last_name: Optional[str] + date_of_birth: Optional[str] + id_doc_country: Optional[Country] + id_doc_type: Optional[IdDocType] + id_doc_front_file: Optional[str] + id_doc_back_file: Optional[str] + legal_name: Optional[str] + alternate_name: Optional[str] + formation_date: Optional[str] + website: Optional[str] + owners: Optional[List[dict]] + incorporation_doc_file: Optional[str] + proof_of_ownership_doc_file: Optional[str] + source_of_funds_doc_type: Optional[SourceOfFundsDocType] + source_of_funds_doc_file: Optional[str] + selfie_file: Optional[str] + purpose_of_transactions: Optional[PurposeOfTransactions] + purpose_of_transactions_explanation: Optional[str] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + business_type: Optional[BusinessType] + business_description: Optional[str] + business_industry: Optional[BusinessIndustry] + estimated_annual_revenue: Optional[EstimatedAnnualRevenue] + source_of_wealth: Optional[SourceOfWealth] + publicly_traded: Optional[bool] + occupation: Optional[str] + external_id: Optional[str] + tos_id: Optional[str] + additional_info: Optional[List[AdditionalInfoItem]] + + +class Customer(TypedDict): + id: str + instance_id: str + customer_id: str + type: Literal["individual", "business"] + kyc_type: KycType + kyc_status: KycStatus + email: str + tax_id: Optional[str] + address_line_1: Optional[str] + address_line_2: Optional[str] + city: Optional[str] + state_province_region: Optional[str] + country: Country + postal_code: Optional[str] + ip_address: Optional[str] + image_url: Optional[str] + phone_number: Optional[str] + proof_of_address_doc_type: Optional[ProofOfAddressDocType] + proof_of_address_doc_file: Optional[str] + first_name: Optional[str] + last_name: Optional[str] + date_of_birth: Optional[str] + id_doc_country: Optional[Country] + id_doc_type: Optional[IdDocType] + id_doc_front_file: Optional[str] + id_doc_back_file: Optional[str] + legal_name: Optional[str] + alternate_name: Optional[str] + formation_date: Optional[str] + website: Optional[str] + owners: Optional[List[dict]] + incorporation_doc_file: Optional[str] + proof_of_ownership_doc_file: Optional[str] + source_of_funds_doc_type: Optional[SourceOfFundsDocType] + source_of_funds_doc_file: Optional[str] + selfie_file: Optional[str] + purpose_of_transactions: Optional[PurposeOfTransactions] + purpose_of_transactions_explanation: Optional[str] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + business_type: Optional[BusinessType] + business_description: Optional[str] + business_industry: Optional[BusinessIndustry] + estimated_annual_revenue: Optional[EstimatedAnnualRevenue] + source_of_wealth: Optional[SourceOfWealth] + publicly_traded: Optional[bool] + occupation: Optional[str] + external_id: Optional[str] + tos_id: Optional[str] + additional_info: Optional[List[AdditionalInfoItem]] + created_at: str + updated_at: str + + +class ListCustomersResponse(TypedDict): + data: List[Customer] + pagination: PaginationMetadata + + +class LimitIncreaseRequestInput(TypedDict): + per_transaction: Optional[int] + daily: Optional[int] + monthly: Optional[int] + supporting_document_type: SupportingDocumentType + supporting_document_file: str + + +class LimitIncreaseRequestResponse(TypedDict): + id: str + + +class RfiSection(TypedDict): + id: str + type: str + title: str + description: Optional[str] + fields: List[dict] + + +class Rfi(TypedDict): + id: str + receiver_id: str + instance_id: str + status: Literal["pending", "submitted", "expired", "cancelled"] + request: List[RfiSection] + response: dict + expires_at: str + submitted_at: Optional[str] + created_at: str + receiver_type: Literal["individual", "business"] + receiver_aiprise_session_id: Optional[str] + receiver_aiprise_user_profile_id: Optional[str] + receiver_kyc_status: str + + +class GetCustomerLimitsResponse(TypedDict): + limits: dict + + +class Success(TypedDict): + success: bool + + +class CustomersResource: + def __init__(self, instance_id: str, client: InternalApiClient): + self._instance_id = instance_id + self._client = client + + async def list(self, params: Optional[PaginationParams] = None) -> BlindpayApiResponse[ListCustomersResponse]: + query_string = "" + if params: + filtered_params = {k: v for k, v in params.items() if v is not None} + if filtered_params: + query_string = f"?{urlencode(filtered_params)}" + return await self._client.get(f"/instances/{self._instance_id}/customers{query_string}") + + async def create(self, data: CreateCustomerInput) -> BlindpayApiResponse[CreateCustomerResponse]: + return await self._client.post(f"/instances/{self._instance_id}/customers", data) + + async def get(self, customer_id: str) -> BlindpayApiResponse[Customer]: + return await self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}") + + async def update(self, customer_id: str, data: UpdateCustomerInput) -> BlindpayApiResponse[Success]: + return await self._client.put(f"/instances/{self._instance_id}/customers/{customer_id}", data) + + async def delete(self, customer_id: str) -> BlindpayApiResponse[Success]: + return await self._client.delete(f"/instances/{self._instance_id}/customers/{customer_id}") + + async def request_limit_increase( + self, customer_id: str, data: LimitIncreaseRequestInput + ) -> BlindpayApiResponse[LimitIncreaseRequestResponse]: + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/limit-increase", data) + + async def get_limit_increase_requests(self, customer_id: str) -> BlindpayApiResponse[List[dict]]: + return await self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/limit-increase") + + async def get_rfi(self, customer_id: str) -> BlindpayApiResponse[Rfi]: + return await self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/rfi") + + async def submit_rfi(self, customer_id: str, data: dict) -> BlindpayApiResponse[None]: + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/rfi", data) + + +class CustomersResourceSync: + def __init__(self, instance_id: str, client: InternalApiClientSync): + self._instance_id = instance_id + self._client = client + + def list(self, params: Optional[PaginationParams] = None) -> BlindpayApiResponse[ListCustomersResponse]: + query_string = "" + if params: + filtered_params = {k: v for k, v in params.items() if v is not None} + if filtered_params: + query_string = f"?{urlencode(filtered_params)}" + return self._client.get(f"/instances/{self._instance_id}/customers{query_string}") + + def create(self, data: CreateCustomerInput) -> BlindpayApiResponse[CreateCustomerResponse]: + return self._client.post(f"/instances/{self._instance_id}/customers", data) + + def get(self, customer_id: str) -> BlindpayApiResponse[Customer]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}") + + def update(self, customer_id: str, data: UpdateCustomerInput) -> BlindpayApiResponse[Success]: + return self._client.put(f"/instances/{self._instance_id}/customers/{customer_id}", data) + + def delete(self, customer_id: str) -> BlindpayApiResponse[Success]: + return self._client.delete(f"/instances/{self._instance_id}/customers/{customer_id}") + + def request_limit_increase( + self, customer_id: str, data: LimitIncreaseRequestInput + ) -> BlindpayApiResponse[LimitIncreaseRequestResponse]: + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/limit-increase", data) + + def get_limit_increase_requests(self, customer_id: str) -> BlindpayApiResponse[List[dict]]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/limit-increase") + + def get_rfi(self, customer_id: str) -> BlindpayApiResponse[Rfi]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/rfi") + + def submit_rfi(self, customer_id: str, data: dict) -> BlindpayApiResponse[None]: + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/rfi", data) + + +def create_customers_resource(instance_id: str, client: InternalApiClient) -> CustomersResource: + return CustomersResource(instance_id, client) + + +def create_customers_resource_sync(instance_id: str, client: InternalApiClientSync) -> CustomersResourceSync: + return CustomersResourceSync(instance_id, client) \ No newline at end of file diff --git a/src/blindpay/resources/customers/offramp_wallets.py b/src/blindpay/resources/customers/offramp_wallets.py new file mode 100644 index 0000000..4caa862 --- /dev/null +++ b/src/blindpay/resources/customers/offramp_wallets.py @@ -0,0 +1,90 @@ +from typing import List, Optional + +from typing_extensions import TypedDict + +from ..._internal.api_client import InternalApiClient, InternalApiClientSync +from ...types import BlindpayApiResponse, Network + + +class CreateOfframpWalletInput(TypedDict): + external_id: Optional[str] + network: Network + + +class CreateOfframpWalletResponse(TypedDict): + id: str + external_id: Optional[str] + circle_wallet_id: Optional[str] + network: Network + address: str + + +class OfframpWallet(TypedDict): + id: str + external_id: Optional[str] + instance_id: str + receiver_id: str + bank_account_id: str + circle_wallet_id: Optional[str] + network: Network + address: str + created_at: str + updated_at: str + + +class ListOfframpWalletsResponse(TypedDict): + data: List[OfframpWallet] + + +class CustomerOfframpWalletsResource: + def __init__(self, instance_id: str, client: InternalApiClient): + self._instance_id = instance_id + self._client = client + + async def create( + self, customer_id: str, bank_account_id: str, data: CreateOfframpWalletInput + ) -> BlindpayApiResponse[CreateOfframpWalletResponse]: + return await self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets", data + ) + + async def list(self, customer_id: str, bank_account_id: str) -> BlindpayApiResponse[ListOfframpWalletsResponse]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets" + ) + + async def get(self, customer_id: str, bank_account_id: str, offramp_wallet_id: str) -> BlindpayApiResponse[OfframpWallet]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets/{offramp_wallet_id}" + ) + + +class CustomerOfframpWalletsResourceSync: + def __init__(self, instance_id: str, client: InternalApiClientSync): + self._instance_id = instance_id + self._client = client + + def create( + self, customer_id: str, bank_account_id: str, data: CreateOfframpWalletInput + ) -> BlindpayApiResponse[CreateOfframpWalletResponse]: + return self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets", data + ) + + def list(self, customer_id: str, bank_account_id: str) -> BlindpayApiResponse[ListOfframpWalletsResponse]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets" + ) + + def get(self, customer_id: str, bank_account_id: str, offramp_wallet_id: str) -> BlindpayApiResponse[OfframpWallet]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets/{offramp_wallet_id}" + ) + + +def create_customer_offramp_wallets_resource(instance_id: str, client: InternalApiClient) -> CustomerOfframpWalletsResource: + return CustomerOfframpWalletsResource(instance_id, client) + + +def create_customer_offramp_wallets_resource_sync(instance_id: str, client: InternalApiClientSync) -> CustomerOfframpWalletsResourceSync: + return CustomerOfframpWalletsResourceSync(instance_id, client) \ No newline at end of file diff --git a/src/blindpay/resources/customers/virtual_accounts.py b/src/blindpay/resources/customers/virtual_accounts.py new file mode 100644 index 0000000..ff61bad --- /dev/null +++ b/src/blindpay/resources/customers/virtual_accounts.py @@ -0,0 +1,121 @@ +from typing import List, Literal, Optional + +from typing_extensions import TypedDict + +from ..._internal.api_client import InternalApiClient, InternalApiClientSync +from ...types import BankingPartner, BlindpayApiResponse, StablecoinToken + +VirtualAccountKycStatus = Literal[ + "verifying", "approved", "rejected", "deprecated", "pending_review", "awaiting_contract", "compliance_request" +] + +SoleProprietorDocType = Literal["master_service_agreement", "salary_slip", "bank_statement"] + + +class CreateVirtualAccountInput(TypedDict): + banking_partner: BankingPartner + token: StablecoinToken + blockchain_wallet_id: str + sole_proprietor_doc_type: Optional[SoleProprietorDocType] + sole_proprietor_doc_file: Optional[str] + + +class UpdateVirtualAccountInput(TypedDict): + token: StablecoinToken + blockchain_wallet_id: str + + +class VirtualAccountUsDetails(TypedDict): + routing_number: str + account_number: str + account_name: str + bank_name: str + bank_address: str + + +class VirtualAccount(TypedDict): + id: str + banking_partner: BankingPartner + kyc_status: VirtualAccountKycStatus + us: VirtualAccountUsDetails + token: StablecoinToken + blockchain_wallet_id: Optional[str] + blockchain_wallet: Optional[dict] + created_at: str + updated_at: str + + +class ListVirtualAccountsResponse(TypedDict): + data: List[VirtualAccount] + + +class Success(TypedDict): + success: bool + + +class CustomerVirtualAccountsResource: + def __init__(self, instance_id: str, client: InternalApiClient): + self._instance_id = instance_id + self._client = client + + async def create( + self, customer_id: str, data: CreateVirtualAccountInput + ) -> BlindpayApiResponse[VirtualAccount]: + return await self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts", data + ) + + async def list(self, customer_id: str) -> BlindpayApiResponse[ListVirtualAccountsResponse]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts" + ) + + async def get(self, customer_id: str, virtual_account_id: str) -> BlindpayApiResponse[VirtualAccount]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts/{virtual_account_id}" + ) + + async def update( + self, customer_id: str, virtual_account_id: str, data: UpdateVirtualAccountInput + ) -> BlindpayApiResponse[Success]: + return await self._client.put( + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts/{virtual_account_id}", data + ) + + +class CustomerVirtualAccountsResourceSync: + def __init__(self, instance_id: str, client: InternalApiClientSync): + self._instance_id = instance_id + self._client = client + + def create( + self, customer_id: str, data: CreateVirtualAccountInput + ) -> BlindpayApiResponse[VirtualAccount]: + return self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts", data + ) + + def list(self, customer_id: str) -> BlindpayApiResponse[ListVirtualAccountsResponse]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts" + ) + + def get(self, customer_id: str, virtual_account_id: str) -> BlindpayApiResponse[VirtualAccount]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts/{virtual_account_id}" + ) + + def update( + self, customer_id: str, virtual_account_id: str, data: UpdateVirtualAccountInput + ) -> BlindpayApiResponse[Success]: + return self._client.put( + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts/{virtual_account_id}", data + ) + + +def create_customer_virtual_accounts_resource(instance_id: str, client: InternalApiClient) -> CustomerVirtualAccountsResource: + return CustomerVirtualAccountsResource(instance_id, client) + + +def create_customer_virtual_accounts_resource_sync(instance_id: str, client: InternalApiClientSync) -> CustomerVirtualAccountsResourceSync: + return CustomerVirtualAccountsResourceSync(instance_id, client) \ No newline at end of file diff --git a/src/blindpay/resources/customers/wallets.py b/src/blindpay/resources/customers/wallets.py new file mode 100644 index 0000000..b63736a --- /dev/null +++ b/src/blindpay/resources/customers/wallets.py @@ -0,0 +1,114 @@ +from typing import List, Optional + +from typing_extensions import TypedDict + +from ..._internal.api_client import InternalApiClient, InternalApiClientSync +from ...types import BlindpayApiResponse, Network, StablecoinToken + + +class CreateWalletInput(TypedDict): + network: Network + external_id: Optional[str] + name: str + + +class Wallet(TypedDict): + id: str + name: str + external_id: Optional[str] + address: Optional[str] + network: Network + created_at: str + + +class ListWalletsResponse(TypedDict): + data: List[Wallet] + + +class TokenBalance(TypedDict): + amount: str + formatted: str + + +class WalletBalance(TypedDict): + USDC: TokenBalance + USDT: TokenBalance + USDB: TokenBalance + + +class Success(TypedDict): + success: bool + + +class CustomerWalletsResource: + def __init__(self, instance_id: str, client: InternalApiClient): + self._instance_id = instance_id + self._client = client + + async def create( + self, customer_id: str, data: CreateWalletInput + ) -> BlindpayApiResponse[Wallet]: + return await self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets", data + ) + + async def list(self, customer_id: str) -> BlindpayApiResponse[ListWalletsResponse]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets" + ) + + async def get(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[Wallet]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets/{wallet_id}" + ) + + async def delete(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[Success]: + return await self._client.delete( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets/{wallet_id}" + ) + + async def get_balance(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[WalletBalance]: + return await self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets/{wallet_id}/balance" + ) + + +class CustomerWalletsResourceSync: + def __init__(self, instance_id: str, client: InternalApiClientSync): + self._instance_id = instance_id + self._client = client + + def create( + self, customer_id: str, data: CreateWalletInput + ) -> BlindpayApiResponse[Wallet]: + return self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets", data + ) + + def list(self, customer_id: str) -> BlindpayApiResponse[ListWalletsResponse]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets" + ) + + def get(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[Wallet]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets/{wallet_id}" + ) + + def delete(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[Success]: + return self._client.delete( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets/{wallet_id}" + ) + + def get_balance(self, customer_id: str, wallet_id: str) -> BlindpayApiResponse[WalletBalance]: + return self._client.get( + f"/instances/{self._instance_id}/customers/{customer_id}/wallets/{wallet_id}/balance" + ) + + +def create_customer_wallets_resource(instance_id: str, client: InternalApiClient) -> CustomerWalletsResource: + return CustomerWalletsResource(instance_id, client) + + +def create_customer_wallets_resource_sync(instance_id: str, client: InternalApiClientSync) -> CustomerWalletsResourceSync: + return CustomerWalletsResourceSync(instance_id, client) \ No newline at end of file diff --git a/src/blindpay/resources/ownership/__init__.py b/src/blindpay/resources/ownership/__init__.py new file mode 100644 index 0000000..61ed1ff --- /dev/null +++ b/src/blindpay/resources/ownership/__init__.py @@ -0,0 +1,17 @@ +from .ownership import ( + MigrateInstanceOwnershipInput, + MigrateInstanceOwnershipResponse, + OwnershipResource, + OwnershipResourceSync, + create_ownership_resource, + create_ownership_resource_sync, +) + +__all__ = [ + "OwnershipResource", + "OwnershipResourceSync", + "create_ownership_resource", + "create_ownership_resource_sync", + "MigrateInstanceOwnershipInput", + "MigrateInstanceOwnershipResponse", +] \ No newline at end of file diff --git a/src/blindpay/resources/ownership/ownership.py b/src/blindpay/resources/ownership/ownership.py new file mode 100644 index 0000000..509cae2 --- /dev/null +++ b/src/blindpay/resources/ownership/ownership.py @@ -0,0 +1,42 @@ +from typing_extensions import TypedDict + +from ..._internal.api_client import InternalApiClient, InternalApiClientSync +from ...types import BlindpayApiResponse + + +class MigrateInstanceOwnershipInput(TypedDict): + user_id: str + + +class MigrateInstanceOwnershipResponse(TypedDict): + success: bool + + +class OwnershipResource: + def __init__(self, instance_id: str, client: InternalApiClient): + self._instance_id = instance_id + self._client = client + + async def migrate_instance_ownership( + self, data: MigrateInstanceOwnershipInput + ) -> BlindpayApiResponse[MigrateInstanceOwnershipResponse]: + return await self._client.post(f"/instances/{self._instance_id}/ownership", data) + + +class OwnershipResourceSync: + def __init__(self, instance_id: str, client: InternalApiClientSync): + self._instance_id = instance_id + self._client = client + + def migrate_instance_ownership( + self, data: MigrateInstanceOwnershipInput + ) -> BlindpayApiResponse[MigrateInstanceOwnershipResponse]: + return self._client.post(f"/instances/{self._instance_id}/ownership", data) + + +def create_ownership_resource(instance_id: str, client: InternalApiClient) -> OwnershipResource: + return OwnershipResource(instance_id, client) + + +def create_ownership_resource_sync(instance_id: str, client: InternalApiClientSync) -> OwnershipResourceSync: + return OwnershipResourceSync(instance_id, client) \ No newline at end of file diff --git a/src/blindpay/resources/payouts/payouts.py b/src/blindpay/resources/payouts/payouts.py index b8db790..ee03f31 100644 --- a/src/blindpay/resources/payouts/payouts.py +++ b/src/blindpay/resources/payouts/payouts.py @@ -96,6 +96,9 @@ class Payout(TypedDict): ted_bank_code: Optional[str] ted_branch_code: Optional[str] ted_cpf_cnpj: Optional[str] + billing_fee_amount: Optional[float] + cpn_payment_id: Optional[str] + sender_legal_name: Optional[str] class ListPayoutsInput(PaginationParams, total=False): diff --git a/src/blindpay/types.py b/src/blindpay/types.py index 5e85ffa..98ca7dc 100644 --- a/src/blindpay/types.py +++ b/src/blindpay/types.py @@ -392,3 +392,17 @@ class TrackingPartnerFee(TypedDict): PaymentMethod = Literal["ach", "wire", "pix", "spei", "transfers", "pse", "international_swift", "rtp", "ted"] KycStatus = Literal["awaiting_contract", "compliance_request"] + +AipriseDocumentType = Literal[ + "ADDRESS_PROOF_DOCUMENT", + "BANK_STATEMENT_DOCUMENT", + "OTHER", + "SOURCE_OF_FUNDS_DOCUMENT", + "TAX_CERTIFICATE", + "USER_SELFIE", + "VISA_DOCUMENT", +] + +ManualExecutionStatus = Literal["failed"] + +WebhookEvent = Literal["receiver.delete"]