Skip to main content

Official Python SDK for HunterTechPay mobile money API

Project description

HunterTechPay SDK - Python

Official SDK for integrating HunterTechPay into your Python applications.

Version: 1.1.0 Last Updated: March 14, 2026


Table of Contents


Installation

pip install requests

Copy the huntertechpay.py file into your project.


Quick Start

Initialization

from huntertechpay import HunterTechPay

hunter = HunterTechPay(
    api_key='htp_live_abc123...',      # API Key provided by HunterTechPay
    secret_key='sk_live_xyz789...',    # Secret Key (NEVER expose on client side!)
    base_url='https://api.huntertechpay.com'  # Production URL
)

Available Methods

1. Get Available Providers

providers = hunter.get_providers('CM')

print(providers)
# {
#   'success': True,
#   'country_code': 'CM',
#   'currency': 'XAF',
#   'providers': [
#     {
#       'provider_code': 'orange_money',
#       'name': 'Orange Money Cameroun',
#       'cashin_service_code': 'OM_CM_CASHIN',   # ✅ Code for deposit
#       'cashout_service_code': 'OM_CM_CASHOUT', # ✅ Code for withdrawal
#       'supports_cashin': True,
#       'supports_cashout': True,
#       'logo_url': 'https://...',
#       'is_active': True
#     }
#   ]
# }

2. Deposit (CASHIN) - Mobile Money → Wallet

Transfer money from a mobile money account to the HunterTechPay wallet.

deposit = hunter.deposit(
    amount=5000,                    # Amount in XAF
    currency='XAF',                 # Currency (must match country)
    country='CM',                   # Country code (CM, SN, CI, etc.)
    phone='+237690000000',          # Customer phone number
    provider='orange_money',        # Selected provider
    reference=f'DEPOSIT_{int(time.time())}',  # Your unique reference
    description='Deposit to wallet',   # Description (optional)
    callback_url='https://mysite.com/webhook',  # Webhook (optional)
)

print('Deposit initiated:')
print(f"Transaction ID: {deposit['transaction_id']}")
print(f"Status: {deposit['status']}")  # 'pending', 'success', etc.

Flow:

  1. User initiates a deposit
  2. System automatically uses the CASHIN service code (e.g., OM_CM_CASHIN)
  3. User receives a USSD push to confirm payment
  4. Amount is debited from mobile money and credited to wallet

3. Withdraw (CASHOUT) - Wallet → Mobile Money

Transfer money from the HunterTechPay wallet to a mobile money account.

withdrawal = hunter.withdraw(
    amount=3000,                       # Amount in XAF
    currency='XAF',                    # Currency (must match country)
    country='CM',                      # Country code
    phone='+237670000000',             # Recipient phone number
    provider='mtn_momo',               # Selected provider
    reference=f'WITHDRAW_{int(time.time())}',  # Your unique reference
    description='Withdrawal to mobile money',  # Description (optional)
    callback_url='https://mysite.com/webhook',  # Webhook (optional)
)

print('Withdrawal initiated:')
print(f"Transaction ID: {withdrawal['transaction_id']}")
print(f"Status: {withdrawal['status']}")

Validation:

  • System checks available balance before authorizing withdrawal
  • System verifies CASHOUT is enabled for merchant

4. Initiate Generic Payment

payment = hunter.initiate_payment(
    amount=5000,
    currency='XAF',
    country='CM',
    phone='+237690000000',
    provider='orange_money',
    reference='ORDER_123',
    description='Purchase product XYZ',
    callback_url='https://mysite.com/webhook',
    return_url='https://mysite.com/success'
)

print(f"Payment initiated: {payment['transaction_id']}")

5. Check Transaction Status

# By transaction_id
status = hunter.check_status('txn_abc123')

# By reference
status = hunter.check_status('ORDER_123', search_type='reference')

print(f"Status: {status['status']}")  # 'pending', 'success', 'failed'
print(f"Amount: {status['amount']}")
print(f"Currency: {status['currency']}")

6. List Transactions

transactions = hunter.list_transactions(
    page=1,
    page_size=50,
    status='success',            # Filter by status (optional)
    start_date='2026-03-01',     # Start date (optional)
    end_date='2026-03-14'        # End date (optional)
)

print(f"Total transactions: {transactions['total']}")
for tx in transactions['transactions']:
    print(f"{tx['transaction_id']}: {tx['amount']} {tx['currency']}")

7. Get Balance

balance = hunter.get_balance('merchant_123')

print(f"Available balance: {balance['available_balance']} {balance['currency']}")
print(f"Pending balance: {balance['pending_balance']} {balance['currency']}")

Complete Examples

Django Example

View (views.py)

from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import csrf_exempt
import os
import time
from huntertechpay import HunterTechPay, HunterTechPayError

# ⚠️ IMPORTANT: Initialize on server side with environment variables
hunter = HunterTechPay(
    api_key=os.environ['HUNTER_API_KEY'],
    secret_key=os.environ['HUNTER_SECRET_KEY'],
)

@require_http_methods(["GET"])
def get_providers(request):
    """Get available providers"""
    try:
        country = request.GET.get('country', 'CM')
        providers = hunter.get_providers(country)
        return JsonResponse(providers)
    except HunterTechPayError as e:
        return JsonResponse(
            {'error': str(e)},
            status=e.status_code
        )

@csrf_exempt
@require_http_methods(["POST"])
def initiate_deposit(request):
    """Initiate a deposit (CASHIN)"""
    import json
    try:
        data = json.loads(request.body)

        deposit = hunter.deposit(
            amount=data['amount'],
            currency='XAF',
            country='CM',
            phone=data['phone'],
            provider=data['provider'],
            reference=f"DEPOSIT_{int(time.time())}",
        )

        return JsonResponse(deposit, status=201)

    except HunterTechPayError as e:
        return JsonResponse(
            {'error': str(e)},
            status=e.status_code
        )
    except Exception as e:
        return JsonResponse(
            {'error': 'Internal server error'},
            status=500
        )

@csrf_exempt
@require_http_methods(["POST"])
def initiate_withdrawal(request):
    """Initiate a withdrawal (CASHOUT)"""
    import json
    try:
        data = json.loads(request.body)

        withdrawal = hunter.withdraw(
            amount=data['amount'],
            currency='XAF',
            country='CM',
            phone=data['phone'],
            provider=data['provider'],
            reference=f"WITHDRAW_{int(time.time())}",
        )

        return JsonResponse(withdrawal, status=201)

    except HunterTechPayError as e:
        # Specific error handling
        error_msg = str(e)

        if e.status_code == 400 and 'Insufficient balance' in error_msg:
            return JsonResponse(
                {'error': 'Insufficient balance', 'details': error_msg},
                status=400
            )
        elif e.status_code == 403 and 'frozen' in error_msg:
            return JsonResponse(
                {'error': 'Account frozen', 'details': error_msg},
                status=403
            )
        else:
            return JsonResponse(
                {'error': error_msg},
                status=e.status_code
            )

Flask Example

from flask import Flask, request, jsonify
import os
import time
from huntertechpay import HunterTechPay, HunterTechPayError

app = Flask(__name__)

# ⚠️ IMPORTANT: Use environment variables
hunter = HunterTechPay(
    api_key=os.environ['HUNTER_API_KEY'],
    secret_key=os.environ['HUNTER_SECRET_KEY'],
)

@app.route('/api/providers', methods=['GET'])
def get_providers():
    """Get available providers"""
    try:
        country = request.args.get('country', 'CM')
        providers = hunter.get_providers(country)
        return jsonify(providers)
    except HunterTechPayError as e:
        return jsonify({'error': str(e)}), e.status_code

@app.route('/api/deposit', methods=['POST'])
def initiate_deposit():
    """Initiate a deposit (CASHIN)"""
    try:
        data = request.get_json()

        deposit = hunter.deposit(
            amount=data['amount'],
            currency='XAF',
            country='CM',
            phone=data['phone'],
            provider=data['provider'],
            reference=f"DEPOSIT_{int(time.time())}",
        )

        return jsonify(deposit), 201

    except HunterTechPayError as e:
        return jsonify({'error': str(e)}), e.status_code
    except Exception as e:
        return jsonify({'error': 'Internal server error'}), 500

@app.route('/api/withdraw', methods=['POST'])
def initiate_withdrawal():
    """Initiate a withdrawal (CASHOUT)"""
    try:
        data = request.get_json()

        withdrawal = hunter.withdraw(
            amount=data['amount'],
            currency='XAF',
            country='CM',
            phone=data['phone'],
            provider=data['provider'],
            reference=f"WITHDRAW_{int(time.time())}",
        )

        return jsonify(withdrawal), 201

    except HunterTechPayError as e:
        # Specific error handling
        error_msg = str(e)

        if e.status_code == 400 and 'Insufficient balance' in error_msg:
            return jsonify({
                'error': 'Insufficient balance',
                'details': error_msg
            }), 400
        elif e.status_code == 403 and 'frozen' in error_msg:
            return jsonify({
                'error': 'Account frozen',
                'details': error_msg
            }), 403
        else:
            return jsonify({'error': error_msg}), e.status_code

if __name__ == '__main__':
    app.run(debug=True)

FastAPI Example

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import os
import time
from huntertechpay import HunterTechPay, HunterTechPayError

app = FastAPI()

# ⚠️ IMPORTANT: Use environment variables
hunter = HunterTechPay(
    api_key=os.environ['HUNTER_API_KEY'],
    secret_key=os.environ['HUNTER_SECRET_KEY'],
)

class DepositRequest(BaseModel):
    amount: float
    phone: str
    provider: str

class WithdrawRequest(BaseModel):
    amount: float
    phone: str
    provider: str

@app.get("/api/providers")
async def get_providers(country: str = 'CM'):
    """Get available providers"""
    try:
        providers = hunter.get_providers(country)
        return providers
    except HunterTechPayError as e:
        raise HTTPException(status_code=e.status_code, detail=str(e))

@app.post("/api/deposit")
async def initiate_deposit(request: DepositRequest):
    """Initiate a deposit (CASHIN)"""
    try:
        deposit = hunter.deposit(
            amount=request.amount,
            currency='XAF',
            country='CM',
            phone=request.phone,
            provider=request.provider,
            reference=f"DEPOSIT_{int(time.time())}",
        )
        return deposit
    except HunterTechPayError as e:
        raise HTTPException(status_code=e.status_code, detail=str(e))

@app.post("/api/withdraw")
async def initiate_withdrawal(request: WithdrawRequest):
    """Initiate a withdrawal (CASHOUT)"""
    try:
        withdrawal = hunter.withdraw(
            amount=request.amount,
            currency='XAF',
            country='CM',
            phone=request.phone,
            provider=request.provider,
            reference=f"WITHDRAW_{int(time.time())}",
        )
        return withdrawal
    except HunterTechPayError as e:
        # Specific error handling
        error_msg = str(e)

        if e.status_code == 400 and 'Insufficient balance' in error_msg:
            raise HTTPException(
                status_code=400,
                detail={'error': 'Insufficient balance', 'details': error_msg}
            )
        elif e.status_code == 403 and 'frozen' in error_msg:
            raise HTTPException(
                status_code=403,
                detail={'error': 'Account frozen', 'details': error_msg}
            )
        else:
            raise HTTPException(status_code=e.status_code, detail=error_msg)

Security - VERY IMPORTANT

❌ NEVER DO THIS

# ❌ DANGER: Secret key in source code
hunter = HunterTechPay(
    api_key='htp_live_...',
    secret_key='sk_live_...'  # ❌ EXPOSED in code!
)

✅ CORRECT APPROACH

Using .env file

.env (NEVER commit to Git):

HUNTER_API_KEY=htp_live_abc123...
HUNTER_SECRET_KEY=sk_live_xyz789...

.gitignore:

.env
*.pyc
__pycache__/

Code:

import os
from dotenv import load_dotenv
from huntertechpay import HunterTechPay

# Load environment variables
load_dotenv()

# ✅ SECURE
hunter = HunterTechPay(
    api_key=os.environ['HUNTER_API_KEY'],
    secret_key=os.environ['HUNTER_SECRET_KEY'],
)

Django settings.py

import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

# ✅ Environment variables
HUNTER_API_KEY = os.environ.get('HUNTER_API_KEY')
HUNTER_SECRET_KEY = os.environ.get('HUNTER_SECRET_KEY')

Error Handling

from huntertechpay import HunterTechPay, HunterTechPayError

hunter = HunterTechPay(
    api_key=os.environ['HUNTER_API_KEY'],
    secret_key=os.environ['HUNTER_SECRET_KEY'],
)

try:
    deposit = hunter.deposit(
        amount=5000,
        currency='XAF',
        country='CM',
        phone='+237690000000',
        provider='orange_money',
        reference='DEP_001'
    )

except HunterTechPayError as e:
    print(f"Error code: {e.status_code}")
    print(f"Message: {e.message}")
    print(f"Data: {e.data}")

    # Specific error handling by code
    if e.status_code == 400:
        if 'Insufficient balance' in str(e):
            print('Insufficient balance')
        elif 'Invalid currency' in str(e):
            print('Invalid currency for this country')
        else:
            print(f'Invalid parameters: {e}')

    elif e.status_code == 403:
        if 'frozen' in str(e):
            print('Wallet frozen')
        elif 'CASHOUT is disabled' in str(e):
            print('Withdrawal disabled for your account')
        else:
            print(f'Access denied: {e}')

    elif e.status_code == 404:
        print('Provider or wallet not found')

    else:
        print(f'Error: {e}')

except Exception as e:
    print(f'Network error: {e}')

Currency/Country Validation

The SDK automatically validates that the currency matches the country:

Country Accepted Currency Rejected Currency
CM (Cameroon) ✅ XAF ❌ XOF
SN (Senegal) ✅ XOF ❌ XAF

Error Example:

# Attempt: XOF in Cameroon
deposit = hunter.deposit(
    currency='XOF',  # ❌ Error
    country='CM'
)

# Error returned:
# "Invalid currency for country CM. Expected XAF, got XOF"

License

MIT


Support

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

huntertechpay-1.0.0.tar.gz (26.9 kB view details)

Uploaded Source

Built Distribution

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

huntertechpay-1.0.0-py3-none-any.whl (25.3 kB view details)

Uploaded Python 3

File details

Details for the file huntertechpay-1.0.0.tar.gz.

File metadata

  • Download URL: huntertechpay-1.0.0.tar.gz
  • Upload date:
  • Size: 26.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for huntertechpay-1.0.0.tar.gz
Algorithm Hash digest
SHA256 bc5f0c51551f82be9a70e98e97422a2a77367dabb80af009c4d062c49226b182
MD5 a4e5cc258201c1bc542fd6508e955a84
BLAKE2b-256 a8ab3f9bdd1c42a00375b6bd5856f09861ffce6d0f5627e1345fb914fa12c4ce

See more details on using hashes here.

File details

Details for the file huntertechpay-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: huntertechpay-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 25.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for huntertechpay-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7bf62e944139ca1bba5bfd0fd9577ea48cc74a73712a437c3734af28aa2d4f61
MD5 4e9d9ee82a25ceb832354ba2d3f39015
BLAKE2b-256 f45b74eed875e300a3b75a15e34e63ccd68459d1865ece3197a4403d98d29286

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