Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.4.0"
".": "1.5.0"
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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" }]
Expand Down
2 changes: 1 addition & 1 deletion src/blindpay/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.4.0"
__version__ = "1.5.0"

from ._internal.exceptions import BlindPayError
from .client import BlindPay, BlindPaySync
Expand Down
2 changes: 1 addition & 1 deletion src/blindpay/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down
18 changes: 18 additions & 0 deletions src/blindpay/resources/payouts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand All @@ -32,4 +41,13 @@
"CreateStellarPayoutResponse",
"CreateEvmPayoutInput",
"CreateEvmPayoutResponse",
"CreateSolanaPayoutInput",
"CreateSolanaPayoutResponse",
"SubmitPayoutDocumentsInput",
"SubmitPayoutDocumentsResponse",
"PayoutStatus",
"PayoutPaymentMethod",
"TrackingDocuments",
"TrackingDocumentsStatus",
"JpmTrackData",
]
65 changes: 65 additions & 0 deletions src/blindpay/resources/payouts/payouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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]
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]:
Expand Down Expand Up @@ -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"}
Expand Down
14 changes: 14 additions & 0 deletions src/blindpay/resources/receivers/receivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
127 changes: 127 additions & 0 deletions tests/resources/test_payouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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",
},
)
Loading