From 0420081d9adf881820a218edcaf8914acdc8fb20 Mon Sep 17 00:00:00 2001 From: alvseven Date: Mon, 8 Jun 2026 13:09:46 -0300 Subject: [PATCH 1/3] feat: add customers resource, deprecate receivers (v2.3.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors blindpay-node v3.11.0: - New customers resource (parallel to receivers, targets /customers/* API paths) - Sub-resources (bank_accounts, virtual_accounts, wallets, custodial_wallets) now hit /customers/{customer_id}/... and accept customer_id input — the receivers namespace still works because both /receivers/* and /customers/* paths are live on the API through the deprecation window - client.customers + client_sync.customers accessors added - client.receivers emits DeprecationWarning on access - v3.0.0 will remove receivers entirely https://www.blindpay.com/changelog/2026-06-04-customers-rename --- src/blindpay/__init__.py | 2 +- src/blindpay/client.py | 61 +- .../resources/bank_accounts/bank_accounts.py | 166 ++-- .../custodial_wallets/custodial_wallets.py | 40 +- src/blindpay/resources/customers/__init__.py | 104 +++ src/blindpay/resources/customers/customers.py | 859 ++++++++++++++++++ .../virtual_accounts/virtual_accounts.py | 38 +- src/blindpay/resources/wallets/blockchain.py | 66 +- src/blindpay/resources/wallets/offramp.py | 36 +- tests/resources/test_bank_accounts.py | 36 +- tests/resources/test_blockchain_wallets.py | 56 +- tests/resources/test_custodial_wallets.py | 48 +- tests/resources/test_offramp_wallets.py | 32 +- tests/resources/test_virtual_accounts.py | 16 +- 14 files changed, 1291 insertions(+), 269 deletions(-) create mode 100644 src/blindpay/resources/customers/__init__.py create mode 100644 src/blindpay/resources/customers/customers.py diff --git a/src/blindpay/__init__.py b/src/blindpay/__init__.py index 2372588..30133f4 100644 --- a/src/blindpay/__init__.py +++ b/src/blindpay/__init__.py @@ -1,4 +1,4 @@ -__version__ = "2.2.0" +__version__ = "2.3.0" from ._internal.exceptions import BlindPayError from .client import BlindPay, BlindPaySync diff --git a/src/blindpay/client.py b/src/blindpay/client.py index 78c3c57..f7eec5c 100644 --- a/src/blindpay/client.py +++ b/src/blindpay/client.py @@ -1,6 +1,7 @@ import base64 import hashlib import hmac +import warnings from functools import cached_property from typing import TYPE_CHECKING, Any, Dict, Literal, Mapping, Optional, TypeVar @@ -24,6 +25,7 @@ from blindpay.resources.payins.quotes import PayinQuotesResource, PayinQuotesResourceSync from blindpay.resources.payouts.payouts import PayoutsResource, PayoutsResourceSync from blindpay.resources.quotes.quotes import QuotesResource, QuotesResourceSync + from blindpay.resources.customers.customers import CustomersResource, CustomersResourceSync from blindpay.resources.receivers.receivers import ReceiversResource, ReceiversResourceSync from blindpay.resources.terms_of_service.terms_of_service import ( TermsOfServiceResource, @@ -39,7 +41,12 @@ from blindpay.resources.wallets.offramp import OfframpWalletsResource, OfframpWalletsResourceSync from blindpay.resources.webhooks.webhooks import WebhookEndpointsResource, WebhookEndpointsResourceSync -__version__ = "2.2.0" +__version__ = "2.3.0" + +_RECEIVERS_DEPRECATION_MESSAGE = ( + "Use 'customers' instead. 'receivers' is deprecated and will be removed in " + "v3.0.0. See https://www.blindpay.com/changelog/2026-06-04-customers-rename" +) T = TypeVar("T") @@ -203,6 +210,27 @@ 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) -> "BankAccountsResource": + from blindpay.resources.bank_accounts.bank_accounts import create_bank_accounts_resource + + return create_bank_accounts_resource(self._instance_id, self._api) + + def __getattr__(self, name: str) -> Any: + return getattr(self._base, name) + + class _ReceiversNamespace: def __init__(self, instance_id: str, api_client: ApiClientImpl) -> None: self._instance_id = instance_id @@ -305,8 +333,13 @@ def payouts(self) -> "PayoutsResource": return create_payouts_resource(self._instance_id, self._api) + @cached_property + def customers(self) -> _CustomersNamespace: + return _CustomersNamespace(self._instance_id, self._api) + @cached_property def receivers(self) -> _ReceiversNamespace: + warnings.warn(_RECEIVERS_DEPRECATION_MESSAGE, DeprecationWarning, stacklevel=2) return _ReceiversNamespace(self._instance_id, self._api) @cached_property @@ -433,6 +466,27 @@ 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) -> "BankAccountsResourceSync": + from blindpay.resources.bank_accounts.bank_accounts import create_bank_accounts_resource_sync + + return create_bank_accounts_resource_sync(self._instance_id, self._api) + + def __getattr__(self, name: str) -> Any: + return getattr(self._base, name) + + class _ReceiversNamespaceSync: def __init__(self, instance_id: str, api_client: ApiClientImplSync) -> None: self._instance_id = instance_id @@ -535,8 +589,13 @@ def payouts(self) -> "PayoutsResourceSync": return create_payouts_resource_sync(self._instance_id, self._api) + @cached_property + def customers(self) -> _CustomersNamespaceSync: + return _CustomersNamespaceSync(self._instance_id, self._api) + @cached_property def receivers(self) -> _ReceiversNamespaceSync: + warnings.warn(_RECEIVERS_DEPRECATION_MESSAGE, DeprecationWarning, stacklevel=2) return _ReceiversNamespaceSync(self._instance_id, self._api) @cached_property diff --git a/src/blindpay/resources/bank_accounts/bank_accounts.py b/src/blindpay/resources/bank_accounts/bank_accounts.py index 246701d..1e52e42 100644 --- a/src/blindpay/resources/bank_accounts/bank_accounts.py +++ b/src/blindpay/resources/bank_accounts/bank_accounts.py @@ -97,7 +97,7 @@ class ListBankAccountsResponse(TypedDict): class GetBankAccountResponse(TypedDict): id: str - receiver_id: str + customer_id: str account_holder_name: str account_number: str routing_number: str @@ -111,7 +111,7 @@ class GetBankAccountResponse(TypedDict): class CreatePixInput(TypedDict): - receiver_id: str + customer_id: str name: str pix_key: str @@ -125,7 +125,7 @@ class CreatePixResponse(TypedDict): class CreateArgentinaTransfersInput(TypedDict): - receiver_id: str + customer_id: str name: str beneficiary_name: str transfers_account: str @@ -143,7 +143,7 @@ class CreateArgentinaTransfersResponse(TypedDict): class CreateSpeiInput(TypedDict): - receiver_id: str + customer_id: str beneficiary_name: str name: str spei_clabe: str @@ -163,7 +163,7 @@ class CreateSpeiResponse(TypedDict): class CreateColombiaAchInput(TypedDict): - receiver_id: str + customer_id: str name: str account_type: BankAccountType ach_cop_beneficiary_first_name: str @@ -191,7 +191,7 @@ class CreateColombiaAchResponse(TypedDict): class _CreateAchInputRequired(TypedDict): - receiver_id: str + customer_id: str class CreateAchInput(_CreateAchInputRequired, total=False): @@ -240,7 +240,7 @@ class CreateAchResponse(TypedDict): class _CreateWireInputRequired(TypedDict): - receiver_id: str + customer_id: str class CreateWireInput(_CreateWireInputRequired, total=False): @@ -279,7 +279,7 @@ class CreateWireResponse(TypedDict): class _CreateInternationalSwiftInputRequired(TypedDict): - receiver_id: str + customer_id: str class CreateInternationalSwiftInput(_CreateInternationalSwiftInputRequired, total=False): @@ -348,7 +348,7 @@ class CreateInternationalSwiftResponse(TypedDict): class _CreateRtpInputRequired(TypedDict): - receiver_id: str + customer_id: str class CreateRtpInput(_CreateRtpInputRequired, total=False): @@ -387,7 +387,7 @@ class CreateRtpResponse(TypedDict): class CreatePixSafeInput(TypedDict): - receiver_id: str + customer_id: str name: str account_number: str account_type: BankAccountType @@ -409,7 +409,7 @@ class CreatePixSafeResponse(TypedDict): class CreateTedInput(TypedDict): - receiver_id: str + customer_id: str name: str account_number: str account_type: BankAccountType @@ -435,78 +435,78 @@ def __init__(self, instance_id: str, client: InternalApiClient): self._instance_id = instance_id self._client = client - async def list(self, receiver_id: str) -> BlindpayApiResponse[ListBankAccountsResponse]: - return await self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts") + 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, receiver_id: str, id: str) -> BlindpayApiResponse[GetBankAccountResponse]: - return await self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{id}") + async def get(self, customer_id: str, id: str) -> BlindpayApiResponse[GetBankAccountResponse]: + return await self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{id}") - async def delete(self, receiver_id: str, id: str) -> BlindpayApiResponse[None]: - return await self._client.delete(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{id}") + async def delete(self, customer_id: str, id: str) -> BlindpayApiResponse[None]: + return await self._client.delete(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{id}") async def create_pix(self, data: CreatePixInput) -> BlindpayApiResponse[CreatePixResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "pix" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_argentina_transfers( self, data: CreateArgentinaTransfersInput ) -> BlindpayApiResponse[CreateArgentinaTransfersResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "transfers_bitso" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_spei(self, data: CreateSpeiInput) -> BlindpayApiResponse[CreateSpeiResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "spei_bitso" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_colombia_ach(self, data: CreateColombiaAchInput) -> BlindpayApiResponse[CreateColombiaAchResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "ach_cop_bitso" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_ach(self, data: CreateAchInput) -> BlindpayApiResponse[CreateAchResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "ach" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_wire(self, data: CreateWireInput) -> BlindpayApiResponse[CreateWireResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "wire" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_international_swift( self, data: CreateInternationalSwiftInput ) -> BlindpayApiResponse[CreateInternationalSwiftResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "international_swift" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_rtp(self, data: CreateRtpInput) -> BlindpayApiResponse[CreateRtpResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "rtp" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_pix_safe(self, data: CreatePixSafeInput) -> BlindpayApiResponse[CreatePixSafeResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "pix_safe" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) async def create_ted(self, data: CreateTedInput) -> BlindpayApiResponse[CreateTedResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "ted" - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) class BankAccountsResourceSync: @@ -514,78 +514,78 @@ def __init__(self, instance_id: str, client: InternalApiClientSync): self._instance_id = instance_id self._client = client - def list(self, receiver_id: str) -> BlindpayApiResponse[ListBankAccountsResponse]: - return self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts") + 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, receiver_id: str, id: str) -> BlindpayApiResponse[GetBankAccountResponse]: - return self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{id}") + def get(self, customer_id: str, id: str) -> BlindpayApiResponse[GetBankAccountResponse]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{id}") - def delete(self, receiver_id: str, id: str) -> BlindpayApiResponse[None]: - return self._client.delete(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{id}") + def delete(self, customer_id: str, id: str) -> BlindpayApiResponse[None]: + return self._client.delete(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{id}") def create_pix(self, data: CreatePixInput) -> BlindpayApiResponse[CreatePixResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "pix" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_argentina_transfers( self, data: CreateArgentinaTransfersInput ) -> BlindpayApiResponse[CreateArgentinaTransfersResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "transfers_bitso" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_spei(self, data: CreateSpeiInput) -> BlindpayApiResponse[CreateSpeiResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "spei_bitso" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_colombia_ach(self, data: CreateColombiaAchInput) -> BlindpayApiResponse[CreateColombiaAchResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "ach_cop_bitso" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_ach(self, data: CreateAchInput) -> BlindpayApiResponse[CreateAchResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "ach" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_wire(self, data: CreateWireInput) -> BlindpayApiResponse[CreateWireResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "wire" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_international_swift( self, data: CreateInternationalSwiftInput ) -> BlindpayApiResponse[CreateInternationalSwiftResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "international_swift" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_rtp(self, data: CreateRtpInput) -> BlindpayApiResponse[CreateRtpResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "rtp" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_pix_safe(self, data: CreatePixSafeInput) -> BlindpayApiResponse[CreatePixSafeResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "pix_safe" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_ted(self, data: CreateTedInput) -> BlindpayApiResponse[CreateTedResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["type"] = "ted" - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) def create_bank_accounts_resource(instance_id: str, client: InternalApiClient) -> BankAccountsResource: diff --git a/src/blindpay/resources/custodial_wallets/custodial_wallets.py b/src/blindpay/resources/custodial_wallets/custodial_wallets.py index 923a35e..9fb969e 100644 --- a/src/blindpay/resources/custodial_wallets/custodial_wallets.py +++ b/src/blindpay/resources/custodial_wallets/custodial_wallets.py @@ -8,7 +8,7 @@ class CustodialWallet(TypedDict): id: str - receiver_id: str + customer_id: str instance_id: str network: Network address: str @@ -28,7 +28,7 @@ class CustodialWalletBalance(TypedDict): class CreateCustodialWalletInput(TypedDict): - receiver_id: str + customer_id: str network: Network @@ -37,12 +37,12 @@ class CreateCustodialWalletInput(TypedDict): class GetCustodialWalletInput(TypedDict): - receiver_id: str + customer_id: str id: str class DeleteCustodialWalletInput(TypedDict): - receiver_id: str + customer_id: str id: str @@ -51,27 +51,27 @@ def __init__(self, instance_id: str, client: InternalApiClient): self._instance_id = instance_id self._client = client - async def list(self, receiver_id: str) -> BlindpayApiResponse[ListCustodialWalletsResponse]: - return await self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/wallets") + async def list(self, customer_id: str) -> BlindpayApiResponse[ListCustodialWalletsResponse]: + return await self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/wallets") async def get(self, data: GetCustodialWalletInput) -> BlindpayApiResponse[CustodialWallet]: return await self._client.get( - f"/instances/{self._instance_id}/receivers/{data['receiver_id']}/wallets/{data['id']}" + f"/instances/{self._instance_id}/customers/{data['customer_id']}/wallets/{data['id']}" ) async def create(self, data: CreateCustodialWalletInput) -> BlindpayApiResponse[CreateCustodialWalletResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} - return await self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/wallets", payload) + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/wallets", payload) async def get_balance(self, data: GetCustodialWalletInput) -> BlindpayApiResponse[CustodialWalletBalance]: return await self._client.get( - f"/instances/{self._instance_id}/receivers/{data['receiver_id']}/wallets/{data['id']}/balance" + f"/instances/{self._instance_id}/customers/{data['customer_id']}/wallets/{data['id']}/balance" ) async def delete(self, data: DeleteCustodialWalletInput) -> BlindpayApiResponse[None]: return await self._client.delete( - f"/instances/{self._instance_id}/receivers/{data['receiver_id']}/wallets/{data['id']}" + f"/instances/{self._instance_id}/customers/{data['customer_id']}/wallets/{data['id']}" ) @@ -80,25 +80,25 @@ def __init__(self, instance_id: str, client: InternalApiClientSync): self._instance_id = instance_id self._client = client - def list(self, receiver_id: str) -> BlindpayApiResponse[ListCustodialWalletsResponse]: - return self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/wallets") + def list(self, customer_id: str) -> BlindpayApiResponse[ListCustodialWalletsResponse]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/wallets") def get(self, data: GetCustodialWalletInput) -> BlindpayApiResponse[CustodialWallet]: - return self._client.get(f"/instances/{self._instance_id}/receivers/{data['receiver_id']}/wallets/{data['id']}") + return self._client.get(f"/instances/{self._instance_id}/customers/{data['customer_id']}/wallets/{data['id']}") def create(self, data: CreateCustodialWalletInput) -> BlindpayApiResponse[CreateCustodialWalletResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/wallets", payload) + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/wallets", payload) def get_balance(self, data: GetCustodialWalletInput) -> BlindpayApiResponse[CustodialWalletBalance]: return self._client.get( - f"/instances/{self._instance_id}/receivers/{data['receiver_id']}/wallets/{data['id']}/balance" + f"/instances/{self._instance_id}/customers/{data['customer_id']}/wallets/{data['id']}/balance" ) def delete(self, data: DeleteCustodialWalletInput) -> BlindpayApiResponse[None]: return self._client.delete( - f"/instances/{self._instance_id}/receivers/{data['receiver_id']}/wallets/{data['id']}" + f"/instances/{self._instance_id}/customers/{data['customer_id']}/wallets/{data['id']}" ) diff --git a/src/blindpay/resources/customers/__init__.py b/src/blindpay/resources/customers/__init__.py new file mode 100644 index 0000000..795c44f --- /dev/null +++ b/src/blindpay/resources/customers/__init__.py @@ -0,0 +1,104 @@ +from .customers import ( + AccountPurpose, + AmlHits, + AmlStatus, + BusinessIndustry, + BusinessType, + BusinessWithStandardKYB, + CreateBusinessWithStandardKYBInput, + CreateBusinessWithStandardKYBResponse, + CreateIndividualWithEnhancedKYCInput, + CreateIndividualWithEnhancedKYCResponse, + CreateIndividualWithStandardKYCInput, + CreateIndividualWithStandardKYCResponse, + EnhancedKycType, + EstimatedAnnualRevenue, + FraudWarning, + GetLimitIncreaseRequestsResponse, + GetCustomerLimitsResponse, + GetCustomerResponse, + IdentificationDocument, + IndividualType, + IndividualWithEnhancedKYC, + IndividualWithStandardKYC, + KycType, + KycWarning, + LimitIncreaseRequest, + LimitIncreaseRequestStatus, + LimitIncreaseRequestSupportingDocumentType, + ListCustomersInput, + ListCustomersPaginatedResponse, + ListCustomersResponse, + Owner, + OwnerRole, + OwnerUpdate, + ProofOfAddressDocType, + PurposeOfTransactions, + CustomerBusinessType, + CustomersResource, + CustomersResourceSync, + CustomerStatus, + RequestLimitIncreaseInput, + RequestLimitIncreaseResponse, + SoleProprietorDocType, + SourceOfFundsDocType, + SourceOfWealth, + TaxType, + TransactionLimit, + UpdateCustomerInput, + create_customers_resource, + create_customers_resource_sync, +) + +__all__ = [ + "create_customers_resource", + "create_customers_resource_sync", + "CustomersResource", + "CustomersResourceSync", + "IndividualWithStandardKYC", + "IndividualWithEnhancedKYC", + "BusinessWithStandardKYB", + "CreateIndividualWithStandardKYCInput", + "CreateIndividualWithEnhancedKYCInput", + "CreateBusinessWithStandardKYBInput", + "UpdateCustomerInput", + "GetCustomerLimitsResponse", + "KycType", + "BusinessType", + "IndividualType", + "EnhancedKycType", + "ProofOfAddressDocType", + "PurposeOfTransactions", + "SourceOfFundsDocType", + "IdentificationDocument", + "OwnerRole", + "KycWarning", + "TransactionLimit", + "Owner", + "CreateBusinessWithStandardKYBResponse", + "CreateIndividualWithEnhancedKYCResponse", + "CreateIndividualWithStandardKYCResponse", + "ListCustomersResponse", + "GetCustomerResponse", + "OwnerUpdate", + "UpdateCustomerInput", + "LimitIncreaseRequest", + "LimitIncreaseRequestStatus", + "LimitIncreaseRequestSupportingDocumentType", + "GetLimitIncreaseRequestsResponse", + "RequestLimitIncreaseInput", + "RequestLimitIncreaseResponse", + "CustomerStatus", + "AccountPurpose", + "CustomerBusinessType", + "BusinessIndustry", + "EstimatedAnnualRevenue", + "SoleProprietorDocType", + "SourceOfWealth", + "TaxType", + "AmlStatus", + "FraudWarning", + "AmlHits", + "ListCustomersInput", + "ListCustomersPaginatedResponse", +] diff --git a/src/blindpay/resources/customers/customers.py b/src/blindpay/resources/customers/customers.py new file mode 100644 index 0000000..43b98cd --- /dev/null +++ b/src/blindpay/resources/customers/customers.py @@ -0,0 +1,859 @@ +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["awaiting_contract", "compliance_request"] + +ProofOfAddressDocType = Literal[ + "UTILITY_BILL", "BANK_STATEMENT", "RENTAL_AGREEMENT", "TAX_DOCUMENT", "GOVERNMENT_CORRESPONDENCE" +] + +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", +] + +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", +] + +SoleProprietorDocType = Literal["bank_statement", "master_service_agreement", "salary_slip"] + +IdentificationDocument = Literal["PASSPORT", "ID_CARD", "DRIVERS"] + +OwnerRole = Literal["beneficial_controlling", "beneficial_owner", "controlling_person"] + +LimitIncreaseRequestStatus = Literal["in_review", "approved", "rejected"] + +LimitIncreaseRequestSupportingDocumentType = Literal[ + "individual_bank_statement", + "individual_tax_return", + "individual_proof_of_income", + "business_bank_statement", + "business_financial_statements", + "business_tax_return", +] + +CustomerStatus = Literal["verifying", "approved", "rejected", "deprecated", "pending_review"] + +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", +] + +CustomerBusinessType = 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", + "2500000000_plus", +] + +SourceOfWealth = Literal[ + "business_dividends_or_profits", + "investments", + "asset_sales", + "client_investor_contributions", + "gambling", + "charitable_contributions", + "inheritance", + "affiliate_or_royalty_income", +] + +TaxType = Literal["SSN", "ITIN"] + +AmlStatus = Literal["clear", "hit", "error"] + + +class FraudWarning(TypedDict): + id: Optional[str] + name: Optional[str] + operation: Optional[str] + score: Optional[float] + + +class AmlHits(TypedDict): + has_sanction_match: bool + has_pep_match: bool + has_watchlist_match: bool + has_crimelist_match: bool + has_adversemedia_match: bool + + +class KycWarning(TypedDict): + code: Optional[str] + message: Optional[str] + resolution_status: Optional[str] + warning_id: Optional[str] + + +class TransactionLimit(TypedDict): + per_transaction: float + daily: float + monthly: float + + +class Owner(TypedDict): + id: str + instance_id: str + customer_id: str + role: OwnerRole + first_name: str + last_name: str + date_of_birth: str + tax_id: str + address_line_1: str + address_line_2: Optional[str] + city: str + state_province_region: str + country: Country + postal_code: str + id_doc_country: Country + id_doc_type: IdentificationDocument + id_doc_front_file: str + id_doc_back_file: Optional[str] + proof_of_address_doc_type: ProofOfAddressDocType + proof_of_address_doc_file: str + ownership_percentage: Optional[int] + title: Optional[str] + tax_type: Optional[TaxType] + + +class IndividualWithStandardKYC(TypedDict): + id: str + type: IndividualType + kyc_type: StandardKycType + kyc_status: KycStatus + kyc_warnings: Optional[List[KycWarning]] + email: str + tax_id: str + address_line_1: str + address_line_2: Optional[str] + city: str + state_province_region: str + country: Country + postal_code: str + ip_address: Optional[str] + image_url: Optional[str] + phone_number: str + proof_of_address_doc_type: ProofOfAddressDocType + proof_of_address_doc_file: str + first_name: str + last_name: str + date_of_birth: str + id_doc_country: Country + id_doc_type: IdentificationDocument + id_doc_front_file: str + id_doc_back_file: str + aiprise_validation_key: str + instance_id: str + tos_id: Optional[str] + created_at: str + updated_at: str + limit: TransactionLimit + fraud_warnings: Optional[List[FraudWarning]] + selfie_file: Optional[str] + is_fbo: Optional[bool] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + business_type: Optional[CustomerBusinessType] + 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] + aml_status: Optional[AmlStatus] + aml_hits: Optional[AmlHits] + is_tos_accepted: Optional[bool] + + +class IndividualWithEnhancedKYC(TypedDict): + id: str + type: IndividualType + kyc_type: EnhancedKycType + kyc_status: KycStatus + kyc_warnings: Optional[List[KycWarning]] + email: str + tax_id: str + address_line_1: str + address_line_2: Optional[str] + city: str + state_province_region: str + country: Country + postal_code: str + ip_address: Optional[str] + image_url: Optional[str] + phone_number: Optional[str] + proof_of_address_doc_type: ProofOfAddressDocType + proof_of_address_doc_file: str + first_name: str + last_name: str + date_of_birth: str + id_doc_country: Country + id_doc_type: IdentificationDocument + id_doc_front_file: str + id_doc_back_file: Optional[str] + aiprise_validation_key: str + instance_id: str + source_of_funds_doc_type: str + source_of_funds_doc_file: str + individual_holding_doc_front_file: str + purpose_of_transactions: PurposeOfTransactions + purpose_of_transactions_explanation: Optional[str] + tos_id: Optional[str] + created_at: str + updated_at: str + limit: TransactionLimit + fraud_warnings: Optional[List[FraudWarning]] + selfie_file: Optional[str] + is_fbo: Optional[bool] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + business_type: Optional[CustomerBusinessType] + 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] + aml_status: Optional[AmlStatus] + aml_hits: Optional[AmlHits] + is_tos_accepted: Optional[bool] + + +class BusinessWithStandardKYB(TypedDict): + id: str + type: BusinessType + kyc_type: StandardKycType + kyc_status: KycStatus + kyc_warnings: Optional[List[KycWarning]] + email: str + tax_id: str + address_line_1: str + address_line_2: Optional[str] + city: str + state_province_region: str + country: Country + postal_code: str + ip_address: Optional[str] + image_url: Optional[str] + phone_number: Optional[str] + proof_of_address_doc_type: ProofOfAddressDocType + proof_of_address_doc_file: str + legal_name: str + alternate_name: Optional[str] + formation_date: str + website: Optional[str] + owners: List[Owner] + incorporation_doc_file: str + proof_of_ownership_doc_file: str + external_id: Optional[str] + instance_id: str + tos_id: Optional[str] + aiprise_validation_key: str + created_at: str + updated_at: str + limit: TransactionLimit + fraud_warnings: Optional[List[FraudWarning]] + selfie_file: Optional[str] + is_fbo: Optional[bool] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + business_type: Optional[CustomerBusinessType] + 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] + aml_status: Optional[AmlStatus] + aml_hits: Optional[AmlHits] + is_tos_accepted: Optional[bool] + + +class CreateIndividualWithStandardKYCInput(TypedDict): + external_id: Optional[str] + address_line_1: str + address_line_2: Optional[str] + city: str + country: Country + date_of_birth: str + email: str + first_name: str + phone_number: Optional[str] + id_doc_country: Country + id_doc_front_file: str + id_doc_type: IdentificationDocument + id_doc_back_file: Optional[str] + last_name: str + postal_code: str + proof_of_address_doc_file: str + proof_of_address_doc_type: ProofOfAddressDocType + state_province_region: str + tax_id: str + tos_id: str + ip_address: Optional[str] + image_url: Optional[str] + selfie_file: Optional[str] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + occupation: Optional[str] + + +class CreateIndividualWithStandardKYCResponse(TypedDict): + id: str + + +class CreateIndividualWithEnhancedKYCInput(TypedDict): + external_id: Optional[str] + address_line_1: str + address_line_2: Optional[str] + city: str + country: Country + date_of_birth: str + email: str + first_name: str + id_doc_country: Country + id_doc_front_file: str + id_doc_type: IdentificationDocument + id_doc_back_file: Optional[str] + individual_holding_doc_front_file: str + last_name: str + postal_code: str + phone_number: Optional[str] + proof_of_address_doc_file: str + proof_of_address_doc_type: ProofOfAddressDocType + purpose_of_transactions: PurposeOfTransactions + source_of_funds_doc_file: str + source_of_funds_doc_type: SourceOfFundsDocType + purpose_of_transactions_explanation: Optional[str] + state_province_region: str + tax_id: str + tos_id: str + ip_address: Optional[str] + image_url: Optional[str] + selfie_file: Optional[str] + is_fbo: Optional[bool] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + occupation: Optional[str] + + +class CreateIndividualWithEnhancedKYCResponse(TypedDict): + id: str + + +class CreateBusinessWithStandardKYBInput(TypedDict): + external_id: Optional[str] + address_line_1: str + address_line_2: Optional[str] + alternate_name: str + city: str + country: Country + email: str + formation_date: str + incorporation_doc_file: str + legal_name: str + owners: List[Owner] + postal_code: str + proof_of_address_doc_file: str + proof_of_address_doc_type: ProofOfAddressDocType + proof_of_ownership_doc_file: str + state_province_region: str + tax_id: str + tos_id: str + website: Optional[str] + ip_address: Optional[str] + image_url: Optional[str] + phone_number: Optional[str] + selfie_file: Optional[str] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + business_type: Optional[CustomerBusinessType] + 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] + + +class CreateBusinessWithStandardKYBResponse(TypedDict): + id: str + + +ListCustomersResponse = List[Union[IndividualWithStandardKYC, IndividualWithEnhancedKYC, BusinessWithStandardKYB]] + + +class ListCustomersPaginatedResponse(TypedDict): + data: List[Union[IndividualWithStandardKYC, IndividualWithEnhancedKYC, BusinessWithStandardKYB]] + pagination: PaginationMetadata + + +class ListCustomersInput(PaginationParams, total=False): + full_name: str + customer_name: str + status: CustomerStatus + customer_id: str + bank_account_id: str + country: str + + +GetCustomerResponse = Union[IndividualWithStandardKYC, IndividualWithEnhancedKYC, BusinessWithStandardKYB] + + +class OwnerUpdate(TypedDict): + id: str + first_name: str + last_name: str + role: OwnerRole + date_of_birth: str + tax_id: str + address_line_1: str + address_line_2: Optional[str] + city: str + state_province_region: str + country: Country + postal_code: str + id_doc_country: Country + id_doc_type: IdentificationDocument + id_doc_front_file: str + id_doc_back_file: Optional[str] + ownership_percentage: Optional[int] + title: Optional[str] + tax_type: Optional[TaxType] + proof_of_address_doc_type: Optional[ProofOfAddressDocType] + proof_of_address_doc_file: Optional[str] + + +class UpdateCustomerInput(TypedDict): + customer_id: str + email: Optional[str] + tax_id: Optional[str] + 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] + 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[IdentificationDocument] + 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[OwnerUpdate]] + 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] + individual_holding_doc_front_file: Optional[str] + purpose_of_transactions: Optional[PurposeOfTransactions] + purpose_of_transactions_explanation: Optional[str] + external_id: Optional[str] + tos_id: Optional[str] + selfie_file: Optional[str] + account_purpose: Optional[AccountPurpose] + account_purpose_other: Optional[str] + business_type: Optional[CustomerBusinessType] + 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] + + +class PayinLimit(TypedDict): + daily: float + monthly: float + + +class PayoutLimit(TypedDict): + daily: float + monthly: float + + +class Limits(TypedDict): + payin: PayinLimit + payout: PayoutLimit + + +class GetCustomerLimitsResponse(TypedDict): + limits: Limits + + +class LimitIncreaseRequest(TypedDict): + id: str + customer_id: str + status: LimitIncreaseRequestStatus + daily: float + monthly: float + per_transaction: float + supporting_document_file: str + supporting_document_type: LimitIncreaseRequestSupportingDocumentType + created_at: str + updated_at: str + + +GetLimitIncreaseRequestsResponse = List[LimitIncreaseRequest] + + +class RequestLimitIncreaseInput(TypedDict): + customer_id: str + daily: float + monthly: float + per_transaction: float + supporting_document_file: str + supporting_document_type: LimitIncreaseRequestSupportingDocumentType + + +class RequestLimitIncreaseResponse(TypedDict): + id: str + + +class CustomersResource: + def __init__(self, instance_id: str, client: InternalApiClient): + self._instance_id = instance_id + self._client = client + + async def list( + self, params: Optional[ListCustomersInput] = None + ) -> BlindpayApiResponse[Union[ListCustomersResponse, ListCustomersPaginatedResponse]]: + 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_individual_with_standard_kyc( + self, data: CreateIndividualWithStandardKYCInput + ) -> BlindpayApiResponse[CreateIndividualWithStandardKYCResponse]: + payload = {"kyc_type": "standard", "type": "individual", **data} + return await self._client.post(f"/instances/{self._instance_id}/customers", payload) + + async def create_individual_with_enhanced_kyc( + self, data: CreateIndividualWithEnhancedKYCInput + ) -> BlindpayApiResponse[CreateIndividualWithEnhancedKYCResponse]: + payload = {"kyc_type": "enhanced", "type": "individual", **data} + return await self._client.post(f"/instances/{self._instance_id}/customers", payload) + + async def create_business_with_standard_kyb( + self, data: CreateBusinessWithStandardKYBInput + ) -> BlindpayApiResponse[CreateBusinessWithStandardKYBResponse]: + payload = {"kyc_type": "standard", "type": "business", **data} + return await self._client.post(f"/instances/{self._instance_id}/customers", payload) + + async def get(self, customer_id: str) -> BlindpayApiResponse[GetCustomerResponse]: + return await self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}") + + async def update(self, data: UpdateCustomerInput) -> BlindpayApiResponse[None]: + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + return await self._client.put(f"/instances/{self._instance_id}/customers/{customer_id}", payload) + + async def delete(self, customer_id: str) -> BlindpayApiResponse[None]: + return await self._client.delete(f"/instances/{self._instance_id}/customers/{customer_id}") + + async def get_limits(self, customer_id: str) -> BlindpayApiResponse[GetCustomerLimitsResponse]: + return await self._client.get(f"/instances/{self._instance_id}/limits/customers/{customer_id}") + + async def get_limit_increase_requests( + self, customer_id: str + ) -> BlindpayApiResponse[GetLimitIncreaseRequestsResponse]: + return await self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/limit-increase") + + async def request_limit_increase( + self, data: RequestLimitIncreaseInput + ) -> BlindpayApiResponse[RequestLimitIncreaseResponse]: + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + return await self._client.post( + f"/instances/{self._instance_id}/customers/{customer_id}/limit-increase", payload + ) + + +class CustomersResourceSync: + def __init__(self, instance_id: str, client: InternalApiClientSync): + self._instance_id = instance_id + self._client = client + + def list( + self, params: Optional[ListCustomersInput] = None + ) -> BlindpayApiResponse[Union[ListCustomersResponse, ListCustomersPaginatedResponse]]: + 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_individual_with_standard_kyc( + self, data: CreateIndividualWithStandardKYCInput + ) -> BlindpayApiResponse[CreateIndividualWithStandardKYCResponse]: + payload = {"kyc_type": "standard", "type": "individual", **data} + return self._client.post(f"/instances/{self._instance_id}/customers", payload) + + def create_individual_with_enhanced_kyc( + self, data: CreateIndividualWithEnhancedKYCInput + ) -> BlindpayApiResponse[CreateIndividualWithEnhancedKYCResponse]: + payload = {"kyc_type": "enhanced", "type": "individual", **data} + return self._client.post(f"/instances/{self._instance_id}/customers", payload) + + def create_business_with_standard_kyb( + self, data: CreateBusinessWithStandardKYBInput + ) -> BlindpayApiResponse[CreateBusinessWithStandardKYBResponse]: + payload = {"kyc_type": "standard", "type": "business", **data} + return self._client.post(f"/instances/{self._instance_id}/customers", payload) + + def get(self, customer_id: str) -> BlindpayApiResponse[GetCustomerResponse]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}") + + def update(self, data: UpdateCustomerInput) -> BlindpayApiResponse[None]: + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + return self._client.put(f"/instances/{self._instance_id}/customers/{customer_id}", payload) + + def delete(self, customer_id: str) -> BlindpayApiResponse[None]: + return self._client.delete(f"/instances/{self._instance_id}/customers/{customer_id}") + + def get_limits(self, customer_id: str) -> BlindpayApiResponse[GetCustomerLimitsResponse]: + return self._client.get(f"/instances/{self._instance_id}/limits/customers/{customer_id}") + + def get_limit_increase_requests(self, customer_id: str) -> BlindpayApiResponse[GetLimitIncreaseRequestsResponse]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/limit-increase") + + def request_limit_increase( + self, data: RequestLimitIncreaseInput + ) -> BlindpayApiResponse[RequestLimitIncreaseResponse]: + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/limit-increase", payload) + + +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) diff --git a/src/blindpay/resources/virtual_accounts/virtual_accounts.py b/src/blindpay/resources/virtual_accounts/virtual_accounts.py index 8ff395b..b90a6cc 100644 --- a/src/blindpay/resources/virtual_accounts/virtual_accounts.py +++ b/src/blindpay/resources/virtual_accounts/virtual_accounts.py @@ -44,7 +44,7 @@ class VirtualAccount(TypedDict): class _CreateVirtualAccountInputRequired(TypedDict): - receiver_id: str + customer_id: str class CreateVirtualAccountInput(_CreateVirtualAccountInputRequired, total=False): @@ -73,26 +73,26 @@ def __init__(self, instance_id: str, client: InternalApiClient): self._instance_id = instance_id self._client = client - async def list(self, receiver_id: str) -> BlindpayApiResponse[ListVirtualAccountsResponse]: - return await self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/virtual-accounts") + 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 update( - self, receiver_id: str, virtual_account_id: str, data: UpdateVirtualAccountInput + self, customer_id: str, virtual_account_id: str, data: UpdateVirtualAccountInput ) -> BlindpayApiResponse[None]: return await self._client.put( - f"/instances/{self._instance_id}/receivers/{receiver_id}/virtual-accounts/{virtual_account_id}", data + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts/{virtual_account_id}", data ) async def create(self, data: CreateVirtualAccountInput) -> BlindpayApiResponse[CreateVirtualAccountResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} return await self._client.post( - f"/instances/{self._instance_id}/receivers/{receiver_id}/virtual-accounts", payload + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts", payload ) - async def get(self, receiver_id: str, virtual_account_id: str) -> BlindpayApiResponse[GetVirtualAccountResponse]: + async def get(self, customer_id: str, virtual_account_id: str) -> BlindpayApiResponse[GetVirtualAccountResponse]: return await self._client.get( - f"/instances/{self._instance_id}/receivers/{receiver_id}/virtual-accounts/{virtual_account_id}" + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts/{virtual_account_id}" ) @@ -101,24 +101,24 @@ def __init__(self, instance_id: str, client: InternalApiClientSync): self._instance_id = instance_id self._client = client - def list(self, receiver_id: str) -> BlindpayApiResponse[ListVirtualAccountsResponse]: - return self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/virtual-accounts") + def list(self, customer_id: str) -> BlindpayApiResponse[ListVirtualAccountsResponse]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts") def update( - self, receiver_id: str, virtual_account_id: str, data: UpdateVirtualAccountInput + self, customer_id: str, virtual_account_id: str, data: UpdateVirtualAccountInput ) -> BlindpayApiResponse[None]: return self._client.put( - f"/instances/{self._instance_id}/receivers/{receiver_id}/virtual-accounts/{virtual_account_id}", data + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts/{virtual_account_id}", data ) def create(self, data: CreateVirtualAccountInput) -> BlindpayApiResponse[CreateVirtualAccountResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/virtual-accounts", payload) + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts", payload) - def get(self, receiver_id: str, virtual_account_id: str) -> BlindpayApiResponse[GetVirtualAccountResponse]: + def get(self, customer_id: str, virtual_account_id: str) -> BlindpayApiResponse[GetVirtualAccountResponse]: return self._client.get( - f"/instances/{self._instance_id}/receivers/{receiver_id}/virtual-accounts/{virtual_account_id}" + f"/instances/{self._instance_id}/customers/{customer_id}/virtual-accounts/{virtual_account_id}" ) diff --git a/src/blindpay/resources/wallets/blockchain.py b/src/blindpay/resources/wallets/blockchain.py index 5985b7e..04b289e 100644 --- a/src/blindpay/resources/wallets/blockchain.py +++ b/src/blindpay/resources/wallets/blockchain.py @@ -17,7 +17,7 @@ class BlockchainWallet(TypedDict): address: Optional[str] signature_tx_hash: Optional[str] is_account_abstraction: bool - receiver_id: str + customer_id: str ListBlockchainWalletsResponse = List[BlockchainWallet] @@ -26,26 +26,26 @@ class BlockchainWallet(TypedDict): class CreateBlockchainWalletWithAddressInput(TypedDict): - receiver_id: str + customer_id: str name: str network: Network address: str class CreateBlockchainWalletWithHashInput(TypedDict): - receiver_id: str + customer_id: str name: str network: Network signature_tx_hash: str class GetBlockchainWalletInput(TypedDict): - receiver_id: str + customer_id: str id: str class DeleteBlockchainWalletInput(TypedDict): - receiver_id: str + customer_id: str id: str @@ -85,44 +85,44 @@ def __init__(self, instance_id: str, client: InternalApiClient): self._instance_id = instance_id self._client = client - async def list(self, receiver_id: str) -> BlindpayApiResponse[ListBlockchainWalletsResponse]: - return await self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets") + 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 create_with_address( self, data: CreateBlockchainWalletWithAddressInput ) -> BlindpayApiResponse[CreateBlockchainWalletResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["is_account_abstraction"] = True return await self._client.post( - f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets", payload + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets", payload ) async def create_with_hash( self, data: CreateBlockchainWalletWithHashInput ) -> BlindpayApiResponse[CreateBlockchainWalletResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["is_account_abstraction"] = False return await self._client.post( - f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets", payload + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets", payload ) - async def get_wallet_message(self, receiver_id: str) -> BlindpayApiResponse[GetBlockchainWalletMessageResponse]: + async def get_wallet_message(self, customer_id: str) -> BlindpayApiResponse[GetBlockchainWalletMessageResponse]: return await self._client.get( - f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets/sign-message" + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/sign-message" ) async def get(self, data: GetBlockchainWalletInput) -> BlindpayApiResponse[GetBlockchainWalletResponse]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] id = data["id"] - return await self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets/{id}") + return await self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/{id}") async def delete(self, data: DeleteBlockchainWalletInput) -> BlindpayApiResponse[None]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] id = data["id"] return await self._client.delete( - f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets/{id}" + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/{id}" ) async def create_asset_trustline(self, address: str) -> BlindpayApiResponse[CreateAssetTrustlineResponse]: @@ -145,39 +145,39 @@ def __init__(self, instance_id: str, client: InternalApiClientSync): self._instance_id = instance_id self._client = client - def list(self, receiver_id: str) -> BlindpayApiResponse[ListBlockchainWalletsResponse]: - return self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets") + def list(self, customer_id: str) -> BlindpayApiResponse[ListBlockchainWalletsResponse]: + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets") def create_with_address( self, data: CreateBlockchainWalletWithAddressInput ) -> BlindpayApiResponse[CreateBlockchainWalletResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["is_account_abstraction"] = True - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets", payload) def create_with_hash( self, data: CreateBlockchainWalletWithHashInput ) -> BlindpayApiResponse[CreateBlockchainWalletResponse]: - receiver_id = data["receiver_id"] - payload = {k: v for k, v in data.items() if k != "receiver_id"} + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} payload["is_account_abstraction"] = False - return self._client.post(f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets", payload) + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets", payload) - def get_wallet_message(self, receiver_id: str) -> BlindpayApiResponse[GetBlockchainWalletMessageResponse]: + def get_wallet_message(self, customer_id: str) -> BlindpayApiResponse[GetBlockchainWalletMessageResponse]: return self._client.get( - f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets/sign-message" + f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/sign-message" ) def get(self, data: GetBlockchainWalletInput) -> BlindpayApiResponse[GetBlockchainWalletResponse]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] id = data["id"] - return self._client.get(f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets/{id}") + return self._client.get(f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/{id}") def delete(self, data: DeleteBlockchainWalletInput) -> BlindpayApiResponse[None]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] id = data["id"] - return self._client.delete(f"/instances/{self._instance_id}/receivers/{receiver_id}/blockchain-wallets/{id}") + return self._client.delete(f"/instances/{self._instance_id}/customers/{customer_id}/blockchain-wallets/{id}") def create_asset_trustline(self, address: str) -> BlindpayApiResponse[CreateAssetTrustlineResponse]: return self._client.post(f"/instances/{self._instance_id}/create-asset-trustline", {"address": address}) diff --git a/src/blindpay/resources/wallets/offramp.py b/src/blindpay/resources/wallets/offramp.py index 5b9d9c6..a916266 100644 --- a/src/blindpay/resources/wallets/offramp.py +++ b/src/blindpay/resources/wallets/offramp.py @@ -12,7 +12,7 @@ class OfframpWallet(TypedDict): id: str external_id: str instance_id: str - receiver_id: str + customer_id: str bank_account_id: str network: TronNetwork address: str @@ -25,12 +25,12 @@ class OfframpWallet(TypedDict): class ListOfframpWalletsInput(TypedDict): - receiver_id: str + customer_id: str bank_account_id: str class CreateOfframpWalletInput(TypedDict): - receiver_id: str + customer_id: str bank_account_id: str external_id: str network: TronNetwork @@ -44,7 +44,7 @@ class CreateOfframpWalletResponse(TypedDict): class GetOfframpWalletInput(TypedDict): - receiver_id: str + customer_id: str bank_account_id: str id: str @@ -55,27 +55,27 @@ def __init__(self, instance_id: str, client: InternalApiClient): self._client = client async def list(self, data: ListOfframpWalletsInput) -> BlindpayApiResponse[ListOfframpWalletsResponse]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] bank_account_id = data["bank_account_id"] return await self._client.get( - f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{bank_account_id}/offramp-wallets" + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets" ) async def create(self, data: CreateOfframpWalletInput) -> BlindpayApiResponse[CreateOfframpWalletResponse]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] bank_account_id = data["bank_account_id"] - payload = {k: v for k, v in data.items() if k not in ["receiver_id", "bank_account_id"]} + payload = {k: v for k, v in data.items() if k not in ["customer_id", "bank_account_id"]} return await self._client.post( - f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{bank_account_id}/offramp-wallets", + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets", payload, ) async def get(self, data: GetOfframpWalletInput) -> BlindpayApiResponse[GetOfframpWalletResponse]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] bank_account_id = data["bank_account_id"] id = data["id"] return await self._client.get( - f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{bank_account_id}/offramp-wallets/{id}" + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets/{id}" ) @@ -85,27 +85,27 @@ def __init__(self, instance_id: str, client: InternalApiClientSync): self._client = client def list(self, data: ListOfframpWalletsInput) -> BlindpayApiResponse[ListOfframpWalletsResponse]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] bank_account_id = data["bank_account_id"] return self._client.get( - f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{bank_account_id}/offramp-wallets" + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets" ) def create(self, data: CreateOfframpWalletInput) -> BlindpayApiResponse[CreateOfframpWalletResponse]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] bank_account_id = data["bank_account_id"] - payload = {k: v for k, v in data.items() if k not in ["receiver_id", "bank_account_id"]} + payload = {k: v for k, v in data.items() if k not in ["customer_id", "bank_account_id"]} return self._client.post( - f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{bank_account_id}/offramp-wallets", + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets", payload, ) def get(self, data: GetOfframpWalletInput) -> BlindpayApiResponse[GetOfframpWalletResponse]: - receiver_id = data["receiver_id"] + customer_id = data["customer_id"] bank_account_id = data["bank_account_id"] id = data["id"] return self._client.get( - f"/instances/{self._instance_id}/receivers/{receiver_id}/bank-accounts/{bank_account_id}/offramp-wallets/{id}" + f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts/{bank_account_id}/offramp-wallets/{id}" ) diff --git a/tests/resources/test_bank_accounts.py b/tests/resources/test_bank_accounts.py index 39681dc..4541e26 100644 --- a/tests/resources/test_bank_accounts.py +++ b/tests/resources/test_bank_accounts.py @@ -25,7 +25,7 @@ async def test_create_pix_bank_account(self): response = await self.blindpay.receivers.bank_accounts.create_pix( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "PIX Account", "pix_key": "14947677768", } @@ -51,7 +51,7 @@ async def test_create_argentina_transfers_bank_account(self): response = await self.blindpay.receivers.bank_accounts.create_argentina_transfers( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Argentina Transfers Account", "beneficiary_name": "Individual full name or business name", "transfers_type": "CVU", @@ -80,7 +80,7 @@ async def test_create_spei_bank_account(self): response = await self.blindpay.receivers.bank_accounts.create_spei( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "SPEI Account", "beneficiary_name": "Individual full name or business name", "spei_protocol": "clabe", @@ -114,7 +114,7 @@ async def test_create_colombia_ach_bank_account(self): response = await self.blindpay.receivers.bank_accounts.create_colombia_ach( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Colombia ACH Account", "account_type": "checking", "ach_cop_beneficiary_first_name": "Fernando", @@ -162,7 +162,7 @@ async def test_create_ach_bank_account(self): response = await self.blindpay.receivers.bank_accounts.create_ach( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "ACH Account", "account_class": "individual", "account_number": "1001001234", @@ -198,7 +198,7 @@ async def test_create_wire_bank_account(self): response = await self.blindpay.receivers.bank_accounts.create_wire( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Wire Account", "account_number": "1001001234", "beneficiary_name": "Individual full name or business name", @@ -256,7 +256,7 @@ async def test_create_international_swift_bank_account(self): response = await self.blindpay.receivers.bank_accounts.create_international_swift( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "International Swift Account", "swift_account_holder_name": "John Doe", "swift_account_number_iban": "123456789", @@ -309,7 +309,7 @@ async def test_create_rtp_bank_account(self): response = await self.blindpay.receivers.bank_accounts.create_rtp( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "John Doe RTP", "beneficiary_name": "John Doe", "routing_number": "121000358", @@ -330,7 +330,7 @@ async def test_create_rtp_bank_account(self): async def test_get_bank_account(self): mocked_bank_account = { "id": "ba_000000000000", - "receiver_id": "rcv_123", + "customer_id": "rcv_123", "account_holder_name": "Individual full name or business name", "account_number": "1001001234", "routing_number": "012345678", @@ -463,7 +463,7 @@ def test_create_pix_bank_account(self): response = self.blindpay.receivers.bank_accounts.create_pix( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "PIX Account", "pix_key": "14947677768", } @@ -488,7 +488,7 @@ def test_create_argentina_transfers_bank_account(self): response = self.blindpay.receivers.bank_accounts.create_argentina_transfers( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Argentina Transfers Account", "beneficiary_name": "Individual full name or business name", "transfers_type": "CVU", @@ -516,7 +516,7 @@ def test_create_spei_bank_account(self): response = self.blindpay.receivers.bank_accounts.create_spei( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "SPEI Account", "beneficiary_name": "Individual full name or business name", "spei_protocol": "clabe", @@ -549,7 +549,7 @@ def test_create_colombia_ach_bank_account(self): response = self.blindpay.receivers.bank_accounts.create_colombia_ach( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Colombia ACH Account", "account_type": "checking", "ach_cop_beneficiary_first_name": "Fernando", @@ -596,7 +596,7 @@ def test_create_ach_bank_account(self): response = self.blindpay.receivers.bank_accounts.create_ach( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "ACH Account", "account_class": "individual", "account_number": "1001001234", @@ -631,7 +631,7 @@ def test_create_wire_bank_account(self): response = self.blindpay.receivers.bank_accounts.create_wire( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Wire Account", "account_number": "1001001234", "beneficiary_name": "Individual full name or business name", @@ -688,7 +688,7 @@ def test_create_international_swift_bank_account(self): response = self.blindpay.receivers.bank_accounts.create_international_swift( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "International Swift Account", "swift_account_holder_name": "John Doe", "swift_account_number_iban": "123456789", @@ -740,7 +740,7 @@ def test_create_rtp_bank_account(self): response = self.blindpay.receivers.bank_accounts.create_rtp( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "John Doe RTP", "beneficiary_name": "John Doe", "routing_number": "121000358", @@ -760,7 +760,7 @@ def test_create_rtp_bank_account(self): def test_get_bank_account(self): mocked_bank_account = { "id": "ba_000000000000", - "receiver_id": "rcv_123", + "customer_id": "rcv_123", "account_holder_name": "Individual full name or business name", "account_number": "1001001234", "routing_number": "012345678", diff --git a/tests/resources/test_blockchain_wallets.py b/tests/resources/test_blockchain_wallets.py index 141b1ee..124edb3 100644 --- a/tests/resources/test_blockchain_wallets.py +++ b/tests/resources/test_blockchain_wallets.py @@ -22,7 +22,7 @@ async def test_get_blockchain_wallet_message(self): assert response["error"] is None assert response["data"] == mocked_message mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets/sign-message" + "GET", "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets/sign-message" ) @pytest.mark.asyncio @@ -35,7 +35,7 @@ async def test_list_blockchain_wallets(self): "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", "signature_tx_hash": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "is_account_abstraction": False, - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } ] @@ -47,7 +47,7 @@ async def test_list_blockchain_wallets(self): assert response["error"] is None assert response["data"] == mocked_wallets mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets" + "GET", "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets" ) @pytest.mark.asyncio @@ -59,7 +59,7 @@ async def test_create_blockchain_wallet_with_address(self): "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", "signature_tx_hash": None, "is_account_abstraction": True, - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } with patch.object(self.blindpay._api, "_request") as mock_request: @@ -67,7 +67,7 @@ async def test_create_blockchain_wallet_with_address(self): response = await self.blindpay.wallets.blockchain.create_with_address( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Wallet Display Name", "network": "polygon", "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", @@ -78,7 +78,7 @@ async def test_create_blockchain_wallet_with_address(self): assert response["data"] == mocked_wallet mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets", + "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets", { "name": "Wallet Display Name", "network": "polygon", @@ -96,7 +96,7 @@ async def test_create_blockchain_wallet_with_hash(self): "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", "signature_tx_hash": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "is_account_abstraction": False, - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } with patch.object(self.blindpay._api, "_request") as mock_request: @@ -104,7 +104,7 @@ async def test_create_blockchain_wallet_with_hash(self): response = await self.blindpay.wallets.blockchain.create_with_hash( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Wallet Display Name", "network": "polygon", "signature_tx_hash": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", @@ -115,7 +115,7 @@ async def test_create_blockchain_wallet_with_hash(self): assert response["data"] == mocked_wallet mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets", + "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets", { "name": "Wallet Display Name", "network": "polygon", @@ -133,7 +133,7 @@ async def test_get_blockchain_wallet(self): "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", "signature_tx_hash": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "is_account_abstraction": False, - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } with patch.object(self.blindpay._api, "_request") as mock_request: @@ -141,7 +141,7 @@ async def test_get_blockchain_wallet(self): response = await self.blindpay.wallets.blockchain.get( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "id": "bw_000000000000", } ) @@ -149,7 +149,7 @@ async def test_get_blockchain_wallet(self): assert response["error"] is None assert response["data"] == mocked_wallet mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets/bw_000000000000" + "GET", "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets/bw_000000000000" ) @pytest.mark.asyncio @@ -159,7 +159,7 @@ async def test_delete_blockchain_wallet(self): response = await self.blindpay.wallets.blockchain.delete( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "id": "bw_000000000000", } ) @@ -168,7 +168,7 @@ async def test_delete_blockchain_wallet(self): assert response["data"] == {"data": None} mock_request.assert_called_once_with( "DELETE", - "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets/bw_000000000000", + "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets/bw_000000000000", None, ) @@ -303,7 +303,7 @@ def test_get_blockchain_wallet_message(self): assert response["error"] is None assert response["data"] == mocked_message mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets/sign-message" + "GET", "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets/sign-message" ) def test_list_blockchain_wallets(self): @@ -315,7 +315,7 @@ def test_list_blockchain_wallets(self): "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", "signature_tx_hash": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "is_account_abstraction": False, - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } ] @@ -327,7 +327,7 @@ def test_list_blockchain_wallets(self): assert response["error"] is None assert response["data"] == mocked_wallets mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets" + "GET", "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets" ) def test_create_blockchain_wallet_with_address(self): @@ -338,7 +338,7 @@ def test_create_blockchain_wallet_with_address(self): "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", "signature_tx_hash": None, "is_account_abstraction": True, - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } with patch.object(self.blindpay._api, "_request") as mock_request: @@ -346,7 +346,7 @@ def test_create_blockchain_wallet_with_address(self): response = self.blindpay.wallets.blockchain.create_with_address( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Wallet Display Name", "network": "polygon", "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", @@ -357,7 +357,7 @@ def test_create_blockchain_wallet_with_address(self): assert response["data"] == mocked_wallet mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets", + "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets", { "name": "Wallet Display Name", "network": "polygon", @@ -374,7 +374,7 @@ def test_create_blockchain_wallet_with_hash(self): "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", "signature_tx_hash": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "is_account_abstraction": False, - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } with patch.object(self.blindpay._api, "_request") as mock_request: @@ -382,7 +382,7 @@ def test_create_blockchain_wallet_with_hash(self): response = self.blindpay.wallets.blockchain.create_with_hash( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "name": "Wallet Display Name", "network": "polygon", "signature_tx_hash": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", @@ -393,7 +393,7 @@ def test_create_blockchain_wallet_with_hash(self): assert response["data"] == mocked_wallet mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets", + "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets", { "name": "Wallet Display Name", "network": "polygon", @@ -410,7 +410,7 @@ def test_get_blockchain_wallet(self): "address": "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", "signature_tx_hash": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", "is_account_abstraction": False, - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } with patch.object(self.blindpay._api, "_request") as mock_request: @@ -418,7 +418,7 @@ def test_get_blockchain_wallet(self): response = self.blindpay.wallets.blockchain.get( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "id": "bw_000000000000", } ) @@ -426,7 +426,7 @@ def test_get_blockchain_wallet(self): assert response["error"] is None assert response["data"] == mocked_wallet mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets/bw_000000000000" + "GET", "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets/bw_000000000000" ) def test_delete_blockchain_wallet(self): @@ -435,7 +435,7 @@ def test_delete_blockchain_wallet(self): response = self.blindpay.wallets.blockchain.delete( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "id": "bw_000000000000", } ) @@ -444,7 +444,7 @@ def test_delete_blockchain_wallet(self): assert response["data"] == {"data": None} mock_request.assert_called_once_with( "DELETE", - "/instances/in_000000000000/receivers/re_000000000000/blockchain-wallets/bw_000000000000", + "/instances/in_000000000000/customers/re_000000000000/blockchain-wallets/bw_000000000000", None, ) diff --git a/tests/resources/test_custodial_wallets.py b/tests/resources/test_custodial_wallets.py index 1909ab0..f1ac1ea 100644 --- a/tests/resources/test_custodial_wallets.py +++ b/tests/resources/test_custodial_wallets.py @@ -14,7 +14,7 @@ def setup(self): async def test_create_custodial_wallet(self): mocked_wallet = { "id": "cw_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "instance_id": "in_000000000000", "network": "solana", "address": "So1ana1234567890", @@ -26,7 +26,7 @@ async def test_create_custodial_wallet(self): response = await self.blindpay.wallets.custodial.create( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "network": "solana", } ) @@ -38,7 +38,7 @@ async def test_create_custodial_wallet(self): assert response["data"]["address"] == "So1ana1234567890" mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/wallets", + "/instances/in_000000000000/customers/re_000000000000/wallets", {"network": "solana"}, ) @@ -47,7 +47,7 @@ async def test_list_custodial_wallets(self): mocked_wallets = [ { "id": "cw_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "instance_id": "in_000000000000", "network": "solana", "address": "So1ana1234567890", @@ -64,13 +64,13 @@ async def test_list_custodial_wallets(self): assert response["data"] is not None assert len(response["data"]) == 1 assert response["data"][0]["id"] == "cw_000000000000" - mock_request.assert_called_once_with("GET", "/instances/in_000000000000/receivers/re_000000000000/wallets") + mock_request.assert_called_once_with("GET", "/instances/in_000000000000/customers/re_000000000000/wallets") @pytest.mark.asyncio async def test_get_custodial_wallet(self): mocked_wallet = { "id": "cw_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "instance_id": "in_000000000000", "network": "solana", "address": "So1ana1234567890", @@ -81,14 +81,14 @@ async def test_get_custodial_wallet(self): mock_request.return_value = {"data": mocked_wallet, "error": None} response = await self.blindpay.wallets.custodial.get( - {"receiver_id": "re_000000000000", "id": "cw_000000000000"} + {"customer_id": "re_000000000000", "id": "cw_000000000000"} ) assert response["error"] is None assert response["data"] is not None assert response["data"]["id"] == "cw_000000000000" mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/wallets/cw_000000000000" + "GET", "/instances/in_000000000000/customers/re_000000000000/wallets/cw_000000000000" ) @pytest.mark.asyncio @@ -111,7 +111,7 @@ async def test_get_custodial_wallet_balance(self): mock_request.return_value = {"data": mocked_balance, "error": None} response = await self.blindpay.wallets.custodial.get_balance( - {"receiver_id": "re_000000000000", "id": "cw_000000000000"} + {"customer_id": "re_000000000000", "id": "cw_000000000000"} ) assert response["error"] is None @@ -122,7 +122,7 @@ async def test_get_custodial_wallet_balance(self): assert response["data"]["usdt"]["amount"] == 0.0 assert response["data"]["usdb"] is None mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/wallets/cw_000000000000/balance" + "GET", "/instances/in_000000000000/customers/re_000000000000/wallets/cw_000000000000/balance" ) @pytest.mark.asyncio @@ -131,12 +131,12 @@ async def test_delete_custodial_wallet(self): mock_request.return_value = {"data": None, "error": None} response = await self.blindpay.wallets.custodial.delete( - {"receiver_id": "re_000000000000", "id": "cw_000000000000"} + {"customer_id": "re_000000000000", "id": "cw_000000000000"} ) assert response["error"] is None mock_request.assert_called_once_with( - "DELETE", "/instances/in_000000000000/receivers/re_000000000000/wallets/cw_000000000000", None + "DELETE", "/instances/in_000000000000/customers/re_000000000000/wallets/cw_000000000000", None ) @@ -148,7 +148,7 @@ def setup(self): def test_create_custodial_wallet(self): mocked_wallet = { "id": "cw_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "instance_id": "in_000000000000", "network": "solana", "address": "So1ana1234567890", @@ -160,7 +160,7 @@ def test_create_custodial_wallet(self): response = self.blindpay.wallets.custodial.create( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "network": "solana", } ) @@ -170,7 +170,7 @@ def test_create_custodial_wallet(self): assert response["data"]["id"] == "cw_000000000000" mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/wallets", + "/instances/in_000000000000/customers/re_000000000000/wallets", {"network": "solana"}, ) @@ -178,7 +178,7 @@ def test_list_custodial_wallets(self): mocked_wallets = [ { "id": "cw_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "instance_id": "in_000000000000", "network": "solana", "address": "So1ana1234567890", @@ -194,12 +194,12 @@ def test_list_custodial_wallets(self): assert response["error"] is None assert response["data"] is not None assert len(response["data"]) == 1 - mock_request.assert_called_once_with("GET", "/instances/in_000000000000/receivers/re_000000000000/wallets") + mock_request.assert_called_once_with("GET", "/instances/in_000000000000/customers/re_000000000000/wallets") def test_get_custodial_wallet(self): mocked_wallet = { "id": "cw_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "instance_id": "in_000000000000", "network": "solana", "address": "So1ana1234567890", @@ -209,13 +209,13 @@ def test_get_custodial_wallet(self): with patch.object(self.blindpay._api, "_request") as mock_request: mock_request.return_value = {"data": mocked_wallet, "error": None} - response = self.blindpay.wallets.custodial.get({"receiver_id": "re_000000000000", "id": "cw_000000000000"}) + response = self.blindpay.wallets.custodial.get({"customer_id": "re_000000000000", "id": "cw_000000000000"}) assert response["error"] is None assert response["data"] is not None assert response["data"]["id"] == "cw_000000000000" mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/wallets/cw_000000000000" + "GET", "/instances/in_000000000000/customers/re_000000000000/wallets/cw_000000000000" ) def test_get_custodial_wallet_balance(self): @@ -237,7 +237,7 @@ def test_get_custodial_wallet_balance(self): mock_request.return_value = {"data": mocked_balance, "error": None} response = self.blindpay.wallets.custodial.get_balance( - {"receiver_id": "re_000000000000", "id": "cw_000000000000"} + {"customer_id": "re_000000000000", "id": "cw_000000000000"} ) assert response["error"] is None @@ -245,7 +245,7 @@ def test_get_custodial_wallet_balance(self): assert response["data"]["usdc"] is not None assert response["data"]["usdc"]["amount"] == 150.50 mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/wallets/cw_000000000000/balance" + "GET", "/instances/in_000000000000/customers/re_000000000000/wallets/cw_000000000000/balance" ) def test_delete_custodial_wallet(self): @@ -253,10 +253,10 @@ def test_delete_custodial_wallet(self): mock_request.return_value = {"data": None, "error": None} response = self.blindpay.wallets.custodial.delete( - {"receiver_id": "re_000000000000", "id": "cw_000000000000"} + {"customer_id": "re_000000000000", "id": "cw_000000000000"} ) assert response["error"] is None mock_request.assert_called_once_with( - "DELETE", "/instances/in_000000000000/receivers/re_000000000000/wallets/cw_000000000000", None + "DELETE", "/instances/in_000000000000/customers/re_000000000000/wallets/cw_000000000000", None ) diff --git a/tests/resources/test_offramp_wallets.py b/tests/resources/test_offramp_wallets.py index 18b923e..0dbdea2 100644 --- a/tests/resources/test_offramp_wallets.py +++ b/tests/resources/test_offramp_wallets.py @@ -17,7 +17,7 @@ async def test_list_offramp_wallets(self): "id": "ow_000000000000", "external_id": "your_external_id", "instance_id": "in_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "bank_account_id": "ba_000000000000", "network": "tron", "address": "TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb", @@ -31,7 +31,7 @@ async def test_list_offramp_wallets(self): response = await self.blindpay.wallets.offramp.list( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "bank_account_id": "ba_000000000000", } ) @@ -40,7 +40,7 @@ async def test_list_offramp_wallets(self): assert response["data"] == mocked_offramp_wallets mock_request.assert_called_once_with( "GET", - "/instances/in_000000000000/receivers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets", + "/instances/in_000000000000/customers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets", ) @pytest.mark.asyncio @@ -59,7 +59,7 @@ async def test_create_offramp_wallet(self): { "external_id": "your_external_id", "network": "tron", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "bank_account_id": "ba_000000000000", } ) @@ -68,7 +68,7 @@ async def test_create_offramp_wallet(self): assert response["data"] == mocked_offramp_wallet mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets", + "/instances/in_000000000000/customers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets", { "external_id": "your_external_id", "network": "tron", @@ -81,7 +81,7 @@ async def test_get_offramp_wallet(self): "id": "ow_000000000000", "external_id": "your_external_id", "instance_id": "in_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "bank_account_id": "ba_000000000000", "network": "tron", "address": "TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb", @@ -96,7 +96,7 @@ async def test_get_offramp_wallet(self): { "id": "ow_000000000000", "bank_account_id": "ba_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } ) @@ -104,7 +104,7 @@ async def test_get_offramp_wallet(self): assert response["data"] == mocked_offramp_wallet mock_request.assert_called_once_with( "GET", - "/instances/in_000000000000/receivers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets/ow_000000000000", + "/instances/in_000000000000/customers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets/ow_000000000000", ) @@ -119,7 +119,7 @@ def test_list_offramp_wallets(self): "id": "ow_000000000000", "external_id": "your_external_id", "instance_id": "in_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "bank_account_id": "ba_000000000000", "network": "tron", "address": "TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb", @@ -133,7 +133,7 @@ def test_list_offramp_wallets(self): response = self.blindpay.wallets.offramp.list( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "bank_account_id": "ba_000000000000", } ) @@ -142,7 +142,7 @@ def test_list_offramp_wallets(self): assert response["data"] == mocked_offramp_wallets mock_request.assert_called_once_with( "GET", - "/instances/in_000000000000/receivers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets", + "/instances/in_000000000000/customers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets", ) def test_create_offramp_wallet(self): @@ -160,7 +160,7 @@ def test_create_offramp_wallet(self): { "external_id": "your_external_id", "network": "tron", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "bank_account_id": "ba_000000000000", } ) @@ -169,7 +169,7 @@ def test_create_offramp_wallet(self): assert response["data"] == mocked_offramp_wallet mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets", + "/instances/in_000000000000/customers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets", { "external_id": "your_external_id", "network": "tron", @@ -181,7 +181,7 @@ def test_get_offramp_wallet(self): "id": "ow_000000000000", "external_id": "your_external_id", "instance_id": "in_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "bank_account_id": "ba_000000000000", "network": "tron", "address": "TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb", @@ -196,7 +196,7 @@ def test_get_offramp_wallet(self): { "id": "ow_000000000000", "bank_account_id": "ba_000000000000", - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", } ) @@ -204,5 +204,5 @@ def test_get_offramp_wallet(self): assert response["data"] == mocked_offramp_wallet mock_request.assert_called_once_with( "GET", - "/instances/in_000000000000/receivers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets/ow_000000000000", + "/instances/in_000000000000/customers/re_000000000000/bank-accounts/ba_000000000000/offramp-wallets/ow_000000000000", ) diff --git a/tests/resources/test_virtual_accounts.py b/tests/resources/test_virtual_accounts.py index c41440e..1e0a632 100644 --- a/tests/resources/test_virtual_accounts.py +++ b/tests/resources/test_virtual_accounts.py @@ -28,7 +28,7 @@ async def test_update_virtual_account(self): assert response["data"] == {"data": None} mock_request.assert_called_once_with( "PUT", - "/instances/in_000000000000/receivers/re_000000000000/virtual-accounts/va_000000000000", + "/instances/in_000000000000/customers/re_000000000000/virtual-accounts/va_000000000000", {"blockchain_wallet_id": "bw_000000000000", "token": "USDC"}, ) @@ -71,7 +71,7 @@ async def test_create_virtual_account(self): response = await self.blindpay.virtual_accounts.create( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "blockchain_wallet_id": "bw_000000000000", "token": "USDC", } @@ -81,7 +81,7 @@ async def test_create_virtual_account(self): assert response["data"] == mocked_virtual_account mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/virtual-accounts", + "/instances/in_000000000000/customers/re_000000000000/virtual-accounts", {"blockchain_wallet_id": "bw_000000000000", "token": "USDC"}, ) @@ -127,7 +127,7 @@ async def test_get_virtual_account(self): assert response["error"] is None assert response["data"] == mocked_virtual_account mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/virtual-accounts/va_000000000000" + "GET", "/instances/in_000000000000/customers/re_000000000000/virtual-accounts/va_000000000000" ) @@ -153,7 +153,7 @@ def test_update_virtual_account(self): assert response["data"] == {"data": None} mock_request.assert_called_once_with( "PUT", - "/instances/in_000000000000/receivers/re_000000000000/virtual-accounts/va_000000000000", + "/instances/in_000000000000/customers/re_000000000000/virtual-accounts/va_000000000000", {"blockchain_wallet_id": "bw_000000000000", "token": "USDC"}, ) @@ -195,7 +195,7 @@ def test_create_virtual_account(self): response = self.blindpay.virtual_accounts.create( { - "receiver_id": "re_000000000000", + "customer_id": "re_000000000000", "blockchain_wallet_id": "bw_000000000000", "token": "USDC", } @@ -205,7 +205,7 @@ def test_create_virtual_account(self): assert response["data"] == mocked_virtual_account mock_request.assert_called_once_with( "POST", - "/instances/in_000000000000/receivers/re_000000000000/virtual-accounts", + "/instances/in_000000000000/customers/re_000000000000/virtual-accounts", {"blockchain_wallet_id": "bw_000000000000", "token": "USDC"}, ) @@ -250,5 +250,5 @@ def test_get_virtual_account(self): assert response["error"] is None assert response["data"] == mocked_virtual_account mock_request.assert_called_once_with( - "GET", "/instances/in_000000000000/receivers/re_000000000000/virtual-accounts/va_000000000000" + "GET", "/instances/in_000000000000/customers/re_000000000000/virtual-accounts/va_000000000000" ) From 24839c3376c9461fc39dc1666cbbe08754605416 Mon Sep 17 00:00:00 2001 From: alvseven Date: Mon, 8 Jun 2026 14:25:44 -0300 Subject: [PATCH 2/3] feat: add SEPA + api-sync content (mirrors node #50) Folds the api-sync content into the customers-resource minor: - New createSepa method on bank-accounts (posts type=sepa to the same /customers/{customer_id}/bank-accounts endpoint) - sepa_beneficiary_bic field on BankAccount, GetBankAccountResponse, CreateInternationalSwift{Input,Response} - migrate_ownership method on instances resource - ManualExecutionStatus + manual_execution_status on Payin - billing_fee_amount + cpn_payment_id on Payout - OfframpWallet network expanded from 'tron'-only to the full Network enum - WebhookEvent literal type (receiver.delete) --- pyproject.toml | 2 +- src/blindpay/__init__.py | 2 + .../resources/bank_accounts/__init__.py | 6 +- .../resources/bank_accounts/bank_accounts.py | 56 ++++++++++++++++++- src/blindpay/resources/instances/__init__.py | 4 ++ src/blindpay/resources/instances/instances.py | 18 ++++++ src/blindpay/resources/payins/__init__.py | 2 + src/blindpay/resources/payins/payins.py | 6 ++ src/blindpay/resources/payouts/payouts.py | 2 + src/blindpay/resources/wallets/offramp.py | 12 ++-- src/blindpay/types.py | 2 + 11 files changed, 100 insertions(+), 12 deletions(-) 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 30133f4..5b079c8 100644 --- a/src/blindpay/__init__.py +++ b/src/blindpay/__init__.py @@ -27,6 +27,7 @@ TrackingTransaction, TransactionDocumentType, TransactionStatus, + WebhookEvent, ) __all__ = [ @@ -46,6 +47,7 @@ "StablecoinToken", "TransactionDocumentType", "TransactionStatus", + "WebhookEvent", "BlindpayApiResponse", "BlindpayErrorResponse", "BlindpaySuccessResponse", diff --git a/src/blindpay/resources/bank_accounts/__init__.py b/src/blindpay/resources/bank_accounts/__init__.py index 697c76b..055c2ec 100644 --- a/src/blindpay/resources/bank_accounts/__init__.py +++ b/src/blindpay/resources/bank_accounts/__init__.py @@ -19,6 +19,8 @@ CreatePixSafeResponse, CreateRtpInput, CreateRtpResponse, + CreateSepaInput, + CreateSepaResponse, CreateSpeiInput, CreateSpeiResponse, CreateTedInput, @@ -27,7 +29,6 @@ CreateWireResponse, GetBankAccountResponse, ListBankAccountsResponse, - OfframpNetwork, PixSafeType, PixType, RtpType, @@ -53,12 +54,12 @@ "CreateWireInput", "CreateInternationalSwiftInput", "CreateRtpInput", + "CreateSepaInput", "CreateTedInput", "AchCopBitsoType", "AchCopDocument", "AchType", "ArgentinaTransfers", - "OfframpNetwork", "PixType", "PixSafeType", "TransfersBitsoType", @@ -75,6 +76,7 @@ "CreatePixResponse", "CreatePixSafeResponse", "CreateRtpResponse", + "CreateSepaResponse", "CreateSpeiResponse", "CreateTedResponse", "CreateWireResponse", diff --git a/src/blindpay/resources/bank_accounts/bank_accounts.py b/src/blindpay/resources/bank_accounts/bank_accounts.py index 1e52e42..a81ab07 100644 --- a/src/blindpay/resources/bank_accounts/bank_accounts.py +++ b/src/blindpay/resources/bank_accounts/bank_accounts.py @@ -8,6 +8,7 @@ BankAccountType, BlindpayApiResponse, Country, + Network, Rail, RecipientRelationship, SpeiProtocol, @@ -15,7 +16,6 @@ ArgentinaTransfers = Literal["CVU", "CBU", "ALIAS"] AchCopDocument = Literal["CC", "CE", "NIT", "PASS", "PEP"] -OfframpNetwork = Literal["tron"] PixType = Literal["pix"] TransfersBitsoType = Literal["transfers_bitso"] SpeiBitsoType = Literal["spei_bitso"] @@ -31,7 +31,7 @@ class OfframpWallet(TypedDict): address: str id: str - network: OfframpNetwork + network: Network external_id: str @@ -54,6 +54,7 @@ class BankAccount(TypedDict): spei_protocol: Optional[str] spei_institution_code: Optional[str] spei_clabe: Optional[str] + sepa_beneficiary_bic: Optional[str] transfers_type: Optional[ArgentinaTransfers] transfers_account: Optional[str] ach_cop_beneficiary_first_name: Optional[str] @@ -105,6 +106,7 @@ class GetBankAccountResponse(TypedDict): bank_name: str swift_code: Optional[str] iban: Optional[str] + sepa_beneficiary_bic: Optional[str] is_primary: bool created_at: str updated_at: str @@ -302,6 +304,7 @@ class CreateInternationalSwiftInput(_CreateInternationalSwiftInputRequired, tota swift_beneficiary_postal_code: str swift_beneficiary_state_province_region: str swift_code_bic: str + sepa_beneficiary_bic: Optional[str] swift_intermediary_bank_account_number_iban: Optional[str] swift_intermediary_bank_country: Optional[Country] swift_intermediary_bank_name: Optional[str] @@ -340,6 +343,7 @@ class CreateInternationalSwiftResponse(TypedDict): swift_bank_city: str swift_bank_state_province_region: str swift_bank_postal_code: str + sepa_beneficiary_bic: Optional[str] swift_intermediary_bank_swift_code_bic: Optional[str] swift_intermediary_bank_account_number_iban: Optional[str] swift_intermediary_bank_name: Optional[str] @@ -386,6 +390,42 @@ class CreateRtpResponse(TypedDict): created_at: str +class _CreateSepaInputRequired(TypedDict): + customer_id: str + name: str + account_class: AccountClass + sepa_iban: str + sepa_beneficiary_bic: str + sepa_beneficiary_legal_name: str + sepa_beneficiary_address_line_1: str + sepa_beneficiary_city: str + sepa_beneficiary_postal_code: str + sepa_beneficiary_country: Country + + +class CreateSepaInput(_CreateSepaInputRequired, total=False): + sepa_beneficiary_address_line_2: Optional[str] + sepa_beneficiary_state_province_region: Optional[str] + + +class CreateSepaResponse(TypedDict): + id: str + type: Literal["sepa"] + name: str + account_class: AccountClass + recipient_relationship: Optional[RecipientRelationship] + sepa_iban: str + sepa_beneficiary_bic: str + sepa_beneficiary_legal_name: str + sepa_beneficiary_address_line_1: str + sepa_beneficiary_address_line_2: Optional[str] + sepa_beneficiary_city: str + sepa_beneficiary_state_province_region: Optional[str] + sepa_beneficiary_postal_code: str + sepa_beneficiary_country: Country + created_at: str + + class CreatePixSafeInput(TypedDict): customer_id: str name: str @@ -508,6 +548,12 @@ async def create_ted(self, data: CreateTedInput) -> BlindpayApiResponse[CreateTe payload["type"] = "ted" return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) + async def create_sepa(self, data: CreateSepaInput) -> BlindpayApiResponse[CreateSepaResponse]: + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + payload["type"] = "sepa" + return await self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) + class BankAccountsResourceSync: def __init__(self, instance_id: str, client: InternalApiClientSync): @@ -587,6 +633,12 @@ def create_ted(self, data: CreateTedInput) -> BlindpayApiResponse[CreateTedRespo payload["type"] = "ted" return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) + def create_sepa(self, data: CreateSepaInput) -> BlindpayApiResponse[CreateSepaResponse]: + customer_id = data["customer_id"] + payload = {k: v for k, v in data.items() if k != "customer_id"} + payload["type"] = "sepa" + return self._client.post(f"/instances/{self._instance_id}/customers/{customer_id}/bank-accounts", payload) + def create_bank_accounts_resource(instance_id: str, client: InternalApiClient) -> BankAccountsResource: return BankAccountsResource(instance_id, client) diff --git a/src/blindpay/resources/instances/__init__.py b/src/blindpay/resources/instances/__init__.py index 4818152..642005f 100644 --- a/src/blindpay/resources/instances/__init__.py +++ b/src/blindpay/resources/instances/__init__.py @@ -4,6 +4,8 @@ InstanceMemberRole, InstancesResource, InstancesResourceSync, + MigrateInstanceOwnershipInput, + MigrateInstanceOwnershipResponse, UpdateInstanceInput, UpdateInstanceMemberRoleInput, create_instances_resource, @@ -17,6 +19,8 @@ "InstancesResourceSync", "UpdateInstanceInput", "UpdateInstanceMemberRoleInput", + "MigrateInstanceOwnershipInput", + "MigrateInstanceOwnershipResponse", "InstanceMemberRole", "GetInstanceMembersResponse", "InstanceMember", diff --git a/src/blindpay/resources/instances/instances.py b/src/blindpay/resources/instances/instances.py index 72a3f12..538cc56 100644 --- a/src/blindpay/resources/instances/instances.py +++ b/src/blindpay/resources/instances/instances.py @@ -34,6 +34,14 @@ class UpdateInstanceMemberRoleInput(TypedDict): role: InstanceMemberRole +class MigrateInstanceOwnershipInput(TypedDict): + user_id: str + + +class MigrateInstanceOwnershipResponse(TypedDict): + success: bool + + class InstancesResource: def __init__(self, instance_id: str, client: InternalApiClient): self._instance_id = instance_id @@ -54,6 +62,11 @@ async def delete_member(self, member_id: str) -> BlindpayApiResponse[None]: async def update_member_role(self, member_id: str, role: InstanceMemberRole) -> BlindpayApiResponse[None]: return await self._client.put(f"/instances/{self._instance_id}/members/{member_id}", {"user_role": role}) + async def migrate_ownership( + self, data: MigrateInstanceOwnershipInput + ) -> BlindpayApiResponse[MigrateInstanceOwnershipResponse]: + return await self._client.post(f"/instances/{self._instance_id}/ownership", data) + class InstancesResourceSync: def __init__(self, instance_id: str, client: InternalApiClientSync): @@ -75,6 +88,11 @@ def delete_member(self, member_id: str) -> BlindpayApiResponse[None]: def update_member_role(self, member_id: str, role: InstanceMemberRole) -> BlindpayApiResponse[None]: return self._client.put(f"/instances/{self._instance_id}/members/{member_id}", {"user_role": role}) + def migrate_ownership( + self, data: MigrateInstanceOwnershipInput + ) -> BlindpayApiResponse[MigrateInstanceOwnershipResponse]: + return self._client.post(f"/instances/{self._instance_id}/ownership", data) + def create_instances_resource(instance_id: str, client: InternalApiClient) -> InstancesResource: return InstancesResource(instance_id, client) diff --git a/src/blindpay/resources/payins/__init__.py b/src/blindpay/resources/payins/__init__.py index f394bf6..f73ac1a 100644 --- a/src/blindpay/resources/payins/__init__.py +++ b/src/blindpay/resources/payins/__init__.py @@ -10,6 +10,7 @@ GetPayinTrackResponse, ListPayinsInput, ListPayinsResponse, + ManualExecutionStatus, Payin, PayinsResource, PayinsResourceSync, @@ -45,6 +46,7 @@ "ExportPayinsResponse", "ListPayinsInput", "ListPayinsResponse", + "ManualExecutionStatus", "CreatePayinInput", "ExportPayinsInput", "CreateEvmPayinResponse", diff --git a/src/blindpay/resources/payins/payins.py b/src/blindpay/resources/payins/payins.py index 2f7ed59..b5604ea 100644 --- a/src/blindpay/resources/payins/payins.py +++ b/src/blindpay/resources/payins/payins.py @@ -1,6 +1,8 @@ from typing import List, Optional, TypedDict from urllib.parse import urlencode +from typing_extensions import Literal + from ..._internal.api_client import InternalApiClient, InternalApiClientSync from ...types import ( BlindpayApiResponse, @@ -15,6 +17,8 @@ TransactionStatus, ) +ManualExecutionStatus = Literal["failed"] + class AchDetails(TypedDict): routing_number: str @@ -62,6 +66,7 @@ class Payin(TypedDict): memo_code: Optional[str] clabe: Optional[str] status: TransactionStatus + manual_execution_status: Optional[ManualExecutionStatus] payin_quote_id: str instance_id: str tracking_transaction: Optional[TrackingTransaction] @@ -139,6 +144,7 @@ class GetPayinTrackResponse(TypedDict): memo_code: str clabe: str status: str + manual_execution_status: Optional[ManualExecutionStatus] payin_quote_id: str instance_id: str tracking_transaction: TrackingTransaction diff --git a/src/blindpay/resources/payouts/payouts.py b/src/blindpay/resources/payouts/payouts.py index b8db790..3f414c5 100644 --- a/src/blindpay/resources/payouts/payouts.py +++ b/src/blindpay/resources/payouts/payouts.py @@ -63,6 +63,8 @@ class Payout(TypedDict): transaction_document_id: str name: str type: Rail + billing_fee_amount: Optional[float] + cpn_payment_id: Optional[str] pix_key: Optional[str] account_number: Optional[str] routing_number: Optional[str] diff --git a/src/blindpay/resources/wallets/offramp.py b/src/blindpay/resources/wallets/offramp.py index a916266..2c76692 100644 --- a/src/blindpay/resources/wallets/offramp.py +++ b/src/blindpay/resources/wallets/offramp.py @@ -1,11 +1,9 @@ from typing import List -from typing_extensions import Literal, TypedDict +from typing_extensions import TypedDict from ..._internal.api_client import InternalApiClient, InternalApiClientSync -from ...types import BlindpayApiResponse - -TronNetwork = Literal["tron"] +from ...types import BlindpayApiResponse, Network class OfframpWallet(TypedDict): @@ -14,7 +12,7 @@ class OfframpWallet(TypedDict): instance_id: str customer_id: str bank_account_id: str - network: TronNetwork + network: Network address: str created_at: str updated_at: str @@ -33,13 +31,13 @@ class CreateOfframpWalletInput(TypedDict): customer_id: str bank_account_id: str external_id: str - network: TronNetwork + network: Network class CreateOfframpWalletResponse(TypedDict): id: str external_id: str - network: TronNetwork + network: Network address: str diff --git a/src/blindpay/types.py b/src/blindpay/types.py index 5e85ffa..2349c77 100644 --- a/src/blindpay/types.py +++ b/src/blindpay/types.py @@ -392,3 +392,5 @@ class TrackingPartnerFee(TypedDict): PaymentMethod = Literal["ach", "wire", "pix", "spei", "transfers", "pse", "international_swift", "rtp", "ted"] KycStatus = Literal["awaiting_contract", "compliance_request"] + +WebhookEvent = Literal["receiver.delete"] From 686a0c0937a8386c8a50904666949e9373eefac8 Mon Sep 17 00:00:00 2001 From: alvseven Date: Mon, 8 Jun 2026 17:49:43 -0300 Subject: [PATCH 3/3] fix: ListBankAccountsResponse type matches actual array response The API returns a plain array, not {data: [...]}. Mirrors node SDK --- src/blindpay/resources/bank_accounts/bank_accounts.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/blindpay/resources/bank_accounts/bank_accounts.py b/src/blindpay/resources/bank_accounts/bank_accounts.py index a81ab07..f0ae44f 100644 --- a/src/blindpay/resources/bank_accounts/bank_accounts.py +++ b/src/blindpay/resources/bank_accounts/bank_accounts.py @@ -92,8 +92,7 @@ class BankAccount(TypedDict): ted_cpf_cnpj: Optional[str] -class ListBankAccountsResponse(TypedDict): - data: List[BankAccount] +ListBankAccountsResponse = List[BankAccount] class GetBankAccountResponse(TypedDict):