Python SDK
Official Python SDK for Moneybag Payment API
Python SDK
Integrate Moneybag payments into your Python applications with our comprehensive SDK. Works seamlessly with Django, Flask, FastAPI, and all modern Python frameworks.
Installation
Using pip
pip install requests
# SDK package coming soon: moneybag-python
Using poetry
poetry add requests
# SDK package coming soon: moneybag-python
Requirements
- Python 3.7 or higher
- requests library
Quick Start
Initialize the Client
import requests
from typing import Dict, Optional, Any
from dataclasses import dataclass
import hashlib
import hmac
@dataclass
class Customer:
name: str
email: str
phone: str
address: Optional[str] = None
city: Optional[str] = None
postcode: Optional[str] = None
country: Optional[str] = "Bangladesh"
class MoneybagClient:
def __init__(self, api_key: str, environment: str = 'sandbox'):
self.api_key = api_key
self.base_url = (
'https://api.moneybag.com.bd/api/v2'
if environment == 'production'
else 'https://sandbox.api.moneybag.com.bd/api/v2'
)
self.session = requests.Session()
self.session.headers.update({
'X-Merchant-API-Key': self.api_key,
'Content-Type': 'application/json'
})
def create_checkout(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Create a payment checkout session"""
response = self.session.post(
f'{self.base_url}/payments/checkout',
json=data,
timeout=30
)
response.raise_for_status()
return response.json()
def verify_payment(self, transaction_id: str) -> Dict[str, Any]:
"""Verify payment status"""
response = self.session.get(
f'{self.base_url}/payments/verify/{transaction_id}',
timeout=30
)
response.raise_for_status()
return response.json()
def create_refund(self, transaction_id: str, amount: Optional[float] = None) -> Dict[str, Any]:
"""Process a refund"""
data = {'transaction_id': transaction_id}
if amount:
data['refund_amount'] = amount
response = self.session.post(
f'{self.base_url}/payments/refund',
json=data,
timeout=30
)
response.raise_for_status()
return response.json()
# Usage
client = MoneybagClient(api_key='your_api_key', environment='sandbox')
Examples
Create a Payment
from datetime import datetime
import os
# Initialize client
moneybag = MoneybagClient(
api_key=os.getenv('MONEYBAG_API_KEY'),
environment='sandbox'
)
# Create checkout
def create_payment(amount: float, customer: Customer) -> str:
try:
checkout_data = {
'order_id': f'ORDER_{datetime.now().timestamp():.0f}',
'order_amount': amount,
'currency': 'BDT',
'order_description': 'Purchase from Python Store',
'success_url': 'https://yoursite.com/payment/success',
'cancel_url': 'https://yoursite.com/payment/cancel',
'fail_url': 'https://yoursite.com/payment/fail',
'customer': {
'name': customer.name,
'email': customer.email,
'phone': customer.phone,
'address': customer.address,
'city': customer.city,
'postcode': customer.postcode,
'country': customer.country
},
'metadata': {
'source': 'python_sdk',
'version': '1.0.0'
}
}
response = moneybag.create_checkout(checkout_data)
if response['success']:
return response['data']['payment_url']
else:
raise Exception(f"Checkout failed: {response.get('message')}")
except requests.exceptions.RequestException as e:
print(f"API Error: {e}")
raise
# Example usage
customer = Customer(
name="John Doe",
email="john@example.com",
phone="+8801700000000",
address="123 Main St",
city="Dhaka",
postcode="1000"
)
payment_url = create_payment(1000.00, customer)
print(f"Redirect to: {payment_url}")
Verify Payment
def verify_payment_status(transaction_id: str) -> bool:
try:
result = moneybag.verify_payment(transaction_id)
if result['success'] and result['data']['status'] == 'SUCCESS':
print(f"Payment verified: {result['data']['amount']} {result['data']['currency']}")
return True
else:
print(f"Payment not verified: {result['data']['status']}")
return False
except requests.exceptions.HTTPError as e:
if e.response.status_code == 404:
print("Transaction not found")
else:
print(f"Verification failed: {e}")
return False
# Usage
is_verified = verify_payment_status("TXN_123456789")
Framework Integration
Django Integration
# moneybag_integration/views.py
from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
import json
class MoneybagPaymentView:
def __init__(self):
self.client = MoneybagClient(
api_key=settings.MONEYBAG_API_KEY,
environment=settings.MONEYBAG_ENVIRONMENT
)
def create_checkout(self, request):
"""Create payment checkout"""
if request.method == 'POST':
data = json.loads(request.body)
try:
checkout = self.client.create_checkout({
'order_id': f'ORDER_{request.user.id}_{datetime.now().timestamp():.0f}',
'order_amount': data['amount'],
'currency': 'BDT',
'order_description': data.get('description', 'Purchase'),
'success_url': request.build_absolute_uri('/payment/success/'),
'cancel_url': request.build_absolute_uri('/payment/cancel/'),
'fail_url': request.build_absolute_uri('/payment/fail/'),
'customer': {
'name': request.user.get_full_name(),
'email': request.user.email,
'phone': data['phone']
}
})
return JsonResponse({
'success': True,
'payment_url': checkout['data']['payment_url']
})
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
}, status=400)
return JsonResponse({'error': 'Method not allowed'}, status=405)
def payment_success(self, request):
"""Handle successful payment"""
transaction_id = request.GET.get('transaction_id')
if transaction_id:
verification = self.client.verify_payment(transaction_id)
if verification['success']:
# Update order status in database
# Send confirmation email
return render(request, 'payment_success.html', {
'transaction': verification['data']
})
return render(request, 'payment_error.html')
@csrf_exempt
def webhook(self, request):
"""Handle webhook notifications"""
signature = request.headers.get('X-Webhook-Signature')
payload = request.body.decode('utf-8')
# Verify signature
if not self.verify_signature(payload, signature):
return JsonResponse({'error': 'Invalid signature'}, status=401)
event = json.loads(payload)
# Process event
if event['event'] == 'payment.success':
# Handle successful payment
pass
elif event['event'] == 'payment.failed':
# Handle failed payment
pass
return JsonResponse({'status': 'ok'})
def verify_signature(self, payload: str, signature: str) -> bool:
"""Verify webhook signature"""
secret = settings.MONEYBAG_WEBHOOK_SECRET
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return f"sha256={expected}" == signature
# urls.py
from django.urls import path
from .views import MoneybagPaymentView
payment_view = MoneybagPaymentView()
urlpatterns = [
path('checkout/', payment_view.create_checkout, name='checkout'),
path('payment/success/', payment_view.payment_success, name='payment_success'),
path('webhook/moneybag/', payment_view.webhook, name='moneybag_webhook'),
]
# settings.py
MONEYBAG_API_KEY = os.getenv('MONEYBAG_API_KEY')
MONEYBAG_WEBHOOK_SECRET = os.getenv('MONEYBAG_WEBHOOK_SECRET')
MONEYBAG_ENVIRONMENT = 'sandbox' if DEBUG else 'production'
Flask Integration
# app.py
from flask import Flask, request, jsonify, redirect, render_template
import os
app = Flask(__name__)
moneybag = MoneybagClient(
api_key=os.getenv('MONEYBAG_API_KEY'),
environment='sandbox'
)
@app.route('/create-payment', methods=['POST'])
def create_payment():
data = request.json
try:
checkout = moneybag.create_checkout({
'order_id': f'ORDER_{datetime.now().timestamp():.0f}',
'order_amount': data['amount'],
'currency': 'BDT',
'order_description': 'Flask Store Purchase',
'success_url': f"{request.host_url}payment/success",
'cancel_url': f"{request.host_url}payment/cancel",
'fail_url': f"{request.host_url}payment/fail",
'customer': data['customer']
})
return jsonify({
'success': True,
'payment_url': checkout['data']['payment_url']
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 400
@app.route('/payment/success')
def payment_success():
transaction_id = request.args.get('transaction_id')
if transaction_id:
verification = moneybag.verify_payment(transaction_id)
if verification['success']:
return render_template('success.html', transaction=verification['data'])
return render_template('error.html')
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Webhook-Signature')
payload = request.get_data(as_text=True)
# Verify signature
secret = os.getenv('MONEYBAG_WEBHOOK_SECRET')
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
if f"sha256={expected}" != signature:
return jsonify({'error': 'Invalid signature'}), 401
event = request.json
# Process webhook event
if event['event'] == 'payment.success':
# Handle successful payment
print(f"Payment successful: {event['data']['transaction_id']}")
return jsonify({'status': 'ok'}), 200
if __name__ == '__main__':
app.run(debug=True)
FastAPI Integration
# main.py
from fastapi import FastAPI, HTTPException, Request, BackgroundTasks
from fastapi.responses import RedirectResponse
from pydantic import BaseModel
import os
import uvicorn
app = FastAPI()
# Initialize Moneybag client
moneybag = MoneybagClient(
api_key=os.getenv('MONEYBAG_API_KEY'),
environment='sandbox'
)
class PaymentRequest(BaseModel):
amount: float
customer_name: str
customer_email: str
customer_phone: str
description: str = "Purchase"
class WebhookEvent(BaseModel):
event: str
data: dict
@app.post("/api/checkout")
async def create_checkout(payment: PaymentRequest):
"""Create payment checkout"""
try:
checkout = moneybag.create_checkout({
'order_id': f'ORDER_{datetime.now().timestamp():.0f}',
'order_amount': payment.amount,
'currency': 'BDT',
'order_description': payment.description,
'success_url': 'https://yoursite.com/payment/success',
'cancel_url': 'https://yoursite.com/payment/cancel',
'fail_url': 'https://yoursite.com/payment/fail',
'customer': {
'name': payment.customer_name,
'email': payment.customer_email,
'phone': payment.customer_phone
}
})
return {
'success': True,
'payment_url': checkout['data']['payment_url']
}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/payment/verify/{transaction_id}")
async def verify_payment(transaction_id: str):
"""Verify payment status"""
try:
result = moneybag.verify_payment(transaction_id)
return result
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.post("/webhook/moneybag")
async def handle_webhook(
request: Request,
background_tasks: BackgroundTasks
):
"""Handle Moneybag webhooks"""
signature = request.headers.get('x-webhook-signature')
body = await request.body()
# Verify signature
if not verify_webhook_signature(body.decode(), signature):
raise HTTPException(status_code=401, detail="Invalid signature")
event = await request.json()
# Process webhook in background
background_tasks.add_task(process_webhook_event, event)
return {"status": "ok"}
def process_webhook_event(event: dict):
"""Process webhook event asynchronously"""
if event['event'] == 'payment.success':
# Update database
# Send confirmation email
print(f"Payment successful: {event['data']['transaction_id']}")
elif event['event'] == 'refund.completed':
# Handle refund
print(f"Refund completed: {event['data']['refund_id']}")
def verify_webhook_signature(payload: str, signature: str) -> bool:
"""Verify webhook signature"""
secret = os.getenv('MONEYBAG_WEBHOOK_SECRET')
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return f"sha256={expected}" == signature
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Async Support
import asyncio
import aiohttp
from typing import Dict, Any
class AsyncMoneybagClient:
def __init__(self, api_key: str, environment: str = 'sandbox'):
self.api_key = api_key
self.base_url = (
'https://api.moneybag.com.bd/api/v2'
if environment == 'production'
else 'https://sandbox.api.moneybag.com.bd/api/v2'
)
self.headers = {
'X-Merchant-API-Key': self.api_key,
'Content-Type': 'application/json'
}
async def create_checkout(self, data: Dict[str, Any]) -> Dict[str, Any]:
async with aiohttp.ClientSession() as session:
async with session.post(
f'{self.base_url}/payments/checkout',
json=data,
headers=self.headers,
timeout=aiohttp.ClientTimeout(total=30)
) as response:
response.raise_for_status()
return await response.json()
async def verify_payment(self, transaction_id: str) -> Dict[str, Any]:
async with aiohttp.ClientSession() as session:
async with session.get(
f'{self.base_url}/payments/verify/{transaction_id}',
headers=self.headers,
timeout=aiohttp.ClientTimeout(total=30)
) as response:
response.raise_for_status()
return await response.json()
# Usage
async def main():
client = AsyncMoneybagClient(api_key='your_api_key')
# Create multiple checkouts concurrently
tasks = [
client.create_checkout(checkout_data1),
client.create_checkout(checkout_data2),
client.create_checkout(checkout_data3)
]
results = await asyncio.gather(*tasks)
print(f"Created {len(results)} checkouts")
asyncio.run(main())
Error Handling
class MoneybagError(Exception):
"""Base exception for Moneybag SDK"""
pass
class AuthenticationError(MoneybagError):
"""API key or authentication error"""
pass
class ValidationError(MoneybagError):
"""Request validation error"""
pass
class PaymentError(MoneybagError):
"""Payment processing error"""
pass
def handle_api_error(response):
"""Handle API error responses"""
if response.status_code == 401:
raise AuthenticationError("Invalid API key")
elif response.status_code == 422:
errors = response.json().get('errors', {})
raise ValidationError(f"Validation failed: {errors}")
elif response.status_code == 400:
message = response.json().get('message', 'Bad request')
raise PaymentError(message)
elif response.status_code >= 500:
raise MoneybagError("Server error, please try again")
else:
response.raise_for_status()
# Usage with retry
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10)
)
def create_payment_with_retry(client, data):
try:
return client.create_checkout(data)
except MoneybagError as e:
print(f"Payment failed: {e}")
raise
Testing
# test_moneybag.py
import unittest
from unittest.mock import Mock, patch
import os
class TestMoneybagClient(unittest.TestCase):
def setUp(self):
self.client = MoneybagClient(
api_key='test_api_key',
environment='sandbox'
)
@patch('requests.Session.post')
def test_create_checkout(self, mock_post):
# Mock response
mock_response = Mock()
mock_response.json.return_value = {
'success': True,
'data': {
'payment_url': 'https://pay.moneybag.com/abc123',
'transaction_id': 'TXN_123'
}
}
mock_post.return_value = mock_response
# Test checkout creation
result = self.client.create_checkout({
'order_id': 'TEST_001',
'order_amount': 100,
'currency': 'BDT'
})
self.assertTrue(result['success'])
self.assertIn('payment_url', result['data'])
def test_verify_signature(self):
payload = '{"event":"payment.success"}'
secret = 'test_secret'
# Generate valid signature
import hmac
import hashlib
signature = 'sha256=' + hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
# Test signature verification
self.assertTrue(
verify_webhook_signature(payload, signature, secret)
)
if __name__ == '__main__':
unittest.main()
Configuration
# config.py
import os
from dataclasses import dataclass
@dataclass
class MoneybagConfig:
api_key: str
webhook_secret: str
environment: str = 'sandbox'
timeout: int = 30
max_retries: int = 3
@classmethod
def from_env(cls):
return cls(
api_key=os.getenv('MONEYBAG_API_KEY'),
webhook_secret=os.getenv('MONEYBAG_WEBHOOK_SECRET'),
environment=os.getenv('MONEYBAG_ENV', 'sandbox'),
timeout=int(os.getenv('MONEYBAG_TIMEOUT', '30')),
max_retries=int(os.getenv('MONEYBAG_MAX_RETRIES', '3'))
)
# Usage
config = MoneybagConfig.from_env()
client = MoneybagClient(config.api_key, config.environment)
Resources
Support
- Email: support@moneybag.com.bd
- GitHub Issues: Report bugs or request features
- Status Page: status.moneybag.com.bd