Hyperswitch Payments SDK — Python client for connector integrations via UniFFI FFI
Project description
hyperswitch-prism
Universal Connector Service — Python SDK
A high-performance, type-safe Python SDK for payment processing through the Universal Connector Service. Connect to 50+ payment processors (Stripe, PayPal, Adyen, and more) through a single, unified API.
📚 Documentation
| Resource | Link |
|---|---|
| Getting Started | Installation · First Payment |
| Architecture | Overview · Core Concepts |
| API Reference | Payment Service |
| Examples | Connector Examples · Smoke Test · Tests |
| Main Project | Prism Docs |
Features
- 🚀 High Performance — Direct UniFFI FFI bindings to Rust core
- 🔌 50+ Connectors — Single SDK for Stripe, PayPal, Adyen, and more
- 🐍 Python Native — Full Python bindings with type hints
- ⚡ Connection Pooling — Built-in HTTP connection pooling via httpx
- 🛡️ Type-Safe — Protobuf-based request/response serialization
- 🔧 Configurable — Per-request or global configuration for timeouts, proxies, and auth
🤖 For AI Assistants
Use
curlto fetch the complete SDK reference:curl -fsSL https://raw.githubusercontent.com/juspay/hyperswitch-prism/main/llm/sdk-reference.mdThis file contains complete SDK documentation including installation, payment operations, error handling, connector configuration, field probe data, and examples for all 70+ connectors.
AI Assistant Context
This SDK is part of Hyperswitch Prism — a unified connector library for payment processors.
What This SDK Does
- Request Transformation: Converts unified payment requests to connector-specific formats (Stripe, Adyen, PayPal, etc.)
- Response Normalization: Transforms connector responses back to a unified schema
- Error Handling: Provides consistent error types (
IntegrationError,ConnectorError,NetworkError) regardless of connector
Architecture
Your Python App
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Service Clients (PaymentClient, CustomerClient, etc.) │
└───────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ConnectorClient (httpx connection pool + HTTP execution) │
└───────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ UniFFI FFI Bindings (connector_service_ffi.py) │
└───────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Rust Core (connector transformation logic) │
└───────────────────────────┬──────────────────────────────────┘
│
▼
Payment Processor APIs (Stripe, Adyen, etc.)
Key Files
| File | Purpose |
|---|---|
src/payments/__init__.py |
Public API exports (clients, types, errors) |
src/payments/connector_client.py |
HTTP execution layer with httpx |
src/payments/generated/connector_service_ffi.py |
UniFFI-generated FFI bindings |
src/payments/generated/payment_pb2.py |
Protobuf message definitions |
Package & Import
- Package Name:
hyperswitch-prism - Installation:
pip install hyperswitch-prism - Import:
from payments import PaymentClient
Installation
pip install hyperswitch-prism
Once installed, the package is imported as payments:
from payments import PaymentClient
Requirements:
- Python 3.9+
- Rust toolchain (for building native bindings from source)
Platform Support:
- ✅ macOS (x64, arm64)
- ✅ Linux (x64, arm64)
- ✅ Windows (x64)
Quick Start
1. Configure the Client
import os
from payments import PaymentClient, SecretString
from payments.generated import sdk_config_pb2, payment_pb2
cfg = sdk_config_pb2.ConnectorConfig(
options=sdk_config_pb2.SdkOptions(environment=sdk_config_pb2.Environment.SANDBOX)
)
cfg.connector_config.CopyFrom(payment_pb2.ConnectorSpecificConfig(
stripe=payment_pb2.StripeConfig(
api_key=SecretString(value=os.environ["STRIPE_API_KEY"])
)
))
2. Process a Payment
import asyncio
from google.protobuf.json_format import ParseDict
req = ParseDict(
{
"merchant_transaction_id": "txn_order_001",
"amount": {
"minorAmount": 1000, # $10.00
"currency": "USD"
},
"capture_method": "AUTOMATIC",
"payment_method": {
"card": {
"card_number": {"value": "4111111111111111"},
"card_exp_month": {"value": "12"},
"card_exp_year": {"value": "2030"},
"card_cvc": {"value": "123"},
"card_holder_name": {"value": "John Doe"}
}
},
"address": {"billing_address": {}},
"auth_type": "NO_THREE_DS",
"return_url": "https://example.com/return",
"order_details": []
},
payment_pb2.PaymentServiceAuthorizeRequest()
)
async def run():
client = PaymentClient(cfg)
resp = await client.authorize(req)
print(payment_pb2.PaymentStatus.Name(resp.status)) # e.g. "CHARGED"
print(resp.connector_transaction_id)
asyncio.run(run())
Service Clients
The SDK provides specialized clients for different service domains:
| Client | Purpose | Key Methods |
|---|---|---|
PaymentClient |
Core payment operations | authorize(), capture(), refund(), void() |
CustomerClient |
Customer management | create() |
PaymentMethodClient |
Secure tokenization | tokenize() |
MerchantAuthenticationClient |
Auth token management | create_server_authentication_token(), create_server_session_authentication_token(), create_client_authentication_token() |
EventClient |
Webhook processing | handle_event() |
RecurringPaymentClient |
Subscription billing | charge() |
PaymentMethodAuthenticationClient |
3DS authentication | pre_authenticate(), authenticate(), post_authenticate() |
Authentication Examples
SecretString is a protobuf message. All credential fields must be constructed as SecretString(value="...") — passing a plain string will raise a proto type error.
Stripe
import os
from payments import SecretString
from payments.generated import sdk_config_pb2, payment_pb2
cfg = sdk_config_pb2.ConnectorConfig(
options=sdk_config_pb2.SdkOptions(environment=sdk_config_pb2.Environment.SANDBOX)
)
cfg.connector_config.CopyFrom(payment_pb2.ConnectorSpecificConfig(
stripe=payment_pb2.StripeConfig(
api_key=SecretString(value=os.environ["STRIPE_API_KEY"])
)
))
PayPal
import os
from payments import SecretString
from payments.generated import sdk_config_pb2, payment_pb2
cfg = sdk_config_pb2.ConnectorConfig(
options=sdk_config_pb2.SdkOptions(environment=sdk_config_pb2.Environment.SANDBOX)
)
cfg.connector_config.CopyFrom(payment_pb2.ConnectorSpecificConfig(
pay_pal=payment_pb2.PayPalConfig(
client_id=SecretString(value=os.environ["PAYPAL_CLIENT_ID"]),
client_secret=SecretString(value=os.environ["PAYPAL_CLIENT_SECRET"])
)
))
Adyen
import os
from payments import SecretString
from payments.generated import sdk_config_pb2, payment_pb2
cfg = sdk_config_pb2.ConnectorConfig(
options=sdk_config_pb2.SdkOptions(environment=sdk_config_pb2.Environment.SANDBOX)
)
cfg.connector_config.CopyFrom(payment_pb2.ConnectorSpecificConfig(
adyen=payment_pb2.AdyenConfig(
api_key=SecretString(value=os.environ["ADYEN_API_KEY"]),
merchant_account=SecretString(value=os.environ["ADYEN_MERCHANT_ACCOUNT"])
# api_secret and review_key are not required for payment or refund operations
)
))
Advanced Configuration
Proxy Settings
from payments import types
proxy_config: types.RequestConfig = {
"http": {
"proxy": {
"httpsUrl": "https://proxy.company.com:8443",
"bypassUrls": ["http://localhost"]
}
}
}
Per-Request Overrides
response = client.authorize(request, {
"http": {
"totalTimeoutMs": 60000 # Override for this request only
}
})
Connection Pooling
Each client instance maintains its own connection pool. For best performance:
# ✅ Create client once, reuse for multiple requests
client = PaymentClient(config, defaults)
for payment in payments:
client.authorize(payment)
Error Handling
from payments import IntegrationError, ConnectorError
try:
response = client.authorize(request)
except IntegrationError as e:
# Request-phase error (auth, URL construction, serialization, etc.)
print(f"Code: {e.error_code}")
print(f"Status: {e.status_code}")
print(f"Message: {e.message}")
except ConnectorError as e:
# Response-phase error (deserialization, transformation, etc.)
print(f"Code: {e.error_code}")
print(f"Status: {e.status_code}")
print(f"Message: {e.message}")
Error Codes
| Code | Description |
|---|---|
CONNECT_TIMEOUT |
Failed to establish connection |
RESPONSE_TIMEOUT |
No response received from gateway |
TOTAL_TIMEOUT |
Overall request timeout exceeded |
NETWORK_FAILURE |
General network error |
INVALID_CONFIGURATION |
Configuration error |
CLIENT_INITIALIZATION |
SDK initialization failed |
Response Handling
Each response type uses a specific status enum. Using the wrong enum returns an incorrect name because PaymentStatus and RefundStatus share overlapping integer values:
| Response type | Correct status enum |
|---|---|
PaymentServiceAuthorizeResponse |
payment_pb2.PaymentStatus |
PaymentServiceCaptureResponse |
payment_pb2.PaymentStatus |
PaymentServiceVoidResponse |
payment_pb2.PaymentStatus |
RefundResponse |
payment_pb2.RefundStatus |
Payment Status
Response status fields are protobuf enum integers, not strings. Use the generated proto module to compare or display them:
from payments.generated import payment_pb2
response = client.authorize(authorize_request)
# Compare against named integer constants
if response.status == payment_pb2.CHARGED:
print("Payment succeeded")
# Decode to a human-readable string for display
status_name = payment_pb2.PaymentStatus.Name(response.status)
print(f"Status: {status_name}") # e.g. "CHARGED"
Comparing
response.status == "CHARGED"will always beFalse. Use the integer constants frompayment_pb2.
Refund Status
Authorize and refund responses use separate, independent enums. PaymentStatus and RefundStatus share overlapping integer values that map to different names:
| Integer | PaymentStatus.Name() |
RefundStatus.Name() |
|---|---|---|
4 |
AUTHENTICATION_PENDING |
REFUND_SUCCESS |
Always use RefundStatus when decoding a refund response:
from payments.generated import payment_pb2
refund_response = client.refund(refund_request)
# Correct: use RefundStatus for refund responses
status_name = payment_pb2.RefundStatus.Name(refund_response.status)
print(f"Refund status: {status_name}") # e.g. "REFUND_SUCCESS" or "REFUND_PENDING"
Adyen refunds return
REFUND_PENDINGwith HTTP 201. This indicates the refund has been accepted for asynchronous processing and is not an error.
Complete Example: PayPal with Access Token
import os
from payments import (
PaymentClient,
MerchantAuthenticationClient,
types
)
# Configure PayPal
paypal_config: types.ConnectorConfig = {
"connectorConfig": {
"paypal": {
"clientId": {"value": os.environ["PAYPAL_CLIENT_ID"]},
"clientSecret": {"value": os.environ["PAYPAL_CLIENT_SECRET"]}
}
}
}
# Step 1: Get access token
auth_client = MerchantAuthenticationClient(paypal_config)
token_response = auth_client.create_server_authentication_token({
"merchantAccessTokenId": "token_001",
"connector": "PAYPAL",
"testMode": True
})
# Step 2: Authorize with access token
payment_client = PaymentClient(paypal_config)
payment_response = payment_client.authorize({
"merchantTransactionId": "txn_001",
"amount": {
"minorAmount": 1000,
"currency": "USD"
},
"captureMethod": "AUTOMATIC",
"paymentMethod": {
"card": {
"cardNumber": {"value": "4111111111111111"},
"cardExpMonth": {"value": "12"},
"cardExpYear": {"value": "2027"},
"cardCvc": {"value": "123"}
}
},
"state": {
"accessToken": {
"token": {"value": token_response.accessToken.value},
"tokenType": "Bearer",
"expiresInSeconds": token_response.expiresInSeconds
}
},
"testMode": True
})
print(f"Payment status: {payment_response.status}")
Architecture
Your App → Service Client → ConnectorClient → UniFFI FFI → Rust Core → Connector API
↓
Connection Pool (httpx)
The SDK uses:
- UniFFI — FFI bindings to Rust
- protobuf — Protocol buffer serialization
- httpx — High-performance HTTP client with connection pooling
Building from Source
# Clone the repository
git clone https://github.com/juspay/connector-service.git
cd connector-service/sdk/python
# Build native library, generate bindings, and pack
make pack
# Run tests
make test-pack
# With live API credentials
STRIPE_API_KEY=sk_test_xxx make test-pack
How it works
make build-lib— buildscrates/ffi/ffiwith--features uniffimake generate-bindings— runsuniffi-bindgento producegenerated/connector_service_ffi.pymake generate-proto— runsgrpc_tools.protocto producegenerated/payment_pb2.pymake pack-archive— runspip wheelto produce the installable.whl
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hyperswitch_prism-0.0.1-py3-none-any.whl.
File metadata
- Download URL: hyperswitch_prism-0.0.1-py3-none-any.whl
- Upload date:
- Size: 23.1 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62d5fbd809d1051520df57e0e17ab9774877cc668d4f06ce2f6a4f50d0a16217
|
|
| MD5 |
9e7a3f1c6fbf73ff3c07c45412a67b64
|
|
| BLAKE2b-256 |
8c84aaed60be7e8029cbca2b716dca2be3c87de30ea90a549787a03ab814acc0
|