Skip to main content

Python SDK for integrating CONXA Wallet payments into AI services

Project description

CONXA Python SDK

Official Python SDK for integrating CONXA Wallet payments into AI services.

Overview

CONXA is a universal wallet for AI services. This SDK enables AI providers (ChatGPT, Claude, etc.) to:

  1. Generate QR codes for users to connect their CONXA wallet
  2. Detect connections when users scan and approve
  3. Charge users for AI usage (pay-per-token)
  4. Check balances and manage sessions

Installation

# Basic installation
pip install conxa-sdk

# With QR code generation support
pip install conxa-sdk[qr]

# From source
pip install .

Quick Start

from conxa import CONXAClient

# Initialize with your API key
client = CONXAClient(
    api_key="pk_live_your_api_key",
    provider_id="your_provider_id",
)

# 1. Generate QR code for user to scan
qr = client.create_payment_qr(
    provider_username="user@example.com",
    expires_in=10  # Optional: QR expires after 10 seconds
)
print(f"Show this QR to user: {qr.qr_base64}")

# 2. Wait for user to connect (or poll manually)
session = client.wait_for_connection(
    provider_username="user@example.com",
    timeout=120,  # Wait up to 2 minutes
    qr_data=qr,  # Pass QR data to check expiration
)
print(f"User connected! Token: {session.session_token}")

# 3. Charge for AI usage
result = client.charge(
    session_token=session.session_token,
    model_name="gpt-4",
    input_tokens=1000,
    output_tokens=500,
)

if result.approved:
    print(f"Charged! New balance: {result.new_balance} tokens")
else:
    print(f"Failed: {result.error}")

Integration Flow

┌─────────────────────────────────────────────────────────────────┐
│                        YOUR AI SERVICE                          │
│                                                                 │
│  1. User visits your website                                    │
│     │                                                           │
│     ▼                                                           │
│  2. Generate QR: client.create_payment_qr(username, expires_in=10) │
│     │                                                           │
│     ▼                                                           │
│  3. Display QR code to user (expires after 10 seconds)         │
│     │                                                           │
│     │    ┌─────────────────────────────────┐                   │
│     │    │      CONXA Mobile App           │                   │
│     └───▶│  User scans QR & approves       │                   │
│          │  Connection established!         │                   │
│          └─────────────────────────────────┘                   │
│     │                                                           │
│     ▼                                                           │
│  4. Detect connection: client.get_session_status(username, qr_data=qr) │
│     │                                                           │
│     ▼                                                           │
│  5. For each AI request:                                        │
│     result = client.charge(session_token, model, tokens)        │
│     │                                                           │
│     ▼                                                           │
│  6. Tokens deducted from user's CONXA wallet                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

API Reference

CONXAClient

Main client class for interacting with CONXA API.

client = CONXAClient(
    api_key="pk_live_xxx",           # Required: Your API key
    provider_id="your_provider_id",   # Required: Your provider ID
    base_url="https://api.conxa.in",  # Optional: API base URL
    timeout=30,                       # Optional: Request timeout
    provider_type="api",              # Optional: "api" or "web"
)

Methods

create_payment_qr(provider_username, limit=None, size=300, expires_in=None)

Generate a QR code for user to scan and connect.

qr = client.create_payment_qr(
    provider_username="user@example.com",
    limit=50000,      # Optional: spending limit in tokens
    size=300,         # Optional: image size in pixels
    expires_in=10,    # Optional: QR expires after N seconds (default: None)
)

# Returns QRCodeData:
# - qr.qr_data: JSON string to encode
# - qr.qr_image: PIL Image object
# - qr.qr_base64: Base64 PNG for HTML <img> tag (validated and guaranteed valid)
# - qr.created_at: Timestamp when QR was created
# - qr.expires_at: Timestamp when QR expires (if expires_in is set)
# - qr.is_expired(): Method to check if QR has expired
# - qr.is_valid_base64(): Method to check if base64 data is valid

QR Code Expiration:

If expires_in is set, the QR code will automatically expire after the specified number of seconds. This is useful for security and ensuring users generate fresh QR codes:

# Generate QR that expires after 10 seconds
qr = client.create_payment_qr(
    provider_username="user@example.com",
    expires_in=10
)

# Check if QR has expired
if qr.is_expired():
    print("QR code expired! Generate a new one.")
    qr = client.create_payment_qr(provider_username="user@example.com", expires_in=10)

# Validate QR code before using in HTML (optional - validation is automatic)
if qr.is_valid_base64():
    html = f'<img src="{qr.qr_base64}" alt="QR Code" />'
else:
    print("Error: Invalid QR code generated")

QR Code Validation:

The SDK automatically validates all QR codes during generation to prevent browser errors. If validation fails, a ValueError is raised with a clear error message:

try:
    qr = client.create_payment_qr(provider_username="user@example.com")
    # QR code is guaranteed to be valid at this point
    # qr.qr_base64 is always a valid data URL
except ValueError as e:
    print(f"QR generation failed: {e}")
    # Handle error appropriately

You can also manually check validation using is_valid_base64():

qr = client.create_payment_qr(provider_username="user@example.com")
if qr.is_valid_base64():
    # Safe to use in HTML
    display_qr(qr.qr_base64)

get_session_status(provider_username, retry_on_rate_limit=True, max_retries=3, qr_data=None)

Check if user has connected their wallet. Automatically handles rate limiting with exponential backoff.

qr = client.create_payment_qr("user@example.com", expires_in=10)
status = client.get_session_status(
    "user@example.com",
    qr_data=qr,  # Optional: Check if QR has expired
    retry_on_rate_limit=True,  # Auto-retry on rate limits
    max_retries=3,  # Max retry attempts
)

# Returns SessionStatus:
# - status.status: "pending", "active", "expired", "not_found"
# - status.session_token: Token for charges (if active)
# - status.expires_at: Session expiration time
# - status.is_active: Boolean helper

# Raises SessionExpiredError if QR code has expired

Rate Limit Handling:

The SDK automatically retries on rate limit errors (429) with exponential backoff. If qr_data is provided and the QR has expired, it will raise SessionExpiredError instead of polling.

wait_for_connection(provider_username, timeout=120, poll_interval=2, on_pending=None, rate_limit_timeout=10, qr_data=None)

Block until user connects or timeout. Automatically handles rate limiting and QR expiration.

qr = client.create_payment_qr("user@example.com", expires_in=10)

session = client.wait_for_connection(
    provider_username="user@example.com",
    timeout=120,              # Maximum wait time in seconds
    poll_interval=2,         # Time between status checks
    on_pending=lambda s: print("Waiting..."),  # Callback on each poll
    rate_limit_timeout=10,   # Close QR after N seconds of rate limiting
    qr_data=qr,             # Optional: Check QR expiration
)

# Raises:
# - ConnectionTimeoutError: If user doesn't connect within timeout
# - SessionExpiredError: If QR expires or rate limited for too long

Features:

  • Automatic retry on rate limit errors with exponential backoff
  • QR expiration checking (if qr_data provided)
  • Rate limit timeout protection (closes QR after extended rate limiting)

charge(session_token, model_name, input_tokens, output_tokens)

Charge user for AI usage.

result = client.charge(
    session_token="ps_xxx",
    model_name="gpt-4",
    input_tokens=1000,
    output_tokens=500,
)

# Returns ChargeResult:
# - result.approved: Boolean
# - result.new_balance: Remaining tokens
# - result.error: Error message (if failed)

get_wallet_balance(wallet_id)

Get wallet token balance (public endpoint).

balance = client.get_wallet_balance("1234567890123456")
# Returns: WalletBalance(wallet_id, tokens)

Exception Handling

from conxa import (
    CONXAError,
    AuthenticationError,
    InsufficientBalanceError,
    SessionExpiredError,
    ConnectionTimeoutError,
    RateLimitError,
)

try:
    # Generate QR with expiration
    qr = client.create_payment_qr("user@example.com", expires_in=10)
    
    # Wait for connection
    session = client.wait_for_connection("user@example.com", qr_data=qr)
    
    # Charge user
    result = client.charge(...)
    
except SessionExpiredError as e:
    print("QR code or session expired - user needs to reconnect")
    # Generate new QR code
    qr = client.create_payment_qr("user@example.com", expires_in=10)
    
except InsufficientBalanceError as e:
    print(f"User has insufficient balance: {e.current_balance} tokens")
    
except RateLimitError as e:
    print(f"Rate limit exceeded. Retry after: {e.retry_after} seconds")
    # SDK automatically retries, but you can handle manually if needed
    
except ConnectionTimeoutError:
    print("User did not connect within timeout period")
    
except AuthenticationError:
    print("Invalid API key")
    
except CONXAError as e:
    print(f"CONXA error: {e}")

Examples

See the examples/ directory for complete integration examples:

  • basic_integration.py - Simple command-line example
  • flask_integration.py - Flask web app with QR display
  • fastapi_integration.py - FastAPI app with WebSocket support

Web Framework Integration

Flask

from flask import Flask, session, jsonify
from conxa import CONXAClient

app = Flask(__name__)
client = CONXAClient(api_key="pk_live_xxx", provider_id="xxx")

@app.route("/connect")
def connect():
    # Generate QR that expires after 10 seconds
    qr = client.create_payment_qr(
        provider_username=session["user_email"],
        expires_in=10
    )
    # Store QR data in session to check expiration
    session["qr_data"] = {
        "created_at": qr.created_at.isoformat() if qr.created_at else None,
        "expires_at": qr.expires_at.isoformat() if qr.expires_at else None,
    }
    return render_template("connect.html", qr_base64=qr.qr_base64)

@app.route("/api/chat", methods=["POST"])
def chat():
    result = client.charge(
        session_token=session["conxa_token"],
        model_name="gpt-4",
        input_tokens=request.json["input_tokens"],
        output_tokens=request.json["output_tokens"],
    )
    if not result.approved:
        return jsonify({"error": "Insufficient balance"}), 402
    # Process AI request...

FastAPI

from fastapi import FastAPI, HTTPException
from conxa import CONXAClient

app = FastAPI()
client = CONXAClient(api_key="pk_live_xxx", provider_id="xxx")

@app.post("/connect")
async def connect(user_id: str):
    # Generate QR that expires after 10 seconds
    qr = client.create_payment_qr(
        provider_username=user_id,
        expires_in=10
    )
    return {
        "qr_base64": qr.qr_base64,
        "expires_at": qr.expires_at.isoformat() if qr.expires_at else None,
    }

@app.post("/chat")
async def chat(user_id: str, message: str, qr_data: dict = None):
    # Check if QR has expired (if provided)
    qr = None
    if qr_data:
        from conxa.models import QRCodeData
        from datetime import datetime
        qr = QRCodeData(
            qr_data="",
            created_at=datetime.fromisoformat(qr_data["created_at"]) if qr_data.get("created_at") else None,
            expires_at=datetime.fromisoformat(qr_data["expires_at"]) if qr_data.get("expires_at") else None,
        )
    
    try:
        status = client.get_session_status(user_id, qr_data=qr)
    except SessionExpiredError:
        raise HTTPException(401, "QR code expired. Please generate a new one.")
    
    if not status.is_active:
        raise HTTPException(401, "User not connected")
    
    result = client.charge(
        session_token=status.session_token,
        model_name="gpt-4",
        input_tokens=len(message) * 4,
        output_tokens=100,
    )
    if not result.approved:
        raise HTTPException(402, "Insufficient balance")
    # Process AI request...

Configuration

Environment Variables

# API Configuration
CONXA_API_KEY=pk_live_your_api_key
CONXA_PROVIDER_ID=your_provider_id
CONXA_API_URL=https://api.conxa.in  # Optional

Using environment variables:

import os
from conxa import CONXAClient

client = CONXAClient(
    api_key=os.getenv("CONXA_API_KEY"),
    provider_id=os.getenv("CONXA_PROVIDER_ID"),
)

Advanced Features

QR Code Expiration

QR codes can be set to expire after a specified duration for security:

# Generate QR that expires after 10 seconds
qr = client.create_payment_qr(
    provider_username="user@example.com",
    expires_in=10
)

# Check expiration status
if qr.is_expired():
    # Generate new QR
    qr = client.create_payment_qr("user@example.com", expires_in=10)

# When checking status, pass QR data to validate expiration
try:
    status = client.get_session_status("user@example.com", qr_data=qr)
except SessionExpiredError:
    print("QR expired - generate a new one")

QR Code Validation

The SDK automatically validates all QR codes during generation to ensure they're valid and can be safely used in HTML. This prevents browser errors like net::ERR_INVALID_URL:

# QR codes are automatically validated during generation
try:
    qr = client.create_payment_qr(provider_username="user@example.com")
    # At this point, qr.qr_base64 is guaranteed to be valid
    # You can safely use it in HTML without additional checks
    html = f'<img src="{qr.qr_base64}" alt="QR Code" />'
except ValueError as e:
    # QR generation failed validation
    print(f"Failed to generate valid QR code: {e}")
    # Handle error appropriately

Manual Validation (Optional):

You can also manually check if a QR code is valid before using it:

qr = client.create_payment_qr(provider_username="user@example.com")

# Check if base64 data is valid (optional - validation happens automatically)
if qr.is_valid_base64():
    # Safe to use in HTML
    display_qr(qr.qr_base64)
else:
    # This should never happen if generation succeeded
    print("Warning: QR code validation failed")

What Gets Validated:

  • Image bytes are not empty
  • Base64 encoding succeeds
  • Data URL format is correct (data:image/png;base64,...)
  • PNG file signature is valid

Rate Limit Handling

The SDK automatically handles rate limiting with exponential backoff:

# Automatic retry on rate limits (default behavior)
status = client.get_session_status(
    "user@example.com",
    retry_on_rate_limit=True,  # Auto-retry (default)
    max_retries=3,              # Max retry attempts
)

# Manual rate limit handling
try:
    status = client.get_session_status("user@example.com", retry_on_rate_limit=False)
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after} seconds")
    time.sleep(e.retry_after or 60)
    status = client.get_session_status("user@example.com")

Best Practices

  1. Always set QR expiration for security:

    qr = client.create_payment_qr(username, expires_in=10)
    
  2. Pass QR data when checking status to validate expiration:

    status = client.get_session_status(username, qr_data=qr)
    
  3. Handle expiration gracefully:

    try:
        session = client.wait_for_connection(username, qr_data=qr)
    except SessionExpiredError:
        # Generate new QR and try again
        qr = client.create_payment_qr(username, expires_in=10)
    
  4. Use rate limit timeout in wait_for_connection:

    session = client.wait_for_connection(
        username,
        rate_limit_timeout=10,  # Close QR after 10s of rate limiting
        qr_data=qr
    )
    
  5. QR codes are automatically validated - no need for manual checks:

    # Validation happens automatically - just use the QR code
    qr = client.create_payment_qr(username)
    html = f'<img src="{qr.qr_base64}" alt="QR Code" />'  # Safe to use
    

Testing

The SDK includes comprehensive validation and testing. You can test QR code generation:

# Run the test suite
python test_qr_generation.py

The test suite validates:

  • QR data JSON generation
  • QR code image generation and base64 encoding
  • SVG generation
  • HTML embedding compatibility

All QR codes generated by the SDK are automatically validated to ensure they work correctly in web browsers.

Support

License

MIT License - see LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

conxa_sdk-1.3.1.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

conxa_sdk-1.3.1-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

File details

Details for the file conxa_sdk-1.3.1.tar.gz.

File metadata

  • Download URL: conxa_sdk-1.3.1.tar.gz
  • Upload date:
  • Size: 21.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for conxa_sdk-1.3.1.tar.gz
Algorithm Hash digest
SHA256 b21e4d4af6d03fde815b130bd1ec487301347b5c73502424faa0bdaf49512774
MD5 e3147ff1222b6b3ee4f6911aba7068b5
BLAKE2b-256 d37847201fbfb1d1907a7fe4c4aabe2e4b28af9be72b62dfd764abbe1c94005d

See more details on using hashes here.

File details

Details for the file conxa_sdk-1.3.1-py3-none-any.whl.

File metadata

  • Download URL: conxa_sdk-1.3.1-py3-none-any.whl
  • Upload date:
  • Size: 18.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for conxa_sdk-1.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2e588f312d5f0aaca4b8f39139023a98353ab042ae5895777222dc8b8c9f51c5
MD5 7efe3c50bae02e9686420fb5052d8b7f
BLAKE2b-256 b782d4eb4e50b1adf390e668d1be55b09bdd20a23ef111b120914a911f01d9a3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page