Getting Started
Installation
Or with uv:
Requirements: Python 3.10+
Dependencies: httpx (HTTP client), pydantic (validation)
Authentication
All clients authenticate automatically using OAuth2 client credentials. Provide
your client_id and client_secret obtained from iHela.
Merchant Services
from ihela_sdk import MerchantClient
client = MerchantClient("your_client_id", "your_client_secret", prod=False)
Banking Services
from ihela_sdk import BankingClient
client = BankingClient("your_client_id", "your_client_secret", prod=False)
Agent Services
from ihela_sdk import AgentClient
client = AgentClient("your_client_id", "your_client_secret", prod=False)
Set prod=True for the production environment. The default (False) uses the
test gateway at https://testgate.ihela.online/.
Environment URLs
| Environment | Gateway URL |
|---|---|
| Test | https://testgate.ihela.online/ |
| Production | https://gate.ihela.online/ |
Override with a custom URL via the ihela_url parameter on any client.
Quick Example
from ihela_sdk import MerchantClient
client = MerchantClient("your_client_id", "your_client_secret")
bill = client.init_bill(
amount=2000,
user="customer@example.com",
description="Invoice #123",
reference="merchant-ref-001",
pin_code="1234",
)
status = client.verify_bill(
bill_code=bill["bill"]["code"],
merchant_reference=bill["bill"]["merchant_reference"],
pin_code="1234",
)
Error Handling
The SDK raises structured exceptions on failures:
from ihela_sdk import (
iHelaError,
iHelaAuthenticationError,
iHelaAPIError,
)
try:
result = client.ping()
except iHelaAuthenticationError:
print("Invalid credentials or connection refused.")
except iHelaAPIError as e:
print(f"Gateway error (HTTP {e.status_code})")
except iHelaError:
print("General client error.")
iHelaAuthenticationError— Raised when authentication fails or the gateway is unreachable.iHelaAPIError— Raised on invalid inputs or gateway errors. The message is intentionally generic to prevent information leakage.
Security
Credential Protection
The SDK never logs client credentials or access tokens. All sensitive fields
(pin_code, access_token, client_secret, password, etc.) in API responses
are automatically masked with ******** before debug logging.
Client objects have a safe __repr__ that only shows the environment flag:
Token Injection
Skip authentication entirely by passing a pre-obtained token — no credentials are stored or transmitted:
client = BankingClient("client_id", "client_secret",
token={"access_token": "...", "token_type": "Bearer"})
Or defer authentication to the first API call:
client = BankingClient("client_id", "client_secret", auto_auth=False)
# Token is acquired only when needed
balance = client.account_balance("76077736")
Token Lifecycle
Clear a token from memory after use:
Request Signing
For Banking and Agent clients, provide a signature_key to HMAC-SHA256 sign
every deposit and withdrawal request:
client = BankingClient("id", "secret", signature_key="your-signing-key")
client.deposit(...)
# Automatically adds X-iHela-Signature header
For incoming webhooks or callbacks, verify signatures received from iHela:
Error Handling Philosophy
The SDK raises a single generic iHelaAPIError for all gateway failures.
The message is intentionally non-specific to prevent bots from mapping your
environment (which accounts exist, which operations are live). Use the
response_code attribute for internal programmatic handling only:
try:
client.deposit(...)
except iHelaAPIError as e:
# Never show response_code to end users
logger.warning(f"Deposit failed with code {e.response_code}")
if e.is_retryable:
await retry_later(...) # Transient failures (5xx, code "07")
return {"error": "Transaction could not be completed"}
Production Guardrails
Circuit Breaker
Prevents cascading failures when the iHela gateway is down. After 5 consecutive failures, all calls are suspended for 30 seconds:
client = BankingClient("id", "secret",
circuit_breaker_threshold=5, # failures before opening
circuit_breaker_cooldown=30.0, # seconds before retry
)
try:
client.ping()
except iHelaCircuitOpenError:
print("Gateway is down — circuit breaker open")
Rate Limiting
Prevent self-DoS with a token bucket per client instance:
client = BankingClient("id", "secret", rate_limit=100) # 100 req/min
try:
client.deposit(...)
except iHelaRateLimitError:
print("Too many requests — slow down")
Idempotency
On deposit and withdrawal operations, if no external_reference is provided,
the SDK auto-generates a UUID to prevent duplicate transactions:
# Automatically gets a UUID external_reference
client.deposit(credit_account="ACT-123", credit_account_holder="John",
amount=500.0, description="Payment", pin_code="1234")
# Or provide your own for idempotency across retries
client.deposit(credit_account="ACT-123", credit_account_holder="John",
amount=500.0, description="Payment", pin_code="1234",
external_reference="my-unique-ref-001")
Async Usage
All clients have async counterparts. Use them in asyncio applications:
from ihela_sdk import AsyncBankingClient
client = AsyncBankingClient("client_id", "client_secret")
balance = await client.account_balance("76077736")
deposit = await client.deposit(
credit_account="ACT-123",
credit_account_holder="John Doe",
amount=500.0,
description="Invoice payment",
external_reference="REF-001",
pin_code="1234",
)
Next Steps
- See the API Reference for all available methods and schemas.
- See Authentication for OAuth2 and SSO setup.