Initial Python SDK release v1.0.0

This commit is contained in:
garfieldheron
2026-02-19 12:17:09 -05:00
commit 42443872c9
17 changed files with 1037 additions and 0 deletions

View File

@@ -0,0 +1 @@
# API modules

53
fetcherpay/api/ledger.py Normal file
View File

@@ -0,0 +1,53 @@
"""
Ledger API
"""
from typing import Optional
class LedgerAPI:
"""Ledger API client"""
def __init__(self, client):
self.client = client
def list_accounts(
self,
limit: int = 25,
cursor: Optional[str] = None,
type: Optional[str] = None
) -> dict:
"""List ledger accounts"""
params = {'limit': limit}
if cursor:
params['cursor'] = cursor
if type:
params['type'] = type
return self.client._request('GET', '/ledger/accounts', params=params)
def retrieve_account(self, account_id: str) -> dict:
"""Retrieve a ledger account"""
return self.client._request('GET', f'/ledger/accounts/{account_id}')
def list_entries(
self,
limit: int = 25,
cursor: Optional[str] = None,
account_id: Optional[str] = None,
payment_id: Optional[str] = None
) -> dict:
"""List ledger entries"""
params = {'limit': limit}
if cursor:
params['cursor'] = cursor
if account_id:
params['account_id'] = account_id
if payment_id:
params['payment_id'] = payment_id
return self.client._request('GET', '/ledger/entries', params=params)
def retrieve_entry(self, entry_id: str) -> dict:
"""Retrieve a ledger entry"""
return self.client._request('GET', f'/ledger/entries/{entry_id}')

View File

@@ -0,0 +1,73 @@
"""
Payment Methods API
"""
from typing import Optional, Dict, Any
class PaymentMethodsAPI:
"""Payment Methods API client"""
def __init__(self, client):
self.client = client
def create(
self,
type: str,
bank_account: Optional[Dict[str, Any]] = None,
card: Optional[Dict[str, Any]] = None,
usdc_wallet: Optional[Dict[str, Any]] = None,
metadata: Optional[Dict[str, Any]] = None,
idempotency_key: Optional[str] = None
) -> dict:
"""
Create a payment method
Args:
type: 'bank_account', 'card', or 'usdc_wallet'
bank_account: Bank account details
card: Card details
usdc_wallet: USDC wallet details
metadata: Optional metadata
idempotency_key: Idempotency key
"""
data = {'type': type}
if bank_account:
data['bank_account'] = bank_account
if card:
data['card'] = card
if usdc_wallet:
data['usdc_wallet'] = usdc_wallet
if metadata:
data['metadata'] = metadata
return self.client._request(
'POST',
'/payment-methods',
json=data,
idempotency_key=idempotency_key
)
def retrieve(self, payment_method_id: str) -> dict:
"""Retrieve a payment method"""
return self.client._request('GET', f'/payment-methods/{payment_method_id}')
def list(
self,
limit: int = 25,
cursor: Optional[str] = None,
type: Optional[str] = None
) -> dict:
"""List payment methods"""
params = {'limit': limit}
if cursor:
params['cursor'] = cursor
if type:
params['type'] = type
return self.client._request('GET', '/payment-methods', params=params)
def delete(self, payment_method_id: str) -> None:
"""Delete a payment method"""
self.client._request('DELETE', f'/payment-methods/{payment_method_id}')

133
fetcherpay/api/payments.py Normal file
View File

@@ -0,0 +1,133 @@
"""
Payments API
"""
from typing import Optional, Dict, Any, List
class PaymentsAPI:
"""Payments API client"""
def __init__(self, client):
self.client = client
def create(
self,
amount: int,
source: Dict[str, str],
destination: Dict[str, str],
currency: str = 'USD',
rail: str = 'auto',
description: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
idempotency_key: Optional[str] = None
) -> Dict[str, Any]:
"""
Create a new payment
Args:
amount: Amount in cents (e.g., 10000 for $100.00)
source: Payment source with payment_method_id
destination: Payment destination with payment_method_id
currency: Currency code (default: USD)
rail: Payment rail (auto, ach, rtp, card, crypto)
description: Optional payment description
metadata: Optional metadata dictionary
idempotency_key: Optional idempotency key
Returns:
Payment object
"""
data = {
'amount': amount,
'currency': currency,
'rail': rail,
'source': source,
'destination': destination,
}
if description:
data['description'] = description
if metadata:
data['metadata'] = metadata
return self.client._request(
'POST',
'/payments',
json=data,
idempotency_key=idempotency_key
)
def retrieve(self, payment_id: str) -> Dict[str, Any]:
"""Retrieve a payment by ID"""
return self.client._request('GET', f'/payments/{payment_id}')
def list(
self,
limit: int = 25,
cursor: Optional[str] = None,
status: Optional[str] = None,
rail: Optional[str] = None
) -> Dict[str, Any]:
"""
List payments
Args:
limit: Number of results (1-100)
cursor: Pagination cursor
status: Filter by status
rail: Filter by rail
"""
params = {'limit': limit}
if cursor:
params['cursor'] = cursor
if status:
params['status'] = status
if rail:
params['rail'] = rail
return self.client._request('GET', '/payments', params=params)
def cancel(
self,
payment_id: str,
reason: Optional[str] = None,
idempotency_key: Optional[str] = None
) -> Dict[str, Any]:
"""Cancel a pending payment"""
data = {'reason': reason} if reason else {}
return self.client._request(
'POST',
f'/payments/{payment_id}/cancel',
json=data,
idempotency_key=idempotency_key
)
def refund(
self,
payment_id: str,
amount: Optional[int] = None,
reason: Optional[str] = None,
idempotency_key: Optional[str] = None
) -> Dict[str, Any]:
"""
Refund a settled payment
Args:
payment_id: Payment ID to refund
amount: Amount to refund (omit for full refund)
reason: Refund reason
idempotency_key: Idempotency key
"""
data = {}
if amount:
data['amount'] = amount
if reason:
data['reason'] = reason
return self.client._request(
'POST',
f'/payments/{payment_id}/refund',
json=data,
idempotency_key=idempotency_key
)

View File

@@ -0,0 +1,80 @@
"""
Webhooks API
"""
from typing import Optional, List, Dict, Any
class WebhooksAPI:
"""Webhooks API client"""
def __init__(self, client):
self.client = client
def create(
self,
url: str,
events: Optional[List[str]] = None,
metadata: Optional[Dict[str, Any]] = None,
idempotency_key: Optional[str] = None
) -> dict:
"""
Create a webhook endpoint
Args:
url: Endpoint URL
events: List of events to subscribe to
metadata: Optional metadata
idempotency_key: Idempotency key
"""
data = {'url': url}
if events:
data['events'] = events
if metadata:
data['metadata'] = metadata
return self.client._request(
'POST',
'/webhooks',
json=data,
idempotency_key=idempotency_key
)
def retrieve(self, webhook_id: str) -> dict:
"""Retrieve a webhook endpoint"""
return self.client._request('GET', f'/webhooks/{webhook_id}')
def list(
self,
limit: int = 25,
cursor: Optional[str] = None
) -> dict:
"""List webhook endpoints"""
params = {'limit': limit}
if cursor:
params['cursor'] = cursor
return self.client._request('GET', '/webhooks', params=params)
def update(
self,
webhook_id: str,
url: Optional[str] = None,
events: Optional[List[str]] = None,
metadata: Optional[Dict[str, Any]] = None
) -> dict:
"""Update a webhook endpoint"""
data = {}
if url:
data['url'] = url
if events:
data['events'] = events
if metadata:
data['metadata'] = metadata
return self.client._request('PUT', f'/webhooks/{webhook_id}', json=data)
def delete(self, webhook_id: str) -> None:
"""Delete a webhook endpoint"""
self.client._request('DELETE', f'/webhooks/{webhook_id}')