Skip to main content
SofizPay Logo

SofizPay Python SDK

The official Python SDK for secure digital payments on the SofizPay platform.

PyPI version License: MIT Python


📋 Table of Contents


🌟 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 aiohttp for 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_sk secure in your backend database.
  • ✅ Use encrypted_sk for all Mission API calls (never the raw secret).
  • ✅ Run payment logic on backend only.

📞 Support


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)

Source distribution for sofizpay-python-sdk 1.1.0
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)

Table of built distributions (wheels) for sofizpay-python-sdk 1.1.0
File Interpreter ABI Platform Actions
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.

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page