📋 Table of Contents
- Overview
- Installation
- Quick Start
- Core Methods
- Digital Services (Missions)
- Bank Integration (CIB)
- Real-time Transaction Streaming
- Response Format
- Security Best Practices
- Support
🌟 Overview
The SofizPay Python SDK provides a complete async interface for integrating DZT digital payments into Python applications, Django backends, and FastAPI services. It offers high-level abstractions for Stellar payments, transaction history, and digital service recharges.
Key Benefits:
- ⚡ Fully Async: Built on
aiohttpfor non-blocking I/O - 🌍 Framework Agnostic: Works perfectly with Django, FastAPI, Flask, or CLI tools
- 📊 Exhaustive History: Captures Path Payments, Trustlines, and account events
- 🏦 CIB/Dahabia: Simple bank deposit link generation
- 📱 Missions: Phone, Internet, and Game top-ups via Mission APIs
📦 Installation
pip
pip install sofizpay-python-sdk
Requirements:
- Python
>= 3.8 - Dependencies:
aiohttp,stellar-sdk
🚀 Quick Start
import asyncio, os
from sofizpay.client import SofizPayClient
async def main():
# Initialize the async client
client = SofizPayClient()
# 1. Check DZT balance
balance = await client.get_balance('YOUR_PUBLIC_KEY')
if balance['success']:
print(f"💰 Balance: {balance['balance']} DZT")
# 2. Send a DZT payment
result = await client.send_payment(
source_secret='YOUR_SECRET_KEY', # 56-char Stellar seed starting with 'S'
destination_public_key='RECIPIENT_PUBLIC_KEY', # Recipient's public key
amount='100', # Amount as string
memo='Invoice #1234' # Optional memo (max 28 chars)
)
if result.get('success'):
print(f"✅ Payment sent! Hash: {result['transactionHash']}")
else:
print(f"❌ Error: {result.get('error')}")
if __name__ == "__main__":
# Run the async main function
asyncio.run(main())
🔧 Core Methods
get_balance(public_key)
Returns the current DZT balance for a given Stellar account.
# Fetch balance for a specific public key
result = await client.get_balance('GCAZI...YOUR_PUBLIC_KEY')
# Response
{
'success': True,
'balance': '1500.0000000',
'publicKey': 'GCAZI...',
'asset_code': 'DZT',
'asset_issuer': 'GCAZI7YBLIDJWIVEL7ETNAZGPP3LC24NO6KAOBWZHUERXQ7M5BC52DLV',
'timestamp': '2025-07-28T10:30:00Z'
}
send_payment(source_secret, destination_public_key, amount, memo?)
Submits a DZT payment to the Stellar network.
result = await client.send_payment(
source_secret='SXXX...YOUR_SECRET', # 56-char Stellar seed starting with 'S'
destination_public_key='GXXX...RECIPIENT', # Recipient's public key
amount='250.50', # Amount in DZT
memo='Order #5567' # Optional memo (max 28 chars)
)
# Success Response
{
'success': True,
'transactionId': 'abc123...hash',
'transactionHash': 'abc123...hash',
'amount': '250.50',
'memo': 'Order #5567',
'destinationPublicKey': 'GXXX...',
'timestamp': '2025-07-28T10:30:00Z'
}
⚠️ Memo Truncation: Memos longer than 28 characters are automatically truncated.
get_transactions(public_key, limit?)
Fetches exhaustive transaction history via Stellar. Includes payments, trustlines, and account creation.
# Fetch up to 100 recent transactions
transactions = await client.get_transactions('YOUR_PUBLIC_KEY', 100)
for tx in transactions:
# Print details for each captured operation
print(f"[{tx['created_at']}] {tx['type']} — {tx['amount']} DZT")
search_transactions_by_memo(public_key, memo, limit?)
Performs a case-insensitive search over recent transactions.
# Perform case-insensitive search over user's recent transactions
results = await client.search_transactions_by_memo('YOUR_PUBLIC_KEY', 'Order #1234', 10)
if results['success']:
print(f"Found {results['totalFound']} matches")
📱 Digital Services (Missions)
Mission APIs allow users to spend DZT on real-world digital services. All calls require encrypted_sk.
Phone Recharge
result = await client.recharge_phone({
'encrypted_sk': 'USER_ENCRYPTED_SECRET_KEY', # User's encrypted secret key
'phone': '0661000000', # Recipient's phone number
'operator': 'mobilis', # 'mobilis' | 'djezzy' | 'ooredoo'
'amount': '100', # Recharge amount
'offer': 'Top' # Offer type (e.g., 'Top', 'Pix')
})
if result['success']:
# Process successful recharge response
print('✅ Phone recharged:', result['data'])
Internet Recharge (Idoom 4G)
result = await client.recharge_internet({
'encrypted_sk': 'USER_ENCRYPTED_SECRET_KEY', # User's encrypted secret key
'phone': '0661000000', # Account phone number
'operator': 'algerie-telecom', # Network provider
'amount': '2000', # Recharge amount
'offer': 'prepaid' # Offer type (e.g., 'prepaid', 'postpaid')
})
Game Top-up (FreeFire, PUBG)
result = await client.recharge_game({
'encrypted_sk': 'USER_ENCRYPTED_SECRET_KEY',
'operator': 'freefire', # e.g., 'freefire', 'pubg'
'playerId': '123456789',
'amount': '500', # 'amount' from getProducts()
'offer': 'diamonds' # 'name' from getProducts()
})
Bill Payment
result = await client.pay_bill({
'encrypted_sk': 'USER_ENCRYPTED_SECRET_KEY',
'operator': 'sonelgaz', # e.g., 'sonelgaz', 'ade'
'bill_id': 'BILL_999',
'amount': '1500'
})
Get Available Products
Fetches the list of available services, operators, and their associated offers (products).
products = await client.get_products()
if products['success']:
# Use products['data'] to populate Missions 'offer' and 'amount'
print('Available services:', products['data'])
Operation History & Details
# Recent operations (paginated)
history = await client.get_operation_history('USER_ENCRYPTED_SK', limit=10, offset=0)
if history['success']:
print('Last 10 operations:', history['data'])
# Details of a specific operation
details = await client.get_operation_details('OPERATION_ID', 'USER_ENCRYPTED_SK')
Get Available Products
# Fetch available charging services and products
products = await client.get_products()
if products.get('success'):
# Iterate through available products to find names and amounts
print('Available services:', products['data'])
🏦 Bank Integration (CIB & EDAHABIA)
Generate secure Dahabia/CIB bank payment links with optional real-time webhook callbacks.
result = await client.make_cib_transaction({
'account': 'YOUR_STELLAR_PUBLIC_KEY', # Your merchant Stellar public key
'amount': 2500, # Amount in DZD (Algerian Dinars)
'full_name': 'Ahmed Benali', # Customer's full name
'phone': '0661234567', # Customer's phone number
'email': 'ahmed@example.com', # Customer's email
'memo': 'Order #789', # Internal order reference
'return_url': 'https://yoursite.com/callback', # Redirect after payment
'webhook_url': 'https://yoursite.com/api/cib-webhook', # Asynchronous Webhook callback
'language': 'ar', # 'ar', 'fr', or 'en'
'redirect': 'no' # 'yes' for auto-redirect
})
if result.get('success'):
# Top-level direct properties
payment_url = result['payment_url']
order_id = result['cib_transaction_id']
print(f"Redirect customer to: {payment_url}")
🧪 Sandbox Environment & Mock Cards
The SofizPay Sandbox simulates the SATIM CIB/EDAHABIA payment system without connecting to real bank accounts.
# Initialize client in Sandbox mode (or pass 'is_sandbox': True)
sandbox_client = SofizPayClient(sandbox=True)
result = await sandbox_client.make_sandbox_cib_transaction({
'account': 'YOUR_PUBLIC_KEY',
'amount': 150.0,
'full_name': 'Sandbox Tester',
'phone': '0555000000',
'email': 'sandbox@example.com',
'webhook_url': 'https://yoursite.com/api/cib-webhook',
'return_url': 'https://yoursite.com/callback'
})
if result.get('success'):
print(f"Sandbox Payment URL: {result['payment_url']}")
cib_id = result['cib_transaction_id']
# Check sandbox order status
status = await sandbox_client.check_sandbox_cib_status(cib_id)
print("Order Status:", status.get('data'))
Sandbox Test Cards
Use the following test credentials on the sandbox payment page:
| Card Number | Expiry | CVV | Cardholder Name | Result | Note |
|---|---|---|---|---|---|
6280581001234567 |
12/28 |
123 |
TEST SUCCESS |
✅ Approved | Use OTP 123456 |
6280581009876543 |
12/29 |
456 |
TEST SUCCESS TWO |
✅ Approved | Use OTP 123456 |
6280581005555555 |
06/28 |
999 |
TEST DECLINED |
❌ Declined by issuer | Tests card refusal |
6280581004444444 |
09/28 |
444 |
TEST INSUFFICIENT |
❌ Insufficient funds | Tests insufficient balance |
6280581003333333 |
03/21 |
333 |
TEST EXPIRED |
❌ Expired card | Tests expired card |
6280581002222222 |
11/28 |
222 |
TEST TIMEOUT |
❌ Timeout | Tests transaction timeout |
🔔 Webhooks & Signature Verification
When a payment succeeds or fails, SofizPay delivers a server-to-server asynchronous HTTP POST event notification (cib.payment.updated) to your registered webhook_url.
Webhook Event Payload
{
"event": "cib.payment.updated",
"payment_status": "success",
"transaction_id": "bb979126-4e55-49dd-8ecc-a1d59867d2b0",
"cib_transaction_id": "5965287001",
"order_number": 5965287001,
"order_status": 2,
"amount": 150000,
"currency": "012",
"resp_code": "00",
"resp_code_desc": "Approved",
"message": "https://yoursite.com/api/cib-webhook/success1500005965287001",
"signature": "MEQCIFz8v982hfkjdsf983274hfkjsdf...=="
}
Verifying Signatures (verify_webhook_signature)
Always verify the RSA SHA-256 signature before processing payments in your database:
from fastapi import FastAPI, Request, HTTPException
from sofizpay.client import SofizPayClient
app = FastAPI()
client = SofizPayClient()
@app.post("/api/cib-webhook")
async def handle_cib_webhook(request: Request):
payload = await request.json()
# 1. Validate official digital signature
if not client.verify_webhook_signature(payload):
raise HTTPException(status_code=400, detail="Invalid digital signature")
# 2. Process order upon confirmation
if payload.get("payment_status") == "success":
order_number = payload.get("order_number") or payload.get("cib_transaction_id")
amount_in_dzd = (payload.get("amount") or 0) / 100
# ✅ Mark order as PAID in your database:
# await db.orders.update({"order_id": order_number}, {"status": "PAID", "amount": amount_in_dzd})
print(f"Payment approved for order {order_number} ({amount_in_dzd} DZD)")
return {"status": "OK"}
🔄 Return URL vs. Webhook Notifications
| Feature | Return URL (return_url) |
Webhook (webhook_url) |
|---|---|---|
| Trigger mechanism | Client browser redirect | Server-to-server direct HTTP POST |
| Reliability | Can fail if user closes browser / network drops | Guaranteed server delivery |
| Security | Susceptible to tampering | Cryptographically signed (RSA-2048) |
| Recommended use | Display "Thank you" screen | Update database, activate subscriptions |
🔴 Real-time Transaction Streaming
The Python SDK uses polling to monitor accounts for new transactions.
setup_transaction_stream(public_key, callback, from_now?, check_interval?)
| Parameter | Type | Default | Description |
|---|---|---|---|
public_key |
str |
required | Stellar account to monitor |
callback |
async function |
required | Called for each new transaction |
from_now |
bool |
True |
True: new txs only; False: load history first |
check_interval |
int |
30 |
Polling interval in seconds |
async def on_tx(tx):
print(f"New {tx['type']}: {tx['amount']} DZT — memo: {tx['memo']}")
stream_id = await client.setup_transaction_stream(
'YOUR_PUBLIC_KEY', on_tx, from_now=True, check_interval=15
)
📤 Response Format
All async methods return a dictionary with a success flag:
# ✅ Success
{
'success': True,
'data': {...}, # method-specific
'timestamp': '2025-07-28T10:30:00Z'
}
# ❌ Failure
{
'success': False,
'error': 'Error message',
'timestamp': '2025-07-28T10:30:00Z'
}
🛡️ Security Best Practices
- ❌ Never expose secret keys in public repositories or client-side code.
- ✅ Use environment variables (
os.getenv('SOFIZPAY_SECRET')). - ✅ Keep
encrypted_sksecure in your backend database. - ✅ Use
encrypted_skfor all Mission API calls (never the raw secret). - ✅ Run payment logic on backend only.
📞 Support
- 🌐 Website: SofizPay.com
- 📚 Full Docs: docs.sofizpay.com
- 🐛 Bug Reports: GitHub Issues
License
MIT © SofizPay Team
Built with ❤️ for Python developers | Version 1.1.0
Metadata
Release files for sofizpay-python-sdk 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| sofizpay_python_sdk-1.1.0.tar.gz | 768.8 kB | Without provenance | Details for sofizpay_python_sdk-1.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| sofizpay_python_sdk-1.1.0-py3-none-any.whl | Python 3 | none | any | Details for sofizpay_python_sdk-1.1.0-py3-none-any.whl |
Total release size: 791.8 kB
Release files / sofizpay_python_sdk-1.1.0.tar.gz
| Download URL | sofizpay_python_sdk-1.1.0.tar.gz |
|---|---|
| Size | 768.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f4018ac71b222549c5a087c2e5e94d2a3750da41b51feaee3981fa6bcca41128
|
|
BLAKE2b-256 checksum How to use checksums |
a11da54f4ef71c0c8af98a1a6d59d1bbd38ad87ad809831085408e24c2476cb7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / sofizpay_python_sdk-1.1.0-py3-none-any.whl
| Download URL | sofizpay_python_sdk-1.1.0-py3-none-any.whl |
|---|---|
| Size | 23.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6409435f7e7c85c6cd033b150e05885f7520710ed99c76eedd94b7d9a61e51cf
|
|
BLAKE2b-256 checksum How to use checksums |
c9ef5356344037d263716c6e3c66b0b28ff45a43d02798f4ce140a979da4520a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for sofizpay-python-sdk 1.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.