diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 822b546..091b884 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.4.0" + ".": "1.5.0" } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index bfec037..a1169c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "blindpay" -version = "1.4.0" +version = "1.5.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 5b7295b..21f7d73 100644 --- a/src/blindpay/__init__.py +++ b/src/blindpay/__init__.py @@ -1,4 +1,4 @@ -__version__ = "1.4.0" +__version__ = "1.5.0" from ._internal.exceptions import BlindPayError from .client import BlindPay, BlindPaySync diff --git a/src/blindpay/client.py b/src/blindpay/client.py index bbb99a4..638964d 100644 --- a/src/blindpay/client.py +++ b/src/blindpay/client.py @@ -38,7 +38,7 @@ from blindpay.resources.wallets.offramp import OfframpWalletsResource, OfframpWalletsResourceSync from blindpay.resources.webhooks.webhooks import WebhookEndpointsResource, WebhookEndpointsResourceSync -__version__ = "1.4.0" +__version__ = "1.5.0" T = TypeVar("T") diff --git a/src/blindpay/resources/payouts/__init__.py b/src/blindpay/resources/payouts/__init__.py index 85905a2..1c329a3 100644 --- a/src/blindpay/resources/payouts/__init__.py +++ b/src/blindpay/resources/payouts/__init__.py @@ -3,15 +3,24 @@ AuthorizeStellarTokenResponse, CreateEvmPayoutInput, CreateEvmPayoutResponse, + CreateSolanaPayoutInput, + CreateSolanaPayoutResponse, CreateStellarPayoutInput, CreateStellarPayoutResponse, ExportPayoutsInput, ExportPayoutsResponse, + JpmTrackData, ListPayoutsInput, ListPayoutsResponse, Payout, + PayoutPaymentMethod, PayoutsResource, PayoutsResourceSync, + PayoutStatus, + SubmitPayoutDocumentsInput, + SubmitPayoutDocumentsResponse, + TrackingDocuments, + TrackingDocumentsStatus, create_payouts_resource, create_payouts_resource_sync, ) @@ -32,4 +41,13 @@ "CreateStellarPayoutResponse", "CreateEvmPayoutInput", "CreateEvmPayoutResponse", + "CreateSolanaPayoutInput", + "CreateSolanaPayoutResponse", + "SubmitPayoutDocumentsInput", + "SubmitPayoutDocumentsResponse", + "PayoutStatus", + "PayoutPaymentMethod", + "TrackingDocuments", + "TrackingDocumentsStatus", + "JpmTrackData", ] diff --git a/src/blindpay/resources/payouts/payouts.py b/src/blindpay/resources/payouts/payouts.py index 2b9eaaf..dfaee48 100644 --- a/src/blindpay/resources/payouts/payouts.py +++ b/src/blindpay/resources/payouts/payouts.py @@ -27,6 +27,33 @@ ArgentinaTransfers = Literal["CVU", "CBU", "ALIAS"] +PayoutStatus = Literal["processing", "failed", "refunded", "completed", "on_hold"] + +PayoutPaymentMethod = Literal[ + "wire", "ach", "pix", "pix_safe", "spei_bitso", "transfers_bitso", "ach_cop_bitso", "international_swift", "rtp" +] + +TrackingDocumentsStatus = Literal["waiting_documents", "compliance_reviewing"] + + +class TrackingDocuments(TypedDict): + step: str + status: Optional[str] + reviewed_by: Optional[str] + completed_at: Optional[str] + + +class JpmTrackData(TypedDict): + jpm_trace_number: Optional[str] + jpm_processing_status: Optional[str] + extended_tracking_status: Optional[str] + jpm_reference_number: Optional[str] + uetr: Optional[str] + fed_imad: Optional[str] + payment_date: Optional[str] + payment_amount: Optional[str] + payment_currency: Optional[str] + class Payout(TypedDict): receiver_id: str @@ -36,11 +63,14 @@ class Payout(TypedDict): signed_transaction: str quote_id: str instance_id: str + partner_fee: Optional[int] tracking_transaction: TrackingTransaction tracking_payment: TrackingPayment tracking_liquidity: TrackingLiquidity tracking_complete: TrackingComplete tracking_partner_fee: TrackingPartnerFee + tracking_documents: Optional[TrackingDocuments] + jpm_track_data: Optional[JpmTrackData] created_at: str updated_at: str image_url: str @@ -58,12 +88,16 @@ class Payout(TypedDict): total_fee_amount: float receiver_local_amount: float currency: Currency # Excluding "USDT" | "USDB" + transaction_fee_amount: Optional[float] transaction_document_file: str transaction_document_type: TransactionDocumentType transaction_document_id: str name: str type: Rail pix_key: Optional[str] + pix_safe_bank_code: Optional[str] + pix_safe_branch_code: Optional[str] + pix_safe_cpf_cnpj: Optional[str] account_number: Optional[str] routing_number: Optional[str] country: Optional[Country] @@ -96,6 +130,13 @@ class Payout(TypedDict): class ListPayoutsInput(PaginationParams, total=False): receiver_id: str + status: PayoutStatus + receiver_name: str + bank_account_id: str + country: str + payment_method: PayoutPaymentMethod + network: str + token: str class ListPayoutsResponse(TypedDict): @@ -155,6 +196,24 @@ class CreateEvmPayoutResponse(TypedDict): receiver_id: str +class CreateSolanaPayoutInput(TypedDict): + quote_id: str + sender_wallet_address: str + signed_transaction: Optional[str] + + +class CreateSolanaPayoutResponse(TypedDict): + id: str + status: TransactionStatus + sender_wallet_address: str + tracking_complete: Optional[TrackingComplete] + tracking_payment: Optional[TrackingPayment] + tracking_transaction: Optional[TrackingTransaction] + tracking_partner_fee: Optional[TrackingPartnerFee] + tracking_liquidity: Optional[TrackingLiquidity] + receiver_id: str + + class SubmitPayoutDocumentsInput(TypedDict): payout_id: str transaction_document_type: TransactionDocumentType @@ -205,6 +264,9 @@ async def create_stellar(self, data: CreateStellarPayoutInput) -> BlindpayApiRes async def create_evm(self, data: CreateEvmPayoutInput) -> BlindpayApiResponse[CreateEvmPayoutResponse]: return await self._client.post(f"/instances/{self._instance_id}/payouts/evm", data) + async def create_solana(self, data: CreateSolanaPayoutInput) -> BlindpayApiResponse[CreateSolanaPayoutResponse]: + return await self._client.post(f"/instances/{self._instance_id}/payouts/solana", data) + async def submit_documents( self, data: SubmitPayoutDocumentsInput ) -> BlindpayApiResponse[SubmitPayoutDocumentsResponse]: @@ -251,6 +313,9 @@ def create_stellar(self, data: CreateStellarPayoutInput) -> BlindpayApiResponse[ def create_evm(self, data: CreateEvmPayoutInput) -> BlindpayApiResponse[CreateEvmPayoutResponse]: return self._client.post(f"/instances/{self._instance_id}/payouts/evm", data) + def create_solana(self, data: CreateSolanaPayoutInput) -> BlindpayApiResponse[CreateSolanaPayoutResponse]: + return self._client.post(f"/instances/{self._instance_id}/payouts/solana", data) + def submit_documents(self, data: SubmitPayoutDocumentsInput) -> BlindpayApiResponse[SubmitPayoutDocumentsResponse]: payout_id = data["payout_id"] payload = {k: v for k, v in data.items() if k != "payout_id"} diff --git a/src/blindpay/resources/receivers/receivers.py b/src/blindpay/resources/receivers/receivers.py index 0dc9be5..69f9462 100644 --- a/src/blindpay/resources/receivers/receivers.py +++ b/src/blindpay/resources/receivers/receivers.py @@ -493,6 +493,13 @@ class CreateIndividualWithStandardKYCInput(TypedDict): account_purpose: Optional[AccountPurpose] account_purpose_other: Optional[str] occupation: Optional[str] + is_fbo: Optional[bool] + business_type: Optional[ReceiverBusinessType] + business_description: Optional[str] + business_industry: Optional[BusinessIndustry] + estimated_annual_revenue: Optional[EstimatedAnnualRevenue] + source_of_wealth: Optional[SourceOfWealth] + publicly_traded: Optional[bool] class CreateIndividualWithStandardKYCResponse(TypedDict): @@ -532,6 +539,12 @@ class CreateIndividualWithEnhancedKYCInput(TypedDict): account_purpose: Optional[AccountPurpose] account_purpose_other: Optional[str] occupation: Optional[str] + business_type: Optional[ReceiverBusinessType] + business_description: Optional[str] + business_industry: Optional[BusinessIndustry] + estimated_annual_revenue: Optional[EstimatedAnnualRevenue] + source_of_wealth: Optional[SourceOfWealth] + publicly_traded: Optional[bool] class CreateIndividualWithEnhancedKYCResponse(TypedDict): @@ -571,6 +584,7 @@ class CreateBusinessWithStandardKYBInput(TypedDict): source_of_wealth: Optional[SourceOfWealth] publicly_traded: Optional[bool] occupation: Optional[str] + is_fbo: Optional[bool] class CreateBusinessWithStandardKYBResponse(TypedDict): diff --git a/tests/resources/test_payouts.py b/tests/resources/test_payouts.py index a39bcd2..93e3ae8 100644 --- a/tests/resources/test_payouts.py +++ b/tests/resources/test_payouts.py @@ -590,6 +590,70 @@ async def test_create_evm_payout(self): }, ) + @pytest.mark.asyncio + async def test_create_solana_payout(self): + mocked_solana_payout = { + "id": "pa_000000000000", + "status": "processing", + "sender_wallet_address": "0x123...890", + "tracking_complete": { + "step": "on_hold", + "status": "tokens_refunded", + "transaction_hash": "0x123...890", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "tracking_payment": { + "step": "on_hold", + "provider_name": "blockchain", + "provider_transaction_id": "0x123...890", + "provider_status": "canceled", + "estimated_time_of_arrival": "5_min", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "tracking_transaction": { + "step": "processing", + "status": "failed", + "transaction_hash": "0x123...890", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "tracking_partner_fee": { + "step": "on_hold", + "transaction_hash": "0x123...890", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "tracking_liquidity": { + "step": "processing", + "provider_transaction_id": "0x123...890", + "provider_status": "deposited", + "estimated_time_of_arrival": "1_business_day", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "receiver_id": "re_000000000000", + } + + with patch.object(self.blindpay._api, "_request") as mock_request: + mock_request.return_value = {"data": mocked_solana_payout, "error": None} + + response = await self.blindpay.payouts.create_solana( + { + "quote_id": "qu_000000000000", + "sender_wallet_address": "0x123...890", + "signed_transaction": "signed_solana_tx", + } + ) + + assert response["error"] is None + assert response["data"] == mocked_solana_payout + mock_request.assert_called_once_with( + "POST", + "/instances/in_000000000000/payouts/solana", + { + "quote_id": "qu_000000000000", + "sender_wallet_address": "0x123...890", + "signed_transaction": "signed_solana_tx", + }, + ) + class TestPayoutsSync: @pytest.fixture(autouse=True) @@ -1168,3 +1232,66 @@ def test_create_evm_payout(self): "sender_wallet_address": "0x123...890", }, ) + + def test_create_solana_payout(self): + mocked_solana_payout = { + "id": "pa_000000000000", + "status": "processing", + "sender_wallet_address": "0x123...890", + "tracking_complete": { + "step": "on_hold", + "status": "tokens_refunded", + "transaction_hash": "0x123...890", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "tracking_payment": { + "step": "on_hold", + "provider_name": "blockchain", + "provider_transaction_id": "0x123...890", + "provider_status": "canceled", + "estimated_time_of_arrival": "5_min", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "tracking_transaction": { + "step": "processing", + "status": "failed", + "transaction_hash": "0x123...890", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "tracking_partner_fee": { + "step": "on_hold", + "transaction_hash": "0x123...890", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "tracking_liquidity": { + "step": "processing", + "provider_transaction_id": "0x123...890", + "provider_status": "deposited", + "estimated_time_of_arrival": "1_business_day", + "completed_at": "2011-10-05T14:48:00.000Z", + }, + "receiver_id": "re_000000000000", + } + + with patch.object(self.blindpay._api, "_request") as mock_request: + mock_request.return_value = {"data": mocked_solana_payout, "error": None} + + response = self.blindpay.payouts.create_solana( + { + "quote_id": "qu_000000000000", + "sender_wallet_address": "0x123...890", + "signed_transaction": "signed_solana_tx", + } + ) + + assert response["error"] is None + assert response["data"] == mocked_solana_payout + mock_request.assert_called_once_with( + "POST", + "/instances/in_000000000000/payouts/solana", + { + "quote_id": "qu_000000000000", + "sender_wallet_address": "0x123...890", + "signed_transaction": "signed_solana_tx", + }, + )